mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-03 15:55:38 +00:00
Merge 'bpf-next 2023-10-16' into loongarch-next
LoongArch architecture changes for 6.7 (BPF CPU v4 support) depend on the bpf changes to fix conflictions in selftests and work, so merge them to create a base.
This commit is contained in:
commit
a6bdc082ad
558 changed files with 44939 additions and 4929 deletions
|
@ -71,6 +71,7 @@ two flavors of JITs, the newer eBPF JIT currently supported on:
|
|||
- s390x
|
||||
- riscv64
|
||||
- riscv32
|
||||
- loongarch64
|
||||
|
||||
And the older cBPF JIT supported on the following archs:
|
||||
|
||||
|
|
|
@ -56,6 +56,16 @@ described in more detail in the footnotes.
|
|||
| | ``BPF_CGROUP_UDP6_RECVMSG`` | ``cgroup/recvmsg6`` | |
|
||||
+ +----------------------------------------+----------------------------------+-----------+
|
||||
| | ``BPF_CGROUP_UDP6_SENDMSG`` | ``cgroup/sendmsg6`` | |
|
||||
| +----------------------------------------+----------------------------------+-----------+
|
||||
| | ``BPF_CGROUP_UNIX_CONNECT`` | ``cgroup/connect_unix`` | |
|
||||
| +----------------------------------------+----------------------------------+-----------+
|
||||
| | ``BPF_CGROUP_UNIX_SENDMSG`` | ``cgroup/sendmsg_unix`` | |
|
||||
| +----------------------------------------+----------------------------------+-----------+
|
||||
| | ``BPF_CGROUP_UNIX_RECVMSG`` | ``cgroup/recvmsg_unix`` | |
|
||||
| +----------------------------------------+----------------------------------+-----------+
|
||||
| | ``BPF_CGROUP_UNIX_GETPEERNAME`` | ``cgroup/getpeername_unix`` | |
|
||||
| +----------------------------------------+----------------------------------+-----------+
|
||||
| | ``BPF_CGROUP_UNIX_GETSOCKNAME`` | ``cgroup/getsockname_unix`` | |
|
||||
+-------------------------------------------+----------------------------------------+----------------------------------+-----------+
|
||||
| ``BPF_PROG_TYPE_CGROUP_SOCK`` | ``BPF_CGROUP_INET4_POST_BIND`` | ``cgroup/post_bind4`` | |
|
||||
+ +----------------------------------------+----------------------------------+-----------+
|
||||
|
|
|
@ -113,7 +113,7 @@ Flags
|
|||
used by ``eth_get_headlen`` to estimate length of all headers for GRO.
|
||||
* ``BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL`` - tells BPF flow dissector to
|
||||
stop parsing as soon as it reaches IPv6 flow label; used by
|
||||
``___skb_get_hash`` and ``__skb_get_hash_symmetric`` to get flow hash.
|
||||
``___skb_get_hash`` to get flow hash.
|
||||
* ``BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP`` - tells BPF flow dissector to stop
|
||||
parsing as soon as it reaches encapsulated headers; used by routing
|
||||
infrastructure.
|
||||
|
|
|
@ -22,6 +22,7 @@ properties:
|
|||
- mediatek,mt7622-wed
|
||||
- mediatek,mt7981-wed
|
||||
- mediatek,mt7986-wed
|
||||
- mediatek,mt7988-wed
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
|
|
|
@ -49,6 +49,8 @@ properties:
|
|||
- hisilicon,peri-subctrl
|
||||
- hpe,gxp-sysreg
|
||||
- intel,lgm-syscon
|
||||
- loongson,ls1b-syscon
|
||||
- loongson,ls1c-syscon
|
||||
- marvell,armada-3700-usb2-host-misc
|
||||
- mediatek,mt8135-pctl-a-syscfg
|
||||
- mediatek,mt8135-pctl-b-syscfg
|
||||
|
|
|
@ -49,6 +49,26 @@ properties:
|
|||
Set if the output SYNCLKO clock should be disabled. Do not mix with
|
||||
microchip,synclko-125.
|
||||
|
||||
microchip,io-drive-strength-microamp:
|
||||
description:
|
||||
IO Pad Drive Strength
|
||||
enum: [8000, 16000]
|
||||
default: 16000
|
||||
|
||||
microchip,hi-drive-strength-microamp:
|
||||
description:
|
||||
High Speed Drive Strength. Controls drive strength of GMII / RGMII /
|
||||
MII / RMII (except TX_CLK/REFCLKI, COL and CRS) and CLKO_25_125 lines.
|
||||
enum: [2000, 4000, 8000, 12000, 16000, 20000, 24000, 28000]
|
||||
default: 24000
|
||||
|
||||
microchip,lo-drive-strength-microamp:
|
||||
description:
|
||||
Low Speed Drive Strength. Controls drive strength of TX_CLK / REFCLKI,
|
||||
COL, CRS, LEDs, PME_N, NTRP_N, SDO and SDI/SDA/MDIO lines.
|
||||
enum: [2000, 4000, 8000, 12000, 16000, 20000, 24000, 28000]
|
||||
default: 8000
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
114
Documentation/devicetree/bindings/net/loongson,ls1b-gmac.yaml
Normal file
114
Documentation/devicetree/bindings/net/loongson,ls1b-gmac.yaml
Normal file
|
@ -0,0 +1,114 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/loongson,ls1b-gmac.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Loongson-1B Gigabit Ethernet MAC Controller
|
||||
|
||||
maintainers:
|
||||
- Keguang Zhang <keguang.zhang@gmail.com>
|
||||
|
||||
description: |
|
||||
Loongson-1B Gigabit Ethernet MAC Controller is based on
|
||||
Synopsys DesignWare MAC (version 3.50a).
|
||||
|
||||
Main features
|
||||
- Dual 10/100/1000Mbps GMAC controllers
|
||||
- Full-duplex operation (IEEE 802.3x flow control automatic transmission)
|
||||
- Half-duplex operation (CSMA/CD Protocol and back-pressure support)
|
||||
- RX Checksum Offload
|
||||
- TX Checksum insertion
|
||||
- MII interface
|
||||
- RGMII interface
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- loongson,ls1b-gmac
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- loongson,ls1b-gmac
|
||||
- const: snps,dwmac-3.50a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: stmmaceth
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: macirq
|
||||
|
||||
loongson,ls1-syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the syscon containing some extra configurations
|
||||
including PHY interface mode.
|
||||
|
||||
phy-mode:
|
||||
enum:
|
||||
- mii
|
||||
- rgmii-id
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- loongson,ls1-syscon
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwmac.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/loongson,ls1x-clk.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
gmac0: ethernet@1fe10000 {
|
||||
compatible = "loongson,ls1b-gmac", "snps,dwmac-3.50a";
|
||||
reg = <0x1fe10000 0x10000>;
|
||||
|
||||
clocks = <&clkc LS1X_CLKID_AHB>;
|
||||
clock-names = "stmmaceth";
|
||||
|
||||
interrupt-parent = <&intc1>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "macirq";
|
||||
|
||||
loongson,ls1-syscon = <&syscon>;
|
||||
|
||||
phy-handle = <&phy0>;
|
||||
phy-mode = "mii";
|
||||
snps,pbl = <1>;
|
||||
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "snps,dwmac-mdio";
|
||||
|
||||
phy0: ethernet-phy@0 {
|
||||
reg = <0x0>;
|
||||
};
|
||||
};
|
||||
};
|
113
Documentation/devicetree/bindings/net/loongson,ls1c-emac.yaml
Normal file
113
Documentation/devicetree/bindings/net/loongson,ls1c-emac.yaml
Normal file
|
@ -0,0 +1,113 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/loongson,ls1c-emac.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Loongson-1C Ethernet MAC Controller
|
||||
|
||||
maintainers:
|
||||
- Keguang Zhang <keguang.zhang@gmail.com>
|
||||
|
||||
description: |
|
||||
Loongson-1C Ethernet MAC Controller is based on
|
||||
Synopsys DesignWare MAC (version 3.50a).
|
||||
|
||||
Main features
|
||||
- 10/100Mbps
|
||||
- Full-duplex operation (IEEE 802.3x flow control automatic transmission)
|
||||
- Half-duplex operation (CSMA/CD Protocol and back-pressure support)
|
||||
- IEEE 802.1Q VLAN tag detection for reception frames
|
||||
- MII interface
|
||||
- RMII interface
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- loongson,ls1c-emac
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- loongson,ls1c-emac
|
||||
- const: snps,dwmac-3.50a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: stmmaceth
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: macirq
|
||||
|
||||
loongson,ls1-syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the syscon containing some extra configurations
|
||||
including PHY interface mode.
|
||||
|
||||
phy-mode:
|
||||
enum:
|
||||
- mii
|
||||
- rmii
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- loongson,ls1-syscon
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwmac.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/loongson,ls1x-clk.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
emac: ethernet@1fe10000 {
|
||||
compatible = "loongson,ls1c-emac", "snps,dwmac-3.50a";
|
||||
reg = <0x1fe10000 0x10000>;
|
||||
|
||||
clocks = <&clkc LS1X_CLKID_AHB>;
|
||||
clock-names = "stmmaceth";
|
||||
|
||||
interrupt-parent = <&intc1>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "macirq";
|
||||
|
||||
loongson,ls1-syscon = <&syscon>;
|
||||
|
||||
phy-handle = <&phy0>;
|
||||
phy-mode = "mii";
|
||||
snps,pbl = <1>;
|
||||
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "snps,dwmac-mdio";
|
||||
|
||||
phy0: ethernet-phy@13 {
|
||||
reg = <0x13>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -394,6 +394,11 @@ properties:
|
|||
When a PFC frame is received with priorities matching the bitmask,
|
||||
the queue is blocked from transmitting for the pause time specified
|
||||
in the PFC frame.
|
||||
|
||||
snps,coe-unsupported:
|
||||
type: boolean
|
||||
description: TX checksum offload is unsupported by the TX queue.
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
required:
|
||||
|
|
|
@ -19,6 +19,7 @@ allOf:
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,am642-icssg-prueth # for AM64x SoC family
|
||||
- ti,am654-icssg-prueth # for AM65x SoC family
|
||||
|
||||
sram:
|
||||
|
@ -106,6 +107,13 @@ properties:
|
|||
phandle to system controller node and register offset
|
||||
to ICSSG control register for RGMII transmit delay
|
||||
|
||||
ti,half-duplex-capable:
|
||||
type: boolean
|
||||
description:
|
||||
Indicates that the PHY output pin COL is routed to ICSSG GPIO pin
|
||||
(PRGx_PRU0/1_GPIO10) as input so that the ICSSG MII port is
|
||||
capable of half duplex operations.
|
||||
|
||||
required:
|
||||
- reg
|
||||
anyOf:
|
||||
|
|
|
@ -20,6 +20,7 @@ properties:
|
|||
items:
|
||||
- enum:
|
||||
- mediatek,mt7986-wo-ccif
|
||||
- mediatek,mt7988-wo-ccif
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
|
|
497
Documentation/driver-api/dpll.rst
Normal file
497
Documentation/driver-api/dpll.rst
Normal file
|
@ -0,0 +1,497 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===============================
|
||||
The Linux kernel dpll subsystem
|
||||
===============================
|
||||
|
||||
DPLL
|
||||
====
|
||||
|
||||
PLL - Phase Locked Loop is an electronic circuit which syntonizes clock
|
||||
signal of a device with an external clock signal. Effectively enabling
|
||||
device to run on the same clock signal beat as provided on a PLL input.
|
||||
|
||||
DPLL - Digital Phase Locked Loop is an integrated circuit which in
|
||||
addition to plain PLL behavior incorporates a digital phase detector
|
||||
and may have digital divider in the loop. As a result, the frequency on
|
||||
DPLL's input and output may be configurable.
|
||||
|
||||
Subsystem
|
||||
=========
|
||||
|
||||
The main purpose of dpll subsystem is to provide general interface
|
||||
to configure devices that use any kind of Digital PLL and could use
|
||||
different sources of input signal to synchronize to, as well as
|
||||
different types of outputs.
|
||||
The main interface is NETLINK_GENERIC based protocol with an event
|
||||
monitoring multicast group defined.
|
||||
|
||||
Device object
|
||||
=============
|
||||
|
||||
Single dpll device object means single Digital PLL circuit and bunch of
|
||||
connected pins.
|
||||
It reports the supported modes of operation and current status to the
|
||||
user in response to the `do` request of netlink command
|
||||
``DPLL_CMD_DEVICE_GET`` and list of dplls registered in the subsystem
|
||||
with `dump` netlink request of the same command.
|
||||
Changing the configuration of dpll device is done with `do` request of
|
||||
netlink ``DPLL_CMD_DEVICE_SET`` command.
|
||||
A device handle is ``DPLL_A_ID``, it shall be provided to get or set
|
||||
configuration of particular device in the system. It can be obtained
|
||||
with a ``DPLL_CMD_DEVICE_GET`` `dump` request or
|
||||
a ``DPLL_CMD_DEVICE_ID_GET`` `do` request, where the one must provide
|
||||
attributes that result in single device match.
|
||||
|
||||
Pin object
|
||||
==========
|
||||
|
||||
A pin is amorphic object which represents either input or output, it
|
||||
could be internal component of the device, as well as externally
|
||||
connected.
|
||||
The number of pins per dpll vary, but usually multiple pins shall be
|
||||
provided for a single dpll device.
|
||||
Pin's properties, capabilities and status is provided to the user in
|
||||
response to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
|
||||
It is also possible to list all the pins that were registered in the
|
||||
system with `dump` request of ``DPLL_CMD_PIN_GET`` command.
|
||||
Configuration of a pin can be changed by `do` request of netlink
|
||||
``DPLL_CMD_PIN_SET`` command.
|
||||
Pin handle is a ``DPLL_A_PIN_ID``, it shall be provided to get or set
|
||||
configuration of particular pin in the system. It can be obtained with
|
||||
``DPLL_CMD_PIN_GET`` `dump` request or ``DPLL_CMD_PIN_ID_GET`` `do`
|
||||
request, where user provides attributes that result in single pin match.
|
||||
|
||||
Pin selection
|
||||
=============
|
||||
|
||||
In general, selected pin (the one which signal is driving the dpll
|
||||
device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only
|
||||
one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll
|
||||
device.
|
||||
|
||||
Pin selection can be done either manually or automatically, depending
|
||||
on hardware capabilities and active dpll device work mode
|
||||
(``DPLL_A_MODE`` attribute). The consequence is that there are
|
||||
differences for each mode in terms of available pin states, as well as
|
||||
for the states the user can request for a dpll device.
|
||||
|
||||
In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
|
||||
one of following pin states:
|
||||
|
||||
- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
|
||||
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
|
||||
device
|
||||
|
||||
In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
|
||||
receive one of following pin states:
|
||||
|
||||
- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
|
||||
input for automatic selection algorithm
|
||||
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
|
||||
a valid input for automatic selection algorithm
|
||||
|
||||
In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
|
||||
pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
|
||||
algorithm locks a dpll device with one of the inputs.
|
||||
|
||||
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``),
|
||||
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``).
|
||||
|
||||
MUX-type pins
|
||||
=============
|
||||
|
||||
A pin can be MUX-type, it aggregates child pins and serves as a pin
|
||||
multiplexer. One or more pins are registered with MUX-type instead of
|
||||
being directly registered to a dpll device.
|
||||
Pins registered with a MUX-type pin provide user with additional nested
|
||||
attribute ``DPLL_A_PIN_PARENT_PIN`` for each parent they were registered
|
||||
with.
|
||||
If a pin was registered with multiple parent pins, they behave like a
|
||||
multiple output multiplexer. In this case output of a
|
||||
``DPLL_CMD_PIN_GET`` would contain multiple pin-parent nested
|
||||
attributes with current state related to each parent, like:
|
||||
|
||||
'pin': [{{
|
||||
'clock-id': 282574471561216,
|
||||
'module-name': 'ice',
|
||||
'capabilities': 4,
|
||||
'id': 13,
|
||||
'parent-pin': [
|
||||
{'parent-id': 2, 'state': 'connected'},
|
||||
{'parent-id': 3, 'state': 'disconnected'}
|
||||
],
|
||||
'type': 'synce-eth-port'
|
||||
}}]
|
||||
|
||||
Only one child pin can provide its signal to the parent MUX-type pin at
|
||||
a time, the selection is done by requesting change of a child pin state
|
||||
on desired parent, with the use of ``DPLL_A_PIN_PARENT`` nested
|
||||
attribute. Example of netlink `set state on parent pin` message format:
|
||||
|
||||
========================== =============================================
|
||||
``DPLL_A_PIN_ID`` child pin id
|
||||
``DPLL_A_PIN_PARENT_PIN`` nested attribute for requesting configuration
|
||||
related to parent pin
|
||||
``DPLL_A_PIN_PARENT_ID`` parent pin id
|
||||
``DPLL_A_PIN_STATE`` requested pin state on parent
|
||||
========================== =============================================
|
||||
|
||||
Pin priority
|
||||
============
|
||||
|
||||
Some devices might offer a capability of automatic pin selection mode
|
||||
(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
|
||||
Usually, automatic selection is performed on the hardware level, which
|
||||
means only pins directly connected to the dpll can be used for automatic
|
||||
input pin selection.
|
||||
In automatic selection mode, the user cannot manually select a input
|
||||
pin for the device, instead the user shall provide all directly
|
||||
connected pins with a priority ``DPLL_A_PIN_PRIO``, the device would
|
||||
pick a highest priority valid signal and use it to control the DPLL
|
||||
device. Example of netlink `set priority on parent pin` message format:
|
||||
|
||||
============================ =============================================
|
||||
``DPLL_A_PIN_ID`` configured pin id
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting configuration
|
||||
related to parent dpll device
|
||||
``DPLL_A_PIN_PARENT_ID`` parent dpll device id
|
||||
``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
|
||||
============================ =============================================
|
||||
|
||||
Child pin of MUX-type pin is not capable of automatic input pin selection,
|
||||
in order to configure active input of a MUX-type pin, the user needs to
|
||||
request desired pin state of the child pin on the parent pin,
|
||||
as described in the ``MUX-type pins`` chapter.
|
||||
|
||||
Configuration commands group
|
||||
============================
|
||||
|
||||
Configuration commands are used to get information about registered
|
||||
dpll devices (and pins), as well as set configuration of device or pins.
|
||||
As dpll devices must be abstracted and reflect real hardware,
|
||||
there is no way to add new dpll device via netlink from user space and
|
||||
each device should be registered by its driver.
|
||||
|
||||
All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
|
||||
any spamming/DoS from unauthorized userspace applications.
|
||||
|
||||
List of netlink commands with possible attributes
|
||||
=================================================
|
||||
|
||||
Constants identifying command types for dpll device uses a
|
||||
``DPLL_CMD_`` prefix and suffix according to command purpose.
|
||||
The dpll device related attributes use a ``DPLL_A_`` prefix and
|
||||
suffix according to attribute purpose.
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_DEVICE_ID_GET`` command to get device ID
|
||||
``DPLL_A_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_TYPE`` attr type of dpll device
|
||||
==================================== =================================
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_DEVICE_GET`` command to get device info or
|
||||
dump list of available devices
|
||||
``DPLL_A_ID`` attr unique dpll device ID
|
||||
``DPLL_A_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_MODE`` attr selection mode
|
||||
``DPLL_A_MODE_SUPPORTED`` attr available selection modes
|
||||
``DPLL_A_LOCK_STATUS`` attr dpll device lock status
|
||||
``DPLL_A_TEMP`` attr device temperature info
|
||||
``DPLL_A_TYPE`` attr type of dpll device
|
||||
==================================== =================================
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_DEVICE_SET`` command to set dpll device config
|
||||
``DPLL_A_ID`` attr internal dpll device index
|
||||
``DPLL_A_MODE`` attr selection mode to configure
|
||||
==================================== =================================
|
||||
|
||||
Constants identifying command types for pins uses a
|
||||
``DPLL_CMD_PIN_`` prefix and suffix according to command purpose.
|
||||
The pin related attributes use a ``DPLL_A_PIN_`` prefix and suffix
|
||||
according to attribute purpose.
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_PIN_ID_GET`` command to get pin ID
|
||||
``DPLL_A_PIN_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_PIN_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_PIN_BOARD_LABEL`` attr pin board label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PANEL_LABEL`` attr pin panel label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PACKAGE_LABEL`` attr pin package label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_TYPE`` attr type of a pin
|
||||
==================================== =================================
|
||||
|
||||
==================================== ==================================
|
||||
``DPLL_CMD_PIN_GET`` command to get pin info or dump
|
||||
list of available pins
|
||||
``DPLL_A_PIN_ID`` attr unique a pin ID
|
||||
``DPLL_A_PIN_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_PIN_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_PIN_BOARD_LABEL`` attr pin board label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PANEL_LABEL`` attr pin panel label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PACKAGE_LABEL`` attr pin package label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_TYPE`` attr type of a pin
|
||||
``DPLL_A_PIN_FREQUENCY`` attr current frequency of a pin
|
||||
``DPLL_A_PIN_FREQUENCY_SUPPORTED`` nested attr provides supported
|
||||
frequencies
|
||||
``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
|
||||
``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` nested attr for each parent device
|
||||
the pin is connected with
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent dpll device id
|
||||
``DPLL_A_PIN_PRIO`` attr priority of pin on the
|
||||
dpll device
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
dpll device
|
||||
``DPLL_A_PIN_DIRECTION`` attr direction of a pin on the
|
||||
parent dpll device
|
||||
``DPLL_A_PIN_PARENT_PIN`` nested attr for each parent pin
|
||||
the pin is connected with
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent pin id
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
pin
|
||||
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
|
||||
==================================== ==================================
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_PIN_SET`` command to set pins configuration
|
||||
``DPLL_A_PIN_ID`` attr unique a pin ID
|
||||
``DPLL_A_PIN_FREQUENCY`` attr requested frequency of a pin
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` nested attr for each parent dpll
|
||||
device configuration request
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent dpll device id
|
||||
``DPLL_A_PIN_DIRECTION`` attr requested direction of a pin
|
||||
``DPLL_A_PIN_PRIO`` attr requested priority of pin on
|
||||
the dpll device
|
||||
``DPLL_A_PIN_STATE`` attr requested state of pin on
|
||||
the dpll device
|
||||
``DPLL_A_PIN_PARENT_PIN`` nested attr for each parent pin
|
||||
configuration request
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent pin id
|
||||
``DPLL_A_PIN_STATE`` attr requested state of pin on
|
||||
parent pin
|
||||
==================================== =================================
|
||||
|
||||
Netlink dump requests
|
||||
=====================
|
||||
|
||||
The ``DPLL_CMD_DEVICE_GET`` and ``DPLL_CMD_PIN_GET`` commands are
|
||||
capable of dump type netlink requests, in which case the response is in
|
||||
the same format as for their ``do`` request, but every device or pin
|
||||
registered in the system is returned.
|
||||
|
||||
SET commands format
|
||||
===================
|
||||
|
||||
``DPLL_CMD_DEVICE_SET`` - to target a dpll device, the user provides
|
||||
``DPLL_A_ID``, which is unique identifier of dpll device in the system,
|
||||
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``.
|
||||
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
|
||||
of parent dplls, targeted by ``DPLL_A_PIN_PARENT_ID`` attribute which is
|
||||
also required inside that nest.
|
||||
For MUX-type pins the ``DPLL_A_PIN_STATE`` attribute is configured in
|
||||
similar way, by enclosing required state in ``DPLL_A_PIN_PARENT_PIN``
|
||||
nested attribute and targeted parent pin id in ``DPLL_A_PIN_PARENT_ID``.
|
||||
|
||||
In general, it is possible to configure multiple parameters at once, but
|
||||
internally each parameter change will be invoked separately, where order
|
||||
of configuration is not guaranteed by any means.
|
||||
|
||||
Configuration pre-defined enums
|
||||
===============================
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/dpll.h
|
||||
|
||||
Notifications
|
||||
=============
|
||||
|
||||
dpll device can provide notifications regarding status changes of the
|
||||
device, i.e. lock status changes, input/output changes or other alarms.
|
||||
There is one multicast group that is used to notify user-space apps via
|
||||
netlink socket: ``DPLL_MCGRP_MONITOR``
|
||||
|
||||
Notifications messages:
|
||||
|
||||
============================== =====================================
|
||||
``DPLL_CMD_DEVICE_CREATE_NTF`` dpll device was created
|
||||
``DPLL_CMD_DEVICE_DELETE_NTF`` dpll device was deleted
|
||||
``DPLL_CMD_DEVICE_CHANGE_NTF`` dpll device has changed
|
||||
``DPLL_CMD_PIN_CREATE_NTF`` dpll pin was created
|
||||
``DPLL_CMD_PIN_DELETE_NTF`` dpll pin was deleted
|
||||
``DPLL_CMD_PIN_CHANGE_NTF`` dpll pin has changed
|
||||
============================== =====================================
|
||||
|
||||
Events format is the same as for the corresponding get command.
|
||||
Format of ``DPLL_CMD_DEVICE_`` events is the same as response of
|
||||
``DPLL_CMD_DEVICE_GET``.
|
||||
Format of ``DPLL_CMD_PIN_`` events is same as response of
|
||||
``DPLL_CMD_PIN_GET``.
|
||||
|
||||
Device driver implementation
|
||||
============================
|
||||
|
||||
Device is allocated by dpll_device_get() call. Second call with the
|
||||
same arguments will not create new object but provides pointer to
|
||||
previously created device for given arguments, it also increases
|
||||
refcount of that object.
|
||||
Device is deallocated by dpll_device_put() call, which first
|
||||
decreases the refcount, once refcount is cleared the object is
|
||||
destroyed.
|
||||
|
||||
Device should implement set of operations and register device via
|
||||
dpll_device_register() at which point it becomes available to the
|
||||
users. Multiple driver instances can obtain reference to it with
|
||||
dpll_device_get(), as well as register dpll device with their own
|
||||
ops and priv.
|
||||
|
||||
The pins are allocated separately with dpll_pin_get(), it works
|
||||
similarly to dpll_device_get(). Function first creates object and then
|
||||
for each call with the same arguments only the object refcount
|
||||
increases. Also dpll_pin_put() works similarly to dpll_device_put().
|
||||
|
||||
A pin can be registered with parent dpll device or parent pin, depending
|
||||
on hardware needs. Each registration requires registerer to provide set
|
||||
of pin callbacks, and private data pointer for calling them:
|
||||
|
||||
- dpll_pin_register() - register pin with a dpll device,
|
||||
- dpll_pin_on_pin_register() - register pin with another MUX type pin.
|
||||
|
||||
Notifications of adding or removing dpll devices are created within
|
||||
subsystem itself.
|
||||
Notifications about registering/deregistering pins are also invoked by
|
||||
the subsystem.
|
||||
Notifications about status changes either of dpll device or a pin are
|
||||
invoked in two ways:
|
||||
|
||||
- after successful change was requested on dpll subsystem, the subsystem
|
||||
calls corresponding notification,
|
||||
- requested by device driver with dpll_device_change_ntf() or
|
||||
dpll_pin_change_ntf() when driver informs about the status change.
|
||||
|
||||
The device driver using dpll interface is not required to implement all
|
||||
the callback operation. Nevertheless, there are few required to be
|
||||
implemented.
|
||||
Required dpll device level callback operations:
|
||||
|
||||
- ``.mode_get``,
|
||||
- ``.lock_status_get``.
|
||||
|
||||
Required pin level callback operations:
|
||||
|
||||
- ``.state_on_dpll_get`` (pins registered with dpll device),
|
||||
- ``.state_on_pin_get`` (pins registered with parent pin),
|
||||
- ``.direction_get``.
|
||||
|
||||
Every other operation handler is checked for existence and
|
||||
``-EOPNOTSUPP`` is returned in case of absence of specific handler.
|
||||
|
||||
The simplest implementation is in the OCP TimeCard driver. The ops
|
||||
structures are defined like this:
|
||||
|
||||
.. code-block:: c
|
||||
static const struct dpll_device_ops dpll_ops = {
|
||||
.lock_status_get = ptp_ocp_dpll_lock_status_get,
|
||||
.mode_get = ptp_ocp_dpll_mode_get,
|
||||
.mode_supported = ptp_ocp_dpll_mode_supported,
|
||||
};
|
||||
|
||||
static const struct dpll_pin_ops dpll_pins_ops = {
|
||||
.frequency_get = ptp_ocp_dpll_frequency_get,
|
||||
.frequency_set = ptp_ocp_dpll_frequency_set,
|
||||
.direction_get = ptp_ocp_dpll_direction_get,
|
||||
.direction_set = ptp_ocp_dpll_direction_set,
|
||||
.state_on_dpll_get = ptp_ocp_dpll_state_get,
|
||||
};
|
||||
|
||||
The registration part is then looks like this part:
|
||||
|
||||
.. code-block:: c
|
||||
clkid = pci_get_dsn(pdev);
|
||||
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
|
||||
if (IS_ERR(bp->dpll)) {
|
||||
err = PTR_ERR(bp->dpll);
|
||||
dev_err(&pdev->dev, "dpll_device_alloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < OCP_SMA_NUM; i++) {
|
||||
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
|
||||
if (IS_ERR(bp->sma[i].dpll_pin)) {
|
||||
err = PTR_ERR(bp->dpll);
|
||||
goto out_dpll;
|
||||
}
|
||||
|
||||
err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
|
||||
&bp->sma[i]);
|
||||
if (err) {
|
||||
dpll_pin_put(bp->sma[i].dpll_pin);
|
||||
goto out_dpll;
|
||||
}
|
||||
}
|
||||
|
||||
In the error path we have to rewind every allocation in the reverse order:
|
||||
|
||||
.. code-block:: c
|
||||
while (i) {
|
||||
--i;
|
||||
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
|
||||
dpll_pin_put(bp->sma[i].dpll_pin);
|
||||
}
|
||||
dpll_device_put(bp->dpll);
|
||||
|
||||
More complex example can be found in Intel's ICE driver or nVidia's mlx5 driver.
|
||||
|
||||
SyncE enablement
|
||||
================
|
||||
For SyncE enablement it is required to allow control over dpll device
|
||||
for a software application which monitors and configures the inputs of
|
||||
dpll device in response to current state of a dpll device and its
|
||||
inputs.
|
||||
In such scenario, dpll device input signal shall be also configurable
|
||||
to drive dpll with signal recovered from the PHY netdevice.
|
||||
This is done by exposing a pin to the netdevice - attaching pin to the
|
||||
netdevice itself with
|
||||
``netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)``.
|
||||
Exposed pin id handle ``DPLL_A_PIN_ID`` is then identifiable by the user
|
||||
as it is attached to rtnetlink respond to get ``RTM_NEWLINK`` command in
|
||||
nested attribute ``IFLA_DPLL_PIN``.
|
|
@ -114,6 +114,7 @@ available subsections can be seen below.
|
|||
zorro
|
||||
hte/index
|
||||
wmi
|
||||
dpll
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
|
488
Documentation/netlink/specs/dpll.yaml
Normal file
488
Documentation/netlink/specs/dpll.yaml
Normal file
|
@ -0,0 +1,488 @@
|
|||
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
|
||||
name: dpll
|
||||
|
||||
doc: DPLL subsystem.
|
||||
|
||||
definitions:
|
||||
-
|
||||
type: enum
|
||||
name: mode
|
||||
doc: |
|
||||
working modes a dpll can support, differentiates if and how dpll selects
|
||||
one of its inputs to syntonize with it, valid values for DPLL_A_MODE
|
||||
attribute
|
||||
entries:
|
||||
-
|
||||
name: manual
|
||||
doc: input can be only selected by sending a request to dpll
|
||||
value: 1
|
||||
-
|
||||
name: automatic
|
||||
doc: highest prio input pin auto selected by dpll
|
||||
render-max: true
|
||||
-
|
||||
type: enum
|
||||
name: lock-status
|
||||
doc: |
|
||||
provides information of dpll device lock status, valid values for
|
||||
DPLL_A_LOCK_STATUS attribute
|
||||
entries:
|
||||
-
|
||||
name: unlocked
|
||||
doc: |
|
||||
dpll was not yet locked to any valid input (or forced by setting
|
||||
DPLL_A_MODE to DPLL_MODE_DETACHED)
|
||||
value: 1
|
||||
-
|
||||
name: locked
|
||||
doc: |
|
||||
dpll is locked to a valid signal, but no holdover available
|
||||
-
|
||||
name: locked-ho-acq
|
||||
doc: |
|
||||
dpll is locked and holdover acquired
|
||||
-
|
||||
name: holdover
|
||||
doc: |
|
||||
dpll is in holdover state - lost a valid lock or was forced
|
||||
by disconnecting all the pins (latter possible only
|
||||
when dpll lock-state was already DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
|
||||
if dpll lock-state was not DPLL_LOCK_STATUS_LOCKED_HO_ACQ, the
|
||||
dpll's lock-state shall remain DPLL_LOCK_STATUS_UNLOCKED)
|
||||
render-max: true
|
||||
-
|
||||
type: const
|
||||
name: temp-divider
|
||||
value: 1000
|
||||
doc: |
|
||||
temperature divider allowing userspace to calculate the
|
||||
temperature as float with three digit decimal precision.
|
||||
Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
|
||||
temperature value.
|
||||
Value of (DPLL_A_TEMP % DPLL_TEMP_DIVIDER) is fractional part of
|
||||
temperature value.
|
||||
-
|
||||
type: enum
|
||||
name: type
|
||||
doc: type of dpll, valid values for DPLL_A_TYPE attribute
|
||||
entries:
|
||||
-
|
||||
name: pps
|
||||
doc: dpll produces Pulse-Per-Second signal
|
||||
value: 1
|
||||
-
|
||||
name: eec
|
||||
doc: dpll drives the Ethernet Equipment Clock
|
||||
render-max: true
|
||||
-
|
||||
type: enum
|
||||
name: pin-type
|
||||
doc: |
|
||||
defines possible types of a pin, valid values for DPLL_A_PIN_TYPE
|
||||
attribute
|
||||
entries:
|
||||
-
|
||||
name: mux
|
||||
doc: aggregates another layer of selectable pins
|
||||
value: 1
|
||||
-
|
||||
name: ext
|
||||
doc: external input
|
||||
-
|
||||
name: synce-eth-port
|
||||
doc: ethernet port PHY's recovered clock
|
||||
-
|
||||
name: int-oscillator
|
||||
doc: device internal oscillator
|
||||
-
|
||||
name: gnss
|
||||
doc: GNSS recovered clock
|
||||
render-max: true
|
||||
-
|
||||
type: enum
|
||||
name: pin-direction
|
||||
doc: |
|
||||
defines possible direction of a pin, valid values for
|
||||
DPLL_A_PIN_DIRECTION attribute
|
||||
entries:
|
||||
-
|
||||
name: input
|
||||
doc: pin used as a input of a signal
|
||||
value: 1
|
||||
-
|
||||
name: output
|
||||
doc: pin used to output the signal
|
||||
render-max: true
|
||||
-
|
||||
type: const
|
||||
name: pin-frequency-1-hz
|
||||
value: 1
|
||||
-
|
||||
type: const
|
||||
name: pin-frequency-10-khz
|
||||
value: 10000
|
||||
-
|
||||
type: const
|
||||
name: pin-frequency-77_5-khz
|
||||
value: 77500
|
||||
-
|
||||
type: const
|
||||
name: pin-frequency-10-mhz
|
||||
value: 10000000
|
||||
-
|
||||
type: enum
|
||||
name: pin-state
|
||||
doc: |
|
||||
defines possible states of a pin, valid values for
|
||||
DPLL_A_PIN_STATE attribute
|
||||
entries:
|
||||
-
|
||||
name: connected
|
||||
doc: pin connected, active input of phase locked loop
|
||||
value: 1
|
||||
-
|
||||
name: disconnected
|
||||
doc: pin disconnected, not considered as a valid input
|
||||
-
|
||||
name: selectable
|
||||
doc: pin enabled for automatic input selection
|
||||
render-max: true
|
||||
-
|
||||
type: flags
|
||||
name: pin-capabilities
|
||||
doc: |
|
||||
defines possible capabilities of a pin, valid flags on
|
||||
DPLL_A_PIN_CAPABILITIES attribute
|
||||
entries:
|
||||
-
|
||||
name: direction-can-change
|
||||
doc: pin direction can be changed
|
||||
-
|
||||
name: priority-can-change
|
||||
doc: pin priority can be changed
|
||||
-
|
||||
name: state-can-change
|
||||
doc: pin state can be changed
|
||||
|
||||
attribute-sets:
|
||||
-
|
||||
name: dpll
|
||||
enum-name: dpll_a
|
||||
attributes:
|
||||
-
|
||||
name: id
|
||||
type: u32
|
||||
-
|
||||
name: module-name
|
||||
type: string
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: clock-id
|
||||
type: u64
|
||||
-
|
||||
name: mode
|
||||
type: u32
|
||||
enum: mode
|
||||
-
|
||||
name: mode-supported
|
||||
type: u32
|
||||
enum: mode
|
||||
multi-attr: true
|
||||
-
|
||||
name: lock-status
|
||||
type: u32
|
||||
enum: lock-status
|
||||
-
|
||||
name: temp
|
||||
type: s32
|
||||
-
|
||||
name: type
|
||||
type: u32
|
||||
enum: type
|
||||
-
|
||||
name: pin
|
||||
enum-name: dpll_a_pin
|
||||
attributes:
|
||||
-
|
||||
name: id
|
||||
type: u32
|
||||
-
|
||||
name: parent-id
|
||||
type: u32
|
||||
-
|
||||
name: module-name
|
||||
type: string
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: clock-id
|
||||
type: u64
|
||||
-
|
||||
name: board-label
|
||||
type: string
|
||||
-
|
||||
name: panel-label
|
||||
type: string
|
||||
-
|
||||
name: package-label
|
||||
type: string
|
||||
-
|
||||
name: type
|
||||
type: u32
|
||||
enum: pin-type
|
||||
-
|
||||
name: direction
|
||||
type: u32
|
||||
enum: pin-direction
|
||||
-
|
||||
name: frequency
|
||||
type: u64
|
||||
-
|
||||
name: frequency-supported
|
||||
type: nest
|
||||
multi-attr: true
|
||||
nested-attributes: frequency-range
|
||||
-
|
||||
name: frequency-min
|
||||
type: u64
|
||||
-
|
||||
name: frequency-max
|
||||
type: u64
|
||||
-
|
||||
name: prio
|
||||
type: u32
|
||||
-
|
||||
name: state
|
||||
type: u32
|
||||
enum: pin-state
|
||||
-
|
||||
name: capabilities
|
||||
type: u32
|
||||
-
|
||||
name: parent-device
|
||||
type: nest
|
||||
multi-attr: true
|
||||
nested-attributes: pin-parent-device
|
||||
-
|
||||
name: parent-pin
|
||||
type: nest
|
||||
multi-attr: true
|
||||
nested-attributes: pin-parent-pin
|
||||
-
|
||||
name: pin-parent-device
|
||||
subset-of: pin
|
||||
attributes:
|
||||
-
|
||||
name: parent-id
|
||||
type: u32
|
||||
-
|
||||
name: direction
|
||||
type: u32
|
||||
-
|
||||
name: prio
|
||||
type: u32
|
||||
-
|
||||
name: state
|
||||
type: u32
|
||||
-
|
||||
name: pin-parent-pin
|
||||
subset-of: pin
|
||||
attributes:
|
||||
-
|
||||
name: parent-id
|
||||
type: u32
|
||||
-
|
||||
name: state
|
||||
type: u32
|
||||
-
|
||||
name: frequency-range
|
||||
subset-of: pin
|
||||
attributes:
|
||||
-
|
||||
name: frequency-min
|
||||
type: u64
|
||||
-
|
||||
name: frequency-max
|
||||
type: u64
|
||||
|
||||
operations:
|
||||
enum-name: dpll_cmd
|
||||
list:
|
||||
-
|
||||
name: device-id-get
|
||||
doc: |
|
||||
Get id of dpll device that matches given attributes
|
||||
attribute-set: dpll
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-lock-doit
|
||||
post: dpll-unlock-doit
|
||||
request:
|
||||
attributes:
|
||||
- module-name
|
||||
- clock-id
|
||||
- type
|
||||
reply:
|
||||
attributes:
|
||||
- id
|
||||
|
||||
-
|
||||
name: device-get
|
||||
doc: |
|
||||
Get list of DPLL devices (dump) or attributes of a single dpll device
|
||||
attribute-set: dpll
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-pre-doit
|
||||
post: dpll-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
reply: &dev-attrs
|
||||
attributes:
|
||||
- id
|
||||
- module-name
|
||||
- mode
|
||||
- mode-supported
|
||||
- lock-status
|
||||
- temp
|
||||
- clock-id
|
||||
- type
|
||||
|
||||
dump:
|
||||
pre: dpll-lock-dumpit
|
||||
post: dpll-unlock-dumpit
|
||||
reply: *dev-attrs
|
||||
|
||||
-
|
||||
name: device-set
|
||||
doc: Set attributes for a DPLL device
|
||||
attribute-set: dpll
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-pre-doit
|
||||
post: dpll-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
-
|
||||
name: device-create-ntf
|
||||
doc: Notification about device appearing
|
||||
notify: device-get
|
||||
mcgrp: monitor
|
||||
-
|
||||
name: device-delete-ntf
|
||||
doc: Notification about device disappearing
|
||||
notify: device-get
|
||||
mcgrp: monitor
|
||||
-
|
||||
name: device-change-ntf
|
||||
doc: Notification about device configuration being changed
|
||||
notify: device-get
|
||||
mcgrp: monitor
|
||||
-
|
||||
name: pin-id-get
|
||||
doc: |
|
||||
Get id of a pin that matches given attributes
|
||||
attribute-set: pin
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-lock-doit
|
||||
post: dpll-unlock-doit
|
||||
request:
|
||||
attributes:
|
||||
- module-name
|
||||
- clock-id
|
||||
- board-label
|
||||
- panel-label
|
||||
- package-label
|
||||
- type
|
||||
reply:
|
||||
attributes:
|
||||
- id
|
||||
|
||||
-
|
||||
name: pin-get
|
||||
doc: |
|
||||
Get list of pins and its attributes.
|
||||
- dump request without any attributes given - list all the pins in the
|
||||
system
|
||||
- dump request with target dpll - list all the pins registered with
|
||||
a given dpll device
|
||||
- do request with target dpll and target pin - single pin attributes
|
||||
attribute-set: pin
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-pin-pre-doit
|
||||
post: dpll-pin-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
reply: &pin-attrs
|
||||
attributes:
|
||||
- id
|
||||
- board-label
|
||||
- panel-label
|
||||
- package-label
|
||||
- type
|
||||
- frequency
|
||||
- frequency-supported
|
||||
- capabilities
|
||||
- parent-device
|
||||
- parent-pin
|
||||
|
||||
dump:
|
||||
pre: dpll-lock-dumpit
|
||||
post: dpll-unlock-dumpit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
reply: *pin-attrs
|
||||
|
||||
-
|
||||
name: pin-set
|
||||
doc: Set attributes of a target pin
|
||||
attribute-set: pin
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-pin-pre-doit
|
||||
post: dpll-pin-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
- frequency
|
||||
- direction
|
||||
- prio
|
||||
- state
|
||||
- parent-device
|
||||
- parent-pin
|
||||
-
|
||||
name: pin-create-ntf
|
||||
doc: Notification about pin appearing
|
||||
notify: pin-get
|
||||
mcgrp: monitor
|
||||
-
|
||||
name: pin-delete-ntf
|
||||
doc: Notification about pin disappearing
|
||||
notify: pin-get
|
||||
mcgrp: monitor
|
||||
-
|
||||
name: pin-change-ntf
|
||||
doc: Notification about pin configuration being changed
|
||||
notify: pin-get
|
||||
mcgrp: monitor
|
||||
|
||||
mcast-groups:
|
||||
list:
|
||||
-
|
||||
name: monitor
|
|
@ -42,6 +42,19 @@ definitions:
|
|||
doc:
|
||||
This feature informs if netdev implements non-linear XDP buffer
|
||||
support in ndo_xdp_xmit callback.
|
||||
-
|
||||
type: flags
|
||||
name: xdp-rx-metadata
|
||||
render-max: true
|
||||
entries:
|
||||
-
|
||||
name: timestamp
|
||||
doc:
|
||||
Device is capable of exposing receive HW timestamp via bpf_xdp_metadata_rx_timestamp().
|
||||
-
|
||||
name: hash
|
||||
doc:
|
||||
Device is capable of exposing receive packet hash via bpf_xdp_metadata_rx_hash().
|
||||
|
||||
attribute-sets:
|
||||
-
|
||||
|
@ -68,6 +81,13 @@ attribute-sets:
|
|||
type: u32
|
||||
checks:
|
||||
min: 1
|
||||
-
|
||||
name: xdp-rx-metadata-features
|
||||
doc: Bitmask of supported XDP receive metadata features.
|
||||
See Documentation/networking/xdp-rx-metadata.rst for more details.
|
||||
type: u64
|
||||
enum: xdp-rx-metadata
|
||||
enum-as-flags: true
|
||||
|
||||
operations:
|
||||
list:
|
||||
|
@ -84,6 +104,7 @@ operations:
|
|||
- ifindex
|
||||
- xdp-features
|
||||
- xdp-zc-max-segs
|
||||
- xdp-rx-metadata-features
|
||||
dump:
|
||||
reply: *dev-all
|
||||
-
|
||||
|
|
|
@ -32,6 +32,7 @@ Contents:
|
|||
intel/e1000
|
||||
intel/e1000e
|
||||
intel/fm10k
|
||||
intel/idpf
|
||||
intel/igb
|
||||
intel/igbvf
|
||||
intel/ixgbe
|
||||
|
|
160
Documentation/networking/device_drivers/ethernet/intel/idpf.rst
Normal file
160
Documentation/networking/device_drivers/ethernet/intel/idpf.rst
Normal file
|
@ -0,0 +1,160 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
==========================================================================
|
||||
idpf Linux* Base Driver for the Intel(R) Infrastructure Data Path Function
|
||||
==========================================================================
|
||||
|
||||
Intel idpf Linux driver.
|
||||
Copyright(C) 2023 Intel Corporation.
|
||||
|
||||
.. contents::
|
||||
|
||||
The idpf driver serves as both the Physical Function (PF) and Virtual Function
|
||||
(VF) driver for the Intel(R) Infrastructure Data Path Function.
|
||||
|
||||
Driver information can be obtained using ethtool, lspci, and ip.
|
||||
|
||||
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
|
||||
|
||||
|
||||
Additional Features and Configurations
|
||||
======================================
|
||||
|
||||
ethtool
|
||||
-------
|
||||
The driver utilizes the ethtool interface for driver configuration and
|
||||
diagnostics, as well as displaying statistical information. The latest ethtool
|
||||
version is required for this functionality. If you don't have one yet, you can
|
||||
obtain it at:
|
||||
https://kernel.org/pub/software/network/ethtool/
|
||||
|
||||
|
||||
Viewing Link Messages
|
||||
---------------------
|
||||
Link messages will not be displayed to the console if the distribution is
|
||||
restricting system messages. In order to see network driver link messages on
|
||||
your console, set dmesg to eight by entering the following::
|
||||
|
||||
# dmesg -n 8
|
||||
|
||||
.. note::
|
||||
This setting is not saved across reboots.
|
||||
|
||||
|
||||
Jumbo Frames
|
||||
------------
|
||||
Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU)
|
||||
to a value larger than the default value of 1500.
|
||||
|
||||
Use the ip command to increase the MTU size. For example, enter the following
|
||||
where <ethX> is the interface number::
|
||||
|
||||
# ip link set mtu 9000 dev <ethX>
|
||||
# ip link set up dev <ethX>
|
||||
|
||||
.. note::
|
||||
The maximum MTU setting for jumbo frames is 9706. This corresponds to the
|
||||
maximum jumbo frame size of 9728 bytes.
|
||||
|
||||
.. note::
|
||||
This driver will attempt to use multiple page sized buffers to receive
|
||||
each jumbo packet. This should help to avoid buffer starvation issues when
|
||||
allocating receive packets.
|
||||
|
||||
.. note::
|
||||
Packet loss may have a greater impact on throughput when you use jumbo
|
||||
frames. If you observe a drop in performance after enabling jumbo frames,
|
||||
enabling flow control may mitigate the issue.
|
||||
|
||||
|
||||
Performance Optimization
|
||||
========================
|
||||
Driver defaults are meant to fit a wide variety of workloads, but if further
|
||||
optimization is required, we recommend experimenting with the following
|
||||
settings.
|
||||
|
||||
|
||||
Interrupt Rate Limiting
|
||||
-----------------------
|
||||
This driver supports an adaptive interrupt throttle rate (ITR) mechanism that
|
||||
is tuned for general workloads. The user can customize the interrupt rate
|
||||
control for specific workloads, via ethtool, adjusting the number of
|
||||
microseconds between interrupts.
|
||||
|
||||
To set the interrupt rate manually, you must disable adaptive mode::
|
||||
|
||||
# ethtool -C <ethX> adaptive-rx off adaptive-tx off
|
||||
|
||||
For lower CPU utilization:
|
||||
- Disable adaptive ITR and lower Rx and Tx interrupts. The examples below
|
||||
affect every queue of the specified interface.
|
||||
|
||||
- Setting rx-usecs and tx-usecs to 80 will limit interrupts to about
|
||||
12,500 interrupts per second per queue::
|
||||
|
||||
# ethtool -C <ethX> adaptive-rx off adaptive-tx off rx-usecs 80
|
||||
tx-usecs 80
|
||||
|
||||
For reduced latency:
|
||||
- Disable adaptive ITR and ITR by setting rx-usecs and tx-usecs to 0
|
||||
using ethtool::
|
||||
|
||||
# ethtool -C <ethX> adaptive-rx off adaptive-tx off rx-usecs 0
|
||||
tx-usecs 0
|
||||
|
||||
Per-queue interrupt rate settings:
|
||||
- The following examples are for queues 1 and 3, but you can adjust other
|
||||
queues.
|
||||
|
||||
- To disable Rx adaptive ITR and set static Rx ITR to 10 microseconds or
|
||||
about 100,000 interrupts/second, for queues 1 and 3::
|
||||
|
||||
# ethtool --per-queue <ethX> queue_mask 0xa --coalesce adaptive-rx off
|
||||
rx-usecs 10
|
||||
|
||||
- To show the current coalesce settings for queues 1 and 3::
|
||||
|
||||
# ethtool --per-queue <ethX> queue_mask 0xa --show-coalesce
|
||||
|
||||
|
||||
|
||||
Virtualized Environments
|
||||
------------------------
|
||||
In addition to the other suggestions in this section, the following may be
|
||||
helpful to optimize performance in VMs.
|
||||
|
||||
- Using the appropriate mechanism (vcpupin) in the VM, pin the CPUs to
|
||||
individual LCPUs, making sure to use a set of CPUs included in the
|
||||
device's local_cpulist: /sys/class/net/<ethX>/device/local_cpulist.
|
||||
|
||||
- Configure as many Rx/Tx queues in the VM as available. (See the idpf driver
|
||||
documentation for the number of queues supported.) For example::
|
||||
|
||||
# ethtool -L <virt_interface> rx <max> tx <max>
|
||||
|
||||
|
||||
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.
|
|
@ -650,8 +650,8 @@ before a conversion to the new layout is being done behind the scenes!
|
|||
|
||||
Currently, the classic BPF format is being used for JITing on most
|
||||
32-bit architectures, whereas x86-64, aarch64, s390x, powerpc64,
|
||||
sparc64, arm32, riscv64, riscv32 perform JIT compilation from eBPF
|
||||
instruction set.
|
||||
sparc64, arm32, riscv64, riscv32, loongarch64 perform JIT compilation
|
||||
from eBPF instruction set.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
|
|
@ -745,6 +745,13 @@ tcp_comp_sack_nr - INTEGER
|
|||
|
||||
Default : 44
|
||||
|
||||
tcp_backlog_ack_defer - BOOLEAN
|
||||
If set, user thread processing socket backlog tries sending
|
||||
one ACK for the whole queue. This helps to avoid potential
|
||||
long latencies at end of a TCP socket syscall.
|
||||
|
||||
Default : true
|
||||
|
||||
tcp_slow_start_after_idle - BOOLEAN
|
||||
If set, provide RFC2861 behavior and time out the congestion
|
||||
window after an idle period. An idle period is defined at
|
||||
|
|
|
@ -105,6 +105,13 @@ bpf_tail_call
|
|||
Adding programs that access metadata kfuncs to the ``BPF_MAP_TYPE_PROG_ARRAY``
|
||||
is currently not supported.
|
||||
|
||||
Supported Devices
|
||||
=================
|
||||
|
||||
It is possible to query which kfunc the particular netdev implements via
|
||||
netlink. See ``xdp-rx-metadata-features`` attribute set in
|
||||
``Documentation/netlink/specs/netdev.yaml``.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
|
|
21
MAINTAINERS
21
MAINTAINERS
|
@ -3614,9 +3614,10 @@ F: Documentation/devicetree/bindings/iio/accel/bosch,bma400.yaml
|
|||
F: drivers/iio/accel/bma400*
|
||||
|
||||
BPF JIT for ARM
|
||||
M: Shubham Bansal <illusionist.neo@gmail.com>
|
||||
M: Russell King <linux@armlinux.org.uk>
|
||||
M: Puranjay Mohan <puranjay12@gmail.com>
|
||||
L: bpf@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: arch/arm/net/
|
||||
|
||||
BPF JIT for ARM64
|
||||
|
@ -4338,8 +4339,7 @@ F: drivers/net/ethernet/broadcom/bcmsysport.*
|
|||
F: drivers/net/ethernet/broadcom/unimac.h
|
||||
|
||||
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||
M: Siva Reddy Kallam <siva.kallam@broadcom.com>
|
||||
M: Prashant Sreedharan <prashant@broadcom.com>
|
||||
M: Pavan Chebbi <pavan.chebbi@broadcom.com>
|
||||
M: Michael Chan <mchan@broadcom.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
|
@ -6352,6 +6352,17 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
|
|||
F: drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
|
||||
F: drivers/net/ethernet/freescale/dpaa2/dpsw*
|
||||
|
||||
DPLL SUBSYSTEM
|
||||
M: Vadim Fedorenko <vadim.fedorenko@linux.dev>
|
||||
M: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
|
||||
M: Jiri Pirko <jiri@resnulli.us>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/driver-api/dpll.rst
|
||||
F: drivers/dpll/*
|
||||
F: include/net/dpll.h
|
||||
F: include/uapi/linux/dpll.h
|
||||
|
||||
DRBD DRIVER
|
||||
M: Philipp Reisner <philipp.reisner@linbit.com>
|
||||
M: Lars Ellenberg <lars.ellenberg@linbit.com>
|
||||
|
@ -14347,9 +14358,11 @@ MIPS/LOONGSON1 ARCHITECTURE
|
|||
M: Keguang Zhang <keguang.zhang@gmail.com>
|
||||
L: linux-mips@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/*/loongson,ls1*.yaml
|
||||
F: arch/mips/include/asm/mach-loongson32/
|
||||
F: arch/mips/loongson32/
|
||||
F: drivers/*/*loongson1*
|
||||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
|
||||
|
||||
MIPS/LOONGSON2EF ARCHITECTURE
|
||||
M: Jiaxun Yang <jiaxun.yang@flygoat.com>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/*
|
||||
* Just-In-Time compiler for eBPF filters on 32bit ARM
|
||||
*
|
||||
* Copyright (c) 2023 Puranjay Mohan <puranjay12@gmail.com>
|
||||
* Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
|
||||
* Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
|
||||
*/
|
||||
|
@ -15,6 +16,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/math64.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/hwcap.h>
|
||||
|
@ -228,6 +230,44 @@ static u32 jit_mod32(u32 dividend, u32 divisor)
|
|||
return dividend % divisor;
|
||||
}
|
||||
|
||||
static s32 jit_sdiv32(s32 dividend, s32 divisor)
|
||||
{
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
static s32 jit_smod32(s32 dividend, s32 divisor)
|
||||
{
|
||||
return dividend % divisor;
|
||||
}
|
||||
|
||||
/* Wrappers for 64-bit div/mod */
|
||||
static u64 jit_udiv64(u64 dividend, u64 divisor)
|
||||
{
|
||||
return div64_u64(dividend, divisor);
|
||||
}
|
||||
|
||||
static u64 jit_mod64(u64 dividend, u64 divisor)
|
||||
{
|
||||
u64 rem;
|
||||
|
||||
div64_u64_rem(dividend, divisor, &rem);
|
||||
return rem;
|
||||
}
|
||||
|
||||
static s64 jit_sdiv64(s64 dividend, s64 divisor)
|
||||
{
|
||||
return div64_s64(dividend, divisor);
|
||||
}
|
||||
|
||||
static s64 jit_smod64(s64 dividend, s64 divisor)
|
||||
{
|
||||
u64 q;
|
||||
|
||||
q = div64_s64(dividend, divisor);
|
||||
|
||||
return dividend - q * divisor;
|
||||
}
|
||||
|
||||
static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
|
||||
{
|
||||
inst |= (cond << 28);
|
||||
|
@ -333,6 +373,9 @@ static u32 arm_bpf_ldst_imm8(u32 op, u8 rt, u8 rn, s16 imm8)
|
|||
#define ARM_LDRD_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_LDRD_I, rt, rn, off)
|
||||
#define ARM_LDRH_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_LDRH_I, rt, rn, off)
|
||||
|
||||
#define ARM_LDRSH_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_LDRSH_I, rt, rn, off)
|
||||
#define ARM_LDRSB_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_LDRSB_I, rt, rn, off)
|
||||
|
||||
#define ARM_STR_I(rt, rn, off) arm_bpf_ldst_imm12(ARM_INST_STR_I, rt, rn, off)
|
||||
#define ARM_STRB_I(rt, rn, off) arm_bpf_ldst_imm12(ARM_INST_STRB_I, rt, rn, off)
|
||||
#define ARM_STRD_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_STRD_I, rt, rn, off)
|
||||
|
@ -474,17 +517,18 @@ static inline int epilogue_offset(const struct jit_ctx *ctx)
|
|||
return to - from - 2;
|
||||
}
|
||||
|
||||
static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op)
|
||||
static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op, u8 sign)
|
||||
{
|
||||
const int exclude_mask = BIT(ARM_R0) | BIT(ARM_R1);
|
||||
const s8 *tmp = bpf2a32[TMP_REG_1];
|
||||
u32 dst;
|
||||
|
||||
#if __LINUX_ARM_ARCH__ == 7
|
||||
if (elf_hwcap & HWCAP_IDIVA) {
|
||||
if (op == BPF_DIV)
|
||||
emit(ARM_UDIV(rd, rm, rn), ctx);
|
||||
else {
|
||||
emit(ARM_UDIV(ARM_IP, rm, rn), ctx);
|
||||
if (op == BPF_DIV) {
|
||||
emit(sign ? ARM_SDIV(rd, rm, rn) : ARM_UDIV(rd, rm, rn), ctx);
|
||||
} else {
|
||||
emit(sign ? ARM_SDIV(ARM_IP, rm, rn) : ARM_UDIV(ARM_IP, rm, rn), ctx);
|
||||
emit(ARM_MLS(rd, rn, ARM_IP, rm), ctx);
|
||||
}
|
||||
return;
|
||||
|
@ -512,8 +556,19 @@ static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op)
|
|||
emit(ARM_PUSH(CALLER_MASK & ~exclude_mask), ctx);
|
||||
|
||||
/* Call appropriate function */
|
||||
emit_mov_i(ARM_IP, op == BPF_DIV ?
|
||||
(u32)jit_udiv32 : (u32)jit_mod32, ctx);
|
||||
if (sign) {
|
||||
if (op == BPF_DIV)
|
||||
dst = (u32)jit_sdiv32;
|
||||
else
|
||||
dst = (u32)jit_smod32;
|
||||
} else {
|
||||
if (op == BPF_DIV)
|
||||
dst = (u32)jit_udiv32;
|
||||
else
|
||||
dst = (u32)jit_mod32;
|
||||
}
|
||||
|
||||
emit_mov_i(ARM_IP, dst, ctx);
|
||||
emit_blx_r(ARM_IP, ctx);
|
||||
|
||||
/* Restore caller-saved registers from stack */
|
||||
|
@ -530,6 +585,78 @@ static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op)
|
|||
emit(ARM_MOV_R(ARM_R0, tmp[1]), ctx);
|
||||
}
|
||||
|
||||
static inline void emit_udivmod64(const s8 *rd, const s8 *rm, const s8 *rn, struct jit_ctx *ctx,
|
||||
u8 op, u8 sign)
|
||||
{
|
||||
u32 dst;
|
||||
|
||||
/* Push caller-saved registers on stack */
|
||||
emit(ARM_PUSH(CALLER_MASK), ctx);
|
||||
|
||||
/*
|
||||
* As we are implementing 64-bit div/mod as function calls, We need to put the dividend in
|
||||
* R0-R1 and the divisor in R2-R3. As we have already pushed these registers on the stack,
|
||||
* we can recover them later after returning from the function call.
|
||||
*/
|
||||
if (rm[1] != ARM_R0 || rn[1] != ARM_R2) {
|
||||
/*
|
||||
* Move Rm to {R1, R0} if it is not already there.
|
||||
*/
|
||||
if (rm[1] != ARM_R0) {
|
||||
if (rn[1] == ARM_R0)
|
||||
emit(ARM_PUSH(BIT(ARM_R0) | BIT(ARM_R1)), ctx);
|
||||
emit(ARM_MOV_R(ARM_R1, rm[0]), ctx);
|
||||
emit(ARM_MOV_R(ARM_R0, rm[1]), ctx);
|
||||
if (rn[1] == ARM_R0) {
|
||||
emit(ARM_POP(BIT(ARM_R2) | BIT(ARM_R3)), ctx);
|
||||
goto cont;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Move Rn to {R3, R2} if it is not already there.
|
||||
*/
|
||||
if (rn[1] != ARM_R2) {
|
||||
emit(ARM_MOV_R(ARM_R3, rn[0]), ctx);
|
||||
emit(ARM_MOV_R(ARM_R2, rn[1]), ctx);
|
||||
}
|
||||
}
|
||||
|
||||
cont:
|
||||
|
||||
/* Call appropriate function */
|
||||
if (sign) {
|
||||
if (op == BPF_DIV)
|
||||
dst = (u32)jit_sdiv64;
|
||||
else
|
||||
dst = (u32)jit_smod64;
|
||||
} else {
|
||||
if (op == BPF_DIV)
|
||||
dst = (u32)jit_udiv64;
|
||||
else
|
||||
dst = (u32)jit_mod64;
|
||||
}
|
||||
|
||||
emit_mov_i(ARM_IP, dst, ctx);
|
||||
emit_blx_r(ARM_IP, ctx);
|
||||
|
||||
/* Save return value */
|
||||
if (rd[1] != ARM_R0) {
|
||||
emit(ARM_MOV_R(rd[0], ARM_R1), ctx);
|
||||
emit(ARM_MOV_R(rd[1], ARM_R0), ctx);
|
||||
}
|
||||
|
||||
/* Recover {R3, R2} and {R1, R0} from stack if they are not Rd */
|
||||
if (rd[1] != ARM_R0 && rd[1] != ARM_R2) {
|
||||
emit(ARM_POP(CALLER_MASK), ctx);
|
||||
} else if (rd[1] != ARM_R0) {
|
||||
emit(ARM_POP(BIT(ARM_R0) | BIT(ARM_R1)), ctx);
|
||||
emit(ARM_ADD_I(ARM_SP, ARM_SP, 8), ctx);
|
||||
} else {
|
||||
emit(ARM_ADD_I(ARM_SP, ARM_SP, 8), ctx);
|
||||
emit(ARM_POP(BIT(ARM_R2) | BIT(ARM_R3)), ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the translated BPF register on stack? */
|
||||
static bool is_stacked(s8 reg)
|
||||
{
|
||||
|
@ -744,12 +871,16 @@ static inline void emit_a32_alu_r64(const bool is64, const s8 dst[],
|
|||
}
|
||||
|
||||
/* dst = src (4 bytes)*/
|
||||
static inline void emit_a32_mov_r(const s8 dst, const s8 src,
|
||||
static inline void emit_a32_mov_r(const s8 dst, const s8 src, const u8 off,
|
||||
struct jit_ctx *ctx) {
|
||||
const s8 *tmp = bpf2a32[TMP_REG_1];
|
||||
s8 rt;
|
||||
|
||||
rt = arm_bpf_get_reg32(src, tmp[0], ctx);
|
||||
if (off && off != 32) {
|
||||
emit(ARM_LSL_I(rt, rt, 32 - off), ctx);
|
||||
emit(ARM_ASR_I(rt, rt, 32 - off), ctx);
|
||||
}
|
||||
arm_bpf_put_reg32(dst, rt, ctx);
|
||||
}
|
||||
|
||||
|
@ -758,15 +889,15 @@ static inline void emit_a32_mov_r64(const bool is64, const s8 dst[],
|
|||
const s8 src[],
|
||||
struct jit_ctx *ctx) {
|
||||
if (!is64) {
|
||||
emit_a32_mov_r(dst_lo, src_lo, ctx);
|
||||
emit_a32_mov_r(dst_lo, src_lo, 0, ctx);
|
||||
if (!ctx->prog->aux->verifier_zext)
|
||||
/* Zero out high 4 bytes */
|
||||
emit_a32_mov_i(dst_hi, 0, ctx);
|
||||
} else if (__LINUX_ARM_ARCH__ < 6 &&
|
||||
ctx->cpu_architecture < CPU_ARCH_ARMv5TE) {
|
||||
/* complete 8 byte move */
|
||||
emit_a32_mov_r(dst_lo, src_lo, ctx);
|
||||
emit_a32_mov_r(dst_hi, src_hi, ctx);
|
||||
emit_a32_mov_r(dst_lo, src_lo, 0, ctx);
|
||||
emit_a32_mov_r(dst_hi, src_hi, 0, ctx);
|
||||
} else if (is_stacked(src_lo) && is_stacked(dst_lo)) {
|
||||
const u8 *tmp = bpf2a32[TMP_REG_1];
|
||||
|
||||
|
@ -782,6 +913,24 @@ static inline void emit_a32_mov_r64(const bool is64, const s8 dst[],
|
|||
}
|
||||
}
|
||||
|
||||
/* dst = (signed)src */
|
||||
static inline void emit_a32_movsx_r64(const bool is64, const u8 off, const s8 dst[], const s8 src[],
|
||||
struct jit_ctx *ctx) {
|
||||
const s8 *tmp = bpf2a32[TMP_REG_1];
|
||||
const s8 *rt;
|
||||
|
||||
rt = arm_bpf_get_reg64(dst, tmp, ctx);
|
||||
|
||||
emit_a32_mov_r(dst_lo, src_lo, off, ctx);
|
||||
if (!is64) {
|
||||
if (!ctx->prog->aux->verifier_zext)
|
||||
/* Zero out high 4 bytes */
|
||||
emit_a32_mov_i(dst_hi, 0, ctx);
|
||||
} else {
|
||||
emit(ARM_ASR_I(rt[0], rt[1], 31), ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shift operations */
|
||||
static inline void emit_a32_alu_i(const s8 dst, const u32 val,
|
||||
struct jit_ctx *ctx, const u8 op) {
|
||||
|
@ -1026,6 +1175,24 @@ static bool is_ldst_imm(s16 off, const u8 size)
|
|||
return -off_max <= off && off <= off_max;
|
||||
}
|
||||
|
||||
static bool is_ldst_imm8(s16 off, const u8 size)
|
||||
{
|
||||
s16 off_max = 0;
|
||||
|
||||
switch (size) {
|
||||
case BPF_B:
|
||||
off_max = 0xff;
|
||||
break;
|
||||
case BPF_W:
|
||||
off_max = 0xfff;
|
||||
break;
|
||||
case BPF_H:
|
||||
off_max = 0xff;
|
||||
break;
|
||||
}
|
||||
return -off_max <= off && off <= off_max;
|
||||
}
|
||||
|
||||
/* *(size *)(dst + off) = src */
|
||||
static inline void emit_str_r(const s8 dst, const s8 src[],
|
||||
s16 off, struct jit_ctx *ctx, const u8 sz){
|
||||
|
@ -1105,6 +1272,50 @@ static inline void emit_ldx_r(const s8 dst[], const s8 src,
|
|||
arm_bpf_put_reg64(dst, rd, ctx);
|
||||
}
|
||||
|
||||
/* dst = *(signed size*)(src + off) */
|
||||
static inline void emit_ldsx_r(const s8 dst[], const s8 src,
|
||||
s16 off, struct jit_ctx *ctx, const u8 sz){
|
||||
const s8 *tmp = bpf2a32[TMP_REG_1];
|
||||
const s8 *rd = is_stacked(dst_lo) ? tmp : dst;
|
||||
s8 rm = src;
|
||||
int add_off;
|
||||
|
||||
if (!is_ldst_imm8(off, sz)) {
|
||||
/*
|
||||
* offset does not fit in the load/store immediate,
|
||||
* construct an ADD instruction to apply the offset.
|
||||
*/
|
||||
add_off = imm8m(off);
|
||||
if (add_off > 0) {
|
||||
emit(ARM_ADD_I(tmp[0], src, add_off), ctx);
|
||||
rm = tmp[0];
|
||||
} else {
|
||||
emit_a32_mov_i(tmp[0], off, ctx);
|
||||
emit(ARM_ADD_R(tmp[0], tmp[0], src), ctx);
|
||||
rm = tmp[0];
|
||||
}
|
||||
off = 0;
|
||||
}
|
||||
|
||||
switch (sz) {
|
||||
case BPF_B:
|
||||
/* Load a Byte with sign extension*/
|
||||
emit(ARM_LDRSB_I(rd[1], rm, off), ctx);
|
||||
break;
|
||||
case BPF_H:
|
||||
/* Load a HalfWord with sign extension*/
|
||||
emit(ARM_LDRSH_I(rd[1], rm, off), ctx);
|
||||
break;
|
||||
case BPF_W:
|
||||
/* Load a Word*/
|
||||
emit(ARM_LDR_I(rd[1], rm, off), ctx);
|
||||
break;
|
||||
}
|
||||
/* Carry the sign extension to upper 32 bits */
|
||||
emit(ARM_ASR_I(rd[0], rd[1], 31), ctx);
|
||||
arm_bpf_put_reg64(dst, rd, ctx);
|
||||
}
|
||||
|
||||
/* Arithmatic Operation */
|
||||
static inline void emit_ar_r(const u8 rd, const u8 rt, const u8 rm,
|
||||
const u8 rn, struct jit_ctx *ctx, u8 op,
|
||||
|
@ -1385,7 +1596,10 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
|||
emit_a32_mov_i(dst_hi, 0, ctx);
|
||||
break;
|
||||
}
|
||||
emit_a32_mov_r64(is64, dst, src, ctx);
|
||||
if (insn->off)
|
||||
emit_a32_movsx_r64(is64, insn->off, dst, src, ctx);
|
||||
else
|
||||
emit_a32_mov_r64(is64, dst, src, ctx);
|
||||
break;
|
||||
case BPF_K:
|
||||
/* Sign-extend immediate value to destination reg */
|
||||
|
@ -1461,7 +1675,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
|||
rt = src_lo;
|
||||
break;
|
||||
}
|
||||
emit_udivmod(rd_lo, rd_lo, rt, ctx, BPF_OP(code));
|
||||
emit_udivmod(rd_lo, rd_lo, rt, ctx, BPF_OP(code), off);
|
||||
arm_bpf_put_reg32(dst_lo, rd_lo, ctx);
|
||||
if (!ctx->prog->aux->verifier_zext)
|
||||
emit_a32_mov_i(dst_hi, 0, ctx);
|
||||
|
@ -1470,7 +1684,19 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
|||
case BPF_ALU64 | BPF_DIV | BPF_X:
|
||||
case BPF_ALU64 | BPF_MOD | BPF_K:
|
||||
case BPF_ALU64 | BPF_MOD | BPF_X:
|
||||
goto notyet;
|
||||
rd = arm_bpf_get_reg64(dst, tmp2, ctx);
|
||||
switch (BPF_SRC(code)) {
|
||||
case BPF_X:
|
||||
rs = arm_bpf_get_reg64(src, tmp, ctx);
|
||||
break;
|
||||
case BPF_K:
|
||||
rs = tmp;
|
||||
emit_a32_mov_se_i64(is64, rs, imm, ctx);
|
||||
break;
|
||||
}
|
||||
emit_udivmod64(rd, rd, rs, ctx, BPF_OP(code), off);
|
||||
arm_bpf_put_reg64(dst, rd, ctx);
|
||||
break;
|
||||
/* dst = dst << imm */
|
||||
/* dst = dst >> imm */
|
||||
/* dst = dst >> imm (signed) */
|
||||
|
@ -1545,10 +1771,12 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
|||
break;
|
||||
/* dst = htole(dst) */
|
||||
/* dst = htobe(dst) */
|
||||
case BPF_ALU | BPF_END | BPF_FROM_LE:
|
||||
case BPF_ALU | BPF_END | BPF_FROM_BE:
|
||||
case BPF_ALU | BPF_END | BPF_FROM_LE: /* also BPF_TO_LE */
|
||||
case BPF_ALU | BPF_END | BPF_FROM_BE: /* also BPF_TO_BE */
|
||||
/* dst = bswap(dst) */
|
||||
case BPF_ALU64 | BPF_END | BPF_FROM_LE: /* also BPF_TO_LE */
|
||||
rd = arm_bpf_get_reg64(dst, tmp, ctx);
|
||||
if (BPF_SRC(code) == BPF_FROM_LE)
|
||||
if (BPF_SRC(code) == BPF_FROM_LE && BPF_CLASS(code) != BPF_ALU64)
|
||||
goto emit_bswap_uxt;
|
||||
switch (imm) {
|
||||
case 16:
|
||||
|
@ -1603,8 +1831,15 @@ exit:
|
|||
case BPF_LDX | BPF_MEM | BPF_H:
|
||||
case BPF_LDX | BPF_MEM | BPF_B:
|
||||
case BPF_LDX | BPF_MEM | BPF_DW:
|
||||
/* LDSX: dst = *(signed size *)(src + off) */
|
||||
case BPF_LDX | BPF_MEMSX | BPF_B:
|
||||
case BPF_LDX | BPF_MEMSX | BPF_H:
|
||||
case BPF_LDX | BPF_MEMSX | BPF_W:
|
||||
rn = arm_bpf_get_reg32(src_lo, tmp2[1], ctx);
|
||||
emit_ldx_r(dst, rn, off, ctx, BPF_SIZE(code));
|
||||
if (BPF_MODE(insn->code) == BPF_MEMSX)
|
||||
emit_ldsx_r(dst, rn, off, ctx, BPF_SIZE(code));
|
||||
else
|
||||
emit_ldx_r(dst, rn, off, ctx, BPF_SIZE(code));
|
||||
break;
|
||||
/* speculation barrier */
|
||||
case BPF_ST | BPF_NOSPEC:
|
||||
|
@ -1761,10 +1996,15 @@ go_jmp:
|
|||
break;
|
||||
/* JMP OFF */
|
||||
case BPF_JMP | BPF_JA:
|
||||
case BPF_JMP32 | BPF_JA:
|
||||
{
|
||||
if (off == 0)
|
||||
if (BPF_CLASS(code) == BPF_JMP32 && imm != 0)
|
||||
jmp_offset = bpf2a32_offset(i + imm, i, ctx);
|
||||
else if (BPF_CLASS(code) == BPF_JMP && off != 0)
|
||||
jmp_offset = bpf2a32_offset(i + off, i, ctx);
|
||||
else
|
||||
break;
|
||||
jmp_offset = bpf2a32_offset(i+off, i, ctx);
|
||||
|
||||
check_imm24(jmp_offset);
|
||||
emit(ARM_B(jmp_offset), ctx);
|
||||
break;
|
||||
|
|
|
@ -79,9 +79,11 @@
|
|||
#define ARM_INST_LDST__IMM12 0x00000fff
|
||||
#define ARM_INST_LDRB_I 0x05500000
|
||||
#define ARM_INST_LDRB_R 0x07d00000
|
||||
#define ARM_INST_LDRSB_I 0x015000d0
|
||||
#define ARM_INST_LDRD_I 0x014000d0
|
||||
#define ARM_INST_LDRH_I 0x015000b0
|
||||
#define ARM_INST_LDRH_R 0x019000b0
|
||||
#define ARM_INST_LDRSH_I 0x015000f0
|
||||
#define ARM_INST_LDR_I 0x05100000
|
||||
#define ARM_INST_LDR_R 0x07900000
|
||||
|
||||
|
@ -137,6 +139,7 @@
|
|||
#define ARM_INST_TST_I 0x03100000
|
||||
|
||||
#define ARM_INST_UDIV 0x0730f010
|
||||
#define ARM_INST_SDIV 0x0710f010
|
||||
|
||||
#define ARM_INST_UMULL 0x00800090
|
||||
|
||||
|
@ -265,6 +268,7 @@
|
|||
#define ARM_TST_I(rn, imm) _AL3_I(ARM_INST_TST, 0, rn, imm)
|
||||
|
||||
#define ARM_UDIV(rd, rn, rm) (ARM_INST_UDIV | (rd) << 16 | (rn) | (rm) << 8)
|
||||
#define ARM_SDIV(rd, rn, rm) (ARM_INST_SDIV | (rd) << 16 | (rn) | (rm) << 8)
|
||||
|
||||
#define ARM_UMULL(rd_lo, rd_hi, rn, rm) (ARM_INST_UMULL | (rd_hi) << 16 \
|
||||
| (rd_lo) << 12 | (rm) << 8 | rn)
|
||||
|
|
|
@ -288,7 +288,7 @@ static bool is_lsi_offset(int offset, int scale)
|
|||
static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
|
||||
{
|
||||
const struct bpf_prog *prog = ctx->prog;
|
||||
const bool is_main_prog = prog->aux->func_idx == 0;
|
||||
const bool is_main_prog = !bpf_is_subprog(prog);
|
||||
const u8 r6 = bpf2a64[BPF_REG_6];
|
||||
const u8 r7 = bpf2a64[BPF_REG_7];
|
||||
const u8 r8 = bpf2a64[BPF_REG_8];
|
||||
|
|
|
@ -556,7 +556,7 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT6_PCREL_RILC(0xc0040000, 0, jit->prologue_plt);
|
||||
jit->prologue_plt_ret = jit->prg;
|
||||
|
||||
if (fp->aux->func_idx == 0) {
|
||||
if (!bpf_is_subprog(fp)) {
|
||||
/* Initialize the tail call counter in the main program. */
|
||||
/* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */
|
||||
_EMIT6(0xd703f000 | STK_OFF_TCCNT, 0xf000 | STK_OFF_TCCNT);
|
||||
|
@ -670,15 +670,18 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
|
|||
static int get_probe_mem_regno(const u8 *insn)
|
||||
{
|
||||
/*
|
||||
* insn must point to llgc, llgh, llgf or lg, which have destination
|
||||
* register at the same position.
|
||||
* insn must point to llgc, llgh, llgf, lg, lgb, lgh or lgf, which have
|
||||
* destination register at the same position.
|
||||
*/
|
||||
if (insn[0] != 0xe3) /* common llgc, llgh, llgf and lg prefix */
|
||||
if (insn[0] != 0xe3) /* common prefix */
|
||||
return -1;
|
||||
if (insn[5] != 0x90 && /* llgc */
|
||||
insn[5] != 0x91 && /* llgh */
|
||||
insn[5] != 0x16 && /* llgf */
|
||||
insn[5] != 0x04) /* lg */
|
||||
insn[5] != 0x04 && /* lg */
|
||||
insn[5] != 0x77 && /* lgb */
|
||||
insn[5] != 0x15 && /* lgh */
|
||||
insn[5] != 0x14) /* lgf */
|
||||
return -1;
|
||||
return insn[1] >> 4;
|
||||
}
|
||||
|
@ -776,6 +779,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
int i, bool extra_pass, u32 stack_depth)
|
||||
{
|
||||
struct bpf_insn *insn = &fp->insnsi[i];
|
||||
s16 branch_oc_off = insn->off;
|
||||
u32 dst_reg = insn->dst_reg;
|
||||
u32 src_reg = insn->src_reg;
|
||||
int last, insn_count = 1;
|
||||
|
@ -788,22 +792,55 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
int err;
|
||||
|
||||
if (BPF_CLASS(insn->code) == BPF_LDX &&
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEM)
|
||||
(BPF_MODE(insn->code) == BPF_PROBE_MEM ||
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX))
|
||||
probe_prg = jit->prg;
|
||||
|
||||
switch (insn->code) {
|
||||
/*
|
||||
* BPF_MOV
|
||||
*/
|
||||
case BPF_ALU | BPF_MOV | BPF_X: /* dst = (u32) src */
|
||||
/* llgfr %dst,%src */
|
||||
EMIT4(0xb9160000, dst_reg, src_reg);
|
||||
if (insn_is_zext(&insn[1]))
|
||||
insn_count = 2;
|
||||
case BPF_ALU | BPF_MOV | BPF_X:
|
||||
switch (insn->off) {
|
||||
case 0: /* DST = (u32) SRC */
|
||||
/* llgfr %dst,%src */
|
||||
EMIT4(0xb9160000, dst_reg, src_reg);
|
||||
if (insn_is_zext(&insn[1]))
|
||||
insn_count = 2;
|
||||
break;
|
||||
case 8: /* DST = (u32)(s8) SRC */
|
||||
/* lbr %dst,%src */
|
||||
EMIT4(0xb9260000, dst_reg, src_reg);
|
||||
/* llgfr %dst,%dst */
|
||||
EMIT4(0xb9160000, dst_reg, dst_reg);
|
||||
break;
|
||||
case 16: /* DST = (u32)(s16) SRC */
|
||||
/* lhr %dst,%src */
|
||||
EMIT4(0xb9270000, dst_reg, src_reg);
|
||||
/* llgfr %dst,%dst */
|
||||
EMIT4(0xb9160000, dst_reg, dst_reg);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BPF_ALU64 | BPF_MOV | BPF_X: /* dst = src */
|
||||
/* lgr %dst,%src */
|
||||
EMIT4(0xb9040000, dst_reg, src_reg);
|
||||
case BPF_ALU64 | BPF_MOV | BPF_X:
|
||||
switch (insn->off) {
|
||||
case 0: /* DST = SRC */
|
||||
/* lgr %dst,%src */
|
||||
EMIT4(0xb9040000, dst_reg, src_reg);
|
||||
break;
|
||||
case 8: /* DST = (s8) SRC */
|
||||
/* lgbr %dst,%src */
|
||||
EMIT4(0xb9060000, dst_reg, src_reg);
|
||||
break;
|
||||
case 16: /* DST = (s16) SRC */
|
||||
/* lghr %dst,%src */
|
||||
EMIT4(0xb9070000, dst_reg, src_reg);
|
||||
break;
|
||||
case 32: /* DST = (s32) SRC */
|
||||
/* lgfr %dst,%src */
|
||||
EMIT4(0xb9140000, dst_reg, src_reg);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BPF_ALU | BPF_MOV | BPF_K: /* dst = (u32) imm */
|
||||
/* llilf %dst,imm */
|
||||
|
@ -912,66 +949,115 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
/*
|
||||
* BPF_DIV / BPF_MOD
|
||||
*/
|
||||
case BPF_ALU | BPF_DIV | BPF_X: /* dst = (u32) dst / (u32) src */
|
||||
case BPF_ALU | BPF_MOD | BPF_X: /* dst = (u32) dst % (u32) src */
|
||||
case BPF_ALU | BPF_DIV | BPF_X:
|
||||
case BPF_ALU | BPF_MOD | BPF_X:
|
||||
{
|
||||
int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0;
|
||||
|
||||
/* lhi %w0,0 */
|
||||
EMIT4_IMM(0xa7080000, REG_W0, 0);
|
||||
/* lr %w1,%dst */
|
||||
EMIT2(0x1800, REG_W1, dst_reg);
|
||||
/* dlr %w0,%src */
|
||||
EMIT4(0xb9970000, REG_W0, src_reg);
|
||||
switch (off) {
|
||||
case 0: /* dst = (u32) dst {/,%} (u32) src */
|
||||
/* xr %w0,%w0 */
|
||||
EMIT2(0x1700, REG_W0, REG_W0);
|
||||
/* lr %w1,%dst */
|
||||
EMIT2(0x1800, REG_W1, dst_reg);
|
||||
/* dlr %w0,%src */
|
||||
EMIT4(0xb9970000, REG_W0, src_reg);
|
||||
break;
|
||||
case 1: /* dst = (u32) ((s32) dst {/,%} (s32) src) */
|
||||
/* lgfr %r1,%dst */
|
||||
EMIT4(0xb9140000, REG_W1, dst_reg);
|
||||
/* dsgfr %r0,%src */
|
||||
EMIT4(0xb91d0000, REG_W0, src_reg);
|
||||
break;
|
||||
}
|
||||
/* llgfr %dst,%rc */
|
||||
EMIT4(0xb9160000, dst_reg, rc_reg);
|
||||
if (insn_is_zext(&insn[1]))
|
||||
insn_count = 2;
|
||||
break;
|
||||
}
|
||||
case BPF_ALU64 | BPF_DIV | BPF_X: /* dst = dst / src */
|
||||
case BPF_ALU64 | BPF_MOD | BPF_X: /* dst = dst % src */
|
||||
case BPF_ALU64 | BPF_DIV | BPF_X:
|
||||
case BPF_ALU64 | BPF_MOD | BPF_X:
|
||||
{
|
||||
int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0;
|
||||
|
||||
/* lghi %w0,0 */
|
||||
EMIT4_IMM(0xa7090000, REG_W0, 0);
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* dlgr %w0,%dst */
|
||||
EMIT4(0xb9870000, REG_W0, src_reg);
|
||||
switch (off) {
|
||||
case 0: /* dst = dst {/,%} src */
|
||||
/* lghi %w0,0 */
|
||||
EMIT4_IMM(0xa7090000, REG_W0, 0);
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* dlgr %w0,%src */
|
||||
EMIT4(0xb9870000, REG_W0, src_reg);
|
||||
break;
|
||||
case 1: /* dst = (s64) dst {/,%} (s64) src */
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* dsgr %w0,%src */
|
||||
EMIT4(0xb90d0000, REG_W0, src_reg);
|
||||
break;
|
||||
}
|
||||
/* lgr %dst,%rc */
|
||||
EMIT4(0xb9040000, dst_reg, rc_reg);
|
||||
break;
|
||||
}
|
||||
case BPF_ALU | BPF_DIV | BPF_K: /* dst = (u32) dst / (u32) imm */
|
||||
case BPF_ALU | BPF_MOD | BPF_K: /* dst = (u32) dst % (u32) imm */
|
||||
case BPF_ALU | BPF_DIV | BPF_K:
|
||||
case BPF_ALU | BPF_MOD | BPF_K:
|
||||
{
|
||||
int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0;
|
||||
|
||||
if (imm == 1) {
|
||||
if (BPF_OP(insn->code) == BPF_MOD)
|
||||
/* lhgi %dst,0 */
|
||||
/* lghi %dst,0 */
|
||||
EMIT4_IMM(0xa7090000, dst_reg, 0);
|
||||
else
|
||||
EMIT_ZERO(dst_reg);
|
||||
break;
|
||||
}
|
||||
/* lhi %w0,0 */
|
||||
EMIT4_IMM(0xa7080000, REG_W0, 0);
|
||||
/* lr %w1,%dst */
|
||||
EMIT2(0x1800, REG_W1, dst_reg);
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit32(jit)) {
|
||||
/* dl %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0, REG_L,
|
||||
EMIT_CONST_U32(imm));
|
||||
switch (off) {
|
||||
case 0: /* dst = (u32) dst {/,%} (u32) imm */
|
||||
/* xr %w0,%w0 */
|
||||
EMIT2(0x1700, REG_W0, REG_W0);
|
||||
/* lr %w1,%dst */
|
||||
EMIT2(0x1800, REG_W1, dst_reg);
|
||||
/* dl %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0,
|
||||
REG_L, EMIT_CONST_U32(imm));
|
||||
break;
|
||||
case 1: /* dst = (s32) dst {/,%} (s32) imm */
|
||||
/* lgfr %r1,%dst */
|
||||
EMIT4(0xb9140000, REG_W1, dst_reg);
|
||||
/* dsgf %r0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x001d, REG_W0, REG_0,
|
||||
REG_L, EMIT_CONST_U32(imm));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* lgfrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc40c0000, dst_reg,
|
||||
_EMIT_CONST_U32(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dlr %w0,%dst */
|
||||
EMIT4(0xb9970000, REG_W0, dst_reg);
|
||||
switch (off) {
|
||||
case 0: /* dst = (u32) dst {/,%} (u32) imm */
|
||||
/* xr %w0,%w0 */
|
||||
EMIT2(0x1700, REG_W0, REG_W0);
|
||||
/* lr %w1,%dst */
|
||||
EMIT2(0x1800, REG_W1, dst_reg);
|
||||
/* lrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc40d0000, dst_reg,
|
||||
_EMIT_CONST_U32(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dlr %w0,%dst */
|
||||
EMIT4(0xb9970000, REG_W0, dst_reg);
|
||||
break;
|
||||
case 1: /* dst = (s32) dst {/,%} (s32) imm */
|
||||
/* lgfr %w1,%dst */
|
||||
EMIT4(0xb9140000, REG_W1, dst_reg);
|
||||
/* lgfrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc40c0000, dst_reg,
|
||||
_EMIT_CONST_U32(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dsgr %w0,%dst */
|
||||
EMIT4(0xb90d0000, REG_W0, dst_reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* llgfr %dst,%rc */
|
||||
EMIT4(0xb9160000, dst_reg, rc_reg);
|
||||
|
@ -979,8 +1065,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
insn_count = 2;
|
||||
break;
|
||||
}
|
||||
case BPF_ALU64 | BPF_DIV | BPF_K: /* dst = dst / imm */
|
||||
case BPF_ALU64 | BPF_MOD | BPF_K: /* dst = dst % imm */
|
||||
case BPF_ALU64 | BPF_DIV | BPF_K:
|
||||
case BPF_ALU64 | BPF_MOD | BPF_K:
|
||||
{
|
||||
int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0;
|
||||
|
||||
|
@ -990,21 +1076,50 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT4_IMM(0xa7090000, dst_reg, 0);
|
||||
break;
|
||||
}
|
||||
/* lghi %w0,0 */
|
||||
EMIT4_IMM(0xa7090000, REG_W0, 0);
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
|
||||
/* dlg %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
switch (off) {
|
||||
case 0: /* dst = dst {/,%} imm */
|
||||
/* lghi %w0,0 */
|
||||
EMIT4_IMM(0xa7090000, REG_W0, 0);
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* dlg %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0,
|
||||
REG_L, EMIT_CONST_U64(imm));
|
||||
break;
|
||||
case 1: /* dst = (s64) dst {/,%} (s64) imm */
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* dsg %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x000d, REG_W0, REG_0,
|
||||
REG_L, EMIT_CONST_U64(imm));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* lgrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, dst_reg,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dlgr %w0,%dst */
|
||||
EMIT4(0xb9870000, REG_W0, dst_reg);
|
||||
switch (off) {
|
||||
case 0: /* dst = dst {/,%} imm */
|
||||
/* lghi %w0,0 */
|
||||
EMIT4_IMM(0xa7090000, REG_W0, 0);
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* lgrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, dst_reg,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dlgr %w0,%dst */
|
||||
EMIT4(0xb9870000, REG_W0, dst_reg);
|
||||
break;
|
||||
case 1: /* dst = (s64) dst {/,%} (s64) imm */
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* lgrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, dst_reg,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dsgr %w0,%dst */
|
||||
EMIT4(0xb90d0000, REG_W0, dst_reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* lgr %dst,%rc */
|
||||
EMIT4(0xb9040000, dst_reg, rc_reg);
|
||||
|
@ -1217,6 +1332,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
}
|
||||
break;
|
||||
case BPF_ALU | BPF_END | BPF_FROM_LE:
|
||||
case BPF_ALU64 | BPF_END | BPF_FROM_LE:
|
||||
switch (imm) {
|
||||
case 16: /* dst = (u16) cpu_to_le16(dst) */
|
||||
/* lrvr %dst,%dst */
|
||||
|
@ -1374,6 +1490,12 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
if (insn_is_zext(&insn[1]))
|
||||
insn_count = 2;
|
||||
break;
|
||||
case BPF_LDX | BPF_MEMSX | BPF_B: /* dst = *(s8 *)(ul) (src + off) */
|
||||
case BPF_LDX | BPF_PROBE_MEMSX | BPF_B:
|
||||
/* lgb %dst,0(off,%src) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0077, dst_reg, src_reg, REG_0, off);
|
||||
jit->seen |= SEEN_MEM;
|
||||
break;
|
||||
case BPF_LDX | BPF_MEM | BPF_H: /* dst = *(u16 *)(ul) (src + off) */
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_H:
|
||||
/* llgh %dst,0(off,%src) */
|
||||
|
@ -1382,6 +1504,12 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
if (insn_is_zext(&insn[1]))
|
||||
insn_count = 2;
|
||||
break;
|
||||
case BPF_LDX | BPF_MEMSX | BPF_H: /* dst = *(s16 *)(ul) (src + off) */
|
||||
case BPF_LDX | BPF_PROBE_MEMSX | BPF_H:
|
||||
/* lgh %dst,0(off,%src) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0015, dst_reg, src_reg, REG_0, off);
|
||||
jit->seen |= SEEN_MEM;
|
||||
break;
|
||||
case BPF_LDX | BPF_MEM | BPF_W: /* dst = *(u32 *)(ul) (src + off) */
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
|
||||
/* llgf %dst,off(%src) */
|
||||
|
@ -1390,6 +1518,12 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
if (insn_is_zext(&insn[1]))
|
||||
insn_count = 2;
|
||||
break;
|
||||
case BPF_LDX | BPF_MEMSX | BPF_W: /* dst = *(s32 *)(ul) (src + off) */
|
||||
case BPF_LDX | BPF_PROBE_MEMSX | BPF_W:
|
||||
/* lgf %dst,off(%src) */
|
||||
jit->seen |= SEEN_MEM;
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0014, dst_reg, src_reg, REG_0, off);
|
||||
break;
|
||||
case BPF_LDX | BPF_MEM | BPF_DW: /* dst = *(u64 *)(ul) (src + off) */
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
|
||||
/* lg %dst,0(off,%src) */
|
||||
|
@ -1570,6 +1704,9 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
* instruction itself (loop) and for BPF with offset 0 we
|
||||
* branch to the instruction behind the branch.
|
||||
*/
|
||||
case BPF_JMP32 | BPF_JA: /* if (true) */
|
||||
branch_oc_off = imm;
|
||||
fallthrough;
|
||||
case BPF_JMP | BPF_JA: /* if (true) */
|
||||
mask = 0xf000; /* j */
|
||||
goto branch_oc;
|
||||
|
@ -1738,14 +1875,16 @@ branch_xu:
|
|||
break;
|
||||
branch_oc:
|
||||
if (!is_first_pass(jit) &&
|
||||
can_use_rel(jit, addrs[i + off + 1])) {
|
||||
can_use_rel(jit, addrs[i + branch_oc_off + 1])) {
|
||||
/* brc mask,off */
|
||||
EMIT4_PCREL_RIC(0xa7040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
mask >> 12,
|
||||
addrs[i + branch_oc_off + 1]);
|
||||
} else {
|
||||
/* brcl mask,off */
|
||||
EMIT6_PCREL_RILC(0xc0040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
mask >> 12,
|
||||
addrs[i + branch_oc_off + 1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include <asm/set_memory.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <asm/text-patching.h>
|
||||
#include <asm/unwind.h>
|
||||
|
||||
static bool all_callee_regs_used[4] = {true, true, true, true};
|
||||
|
||||
static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
|
||||
{
|
||||
|
@ -255,6 +258,14 @@ struct jit_context {
|
|||
/* Number of bytes that will be skipped on tailcall */
|
||||
#define X86_TAIL_CALL_OFFSET (11 + ENDBR_INSN_SIZE)
|
||||
|
||||
static void push_r12(u8 **pprog)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
||||
EMIT2(0x41, 0x54); /* push r12 */
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void push_callee_regs(u8 **pprog, bool *callee_regs_used)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
@ -270,6 +281,14 @@ static void push_callee_regs(u8 **pprog, bool *callee_regs_used)
|
|||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void pop_r12(u8 **pprog)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
||||
EMIT2(0x41, 0x5C); /* pop r12 */
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void pop_callee_regs(u8 **pprog, bool *callee_regs_used)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
@ -291,7 +310,8 @@ static void pop_callee_regs(u8 **pprog, bool *callee_regs_used)
|
|||
* while jumping to another program
|
||||
*/
|
||||
static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf,
|
||||
bool tail_call_reachable, bool is_subprog)
|
||||
bool tail_call_reachable, bool is_subprog,
|
||||
bool is_exception_cb)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
||||
|
@ -303,12 +323,30 @@ static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf,
|
|||
prog += X86_PATCH_SIZE;
|
||||
if (!ebpf_from_cbpf) {
|
||||
if (tail_call_reachable && !is_subprog)
|
||||
/* When it's the entry of the whole tailcall context,
|
||||
* zeroing rax means initialising tail_call_cnt.
|
||||
*/
|
||||
EMIT2(0x31, 0xC0); /* xor eax, eax */
|
||||
else
|
||||
/* Keep the same instruction layout. */
|
||||
EMIT2(0x66, 0x90); /* nop2 */
|
||||
}
|
||||
EMIT1(0x55); /* push rbp */
|
||||
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
||||
/* Exception callback receives FP as third parameter */
|
||||
if (is_exception_cb) {
|
||||
EMIT3(0x48, 0x89, 0xF4); /* mov rsp, rsi */
|
||||
EMIT3(0x48, 0x89, 0xD5); /* mov rbp, rdx */
|
||||
/* The main frame must have exception_boundary as true, so we
|
||||
* first restore those callee-saved regs from stack, before
|
||||
* reusing the stack frame.
|
||||
*/
|
||||
pop_callee_regs(&prog, all_callee_regs_used);
|
||||
pop_r12(&prog);
|
||||
/* Reset the stack frame. */
|
||||
EMIT3(0x48, 0x89, 0xEC); /* mov rsp, rbp */
|
||||
} else {
|
||||
EMIT1(0x55); /* push rbp */
|
||||
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
||||
}
|
||||
|
||||
/* X86_TAIL_CALL_OFFSET is here */
|
||||
EMIT_ENDBR();
|
||||
|
@ -467,7 +505,8 @@ static void emit_return(u8 **pprog, u8 *ip)
|
|||
* goto *(prog->bpf_func + prologue_size);
|
||||
* out:
|
||||
*/
|
||||
static void emit_bpf_tail_call_indirect(u8 **pprog, bool *callee_regs_used,
|
||||
static void emit_bpf_tail_call_indirect(struct bpf_prog *bpf_prog,
|
||||
u8 **pprog, bool *callee_regs_used,
|
||||
u32 stack_depth, u8 *ip,
|
||||
struct jit_context *ctx)
|
||||
{
|
||||
|
@ -517,7 +556,12 @@ static void emit_bpf_tail_call_indirect(u8 **pprog, bool *callee_regs_used,
|
|||
offset = ctx->tail_call_indirect_label - (prog + 2 - start);
|
||||
EMIT2(X86_JE, offset); /* je out */
|
||||
|
||||
pop_callee_regs(&prog, callee_regs_used);
|
||||
if (bpf_prog->aux->exception_boundary) {
|
||||
pop_callee_regs(&prog, all_callee_regs_used);
|
||||
pop_r12(&prog);
|
||||
} else {
|
||||
pop_callee_regs(&prog, callee_regs_used);
|
||||
}
|
||||
|
||||
EMIT1(0x58); /* pop rax */
|
||||
if (stack_depth)
|
||||
|
@ -541,7 +585,8 @@ static void emit_bpf_tail_call_indirect(u8 **pprog, bool *callee_regs_used,
|
|||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_bpf_tail_call_direct(struct bpf_jit_poke_descriptor *poke,
|
||||
static void emit_bpf_tail_call_direct(struct bpf_prog *bpf_prog,
|
||||
struct bpf_jit_poke_descriptor *poke,
|
||||
u8 **pprog, u8 *ip,
|
||||
bool *callee_regs_used, u32 stack_depth,
|
||||
struct jit_context *ctx)
|
||||
|
@ -570,7 +615,13 @@ static void emit_bpf_tail_call_direct(struct bpf_jit_poke_descriptor *poke,
|
|||
emit_jump(&prog, (u8 *)poke->tailcall_target + X86_PATCH_SIZE,
|
||||
poke->tailcall_bypass);
|
||||
|
||||
pop_callee_regs(&prog, callee_regs_used);
|
||||
if (bpf_prog->aux->exception_boundary) {
|
||||
pop_callee_regs(&prog, all_callee_regs_used);
|
||||
pop_r12(&prog);
|
||||
} else {
|
||||
pop_callee_regs(&prog, callee_regs_used);
|
||||
}
|
||||
|
||||
EMIT1(0x58); /* pop rax */
|
||||
if (stack_depth)
|
||||
EMIT3_off32(0x48, 0x81, 0xC4, round_up(stack_depth, 8));
|
||||
|
@ -1018,6 +1069,10 @@ static void emit_shiftx(u8 **pprog, u32 dst_reg, u8 src_reg, bool is64, u8 op)
|
|||
|
||||
#define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp)))
|
||||
|
||||
/* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */
|
||||
#define RESTORE_TAIL_CALL_CNT(stack) \
|
||||
EMIT3_off32(0x48, 0x8B, 0x85, -round_up(stack, 8) - 8)
|
||||
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image,
|
||||
int oldproglen, struct jit_context *ctx, bool jmp_padding)
|
||||
{
|
||||
|
@ -1041,8 +1096,20 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
|
|||
|
||||
emit_prologue(&prog, bpf_prog->aux->stack_depth,
|
||||
bpf_prog_was_classic(bpf_prog), tail_call_reachable,
|
||||
bpf_prog->aux->func_idx != 0);
|
||||
push_callee_regs(&prog, callee_regs_used);
|
||||
bpf_is_subprog(bpf_prog), bpf_prog->aux->exception_cb);
|
||||
/* Exception callback will clobber callee regs for its own use, and
|
||||
* restore the original callee regs from main prog's stack frame.
|
||||
*/
|
||||
if (bpf_prog->aux->exception_boundary) {
|
||||
/* We also need to save r12, which is not mapped to any BPF
|
||||
* register, as we throw after entry into the kernel, which may
|
||||
* overwrite r12.
|
||||
*/
|
||||
push_r12(&prog);
|
||||
push_callee_regs(&prog, all_callee_regs_used);
|
||||
} else {
|
||||
push_callee_regs(&prog, callee_regs_used);
|
||||
}
|
||||
|
||||
ilen = prog - temp;
|
||||
if (rw_image)
|
||||
|
@ -1623,9 +1690,7 @@ st: if (is_imm8(insn->off))
|
|||
|
||||
func = (u8 *) __bpf_call_base + imm32;
|
||||
if (tail_call_reachable) {
|
||||
/* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */
|
||||
EMIT3_off32(0x48, 0x8B, 0x85,
|
||||
-round_up(bpf_prog->aux->stack_depth, 8) - 8);
|
||||
RESTORE_TAIL_CALL_CNT(bpf_prog->aux->stack_depth);
|
||||
if (!imm32)
|
||||
return -EINVAL;
|
||||
offs = 7 + x86_call_depth_emit_accounting(&prog, func);
|
||||
|
@ -1641,13 +1706,15 @@ st: if (is_imm8(insn->off))
|
|||
|
||||
case BPF_JMP | BPF_TAIL_CALL:
|
||||
if (imm32)
|
||||
emit_bpf_tail_call_direct(&bpf_prog->aux->poke_tab[imm32 - 1],
|
||||
emit_bpf_tail_call_direct(bpf_prog,
|
||||
&bpf_prog->aux->poke_tab[imm32 - 1],
|
||||
&prog, image + addrs[i - 1],
|
||||
callee_regs_used,
|
||||
bpf_prog->aux->stack_depth,
|
||||
ctx);
|
||||
else
|
||||
emit_bpf_tail_call_indirect(&prog,
|
||||
emit_bpf_tail_call_indirect(bpf_prog,
|
||||
&prog,
|
||||
callee_regs_used,
|
||||
bpf_prog->aux->stack_depth,
|
||||
image + addrs[i - 1],
|
||||
|
@ -1900,7 +1967,12 @@ emit_jmp:
|
|||
seen_exit = true;
|
||||
/* Update cleanup_addr */
|
||||
ctx->cleanup_addr = proglen;
|
||||
pop_callee_regs(&prog, callee_regs_used);
|
||||
if (bpf_prog->aux->exception_boundary) {
|
||||
pop_callee_regs(&prog, all_callee_regs_used);
|
||||
pop_r12(&prog);
|
||||
} else {
|
||||
pop_callee_regs(&prog, callee_regs_used);
|
||||
}
|
||||
EMIT1(0xC9); /* leave */
|
||||
emit_return(&prog, image + addrs[i - 1] + (prog - temp));
|
||||
break;
|
||||
|
@ -2400,6 +2472,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
* [ ... ]
|
||||
* [ stack_arg2 ]
|
||||
* RBP - arg_stack_off [ stack_arg1 ]
|
||||
* RSP [ tail_call_cnt ] BPF_TRAMP_F_TAIL_CALL_CTX
|
||||
*/
|
||||
|
||||
/* room for return value of orig_call or fentry prog */
|
||||
|
@ -2464,6 +2537,8 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
else
|
||||
/* sub rsp, stack_size */
|
||||
EMIT4(0x48, 0x83, 0xEC, stack_size);
|
||||
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
|
||||
EMIT1(0x50); /* push rax */
|
||||
/* mov QWORD PTR [rbp - rbx_off], rbx */
|
||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_6, -rbx_off);
|
||||
|
||||
|
@ -2516,9 +2591,15 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
restore_regs(m, &prog, regs_off);
|
||||
save_args(m, &prog, arg_stack_off, true);
|
||||
|
||||
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
|
||||
/* Before calling the original function, restore the
|
||||
* tail_call_cnt from stack to rax.
|
||||
*/
|
||||
RESTORE_TAIL_CALL_CNT(stack_size);
|
||||
|
||||
if (flags & BPF_TRAMP_F_ORIG_STACK) {
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, 8);
|
||||
EMIT2(0xff, 0xd0); /* call *rax */
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, 8);
|
||||
EMIT2(0xff, 0xd3); /* call *rbx */
|
||||
} else {
|
||||
/* call original function */
|
||||
if (emit_rsb_call(&prog, orig_call, prog)) {
|
||||
|
@ -2569,7 +2650,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
} else if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
|
||||
/* Before running the original function, restore the
|
||||
* tail_call_cnt from stack to rax.
|
||||
*/
|
||||
RESTORE_TAIL_CALL_CNT(stack_size);
|
||||
|
||||
/* restore return value of orig_call or fentry prog back into RAX */
|
||||
if (save_ret)
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
|
||||
|
@ -2913,3 +2999,29 @@ void bpf_jit_free(struct bpf_prog *prog)
|
|||
|
||||
bpf_prog_unlock_free(prog);
|
||||
}
|
||||
|
||||
bool bpf_jit_supports_exceptions(void)
|
||||
{
|
||||
/* We unwind through both kernel frames (starting from within bpf_throw
|
||||
* call) and BPF frames. Therefore we require ORC unwinder to be enabled
|
||||
* to walk kernel frames and reach BPF frames in the stack trace.
|
||||
*/
|
||||
return IS_ENABLED(CONFIG_UNWINDER_ORC);
|
||||
}
|
||||
|
||||
void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie)
|
||||
{
|
||||
#if defined(CONFIG_UNWINDER_ORC)
|
||||
struct unwind_state state;
|
||||
unsigned long addr;
|
||||
|
||||
for (unwind_start(&state, current, NULL, NULL); !unwind_done(&state);
|
||||
unwind_next_frame(&state)) {
|
||||
addr = unwind_get_return_address(&state);
|
||||
if (!addr || !consume_fn(cookie, (u64)addr, (u64)state.sp, (u64)state.bp))
|
||||
break;
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
WARN(1, "verification of programs using bpf_throw should have failed\n");
|
||||
}
|
||||
|
|
|
@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
|
|||
|
||||
source "drivers/cdx/Kconfig"
|
||||
|
||||
source "drivers/dpll/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -197,5 +197,6 @@ obj-$(CONFIG_PECI) += peci/
|
|||
obj-$(CONFIG_HTE) += hte/
|
||||
obj-$(CONFIG_DRM_ACCEL) += accel/
|
||||
obj-$(CONFIG_CDX_BUS) += cdx/
|
||||
obj-$(CONFIG_DPLL) += dpll/
|
||||
|
||||
obj-$(CONFIG_S390) += s390/
|
||||
|
|
7
drivers/dpll/Kconfig
Normal file
7
drivers/dpll/Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Generic DPLL drivers configuration
|
||||
#
|
||||
|
||||
config DPLL
|
||||
bool
|
9
drivers/dpll/Makefile
Normal file
9
drivers/dpll/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for DPLL drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DPLL) += dpll.o
|
||||
dpll-y += dpll_core.o
|
||||
dpll-y += dpll_netlink.o
|
||||
dpll-y += dpll_nl.o
|
798
drivers/dpll/dpll_core.c
Normal file
798
drivers/dpll/dpll_core.c
Normal file
|
@ -0,0 +1,798 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dpll_core.c - DPLL subsystem kernel-space interface implementation.
|
||||
*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates
|
||||
* Copyright (c) 2023 Intel Corporation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "dpll_core.h"
|
||||
#include "dpll_netlink.h"
|
||||
|
||||
/* Mutex lock to protect DPLL subsystem devices and pins */
|
||||
DEFINE_MUTEX(dpll_lock);
|
||||
|
||||
DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
|
||||
DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
|
||||
|
||||
static u32 dpll_xa_id;
|
||||
|
||||
#define ASSERT_DPLL_REGISTERED(d) \
|
||||
WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
|
||||
#define ASSERT_DPLL_NOT_REGISTERED(d) \
|
||||
WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
|
||||
#define ASSERT_PIN_REGISTERED(p) \
|
||||
WARN_ON_ONCE(!xa_get_mark(&dpll_pin_xa, (p)->id, DPLL_REGISTERED))
|
||||
|
||||
struct dpll_device_registration {
|
||||
struct list_head list;
|
||||
const struct dpll_device_ops *ops;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct dpll_pin_registration {
|
||||
struct list_head list;
|
||||
const struct dpll_pin_ops *ops;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct dpll_device *dpll_device_get_by_id(int id)
|
||||
{
|
||||
if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
|
||||
return xa_load(&dpll_device_xa, id);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct dpll_pin_registration *
|
||||
dpll_pin_registration_find(struct dpll_pin_ref *ref,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
|
||||
list_for_each_entry(reg, &ref->registration_list, list) {
|
||||
if (reg->ops == ops && reg->priv == priv)
|
||||
return reg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
struct dpll_pin_ref *ref;
|
||||
bool ref_exists = false;
|
||||
unsigned long i;
|
||||
int ret;
|
||||
|
||||
xa_for_each(xa_pins, i, ref) {
|
||||
if (ref->pin != pin)
|
||||
continue;
|
||||
reg = dpll_pin_registration_find(ref, ops, priv);
|
||||
if (reg) {
|
||||
refcount_inc(&ref->refcount);
|
||||
return 0;
|
||||
}
|
||||
ref_exists = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ref_exists) {
|
||||
ref = kzalloc(sizeof(*ref), GFP_KERNEL);
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
ref->pin = pin;
|
||||
INIT_LIST_HEAD(&ref->registration_list);
|
||||
ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(ref);
|
||||
return ret;
|
||||
}
|
||||
refcount_set(&ref->refcount, 1);
|
||||
}
|
||||
|
||||
reg = kzalloc(sizeof(*reg), GFP_KERNEL);
|
||||
if (!reg) {
|
||||
if (!ref_exists) {
|
||||
xa_erase(xa_pins, pin->pin_idx);
|
||||
kfree(ref);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
reg->ops = ops;
|
||||
reg->priv = priv;
|
||||
if (ref_exists)
|
||||
refcount_inc(&ref->refcount);
|
||||
list_add_tail(®->list, &ref->registration_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
struct dpll_pin_ref *ref;
|
||||
unsigned long i;
|
||||
|
||||
xa_for_each(xa_pins, i, ref) {
|
||||
if (ref->pin != pin)
|
||||
continue;
|
||||
reg = dpll_pin_registration_find(ref, ops, priv);
|
||||
if (WARN_ON(!reg))
|
||||
return -EINVAL;
|
||||
if (refcount_dec_and_test(&ref->refcount)) {
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
xa_erase(xa_pins, i);
|
||||
WARN_ON(!list_empty(&ref->registration_list));
|
||||
kfree(ref);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
struct dpll_pin_ref *ref;
|
||||
bool ref_exists = false;
|
||||
unsigned long i;
|
||||
int ret;
|
||||
|
||||
xa_for_each(xa_dplls, i, ref) {
|
||||
if (ref->dpll != dpll)
|
||||
continue;
|
||||
reg = dpll_pin_registration_find(ref, ops, priv);
|
||||
if (reg) {
|
||||
refcount_inc(&ref->refcount);
|
||||
return 0;
|
||||
}
|
||||
ref_exists = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ref_exists) {
|
||||
ref = kzalloc(sizeof(*ref), GFP_KERNEL);
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
ref->dpll = dpll;
|
||||
INIT_LIST_HEAD(&ref->registration_list);
|
||||
ret = xa_insert(xa_dplls, dpll->id, ref, GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(ref);
|
||||
return ret;
|
||||
}
|
||||
refcount_set(&ref->refcount, 1);
|
||||
}
|
||||
|
||||
reg = kzalloc(sizeof(*reg), GFP_KERNEL);
|
||||
if (!reg) {
|
||||
if (!ref_exists) {
|
||||
xa_erase(xa_dplls, dpll->id);
|
||||
kfree(ref);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
reg->ops = ops;
|
||||
reg->priv = priv;
|
||||
if (ref_exists)
|
||||
refcount_inc(&ref->refcount);
|
||||
list_add_tail(®->list, &ref->registration_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
struct dpll_pin_ref *ref;
|
||||
unsigned long i;
|
||||
|
||||
xa_for_each(xa_dplls, i, ref) {
|
||||
if (ref->dpll != dpll)
|
||||
continue;
|
||||
reg = dpll_pin_registration_find(ref, ops, priv);
|
||||
if (WARN_ON(!reg))
|
||||
return;
|
||||
if (refcount_dec_and_test(&ref->refcount)) {
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
xa_erase(xa_dplls, i);
|
||||
WARN_ON(!list_empty(&ref->registration_list));
|
||||
kfree(ref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)
|
||||
{
|
||||
struct dpll_pin_ref *ref;
|
||||
unsigned long i = 0;
|
||||
|
||||
ref = xa_find(xa_refs, &i, ULONG_MAX, XA_PRESENT);
|
||||
WARN_ON(!ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
static struct dpll_device *
|
||||
dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
|
||||
{
|
||||
struct dpll_device *dpll;
|
||||
int ret;
|
||||
|
||||
dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
|
||||
if (!dpll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
refcount_set(&dpll->refcount, 1);
|
||||
INIT_LIST_HEAD(&dpll->registration_list);
|
||||
dpll->device_idx = device_idx;
|
||||
dpll->clock_id = clock_id;
|
||||
dpll->module = module;
|
||||
ret = xa_alloc_cyclic(&dpll_device_xa, &dpll->id, dpll, xa_limit_32b,
|
||||
&dpll_xa_id, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
kfree(dpll);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
|
||||
|
||||
return dpll;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpll_device_get - find existing or create new dpll device
|
||||
* @clock_id: clock_id of creator
|
||||
* @device_idx: idx given by device driver
|
||||
* @module: reference to registering module
|
||||
*
|
||||
* Get existing object of a dpll device, unique for given arguments.
|
||||
* Create new if doesn't exist yet.
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Return:
|
||||
* * valid dpll_device struct pointer if succeeded
|
||||
* * ERR_PTR(X) - error
|
||||
*/
|
||||
struct dpll_device *
|
||||
dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
|
||||
{
|
||||
struct dpll_device *dpll, *ret = NULL;
|
||||
unsigned long index;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
xa_for_each(&dpll_device_xa, index, dpll) {
|
||||
if (dpll->clock_id == clock_id &&
|
||||
dpll->device_idx == device_idx &&
|
||||
dpll->module == module) {
|
||||
ret = dpll;
|
||||
refcount_inc(&ret->refcount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ret)
|
||||
ret = dpll_device_alloc(clock_id, device_idx, module);
|
||||
mutex_unlock(&dpll_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_device_get);
|
||||
|
||||
/**
|
||||
* dpll_device_put - decrease the refcount and free memory if possible
|
||||
* @dpll: dpll_device struct pointer
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Drop reference for a dpll device, if all references are gone, delete
|
||||
* dpll device object.
|
||||
*/
|
||||
void dpll_device_put(struct dpll_device *dpll)
|
||||
{
|
||||
mutex_lock(&dpll_lock);
|
||||
if (refcount_dec_and_test(&dpll->refcount)) {
|
||||
ASSERT_DPLL_NOT_REGISTERED(dpll);
|
||||
WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
|
||||
xa_destroy(&dpll->pin_refs);
|
||||
xa_erase(&dpll_device_xa, dpll->id);
|
||||
WARN_ON(!list_empty(&dpll->registration_list));
|
||||
kfree(dpll);
|
||||
}
|
||||
mutex_unlock(&dpll_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_device_put);
|
||||
|
||||
static struct dpll_device_registration *
|
||||
dpll_device_registration_find(struct dpll_device *dpll,
|
||||
const struct dpll_device_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_device_registration *reg;
|
||||
|
||||
list_for_each_entry(reg, &dpll->registration_list, list) {
|
||||
if (reg->ops == ops && reg->priv == priv)
|
||||
return reg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpll_device_register - register the dpll device in the subsystem
|
||||
* @dpll: pointer to a dpll
|
||||
* @type: type of a dpll
|
||||
* @ops: ops for a dpll device
|
||||
* @priv: pointer to private information of owner
|
||||
*
|
||||
* Make dpll device available for user space.
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Return:
|
||||
* * 0 on success
|
||||
* * negative - error value
|
||||
*/
|
||||
int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
|
||||
const struct dpll_device_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_device_registration *reg;
|
||||
bool first_registration = false;
|
||||
|
||||
if (WARN_ON(!ops))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!ops->mode_get))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!ops->lock_status_get))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
reg = dpll_device_registration_find(dpll, ops, priv);
|
||||
if (reg) {
|
||||
mutex_unlock(&dpll_lock);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
reg = kzalloc(sizeof(*reg), GFP_KERNEL);
|
||||
if (!reg) {
|
||||
mutex_unlock(&dpll_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
reg->ops = ops;
|
||||
reg->priv = priv;
|
||||
dpll->type = type;
|
||||
first_registration = list_empty(&dpll->registration_list);
|
||||
list_add_tail(®->list, &dpll->registration_list);
|
||||
if (!first_registration) {
|
||||
mutex_unlock(&dpll_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
|
||||
dpll_device_create_ntf(dpll);
|
||||
mutex_unlock(&dpll_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_device_register);
|
||||
|
||||
/**
|
||||
* dpll_device_unregister - unregister dpll device
|
||||
* @dpll: registered dpll pointer
|
||||
* @ops: ops for a dpll device
|
||||
* @priv: pointer to private information of owner
|
||||
*
|
||||
* Unregister device, make it unavailable for userspace.
|
||||
* Note: It does not free the memory
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
*/
|
||||
void dpll_device_unregister(struct dpll_device *dpll,
|
||||
const struct dpll_device_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_device_registration *reg;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
ASSERT_DPLL_REGISTERED(dpll);
|
||||
dpll_device_delete_ntf(dpll);
|
||||
reg = dpll_device_registration_find(dpll, ops, priv);
|
||||
if (WARN_ON(!reg)) {
|
||||
mutex_unlock(&dpll_lock);
|
||||
return;
|
||||
}
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
|
||||
if (!list_empty(&dpll->registration_list)) {
|
||||
mutex_unlock(&dpll_lock);
|
||||
return;
|
||||
}
|
||||
xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
|
||||
mutex_unlock(&dpll_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_device_unregister);
|
||||
|
||||
static struct dpll_pin *
|
||||
dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
|
||||
const struct dpll_pin_properties *prop)
|
||||
{
|
||||
struct dpll_pin *pin;
|
||||
int ret;
|
||||
|
||||
pin = kzalloc(sizeof(*pin), GFP_KERNEL);
|
||||
if (!pin)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
pin->pin_idx = pin_idx;
|
||||
pin->clock_id = clock_id;
|
||||
pin->module = module;
|
||||
if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
|
||||
prop->type > DPLL_PIN_TYPE_MAX)) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
pin->prop = prop;
|
||||
refcount_set(&pin->refcount, 1);
|
||||
xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
|
||||
xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
|
||||
ret = xa_alloc(&dpll_pin_xa, &pin->id, pin, xa_limit_16b, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err;
|
||||
return pin;
|
||||
err:
|
||||
xa_destroy(&pin->dpll_refs);
|
||||
xa_destroy(&pin->parent_refs);
|
||||
kfree(pin);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpll_pin_get - find existing or create new dpll pin
|
||||
* @clock_id: clock_id of creator
|
||||
* @pin_idx: idx given by dev driver
|
||||
* @module: reference to registering module
|
||||
* @prop: dpll pin properties
|
||||
*
|
||||
* Get existing object of a pin (unique for given arguments) or create new
|
||||
* if doesn't exist yet.
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Return:
|
||||
* * valid allocated dpll_pin struct pointer if succeeded
|
||||
* * ERR_PTR(X) - error
|
||||
*/
|
||||
struct dpll_pin *
|
||||
dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
|
||||
const struct dpll_pin_properties *prop)
|
||||
{
|
||||
struct dpll_pin *pos, *ret = NULL;
|
||||
unsigned long i;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
xa_for_each(&dpll_pin_xa, i, pos) {
|
||||
if (pos->clock_id == clock_id &&
|
||||
pos->pin_idx == pin_idx &&
|
||||
pos->module == module) {
|
||||
ret = pos;
|
||||
refcount_inc(&ret->refcount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ret)
|
||||
ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
|
||||
mutex_unlock(&dpll_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_pin_get);
|
||||
|
||||
/**
|
||||
* dpll_pin_put - decrease the refcount and free memory if possible
|
||||
* @pin: pointer to a pin to be put
|
||||
*
|
||||
* Drop reference for a pin, if all references are gone, delete pin object.
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
*/
|
||||
void dpll_pin_put(struct dpll_pin *pin)
|
||||
{
|
||||
mutex_lock(&dpll_lock);
|
||||
if (refcount_dec_and_test(&pin->refcount)) {
|
||||
xa_destroy(&pin->dpll_refs);
|
||||
xa_destroy(&pin->parent_refs);
|
||||
xa_erase(&dpll_pin_xa, pin->id);
|
||||
kfree(pin);
|
||||
}
|
||||
mutex_unlock(&dpll_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_pin_put);
|
||||
|
||||
static int
|
||||
__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
|
||||
if (ret)
|
||||
goto ref_pin_del;
|
||||
xa_set_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED);
|
||||
dpll_pin_create_ntf(pin);
|
||||
|
||||
return ret;
|
||||
|
||||
ref_pin_del:
|
||||
dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpll_pin_register - register the dpll pin in the subsystem
|
||||
* @dpll: pointer to a dpll
|
||||
* @pin: pointer to a dpll pin
|
||||
* @ops: ops for a dpll pin ops
|
||||
* @priv: pointer to private information of owner
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Return:
|
||||
* * 0 on success
|
||||
* * negative - error value
|
||||
*/
|
||||
int
|
||||
dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!ops) ||
|
||||
WARN_ON(!ops->state_on_dpll_get) ||
|
||||
WARN_ON(!ops->direction_get))
|
||||
return -EINVAL;
|
||||
if (ASSERT_DPLL_REGISTERED(dpll))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
if (WARN_ON(!(dpll->module == pin->module &&
|
||||
dpll->clock_id == pin->clock_id)))
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = __dpll_pin_register(dpll, pin, ops, priv);
|
||||
mutex_unlock(&dpll_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_pin_register);
|
||||
|
||||
static void
|
||||
__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
|
||||
dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv);
|
||||
if (xa_empty(&pin->dpll_refs))
|
||||
xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpll_pin_unregister - unregister dpll pin from dpll device
|
||||
* @dpll: registered dpll pointer
|
||||
* @pin: pointer to a pin
|
||||
* @ops: ops for a dpll pin
|
||||
* @priv: pointer to private information of owner
|
||||
*
|
||||
* Note: It does not free the memory
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
*/
|
||||
void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
if (WARN_ON(xa_empty(&dpll->pin_refs)))
|
||||
return;
|
||||
if (WARN_ON(!xa_empty(&pin->parent_refs)))
|
||||
return;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
dpll_pin_delete_ntf(pin);
|
||||
__dpll_pin_unregister(dpll, pin, ops, priv);
|
||||
mutex_unlock(&dpll_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_pin_unregister);
|
||||
|
||||
/**
|
||||
* dpll_pin_on_pin_register - register a pin with a parent pin
|
||||
* @parent: pointer to a parent pin
|
||||
* @pin: pointer to a pin
|
||||
* @ops: ops for a dpll pin
|
||||
* @priv: pointer to private information of owner
|
||||
*
|
||||
* Register a pin with a parent pin, create references between them and
|
||||
* between newly registered pin and dplls connected with a parent pin.
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Return:
|
||||
* * 0 on success
|
||||
* * negative - error value
|
||||
*/
|
||||
int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_ref *ref;
|
||||
unsigned long i, stop;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(!ops) ||
|
||||
WARN_ON(!ops->state_on_pin_get) ||
|
||||
WARN_ON(!ops->direction_get))
|
||||
return -EINVAL;
|
||||
if (ASSERT_PIN_REGISTERED(parent))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
refcount_inc(&pin->refcount);
|
||||
xa_for_each(&parent->dpll_refs, i, ref) {
|
||||
ret = __dpll_pin_register(ref->dpll, pin, ops, priv);
|
||||
if (ret) {
|
||||
stop = i;
|
||||
goto dpll_unregister;
|
||||
}
|
||||
dpll_pin_create_ntf(pin);
|
||||
}
|
||||
mutex_unlock(&dpll_lock);
|
||||
|
||||
return ret;
|
||||
|
||||
dpll_unregister:
|
||||
xa_for_each(&parent->dpll_refs, i, ref)
|
||||
if (i < stop) {
|
||||
__dpll_pin_unregister(ref->dpll, pin, ops, priv);
|
||||
dpll_pin_delete_ntf(pin);
|
||||
}
|
||||
refcount_dec(&pin->refcount);
|
||||
dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
|
||||
unlock:
|
||||
mutex_unlock(&dpll_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);
|
||||
|
||||
/**
|
||||
* dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
|
||||
* @parent: pointer to a parent pin
|
||||
* @pin: pointer to a pin
|
||||
* @ops: ops for a dpll pin
|
||||
* @priv: pointer to private information of owner
|
||||
*
|
||||
* Context: Acquires a lock (dpll_lock)
|
||||
* Note: It does not free the memory
|
||||
*/
|
||||
void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv)
|
||||
{
|
||||
struct dpll_pin_ref *ref;
|
||||
unsigned long i;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
dpll_pin_delete_ntf(pin);
|
||||
dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
|
||||
refcount_dec(&pin->refcount);
|
||||
xa_for_each(&pin->dpll_refs, i, ref)
|
||||
__dpll_pin_unregister(ref->dpll, pin, ops, priv);
|
||||
mutex_unlock(&dpll_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void *dpll_priv(struct dpll_device *dpll)
|
||||
{
|
||||
struct dpll_device_registration *reg;
|
||||
|
||||
reg = dpll_device_registration_first(dpll);
|
||||
return reg->priv;
|
||||
}
|
||||
|
||||
const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
|
||||
{
|
||||
struct dpll_device_registration *reg;
|
||||
|
||||
reg = dpll_device_registration_first(dpll);
|
||||
return reg->ops;
|
||||
}
|
||||
|
||||
static struct dpll_pin_registration *
|
||||
dpll_pin_registration_first(struct dpll_pin_ref *ref)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
|
||||
reg = list_first_entry_or_null(&ref->registration_list,
|
||||
struct dpll_pin_registration, list);
|
||||
WARN_ON(!reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
void *dpll_pin_on_dpll_priv(struct dpll_device *dpll,
|
||||
struct dpll_pin *pin)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
struct dpll_pin_ref *ref;
|
||||
|
||||
ref = xa_load(&dpll->pin_refs, pin->pin_idx);
|
||||
if (!ref)
|
||||
return NULL;
|
||||
reg = dpll_pin_registration_first(ref);
|
||||
return reg->priv;
|
||||
}
|
||||
|
||||
void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
|
||||
struct dpll_pin *pin)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
struct dpll_pin_ref *ref;
|
||||
|
||||
ref = xa_load(&pin->parent_refs, parent->pin_idx);
|
||||
if (!ref)
|
||||
return NULL;
|
||||
reg = dpll_pin_registration_first(ref);
|
||||
return reg->priv;
|
||||
}
|
||||
|
||||
const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
|
||||
reg = dpll_pin_registration_first(ref);
|
||||
return reg->ops;
|
||||
}
|
||||
|
||||
static int __init dpll_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genl_register_family(&dpll_nl_family);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mutex_destroy(&dpll_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dpll_exit(void)
|
||||
{
|
||||
genl_unregister_family(&dpll_nl_family);
|
||||
mutex_destroy(&dpll_lock);
|
||||
}
|
||||
|
||||
subsys_initcall(dpll_init);
|
||||
module_exit(dpll_exit);
|
89
drivers/dpll/dpll_core.h
Normal file
89
drivers/dpll/dpll_core.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates
|
||||
* Copyright (c) 2023 Intel and affiliates
|
||||
*/
|
||||
|
||||
#ifndef __DPLL_CORE_H__
|
||||
#define __DPLL_CORE_H__
|
||||
|
||||
#include <linux/dpll.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/refcount.h>
|
||||
#include "dpll_nl.h"
|
||||
|
||||
#define DPLL_REGISTERED XA_MARK_1
|
||||
|
||||
/**
|
||||
* struct dpll_device - stores DPLL device internal data
|
||||
* @id: unique id number for device given by dpll subsystem
|
||||
* @device_idx: id given by dev driver
|
||||
* @clock_id: unique identifier (clock_id) of a dpll
|
||||
* @module: module of creator
|
||||
* @type: type of a dpll
|
||||
* @pin_refs: stores pins registered within a dpll
|
||||
* @refcount: refcount
|
||||
* @registration_list: list of registered ops and priv data of dpll owners
|
||||
**/
|
||||
struct dpll_device {
|
||||
u32 id;
|
||||
u32 device_idx;
|
||||
u64 clock_id;
|
||||
struct module *module;
|
||||
enum dpll_type type;
|
||||
struct xarray pin_refs;
|
||||
refcount_t refcount;
|
||||
struct list_head registration_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpll_pin - structure for a dpll pin
|
||||
* @id: unique id number for pin given by dpll subsystem
|
||||
* @pin_idx: index of a pin given by dev driver
|
||||
* @clock_id: clock_id of creator
|
||||
* @module: module of creator
|
||||
* @dpll_refs: hold referencees to dplls pin was registered with
|
||||
* @parent_refs: hold references to parent pins pin was registered with
|
||||
* @prop: pointer to pin properties given by registerer
|
||||
* @rclk_dev_name: holds name of device when pin can recover clock from it
|
||||
* @refcount: refcount
|
||||
**/
|
||||
struct dpll_pin {
|
||||
u32 id;
|
||||
u32 pin_idx;
|
||||
u64 clock_id;
|
||||
struct module *module;
|
||||
struct xarray dpll_refs;
|
||||
struct xarray parent_refs;
|
||||
const struct dpll_pin_properties *prop;
|
||||
refcount_t refcount;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpll_pin_ref - structure for referencing either dpll or pins
|
||||
* @dpll: pointer to a dpll
|
||||
* @pin: pointer to a pin
|
||||
* @registration_list: list of ops and priv data registered with the ref
|
||||
* @refcount: refcount
|
||||
**/
|
||||
struct dpll_pin_ref {
|
||||
union {
|
||||
struct dpll_device *dpll;
|
||||
struct dpll_pin *pin;
|
||||
};
|
||||
struct list_head registration_list;
|
||||
refcount_t refcount;
|
||||
};
|
||||
|
||||
void *dpll_priv(struct dpll_device *dpll);
|
||||
void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, struct dpll_pin *pin);
|
||||
void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
|
||||
|
||||
const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
|
||||
struct dpll_device *dpll_device_get_by_id(int id);
|
||||
const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
|
||||
struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
|
||||
extern struct xarray dpll_device_xa;
|
||||
extern struct xarray dpll_pin_xa;
|
||||
extern struct mutex dpll_lock;
|
||||
#endif
|
1253
drivers/dpll/dpll_netlink.c
Normal file
1253
drivers/dpll/dpll_netlink.c
Normal file
File diff suppressed because it is too large
Load diff
13
drivers/dpll/dpll_netlink.h
Normal file
13
drivers/dpll/dpll_netlink.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates
|
||||
* Copyright (c) 2023 Intel and affiliates
|
||||
*/
|
||||
|
||||
int dpll_device_create_ntf(struct dpll_device *dpll);
|
||||
|
||||
int dpll_device_delete_ntf(struct dpll_device *dpll);
|
||||
|
||||
int dpll_pin_create_ntf(struct dpll_pin *pin);
|
||||
|
||||
int dpll_pin_delete_ntf(struct dpll_pin *pin);
|
162
drivers/dpll/dpll_nl.c
Normal file
162
drivers/dpll/dpll_nl.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/dpll.yaml */
|
||||
/* YNL-GEN kernel source */
|
||||
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include "dpll_nl.h"
|
||||
|
||||
#include <uapi/linux/dpll.h>
|
||||
|
||||
/* Common nested types */
|
||||
const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_STATE + 1] = {
|
||||
[DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
};
|
||||
|
||||
const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = {
|
||||
[DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_ID_GET - do */
|
||||
static const struct nla_policy dpll_device_id_get_nl_policy[DPLL_A_TYPE + 1] = {
|
||||
[DPLL_A_MODULE_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DPLL_A_CLOCK_ID] = { .type = NLA_U64, },
|
||||
[DPLL_A_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_GET - do */
|
||||
static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_SET - do */
|
||||
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_ID + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_ID_GET - do */
|
||||
static const struct nla_policy dpll_pin_id_get_nl_policy[DPLL_A_PIN_TYPE + 1] = {
|
||||
[DPLL_A_PIN_MODULE_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DPLL_A_PIN_CLOCK_ID] = { .type = NLA_U64, },
|
||||
[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_CMD_PIN_GET - do */
|
||||
static const struct nla_policy dpll_pin_get_do_nl_policy[DPLL_A_PIN_ID + 1] = {
|
||||
[DPLL_A_PIN_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_GET - dump */
|
||||
static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_PIN_ID + 1] = {
|
||||
[DPLL_A_PIN_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_SET - do */
|
||||
static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_PIN + 1] = {
|
||||
[DPLL_A_PIN_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_FREQUENCY] = { .type = NLA_U64, },
|
||||
[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
[DPLL_A_PIN_PARENT_DEVICE] = NLA_POLICY_NESTED(dpll_pin_parent_device_nl_policy),
|
||||
[DPLL_A_PIN_PARENT_PIN] = NLA_POLICY_NESTED(dpll_pin_parent_pin_nl_policy),
|
||||
};
|
||||
|
||||
/* Ops table for dpll */
|
||||
static const struct genl_split_ops dpll_nl_ops[] = {
|
||||
{
|
||||
.cmd = DPLL_CMD_DEVICE_ID_GET,
|
||||
.pre_doit = dpll_lock_doit,
|
||||
.doit = dpll_nl_device_id_get_doit,
|
||||
.post_doit = dpll_unlock_doit,
|
||||
.policy = dpll_device_id_get_nl_policy,
|
||||
.maxattr = DPLL_A_TYPE,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_DEVICE_GET,
|
||||
.pre_doit = dpll_pre_doit,
|
||||
.doit = dpll_nl_device_get_doit,
|
||||
.post_doit = dpll_post_doit,
|
||||
.policy = dpll_device_get_nl_policy,
|
||||
.maxattr = DPLL_A_ID,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_DEVICE_GET,
|
||||
.start = dpll_lock_dumpit,
|
||||
.dumpit = dpll_nl_device_get_dumpit,
|
||||
.done = dpll_unlock_dumpit,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_DEVICE_SET,
|
||||
.pre_doit = dpll_pre_doit,
|
||||
.doit = dpll_nl_device_set_doit,
|
||||
.post_doit = dpll_post_doit,
|
||||
.policy = dpll_device_set_nl_policy,
|
||||
.maxattr = DPLL_A_ID,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_PIN_ID_GET,
|
||||
.pre_doit = dpll_lock_doit,
|
||||
.doit = dpll_nl_pin_id_get_doit,
|
||||
.post_doit = dpll_unlock_doit,
|
||||
.policy = dpll_pin_id_get_nl_policy,
|
||||
.maxattr = DPLL_A_PIN_TYPE,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_PIN_GET,
|
||||
.pre_doit = dpll_pin_pre_doit,
|
||||
.doit = dpll_nl_pin_get_doit,
|
||||
.post_doit = dpll_pin_post_doit,
|
||||
.policy = dpll_pin_get_do_nl_policy,
|
||||
.maxattr = DPLL_A_PIN_ID,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_PIN_GET,
|
||||
.start = dpll_lock_dumpit,
|
||||
.dumpit = dpll_nl_pin_get_dumpit,
|
||||
.done = dpll_unlock_dumpit,
|
||||
.policy = dpll_pin_get_dump_nl_policy,
|
||||
.maxattr = DPLL_A_PIN_ID,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
|
||||
},
|
||||
{
|
||||
.cmd = DPLL_CMD_PIN_SET,
|
||||
.pre_doit = dpll_pin_pre_doit,
|
||||
.doit = dpll_nl_pin_set_doit,
|
||||
.post_doit = dpll_pin_post_doit,
|
||||
.policy = dpll_pin_set_nl_policy,
|
||||
.maxattr = DPLL_A_PIN_PARENT_PIN,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group dpll_nl_mcgrps[] = {
|
||||
[DPLL_NLGRP_MONITOR] = { "monitor", },
|
||||
};
|
||||
|
||||
struct genl_family dpll_nl_family __ro_after_init = {
|
||||
.name = DPLL_FAMILY_NAME,
|
||||
.version = DPLL_FAMILY_VERSION,
|
||||
.netnsok = true,
|
||||
.parallel_ops = true,
|
||||
.module = THIS_MODULE,
|
||||
.split_ops = dpll_nl_ops,
|
||||
.n_split_ops = ARRAY_SIZE(dpll_nl_ops),
|
||||
.mcgrps = dpll_nl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(dpll_nl_mcgrps),
|
||||
};
|
51
drivers/dpll/dpll_nl.h
Normal file
51
drivers/dpll/dpll_nl.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/dpll.yaml */
|
||||
/* YNL-GEN kernel header */
|
||||
|
||||
#ifndef _LINUX_DPLL_GEN_H
|
||||
#define _LINUX_DPLL_GEN_H
|
||||
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include <uapi/linux/dpll.h>
|
||||
|
||||
/* Common nested types */
|
||||
extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_STATE + 1];
|
||||
extern const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1];
|
||||
|
||||
int dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
void
|
||||
dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
void
|
||||
dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
void
|
||||
dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int dpll_lock_dumpit(struct netlink_callback *cb);
|
||||
int dpll_unlock_dumpit(struct netlink_callback *cb);
|
||||
|
||||
int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
|
||||
int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
|
||||
int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
enum {
|
||||
DPLL_NLGRP_MONITOR,
|
||||
};
|
||||
|
||||
extern struct genl_family dpll_nl_family;
|
||||
|
||||
#endif /* _LINUX_DPLL_GEN_H */
|
|
@ -324,14 +324,12 @@ static int b53_mmap_probe(struct platform_device *pdev)
|
|||
return b53_switch_register(dev);
|
||||
}
|
||||
|
||||
static int b53_mmap_remove(struct platform_device *pdev)
|
||||
static void b53_mmap_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct b53_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (dev)
|
||||
b53_switch_remove(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void b53_mmap_shutdown(struct platform_device *pdev)
|
||||
|
@ -372,7 +370,7 @@ MODULE_DEVICE_TABLE(of, b53_mmap_of_table);
|
|||
|
||||
static struct platform_driver b53_mmap_driver = {
|
||||
.probe = b53_mmap_probe,
|
||||
.remove = b53_mmap_remove,
|
||||
.remove_new = b53_mmap_remove,
|
||||
.shutdown = b53_mmap_shutdown,
|
||||
.driver = {
|
||||
.name = "b53-switch",
|
||||
|
|
|
@ -657,17 +657,15 @@ static int b53_srab_probe(struct platform_device *pdev)
|
|||
return b53_switch_register(dev);
|
||||
}
|
||||
|
||||
static int b53_srab_remove(struct platform_device *pdev)
|
||||
static void b53_srab_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct b53_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (!dev)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
b53_srab_intr_set(dev->priv, false);
|
||||
b53_switch_remove(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void b53_srab_shutdown(struct platform_device *pdev)
|
||||
|
@ -684,7 +682,7 @@ static void b53_srab_shutdown(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver b53_srab_driver = {
|
||||
.probe = b53_srab_probe,
|
||||
.remove = b53_srab_remove,
|
||||
.remove_new = b53_srab_remove,
|
||||
.shutdown = b53_srab_shutdown,
|
||||
.driver = {
|
||||
.name = "b53-srab-switch",
|
||||
|
|
|
@ -1543,12 +1543,12 @@ out_clk:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_sf2_sw_remove(struct platform_device *pdev)
|
||||
static void bcm_sf2_sw_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm_sf2_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
priv->wol_ports_mask = 0;
|
||||
/* Disable interrupts */
|
||||
|
@ -1560,8 +1560,6 @@ static int bcm_sf2_sw_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(priv->clk);
|
||||
if (priv->type == BCM7278_DEVICE_ID)
|
||||
reset_control_assert(priv->rcdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm_sf2_sw_shutdown(struct platform_device *pdev)
|
||||
|
@ -1607,7 +1605,7 @@ static SIMPLE_DEV_PM_OPS(bcm_sf2_pm_ops,
|
|||
|
||||
static struct platform_driver bcm_sf2_driver = {
|
||||
.probe = bcm_sf2_sw_probe,
|
||||
.remove = bcm_sf2_sw_remove,
|
||||
.remove_new = bcm_sf2_sw_remove,
|
||||
.shutdown = bcm_sf2_sw_shutdown,
|
||||
.driver = {
|
||||
.name = "brcm-sf2",
|
||||
|
|
|
@ -2060,18 +2060,16 @@ err_ptp_setup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int hellcreek_remove(struct platform_device *pdev)
|
||||
static void hellcreek_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hellcreek *hellcreek = platform_get_drvdata(pdev);
|
||||
|
||||
if (!hellcreek)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
hellcreek_hwtstamp_free(hellcreek);
|
||||
hellcreek_ptp_free(hellcreek);
|
||||
dsa_unregister_switch(hellcreek->ds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hellcreek_shutdown(struct platform_device *pdev)
|
||||
|
@ -2107,7 +2105,7 @@ MODULE_DEVICE_TABLE(of, hellcreek_of_match);
|
|||
|
||||
static struct platform_driver hellcreek_driver = {
|
||||
.probe = hellcreek_probe,
|
||||
.remove = hellcreek_remove,
|
||||
.remove_new = hellcreek_remove,
|
||||
.shutdown = hellcreek_shutdown,
|
||||
.driver = {
|
||||
.name = "hellcreek",
|
||||
|
|
|
@ -2207,13 +2207,13 @@ put_mdio_node:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int gswip_remove(struct platform_device *pdev)
|
||||
static void gswip_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gswip_priv *priv = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
/* disable the switch */
|
||||
gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB);
|
||||
|
@ -2228,8 +2228,6 @@ static int gswip_remove(struct platform_device *pdev)
|
|||
|
||||
for (i = 0; i < priv->num_gphy_fw; i++)
|
||||
gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gswip_shutdown(struct platform_device *pdev)
|
||||
|
@ -2266,7 +2264,7 @@ MODULE_DEVICE_TABLE(of, gswip_of_match);
|
|||
|
||||
static struct platform_driver gswip_driver = {
|
||||
.probe = gswip_probe,
|
||||
.remove = gswip_remove,
|
||||
.remove_new = gswip_remove,
|
||||
.shutdown = gswip_shutdown,
|
||||
.driver = {
|
||||
.name = "gswip",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_switch.o
|
||||
ksz_switch-objs := ksz_common.o
|
||||
ksz_switch-objs += ksz9477.o
|
||||
ksz_switch-objs += ksz9477.o ksz9477_acl.o ksz9477_tc_flower.o
|
||||
ksz_switch-objs += ksz8795.o
|
||||
ksz_switch-objs += lan937x_main.o
|
||||
|
||||
|
|
|
@ -442,20 +442,6 @@
|
|||
#define TOS_PRIO_M KS_PRIO_M
|
||||
#define TOS_PRIO_S KS_PRIO_S
|
||||
|
||||
#define REG_SW_CTRL_20 0xA3
|
||||
|
||||
#define SW_GMII_DRIVE_STRENGTH_S 4
|
||||
#define SW_DRIVE_STRENGTH_M 0x7
|
||||
#define SW_DRIVE_STRENGTH_2MA 0
|
||||
#define SW_DRIVE_STRENGTH_4MA 1
|
||||
#define SW_DRIVE_STRENGTH_8MA 2
|
||||
#define SW_DRIVE_STRENGTH_12MA 3
|
||||
#define SW_DRIVE_STRENGTH_16MA 4
|
||||
#define SW_DRIVE_STRENGTH_20MA 5
|
||||
#define SW_DRIVE_STRENGTH_24MA 6
|
||||
#define SW_DRIVE_STRENGTH_28MA 7
|
||||
#define SW_MII_DRIVE_STRENGTH_S 0
|
||||
|
||||
#define REG_SW_CTRL_21 0xA4
|
||||
|
||||
#define SW_IPV6_MLD_OPTION BIT(3)
|
||||
|
|
|
@ -1004,6 +1004,8 @@ void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
|
|||
/* clear pending interrupts */
|
||||
if (dev->info->internal_phy[port])
|
||||
ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
|
||||
|
||||
ksz9477_port_acl_init(dev, port);
|
||||
}
|
||||
|
||||
void ksz9477_config_cpu_port(struct dsa_switch *ds)
|
||||
|
|
|
@ -57,4 +57,40 @@ int ksz9477_switch_init(struct ksz_device *dev);
|
|||
void ksz9477_switch_exit(struct ksz_device *dev);
|
||||
void ksz9477_port_queue_split(struct ksz_device *dev, int port);
|
||||
|
||||
int ksz9477_port_acl_init(struct ksz_device *dev, int port);
|
||||
void ksz9477_port_acl_free(struct ksz_device *dev, int port);
|
||||
int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
|
||||
struct flow_cls_offload *cls, bool ingress);
|
||||
int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
|
||||
struct flow_cls_offload *cls, bool ingress);
|
||||
|
||||
#define KSZ9477_ACL_ENTRY_SIZE 18
|
||||
#define KSZ9477_ACL_MAX_ENTRIES 16
|
||||
|
||||
struct ksz9477_acl_entry {
|
||||
u8 entry[KSZ9477_ACL_ENTRY_SIZE];
|
||||
unsigned long cookie;
|
||||
u32 prio;
|
||||
};
|
||||
|
||||
struct ksz9477_acl_entries {
|
||||
struct ksz9477_acl_entry entries[KSZ9477_ACL_MAX_ENTRIES];
|
||||
int entries_count;
|
||||
};
|
||||
|
||||
struct ksz9477_acl_priv {
|
||||
struct ksz9477_acl_entries acles;
|
||||
};
|
||||
|
||||
void ksz9477_acl_remove_entries(struct ksz_device *dev, int port,
|
||||
struct ksz9477_acl_entries *acles,
|
||||
unsigned long cookie);
|
||||
int ksz9477_acl_write_list(struct ksz_device *dev, int port);
|
||||
int ksz9477_sort_acl_entries(struct ksz_device *dev, int port);
|
||||
void ksz9477_acl_action_rule_cfg(u8 *entry, bool force_prio, u8 prio_val);
|
||||
void ksz9477_acl_processing_rule_set_action(u8 *entry, u8 action_idx);
|
||||
void ksz9477_acl_match_process_l2(struct ksz_device *dev, int port,
|
||||
u16 ethtype, u8 *src_mac, u8 *dst_mac,
|
||||
unsigned long cookie, u32 prio);
|
||||
|
||||
#endif
|
||||
|
|
1436
drivers/net/dsa/microchip/ksz9477_acl.c
Normal file
1436
drivers/net/dsa/microchip/ksz9477_acl.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -112,19 +112,6 @@
|
|||
|
||||
#define REG_SW_IBA_SYNC__1 0x010C
|
||||
|
||||
#define REG_SW_IO_STRENGTH__1 0x010D
|
||||
#define SW_DRIVE_STRENGTH_M 0x7
|
||||
#define SW_DRIVE_STRENGTH_2MA 0
|
||||
#define SW_DRIVE_STRENGTH_4MA 1
|
||||
#define SW_DRIVE_STRENGTH_8MA 2
|
||||
#define SW_DRIVE_STRENGTH_12MA 3
|
||||
#define SW_DRIVE_STRENGTH_16MA 4
|
||||
#define SW_DRIVE_STRENGTH_20MA 5
|
||||
#define SW_DRIVE_STRENGTH_24MA 6
|
||||
#define SW_DRIVE_STRENGTH_28MA 7
|
||||
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
|
||||
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
|
||||
|
||||
#define REG_SW_IBA_STATUS__4 0x0110
|
||||
|
||||
#define SW_IBA_REQ BIT(31)
|
||||
|
|
281
drivers/net/dsa/microchip/ksz9477_tc_flower.c
Normal file
281
drivers/net/dsa/microchip/ksz9477_tc_flower.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
|
||||
|
||||
#include "ksz9477.h"
|
||||
#include "ksz9477_reg.h"
|
||||
#include "ksz_common.h"
|
||||
|
||||
#define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
|
||||
#define KSZ9477_MAX_TC 7
|
||||
|
||||
/**
|
||||
* ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure
|
||||
* ACL entries accordingly.
|
||||
* @dev: Pointer to the ksz_device.
|
||||
* @port: Port number.
|
||||
* @extack: Pointer to the netlink_ext_ack.
|
||||
* @rule: Pointer to the flow_rule.
|
||||
* @cookie: The cookie to associate with the entry.
|
||||
* @prio: The priority of the entry.
|
||||
*
|
||||
* This function parses the Layer 2 key from the flow rule and configures
|
||||
* the corresponding ACL entries. It checks for unsupported offloads and
|
||||
* available entries before proceeding with the configuration.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static int ksz9477_flower_parse_key_l2(struct ksz_device *dev, int port,
|
||||
struct netlink_ext_ack *extack,
|
||||
struct flow_rule *rule,
|
||||
unsigned long cookie, u32 prio)
|
||||
{
|
||||
struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
|
||||
struct flow_match_eth_addrs ematch;
|
||||
struct ksz9477_acl_entries *acles;
|
||||
int required_entries;
|
||||
u8 *src_mac = NULL;
|
||||
u8 *dst_mac = NULL;
|
||||
u16 ethtype = 0;
|
||||
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
|
||||
struct flow_match_basic match;
|
||||
|
||||
flow_rule_match_basic(rule, &match);
|
||||
|
||||
if (match.key->n_proto) {
|
||||
if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"ethernet type mask must be a full mask");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ethtype = be16_to_cpu(match.key->n_proto);
|
||||
}
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
||||
flow_rule_match_eth_addrs(rule, &ematch);
|
||||
|
||||
if (!is_zero_ether_addr(ematch.key->src)) {
|
||||
if (!is_broadcast_ether_addr(ematch.mask->src))
|
||||
goto not_full_mask_err;
|
||||
|
||||
src_mac = ematch.key->src;
|
||||
}
|
||||
|
||||
if (!is_zero_ether_addr(ematch.key->dst)) {
|
||||
if (!is_broadcast_ether_addr(ematch.mask->dst))
|
||||
goto not_full_mask_err;
|
||||
|
||||
dst_mac = ematch.key->dst;
|
||||
}
|
||||
}
|
||||
|
||||
acles = &acl->acles;
|
||||
/* ACL supports only one MAC per entry */
|
||||
required_entries = src_mac && dst_mac ? 2 : 1;
|
||||
|
||||
/* Check if there are enough available entries */
|
||||
if (acles->entries_count + required_entries > KSZ9477_ACL_MAX_ENTRIES) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ksz9477_acl_match_process_l2(dev, port, ethtype, src_mac, dst_mac,
|
||||
cookie, prio);
|
||||
|
||||
return 0;
|
||||
|
||||
not_full_mask_err:
|
||||
NL_SET_ERR_MSG_MOD(extack, "MAC address mask must be a full mask");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz9477_flower_parse_key - Parse flow rule keys for a specified port on a
|
||||
* ksz_device.
|
||||
* @dev: The ksz_device instance.
|
||||
* @port: The port number to parse the flow rule keys for.
|
||||
* @extack: The netlink extended ACK for reporting errors.
|
||||
* @rule: The flow_rule to parse.
|
||||
* @cookie: The cookie to associate with the entry.
|
||||
* @prio: The priority of the entry.
|
||||
*
|
||||
* This function checks if the used keys in the flow rule are supported by
|
||||
* the device and parses the L2 keys if they match. If unsupported keys are
|
||||
* used, an error message is set in the extended ACK.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static int ksz9477_flower_parse_key(struct ksz_device *dev, int port,
|
||||
struct netlink_ext_ack *extack,
|
||||
struct flow_rule *rule,
|
||||
unsigned long cookie, u32 prio)
|
||||
{
|
||||
struct flow_dissector *dissector = rule->match.dissector;
|
||||
int ret;
|
||||
|
||||
if (dissector->used_keys &
|
||||
~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
|
||||
BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
|
||||
BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Unsupported keys used");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) ||
|
||||
flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
||||
ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule,
|
||||
cookie, prio);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz9477_flower_parse_action - Parse flow rule actions for a specified port
|
||||
* on a ksz_device.
|
||||
* @dev: The ksz_device instance.
|
||||
* @port: The port number to parse the flow rule actions for.
|
||||
* @extack: The netlink extended ACK for reporting errors.
|
||||
* @cls: The flow_cls_offload instance containing the flow rule.
|
||||
* @entry_idx: The index of the ACL entry to store the action.
|
||||
*
|
||||
* This function checks if the actions in the flow rule are supported by
|
||||
* the device. Currently, only actions that change priorities are supported.
|
||||
* If unsupported actions are encountered, an error message is set in the
|
||||
* extended ACK.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static int ksz9477_flower_parse_action(struct ksz_device *dev, int port,
|
||||
struct netlink_ext_ack *extack,
|
||||
struct flow_cls_offload *cls,
|
||||
int entry_idx)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
|
||||
struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
|
||||
const struct flow_action_entry *act;
|
||||
struct ksz9477_acl_entry *entry;
|
||||
bool prio_force = false;
|
||||
u8 prio_val = 0;
|
||||
int i;
|
||||
|
||||
if (TC_H_MIN(cls->classid)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "hw_tc is not supported. Use: action skbedit prio");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_action_for_each(i, act, &rule->action) {
|
||||
switch (act->id) {
|
||||
case FLOW_ACTION_PRIORITY:
|
||||
if (act->priority > KSZ9477_MAX_TC) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Priority value is too high");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
prio_force = true;
|
||||
prio_val = act->priority;
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_MOD(extack, "action not supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
/* pick entry to store action */
|
||||
entry = &acl->acles.entries[entry_idx];
|
||||
|
||||
ksz9477_acl_action_rule_cfg(entry->entry, prio_force, prio_val);
|
||||
ksz9477_acl_processing_rule_set_action(entry->entry, entry_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz9477_cls_flower_add - Add a flow classification rule for a specified port
|
||||
* on a ksz_device.
|
||||
* @ds: The DSA switch instance.
|
||||
* @port: The port number to add the flow classification rule to.
|
||||
* @cls: The flow_cls_offload instance containing the flow rule.
|
||||
* @ingress: A flag indicating if the rule is applied on the ingress path.
|
||||
*
|
||||
* This function adds a flow classification rule for a specified port on a
|
||||
* ksz_device. It checks if the ACL offloading is supported and parses the flow
|
||||
* keys and actions. If the ACL is not supported, it returns an error. If there
|
||||
* are unprocessed entries, it parses the action for the rule.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
|
||||
struct flow_cls_offload *cls, bool ingress)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
|
||||
struct netlink_ext_ack *extack = cls->common.extack;
|
||||
struct ksz_device *dev = ds->priv;
|
||||
struct ksz9477_acl_priv *acl;
|
||||
int action_entry_idx;
|
||||
int ret;
|
||||
|
||||
acl = dev->ports[port].acl_priv;
|
||||
|
||||
if (!acl) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "ACL offloading is not supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* A complex rule set can take multiple entries. Use first entry
|
||||
* to store the action.
|
||||
*/
|
||||
action_entry_idx = acl->acles.entries_count;
|
||||
|
||||
ret = ksz9477_flower_parse_key(dev, port, extack, rule, cls->cookie,
|
||||
cls->common.prio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz9477_flower_parse_action(dev, port, extack, cls,
|
||||
action_entry_idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz9477_sort_acl_entries(dev, port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ksz9477_acl_write_list(dev, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz9477_cls_flower_del - Remove a flow classification rule for a specified
|
||||
* port on a ksz_device.
|
||||
* @ds: The DSA switch instance.
|
||||
* @port: The port number to remove the flow classification rule from.
|
||||
* @cls: The flow_cls_offload instance containing the flow rule.
|
||||
* @ingress: A flag indicating if the rule is applied on the ingress path.
|
||||
*
|
||||
* This function removes a flow classification rule for a specified port on a
|
||||
* ksz_device. It checks if the ACL is initialized, and if not, returns an
|
||||
* error. If the ACL is initialized, it removes entries with the specified
|
||||
* cookie and rewrites the ACL list.
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
|
||||
struct flow_cls_offload *cls, bool ingress)
|
||||
{
|
||||
unsigned long cookie = cls->cookie;
|
||||
struct ksz_device *dev = ds->priv;
|
||||
struct ksz9477_acl_priv *acl;
|
||||
|
||||
acl = dev->ports[port].acl_priv;
|
||||
|
||||
if (!acl)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ksz9477_acl_remove_entries(dev, port, &acl->acles, cookie);
|
||||
|
||||
return ksz9477_acl_write_list(dev, port);
|
||||
}
|
|
@ -186,6 +186,72 @@ static const struct ksz_mib_names ksz9477_mib_names[] = {
|
|||
{ 0x83, "tx_discards" },
|
||||
};
|
||||
|
||||
struct ksz_driver_strength_prop {
|
||||
const char *name;
|
||||
int offset;
|
||||
int value;
|
||||
};
|
||||
|
||||
enum ksz_driver_strength_type {
|
||||
KSZ_DRIVER_STRENGTH_HI,
|
||||
KSZ_DRIVER_STRENGTH_LO,
|
||||
KSZ_DRIVER_STRENGTH_IO,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ksz_drive_strength - drive strength mapping
|
||||
* @reg_val: register value
|
||||
* @microamp: microamp value
|
||||
*/
|
||||
struct ksz_drive_strength {
|
||||
u32 reg_val;
|
||||
u32 microamp;
|
||||
};
|
||||
|
||||
/* ksz9477_drive_strengths - Drive strength mapping for KSZ9477 variants
|
||||
*
|
||||
* This values are not documented in KSZ9477 variants but confirmed by
|
||||
* Microchip that KSZ9477, KSZ9567, KSZ8567, KSZ9897, KSZ9896, KSZ9563, KSZ9893
|
||||
* and KSZ8563 are using same register (drive strength) settings like KSZ8795.
|
||||
*
|
||||
* Documentation in KSZ8795CLX provides more information with some
|
||||
* recommendations:
|
||||
* - for high speed signals
|
||||
* 1. 4 mA or 8 mA is often used for MII, RMII, and SPI interface with using
|
||||
* 2.5V or 3.3V VDDIO.
|
||||
* 2. 12 mA or 16 mA is often used for MII, RMII, and SPI interface with
|
||||
* using 1.8V VDDIO.
|
||||
* 3. 20 mA or 24 mA is often used for GMII/RGMII interface with using 2.5V
|
||||
* or 3.3V VDDIO.
|
||||
* 4. 28 mA is often used for GMII/RGMII interface with using 1.8V VDDIO.
|
||||
* 5. In same interface, the heavy loading should use higher one of the
|
||||
* drive current strength.
|
||||
* - for low speed signals
|
||||
* 1. 3.3V VDDIO, use either 4 mA or 8 mA.
|
||||
* 2. 2.5V VDDIO, use either 8 mA or 12 mA.
|
||||
* 3. 1.8V VDDIO, use either 12 mA or 16 mA.
|
||||
* 4. If it is heavy loading, can use higher drive current strength.
|
||||
*/
|
||||
static const struct ksz_drive_strength ksz9477_drive_strengths[] = {
|
||||
{ SW_DRIVE_STRENGTH_2MA, 2000 },
|
||||
{ SW_DRIVE_STRENGTH_4MA, 4000 },
|
||||
{ SW_DRIVE_STRENGTH_8MA, 8000 },
|
||||
{ SW_DRIVE_STRENGTH_12MA, 12000 },
|
||||
{ SW_DRIVE_STRENGTH_16MA, 16000 },
|
||||
{ SW_DRIVE_STRENGTH_20MA, 20000 },
|
||||
{ SW_DRIVE_STRENGTH_24MA, 24000 },
|
||||
{ SW_DRIVE_STRENGTH_28MA, 28000 },
|
||||
};
|
||||
|
||||
/* ksz8830_drive_strengths - Drive strength mapping for KSZ8830, KSZ8873, ..
|
||||
* variants.
|
||||
* This values are documented in KSZ8873 and KSZ8863 datasheets.
|
||||
*/
|
||||
static const struct ksz_drive_strength ksz8830_drive_strengths[] = {
|
||||
{ 0, 8000 },
|
||||
{ KSZ8873_DRIVE_STRENGTH_16MA, 16000 },
|
||||
};
|
||||
|
||||
static const struct ksz_dev_ops ksz8_dev_ops = {
|
||||
.setup = ksz8_setup,
|
||||
.get_port_addr = ksz8_get_port_addr,
|
||||
|
@ -2498,8 +2564,7 @@ static int ksz_port_mdb_del(struct dsa_switch *ds, int port,
|
|||
return dev->dev_ops->mdb_del(dev, port, mdb, db);
|
||||
}
|
||||
|
||||
static int ksz_enable_port(struct dsa_switch *ds, int port,
|
||||
struct phy_device *phy)
|
||||
static int ksz_port_setup(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
|
||||
|
@ -2562,6 +2627,23 @@ void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
|
|||
ksz_update_port_member(dev, port);
|
||||
}
|
||||
|
||||
static void ksz_port_teardown(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
|
||||
switch (dev->chip_id) {
|
||||
case KSZ8563_CHIP_ID:
|
||||
case KSZ9477_CHIP_ID:
|
||||
case KSZ9563_CHIP_ID:
|
||||
case KSZ9567_CHIP_ID:
|
||||
case KSZ9893_CHIP_ID:
|
||||
case KSZ9896_CHIP_ID:
|
||||
case KSZ9897_CHIP_ID:
|
||||
if (dsa_is_user_port(ds, port))
|
||||
ksz9477_port_acl_free(dev, port);
|
||||
}
|
||||
}
|
||||
|
||||
static int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port,
|
||||
struct switchdev_brport_flags flags,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
@ -3107,6 +3189,44 @@ static int ksz_switch_detect(struct ksz_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ksz_cls_flower_add(struct dsa_switch *ds, int port,
|
||||
struct flow_cls_offload *cls, bool ingress)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
|
||||
switch (dev->chip_id) {
|
||||
case KSZ8563_CHIP_ID:
|
||||
case KSZ9477_CHIP_ID:
|
||||
case KSZ9563_CHIP_ID:
|
||||
case KSZ9567_CHIP_ID:
|
||||
case KSZ9893_CHIP_ID:
|
||||
case KSZ9896_CHIP_ID:
|
||||
case KSZ9897_CHIP_ID:
|
||||
return ksz9477_cls_flower_add(ds, port, cls, ingress);
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int ksz_cls_flower_del(struct dsa_switch *ds, int port,
|
||||
struct flow_cls_offload *cls, bool ingress)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
|
||||
switch (dev->chip_id) {
|
||||
case KSZ8563_CHIP_ID:
|
||||
case KSZ9477_CHIP_ID:
|
||||
case KSZ9563_CHIP_ID:
|
||||
case KSZ9567_CHIP_ID:
|
||||
case KSZ9893_CHIP_ID:
|
||||
case KSZ9896_CHIP_ID:
|
||||
case KSZ9897_CHIP_ID:
|
||||
return ksz9477_cls_flower_del(ds, port, cls, ingress);
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Bandwidth is calculated by idle slope/transmission speed. Then the Bandwidth
|
||||
* is converted to Hex-decimal using the successive multiplication method. On
|
||||
* every step, integer part is taken and decimal part is carry forwarded.
|
||||
|
@ -3431,7 +3551,7 @@ static const struct dsa_switch_ops ksz_switch_ops = {
|
|||
.phylink_mac_config = ksz_phylink_mac_config,
|
||||
.phylink_mac_link_up = ksz_phylink_mac_link_up,
|
||||
.phylink_mac_link_down = ksz_mac_link_down,
|
||||
.port_enable = ksz_enable_port,
|
||||
.port_setup = ksz_port_setup,
|
||||
.set_ageing_time = ksz_set_ageing_time,
|
||||
.get_strings = ksz_get_strings,
|
||||
.get_ethtool_stats = ksz_get_ethtool_stats,
|
||||
|
@ -3439,6 +3559,7 @@ static const struct dsa_switch_ops ksz_switch_ops = {
|
|||
.port_bridge_join = ksz_port_bridge_join,
|
||||
.port_bridge_leave = ksz_port_bridge_leave,
|
||||
.port_stp_state_set = ksz_port_stp_state_set,
|
||||
.port_teardown = ksz_port_teardown,
|
||||
.port_pre_bridge_flags = ksz_port_pre_bridge_flags,
|
||||
.port_bridge_flags = ksz_port_bridge_flags,
|
||||
.port_fast_age = ksz_port_fast_age,
|
||||
|
@ -3461,6 +3582,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
|
|||
.port_hwtstamp_set = ksz_hwtstamp_set,
|
||||
.port_txtstamp = ksz_port_txtstamp,
|
||||
.port_rxtstamp = ksz_port_rxtstamp,
|
||||
.cls_flower_add = ksz_cls_flower_add,
|
||||
.cls_flower_del = ksz_cls_flower_del,
|
||||
.port_setup_tc = ksz_setup_tc,
|
||||
.get_mac_eee = ksz_get_mac_eee,
|
||||
.set_mac_eee = ksz_set_mac_eee,
|
||||
|
@ -3530,6 +3653,245 @@ static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
|
|||
dev->ports[port_num].rgmii_tx_val = tx_delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz_drive_strength_to_reg() - Convert drive strength value to corresponding
|
||||
* register value.
|
||||
* @array: The array of drive strength values to search.
|
||||
* @array_size: The size of the array.
|
||||
* @microamp: The drive strength value in microamp to be converted.
|
||||
*
|
||||
* This function searches the array of drive strength values for the given
|
||||
* microamp value and returns the corresponding register value for that drive.
|
||||
*
|
||||
* Returns: If found, the corresponding register value for that drive strength
|
||||
* is returned. Otherwise, -EINVAL is returned indicating an invalid value.
|
||||
*/
|
||||
static int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array,
|
||||
size_t array_size, int microamp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < array_size; i++) {
|
||||
if (array[i].microamp == microamp)
|
||||
return array[i].reg_val;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz_drive_strength_error() - Report invalid drive strength value
|
||||
* @dev: ksz device
|
||||
* @array: The array of drive strength values to search.
|
||||
* @array_size: The size of the array.
|
||||
* @microamp: Invalid drive strength value in microamp
|
||||
*
|
||||
* This function logs an error message when an unsupported drive strength value
|
||||
* is detected. It lists out all the supported drive strength values for
|
||||
* reference in the error message.
|
||||
*/
|
||||
static void ksz_drive_strength_error(struct ksz_device *dev,
|
||||
const struct ksz_drive_strength *array,
|
||||
size_t array_size, int microamp)
|
||||
{
|
||||
char supported_values[100];
|
||||
size_t remaining_size;
|
||||
int added_len;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
remaining_size = sizeof(supported_values);
|
||||
ptr = supported_values;
|
||||
|
||||
for (i = 0; i < array_size; i++) {
|
||||
added_len = snprintf(ptr, remaining_size,
|
||||
i == 0 ? "%d" : ", %d", array[i].microamp);
|
||||
|
||||
if (added_len >= remaining_size)
|
||||
break;
|
||||
|
||||
ptr += added_len;
|
||||
remaining_size -= added_len;
|
||||
}
|
||||
|
||||
dev_err(dev->dev, "Invalid drive strength %d, supported values are %s\n",
|
||||
microamp, supported_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz9477_drive_strength_write() - Set the drive strength for specific KSZ9477
|
||||
* chip variants.
|
||||
* @dev: ksz device
|
||||
* @props: Array of drive strength properties to be applied
|
||||
* @num_props: Number of properties in the array
|
||||
*
|
||||
* This function configures the drive strength for various KSZ9477 chip variants
|
||||
* based on the provided properties. It handles chip-specific nuances and
|
||||
* ensures only valid drive strengths are written to the respective chip.
|
||||
*
|
||||
* Return: 0 on successful configuration, a negative error code on failure.
|
||||
*/
|
||||
static int ksz9477_drive_strength_write(struct ksz_device *dev,
|
||||
struct ksz_driver_strength_prop *props,
|
||||
int num_props)
|
||||
{
|
||||
size_t array_size = ARRAY_SIZE(ksz9477_drive_strengths);
|
||||
int i, ret, reg;
|
||||
u8 mask = 0;
|
||||
u8 val = 0;
|
||||
|
||||
if (props[KSZ_DRIVER_STRENGTH_IO].value != -1)
|
||||
dev_warn(dev->dev, "%s is not supported by this chip variant\n",
|
||||
props[KSZ_DRIVER_STRENGTH_IO].name);
|
||||
|
||||
if (dev->chip_id == KSZ8795_CHIP_ID ||
|
||||
dev->chip_id == KSZ8794_CHIP_ID ||
|
||||
dev->chip_id == KSZ8765_CHIP_ID)
|
||||
reg = KSZ8795_REG_SW_CTRL_20;
|
||||
else
|
||||
reg = KSZ9477_REG_SW_IO_STRENGTH;
|
||||
|
||||
for (i = 0; i < num_props; i++) {
|
||||
if (props[i].value == -1)
|
||||
continue;
|
||||
|
||||
ret = ksz_drive_strength_to_reg(ksz9477_drive_strengths,
|
||||
array_size, props[i].value);
|
||||
if (ret < 0) {
|
||||
ksz_drive_strength_error(dev, ksz9477_drive_strengths,
|
||||
array_size, props[i].value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mask |= SW_DRIVE_STRENGTH_M << props[i].offset;
|
||||
val |= ret << props[i].offset;
|
||||
}
|
||||
|
||||
return ksz_rmw8(dev, reg, mask, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz8830_drive_strength_write() - Set the drive strength configuration for
|
||||
* KSZ8830 compatible chip variants.
|
||||
* @dev: ksz device
|
||||
* @props: Array of drive strength properties to be set
|
||||
* @num_props: Number of properties in the array
|
||||
*
|
||||
* This function applies the specified drive strength settings to KSZ8830 chip
|
||||
* variants (KSZ8873, KSZ8863).
|
||||
* It ensures the configurations align with what the chip variant supports and
|
||||
* warns or errors out on unsupported settings.
|
||||
*
|
||||
* Return: 0 on success, error code otherwise
|
||||
*/
|
||||
static int ksz8830_drive_strength_write(struct ksz_device *dev,
|
||||
struct ksz_driver_strength_prop *props,
|
||||
int num_props)
|
||||
{
|
||||
size_t array_size = ARRAY_SIZE(ksz8830_drive_strengths);
|
||||
int microamp;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num_props; i++) {
|
||||
if (props[i].value == -1 || i == KSZ_DRIVER_STRENGTH_IO)
|
||||
continue;
|
||||
|
||||
dev_warn(dev->dev, "%s is not supported by this chip variant\n",
|
||||
props[i].name);
|
||||
}
|
||||
|
||||
microamp = props[KSZ_DRIVER_STRENGTH_IO].value;
|
||||
ret = ksz_drive_strength_to_reg(ksz8830_drive_strengths, array_size,
|
||||
microamp);
|
||||
if (ret < 0) {
|
||||
ksz_drive_strength_error(dev, ksz8830_drive_strengths,
|
||||
array_size, microamp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ksz_rmw8(dev, KSZ8873_REG_GLOBAL_CTRL_12,
|
||||
KSZ8873_DRIVE_STRENGTH_16MA, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz_parse_drive_strength() - Extract and apply drive strength configurations
|
||||
* from device tree properties.
|
||||
* @dev: ksz device
|
||||
*
|
||||
* This function reads the specified drive strength properties from the
|
||||
* device tree, validates against the supported chip variants, and sets
|
||||
* them accordingly. An error should be critical here, as the drive strength
|
||||
* settings are crucial for EMI compliance.
|
||||
*
|
||||
* Return: 0 on success, error code otherwise
|
||||
*/
|
||||
static int ksz_parse_drive_strength(struct ksz_device *dev)
|
||||
{
|
||||
struct ksz_driver_strength_prop of_props[] = {
|
||||
[KSZ_DRIVER_STRENGTH_HI] = {
|
||||
.name = "microchip,hi-drive-strength-microamp",
|
||||
.offset = SW_HI_SPEED_DRIVE_STRENGTH_S,
|
||||
.value = -1,
|
||||
},
|
||||
[KSZ_DRIVER_STRENGTH_LO] = {
|
||||
.name = "microchip,lo-drive-strength-microamp",
|
||||
.offset = SW_LO_SPEED_DRIVE_STRENGTH_S,
|
||||
.value = -1,
|
||||
},
|
||||
[KSZ_DRIVER_STRENGTH_IO] = {
|
||||
.name = "microchip,io-drive-strength-microamp",
|
||||
.offset = 0, /* don't care */
|
||||
.value = -1,
|
||||
},
|
||||
};
|
||||
struct device_node *np = dev->dev->of_node;
|
||||
bool have_any_prop = false;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(of_props); i++) {
|
||||
ret = of_property_read_u32(np, of_props[i].name,
|
||||
&of_props[i].value);
|
||||
if (ret && ret != -EINVAL)
|
||||
dev_warn(dev->dev, "Failed to read %s\n",
|
||||
of_props[i].name);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
have_any_prop = true;
|
||||
}
|
||||
|
||||
if (!have_any_prop)
|
||||
return 0;
|
||||
|
||||
switch (dev->chip_id) {
|
||||
case KSZ8830_CHIP_ID:
|
||||
return ksz8830_drive_strength_write(dev, of_props,
|
||||
ARRAY_SIZE(of_props));
|
||||
case KSZ8795_CHIP_ID:
|
||||
case KSZ8794_CHIP_ID:
|
||||
case KSZ8765_CHIP_ID:
|
||||
case KSZ8563_CHIP_ID:
|
||||
case KSZ9477_CHIP_ID:
|
||||
case KSZ9563_CHIP_ID:
|
||||
case KSZ9567_CHIP_ID:
|
||||
case KSZ9893_CHIP_ID:
|
||||
case KSZ9896_CHIP_ID:
|
||||
case KSZ9897_CHIP_ID:
|
||||
return ksz9477_drive_strength_write(dev, of_props,
|
||||
ARRAY_SIZE(of_props));
|
||||
default:
|
||||
for (i = 0; i < ARRAY_SIZE(of_props); i++) {
|
||||
if (of_props[i].value == -1)
|
||||
continue;
|
||||
|
||||
dev_warn(dev->dev, "%s is not supported by this chip variant\n",
|
||||
of_props[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksz_switch_register(struct ksz_device *dev)
|
||||
{
|
||||
const struct ksz_chip_data *info;
|
||||
|
@ -3612,6 +3974,10 @@ int ksz_switch_register(struct ksz_device *dev)
|
|||
for (port_num = 0; port_num < dev->info->port_cnt; ++port_num)
|
||||
dev->ports[port_num].interface = PHY_INTERFACE_MODE_NA;
|
||||
if (dev->dev->of_node) {
|
||||
ret = ksz_parse_drive_strength(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_get_phy_mode(dev->dev->of_node, &interface);
|
||||
if (ret == 0)
|
||||
dev->compat_interface = interface;
|
||||
|
|
|
@ -117,6 +117,7 @@ struct ksz_port {
|
|||
u32 rgmii_tx_val;
|
||||
u32 rgmii_rx_val;
|
||||
struct ksz_device *ksz_dev;
|
||||
void *acl_priv;
|
||||
struct ksz_irq pirq;
|
||||
u8 num;
|
||||
#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
|
||||
|
@ -689,6 +690,26 @@ static inline int is_lan937x(struct ksz_device *dev)
|
|||
#define KSZ8_LEGAL_PACKET_SIZE 1518
|
||||
#define KSZ9477_MAX_FRAME_SIZE 9000
|
||||
|
||||
#define KSZ8873_REG_GLOBAL_CTRL_12 0x0e
|
||||
/* Drive Strength of I/O Pad
|
||||
* 0: 8mA, 1: 16mA
|
||||
*/
|
||||
#define KSZ8873_DRIVE_STRENGTH_16MA BIT(6)
|
||||
|
||||
#define KSZ8795_REG_SW_CTRL_20 0xa3
|
||||
#define KSZ9477_REG_SW_IO_STRENGTH 0x010d
|
||||
#define SW_DRIVE_STRENGTH_M 0x7
|
||||
#define SW_DRIVE_STRENGTH_2MA 0
|
||||
#define SW_DRIVE_STRENGTH_4MA 1
|
||||
#define SW_DRIVE_STRENGTH_8MA 2
|
||||
#define SW_DRIVE_STRENGTH_12MA 3
|
||||
#define SW_DRIVE_STRENGTH_16MA 4
|
||||
#define SW_DRIVE_STRENGTH_20MA 5
|
||||
#define SW_DRIVE_STRENGTH_24MA 6
|
||||
#define SW_DRIVE_STRENGTH_28MA 7
|
||||
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
|
||||
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
|
||||
|
||||
#define KSZ9477_REG_PORT_OUT_RATE_0 0x0420
|
||||
#define KSZ9477_OUT_RATE_NO_LIMIT 0
|
||||
|
||||
|
|
|
@ -63,15 +63,12 @@ mt7988_probe(struct platform_device *pdev)
|
|||
return dsa_register_switch(priv->ds);
|
||||
}
|
||||
|
||||
static int
|
||||
mt7988_remove(struct platform_device *pdev)
|
||||
static void mt7988_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mt7530_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (priv)
|
||||
mt7530_remove_common(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt7988_shutdown(struct platform_device *pdev)
|
||||
|
@ -88,7 +85,7 @@ static void mt7988_shutdown(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver mt7988_platform_driver = {
|
||||
.probe = mt7988_probe,
|
||||
.remove = mt7988_remove,
|
||||
.remove_new = mt7988_remove,
|
||||
.shutdown = mt7988_shutdown,
|
||||
.driver = {
|
||||
.name = "mt7530-mmio",
|
||||
|
|
|
@ -208,7 +208,7 @@ static void mv88e639x_sgmii_pcs_pre_config(struct phylink_pcs *pcs,
|
|||
|
||||
static int mv88e6390_erratum_3_14(struct mv88e639x_pcs *mpcs)
|
||||
{
|
||||
const int lanes[] = { MV88E6390_PORT9_LANE0, MV88E6390_PORT9_LANE1,
|
||||
static const int lanes[] = { MV88E6390_PORT9_LANE0, MV88E6390_PORT9_LANE1,
|
||||
MV88E6390_PORT9_LANE2, MV88E6390_PORT9_LANE3,
|
||||
MV88E6390_PORT10_LANE0, MV88E6390_PORT10_LANE1,
|
||||
MV88E6390_PORT10_LANE2, MV88E6390_PORT10_LANE3 };
|
||||
|
|
|
@ -115,19 +115,17 @@ err_free_felix:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ocelot_ext_remove(struct platform_device *pdev)
|
||||
static void ocelot_ext_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct felix *felix = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (!felix)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
dsa_unregister_switch(felix->ds);
|
||||
|
||||
kfree(felix->ds);
|
||||
kfree(felix);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ocelot_ext_shutdown(struct platform_device *pdev)
|
||||
|
@ -154,7 +152,7 @@ static struct platform_driver ocelot_ext_switch_driver = {
|
|||
.of_match_table = ocelot_ext_switch_of_match,
|
||||
},
|
||||
.probe = ocelot_ext_probe,
|
||||
.remove = ocelot_ext_remove,
|
||||
.remove_new = ocelot_ext_remove,
|
||||
.shutdown = ocelot_ext_shutdown,
|
||||
};
|
||||
module_platform_driver(ocelot_ext_switch_driver);
|
||||
|
|
|
@ -1029,19 +1029,17 @@ err_alloc_felix:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int seville_remove(struct platform_device *pdev)
|
||||
static void seville_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct felix *felix = platform_get_drvdata(pdev);
|
||||
|
||||
if (!felix)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
dsa_unregister_switch(felix->ds);
|
||||
|
||||
kfree(felix->ds);
|
||||
kfree(felix);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void seville_shutdown(struct platform_device *pdev)
|
||||
|
@ -1064,7 +1062,7 @@ MODULE_DEVICE_TABLE(of, seville_of_match);
|
|||
|
||||
static struct platform_driver seville_vsc9953_driver = {
|
||||
.probe = seville_probe,
|
||||
.remove = seville_remove,
|
||||
.remove_new = seville_remove,
|
||||
.shutdown = seville_shutdown,
|
||||
.driver = {
|
||||
.name = "mscc_seville",
|
||||
|
|
|
@ -506,12 +506,12 @@ static int realtek_smi_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int realtek_smi_remove(struct platform_device *pdev)
|
||||
static void realtek_smi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct realtek_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
dsa_unregister_switch(priv->ds);
|
||||
if (priv->slave_mii_bus)
|
||||
|
@ -520,8 +520,6 @@ static int realtek_smi_remove(struct platform_device *pdev)
|
|||
/* leave the device reset asserted */
|
||||
if (priv->reset)
|
||||
gpiod_set_value(priv->reset, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void realtek_smi_shutdown(struct platform_device *pdev)
|
||||
|
@ -559,7 +557,7 @@ static struct platform_driver realtek_smi_driver = {
|
|||
.of_match_table = realtek_smi_of_match,
|
||||
},
|
||||
.probe = realtek_smi_probe,
|
||||
.remove = realtek_smi_remove,
|
||||
.remove_new = realtek_smi_remove,
|
||||
.shutdown = realtek_smi_shutdown,
|
||||
};
|
||||
module_platform_driver(realtek_smi_driver);
|
||||
|
|
|
@ -95,12 +95,6 @@
|
|||
#define RTL8366RB_PAACR_RX_PAUSE BIT(6)
|
||||
#define RTL8366RB_PAACR_AN BIT(7)
|
||||
|
||||
#define RTL8366RB_PAACR_CPU_PORT (RTL8366RB_PAACR_SPEED_1000M | \
|
||||
RTL8366RB_PAACR_FULL_DUPLEX | \
|
||||
RTL8366RB_PAACR_LINK_UP | \
|
||||
RTL8366RB_PAACR_TX_PAUSE | \
|
||||
RTL8366RB_PAACR_RX_PAUSE)
|
||||
|
||||
/* bits 0..7 = port 0, bits 8..15 = port 1 */
|
||||
#define RTL8366RB_PSTAT0 0x0014
|
||||
/* bits 0..7 = port 2, bits 8..15 = port 3 */
|
||||
|
@ -1081,29 +1075,61 @@ rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
|
|||
int speed, int duplex, bool tx_pause, bool rx_pause)
|
||||
{
|
||||
struct realtek_priv *priv = ds->priv;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* Allow forcing the mode on the fixed CPU port, no autonegotiation.
|
||||
* We assume autonegotiation works on the PHY-facing ports.
|
||||
*/
|
||||
if (port != priv->cpu_port)
|
||||
return;
|
||||
|
||||
dev_dbg(priv->dev, "MAC link up on CPU port (%d)\n", port);
|
||||
|
||||
/* Force the fixed CPU port into 1Gbit mode, no autonegotiation */
|
||||
ret = regmap_update_bits(priv->map, RTL8366RB_MAC_FORCE_CTRL_REG,
|
||||
BIT(port), BIT(port));
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to force 1Gbit on CPU port\n");
|
||||
dev_err(priv->dev, "failed to force CPU port\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Conjure port config */
|
||||
switch (speed) {
|
||||
case SPEED_10:
|
||||
val = RTL8366RB_PAACR_SPEED_10M;
|
||||
break;
|
||||
case SPEED_100:
|
||||
val = RTL8366RB_PAACR_SPEED_100M;
|
||||
break;
|
||||
case SPEED_1000:
|
||||
val = RTL8366RB_PAACR_SPEED_1000M;
|
||||
break;
|
||||
default:
|
||||
val = RTL8366RB_PAACR_SPEED_1000M;
|
||||
break;
|
||||
}
|
||||
|
||||
if (duplex == DUPLEX_FULL)
|
||||
val |= RTL8366RB_PAACR_FULL_DUPLEX;
|
||||
|
||||
if (tx_pause)
|
||||
val |= RTL8366RB_PAACR_TX_PAUSE;
|
||||
|
||||
if (rx_pause)
|
||||
val |= RTL8366RB_PAACR_RX_PAUSE;
|
||||
|
||||
val |= RTL8366RB_PAACR_LINK_UP;
|
||||
|
||||
ret = regmap_update_bits(priv->map, RTL8366RB_PAACR2,
|
||||
0xFF00U,
|
||||
RTL8366RB_PAACR_CPU_PORT << 8);
|
||||
val << 8);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to set PAACR on CPU port\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(priv->dev, "set PAACR to %04x\n", val);
|
||||
|
||||
/* Enable the CPU port */
|
||||
ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port),
|
||||
0);
|
||||
|
|
|
@ -1272,19 +1272,17 @@ free_pcs:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int a5psw_remove(struct platform_device *pdev)
|
||||
static void a5psw_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct a5psw *a5psw = platform_get_drvdata(pdev);
|
||||
|
||||
if (!a5psw)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
dsa_unregister_switch(&a5psw->ds);
|
||||
a5psw_pcs_free(a5psw);
|
||||
clk_disable_unprepare(a5psw->hclk);
|
||||
clk_disable_unprepare(a5psw->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a5psw_shutdown(struct platform_device *pdev)
|
||||
|
@ -1311,7 +1309,7 @@ static struct platform_driver a5psw_driver = {
|
|||
.of_match_table = a5psw_of_mtable,
|
||||
},
|
||||
.probe = a5psw_probe,
|
||||
.remove = a5psw_remove,
|
||||
.remove_new = a5psw_remove,
|
||||
.shutdown = a5psw_shutdown,
|
||||
};
|
||||
module_platform_driver(a5psw_driver);
|
||||
|
|
|
@ -153,14 +153,14 @@ static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv,
|
|||
{
|
||||
const struct sja1105_regs *regs = priv->info->regs;
|
||||
struct sja1105_cgu_mii_ctrl mii_tx_clk;
|
||||
const int mac_clk_sources[] = {
|
||||
static const int mac_clk_sources[] = {
|
||||
CLKSRC_MII0_TX_CLK,
|
||||
CLKSRC_MII1_TX_CLK,
|
||||
CLKSRC_MII2_TX_CLK,
|
||||
CLKSRC_MII3_TX_CLK,
|
||||
CLKSRC_MII4_TX_CLK,
|
||||
};
|
||||
const int phy_clk_sources[] = {
|
||||
static const int phy_clk_sources[] = {
|
||||
CLKSRC_IDIV0,
|
||||
CLKSRC_IDIV1,
|
||||
CLKSRC_IDIV2,
|
||||
|
@ -194,7 +194,7 @@ sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port)
|
|||
const struct sja1105_regs *regs = priv->info->regs;
|
||||
struct sja1105_cgu_mii_ctrl mii_rx_clk;
|
||||
u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
|
||||
const int clk_sources[] = {
|
||||
static const int clk_sources[] = {
|
||||
CLKSRC_MII0_RX_CLK,
|
||||
CLKSRC_MII1_RX_CLK,
|
||||
CLKSRC_MII2_RX_CLK,
|
||||
|
@ -221,7 +221,7 @@ sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port)
|
|||
const struct sja1105_regs *regs = priv->info->regs;
|
||||
struct sja1105_cgu_mii_ctrl mii_ext_tx_clk;
|
||||
u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
|
||||
const int clk_sources[] = {
|
||||
static const int clk_sources[] = {
|
||||
CLKSRC_IDIV0,
|
||||
CLKSRC_IDIV1,
|
||||
CLKSRC_IDIV2,
|
||||
|
@ -248,7 +248,7 @@ sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port)
|
|||
const struct sja1105_regs *regs = priv->info->regs;
|
||||
struct sja1105_cgu_mii_ctrl mii_ext_rx_clk;
|
||||
u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
|
||||
const int clk_sources[] = {
|
||||
static const int clk_sources[] = {
|
||||
CLKSRC_IDIV0,
|
||||
CLKSRC_IDIV1,
|
||||
CLKSRC_IDIV2,
|
||||
|
@ -349,8 +349,13 @@ static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv,
|
|||
if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) {
|
||||
clksrc = CLKSRC_PLL0;
|
||||
} else {
|
||||
int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2,
|
||||
CLKSRC_IDIV3, CLKSRC_IDIV4};
|
||||
static const int clk_sources[] = {
|
||||
CLKSRC_IDIV0,
|
||||
CLKSRC_IDIV1,
|
||||
CLKSRC_IDIV2,
|
||||
CLKSRC_IDIV3,
|
||||
CLKSRC_IDIV4,
|
||||
};
|
||||
clksrc = clk_sources[port];
|
||||
}
|
||||
|
||||
|
@ -638,7 +643,7 @@ static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv,
|
|||
const struct sja1105_regs *regs = priv->info->regs;
|
||||
struct sja1105_cgu_mii_ctrl ref_clk;
|
||||
u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
|
||||
const int clk_sources[] = {
|
||||
static const int clk_sources[] = {
|
||||
CLKSRC_MII0_TX_CLK,
|
||||
CLKSRC_MII1_TX_CLK,
|
||||
CLKSRC_MII2_TX_CLK,
|
||||
|
|
|
@ -112,16 +112,14 @@ static int vsc73xx_platform_probe(struct platform_device *pdev)
|
|||
return vsc73xx_probe(&vsc_platform->vsc);
|
||||
}
|
||||
|
||||
static int vsc73xx_platform_remove(struct platform_device *pdev)
|
||||
static void vsc73xx_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev);
|
||||
|
||||
if (!vsc_platform)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
vsc73xx_remove(&vsc_platform->vsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vsc73xx_platform_shutdown(struct platform_device *pdev)
|
||||
|
@ -160,7 +158,7 @@ MODULE_DEVICE_TABLE(of, vsc73xx_of_match);
|
|||
|
||||
static struct platform_driver vsc73xx_platform_driver = {
|
||||
.probe = vsc73xx_platform_probe,
|
||||
.remove = vsc73xx_platform_remove,
|
||||
.remove_new = vsc73xx_platform_remove,
|
||||
.shutdown = vsc73xx_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "vsc73xx-platform",
|
||||
|
|
|
@ -811,7 +811,7 @@ static int ax_init_dev(struct net_device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ax_remove(struct platform_device *pdev)
|
||||
static void ax_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
@ -832,8 +832,6 @@ static int ax_remove(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1011,7 +1009,7 @@ static struct platform_driver axdrv = {
|
|||
.name = "ax88796",
|
||||
},
|
||||
.probe = ax_probe,
|
||||
.remove = ax_remove,
|
||||
.remove_new = ax_remove,
|
||||
.suspend = ax_suspend,
|
||||
.resume = ax_resume,
|
||||
};
|
||||
|
|
|
@ -441,7 +441,7 @@ static int mcf8390_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mcf8390_remove(struct platform_device *pdev)
|
||||
static void mcf8390_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct resource *mem;
|
||||
|
@ -450,7 +450,6 @@ static int mcf8390_remove(struct platform_device *pdev)
|
|||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mcf8390_drv = {
|
||||
|
@ -458,7 +457,7 @@ static struct platform_driver mcf8390_drv = {
|
|||
.name = "mcf8390",
|
||||
},
|
||||
.probe = mcf8390_probe,
|
||||
.remove = mcf8390_remove,
|
||||
.remove_new = mcf8390_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mcf8390_drv);
|
||||
|
|
|
@ -823,7 +823,7 @@ static int __init ne_drv_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ne_drv_remove(struct platform_device *pdev)
|
||||
static void ne_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
|
@ -842,7 +842,6 @@ static int ne_drv_remove(struct platform_device *pdev)
|
|||
release_region(dev->base_addr, NE_IO_EXTENT);
|
||||
free_netdev(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove unused devices or all if true. */
|
||||
|
@ -895,7 +894,7 @@ static int ne_drv_resume(struct platform_device *pdev)
|
|||
#endif
|
||||
|
||||
static struct platform_driver ne_driver = {
|
||||
.remove = ne_drv_remove,
|
||||
.remove_new = ne_drv_remove,
|
||||
.suspend = ne_drv_suspend,
|
||||
.resume = ne_drv_resume,
|
||||
.driver = {
|
||||
|
|
|
@ -1582,15 +1582,13 @@ static int owl_emac_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int owl_emac_remove(struct platform_device *pdev)
|
||||
static void owl_emac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct owl_emac_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
netif_napi_del(&priv->napi);
|
||||
phy_disconnect(priv->netdev->phydev);
|
||||
cancel_work_sync(&priv->mac_reset_task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id owl_emac_of_match[] = {
|
||||
|
@ -1609,7 +1607,7 @@ static struct platform_driver owl_emac_driver = {
|
|||
.pm = &owl_emac_pm_ops,
|
||||
},
|
||||
.probe = owl_emac_probe,
|
||||
.remove = owl_emac_remove,
|
||||
.remove_new = owl_emac_remove,
|
||||
};
|
||||
module_platform_driver(owl_emac_driver);
|
||||
|
||||
|
|
|
@ -1525,7 +1525,7 @@ error1:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int greth_of_remove(struct platform_device *of_dev)
|
||||
static void greth_of_remove(struct platform_device *of_dev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(of_dev);
|
||||
struct greth_private *greth = netdev_priv(ndev);
|
||||
|
@ -1544,8 +1544,6 @@ static int greth_of_remove(struct platform_device *of_dev)
|
|||
of_iounmap(&of_dev->resource[0], greth->regs, resource_size(&of_dev->resource[0]));
|
||||
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id greth_of_match[] = {
|
||||
|
@ -1566,7 +1564,7 @@ static struct platform_driver greth_of_driver = {
|
|||
.of_match_table = greth_of_match,
|
||||
},
|
||||
.probe = greth_of_probe,
|
||||
.remove = greth_of_remove,
|
||||
.remove_new = greth_of_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(greth_of_driver);
|
||||
|
|
|
@ -1083,7 +1083,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int emac_remove(struct platform_device *pdev)
|
||||
static void emac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct emac_board_info *db = netdev_priv(ndev);
|
||||
|
@ -1101,7 +1101,6 @@ static int emac_remove(struct platform_device *pdev)
|
|||
free_netdev(ndev);
|
||||
|
||||
dev_dbg(&pdev->dev, "released and freed device\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emac_suspend(struct platform_device *dev, pm_message_t state)
|
||||
|
@ -1143,7 +1142,7 @@ static struct platform_driver emac_driver = {
|
|||
.of_match_table = emac_of_match,
|
||||
},
|
||||
.probe = emac_probe,
|
||||
.remove = emac_remove,
|
||||
.remove_new = emac_remove,
|
||||
.suspend = emac_suspend,
|
||||
.resume = emac_resume,
|
||||
};
|
||||
|
|
|
@ -1464,7 +1464,7 @@ err_free_netdev:
|
|||
|
||||
/* Remove Altera TSE MAC device
|
||||
*/
|
||||
static int altera_tse_remove(struct platform_device *pdev)
|
||||
static void altera_tse_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct altera_tse_private *priv = netdev_priv(ndev);
|
||||
|
@ -1476,8 +1476,6 @@ static int altera_tse_remove(struct platform_device *pdev)
|
|||
lynx_pcs_destroy(priv->pcs);
|
||||
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct altera_dmaops altera_dtype_sgdma = {
|
||||
|
@ -1528,7 +1526,7 @@ MODULE_DEVICE_TABLE(of, altera_tse_ids);
|
|||
|
||||
static struct platform_driver altera_tse_driver = {
|
||||
.probe = altera_tse_probe,
|
||||
.remove = altera_tse_remove,
|
||||
.remove_new = altera_tse_remove,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.driver = {
|
||||
|
|
|
@ -1323,7 +1323,7 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int au1000_remove(struct platform_device *pdev)
|
||||
static void au1000_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct au1000_private *aup = netdev_priv(dev);
|
||||
|
@ -1359,13 +1359,11 @@ static int au1000_remove(struct platform_device *pdev)
|
|||
release_mem_region(macen->start, resource_size(macen));
|
||||
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver au1000_eth_driver = {
|
||||
.probe = au1000_probe,
|
||||
.remove = au1000_remove,
|
||||
.remove_new = au1000_remove,
|
||||
.driver = {
|
||||
.name = "au1000-eth",
|
||||
},
|
||||
|
|
|
@ -445,12 +445,13 @@ int pdsc_setup(struct pdsc *pdsc, bool init)
|
|||
goto err_out_teardown;
|
||||
|
||||
/* Set up the VIFs */
|
||||
err = pdsc_viftypes_init(pdsc);
|
||||
if (err)
|
||||
goto err_out_teardown;
|
||||
if (init) {
|
||||
err = pdsc_viftypes_init(pdsc);
|
||||
if (err)
|
||||
goto err_out_teardown;
|
||||
|
||||
if (init)
|
||||
pdsc_debugfs_add_viftype(pdsc);
|
||||
}
|
||||
|
||||
clear_bit(PDSC_S_FW_DEAD, &pdsc->state);
|
||||
return 0;
|
||||
|
@ -469,8 +470,10 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing)
|
|||
pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
|
||||
pdsc_qcq_free(pdsc, &pdsc->adminqcq);
|
||||
|
||||
kfree(pdsc->viftype_status);
|
||||
pdsc->viftype_status = NULL;
|
||||
if (removing) {
|
||||
kfree(pdsc->viftype_status);
|
||||
pdsc->viftype_status = NULL;
|
||||
}
|
||||
|
||||
if (pdsc->intr_info) {
|
||||
for (i = 0; i < pdsc->nintrs; i++)
|
||||
|
@ -512,7 +515,7 @@ void pdsc_stop(struct pdsc *pdsc)
|
|||
PDS_CORE_INTR_MASK_SET);
|
||||
}
|
||||
|
||||
static void pdsc_fw_down(struct pdsc *pdsc)
|
||||
void pdsc_fw_down(struct pdsc *pdsc)
|
||||
{
|
||||
union pds_core_notifyq_comp reset_event = {
|
||||
.reset.ecode = cpu_to_le16(PDS_EVENT_RESET),
|
||||
|
@ -520,10 +523,13 @@ static void pdsc_fw_down(struct pdsc *pdsc)
|
|||
};
|
||||
|
||||
if (test_and_set_bit(PDSC_S_FW_DEAD, &pdsc->state)) {
|
||||
dev_err(pdsc->dev, "%s: already happening\n", __func__);
|
||||
dev_warn(pdsc->dev, "%s: already happening\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdsc->pdev->is_virtfn)
|
||||
return;
|
||||
|
||||
/* Notify clients of fw_down */
|
||||
if (pdsc->fw_reporter)
|
||||
devlink_health_report(pdsc->fw_reporter, "FW down reported", pdsc);
|
||||
|
@ -533,7 +539,7 @@ static void pdsc_fw_down(struct pdsc *pdsc)
|
|||
pdsc_teardown(pdsc, PDSC_TEARDOWN_RECOVERY);
|
||||
}
|
||||
|
||||
static void pdsc_fw_up(struct pdsc *pdsc)
|
||||
void pdsc_fw_up(struct pdsc *pdsc)
|
||||
{
|
||||
union pds_core_notifyq_comp reset_event = {
|
||||
.reset.ecode = cpu_to_le16(PDS_EVENT_RESET),
|
||||
|
@ -546,6 +552,11 @@ static void pdsc_fw_up(struct pdsc *pdsc)
|
|||
return;
|
||||
}
|
||||
|
||||
if (pdsc->pdev->is_virtfn) {
|
||||
clear_bit(PDSC_S_FW_DEAD, &pdsc->state);
|
||||
return;
|
||||
}
|
||||
|
||||
err = pdsc_setup(pdsc, PDSC_SETUP_RECOVERY);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
@ -567,6 +578,18 @@ err_out:
|
|||
pdsc_teardown(pdsc, PDSC_TEARDOWN_RECOVERY);
|
||||
}
|
||||
|
||||
static void pdsc_check_pci_health(struct pdsc *pdsc)
|
||||
{
|
||||
u8 fw_status = ioread8(&pdsc->info_regs->fw_status);
|
||||
|
||||
/* is PCI broken? */
|
||||
if (fw_status != PDS_RC_BAD_PCI)
|
||||
return;
|
||||
|
||||
pdsc_reset_prepare(pdsc->pdev);
|
||||
pdsc_reset_done(pdsc->pdev);
|
||||
}
|
||||
|
||||
void pdsc_health_thread(struct work_struct *work)
|
||||
{
|
||||
struct pdsc *pdsc = container_of(work, struct pdsc, health_work);
|
||||
|
@ -593,6 +616,8 @@ void pdsc_health_thread(struct work_struct *work)
|
|||
pdsc_fw_down(pdsc);
|
||||
}
|
||||
|
||||
pdsc_check_pci_health(pdsc);
|
||||
|
||||
pdsc->fw_generation = pdsc->fw_status & PDS_CORE_FW_STS_F_GENERATION;
|
||||
|
||||
out_unlock:
|
||||
|
|
|
@ -283,6 +283,9 @@ int pdsc_devcmd_reset(struct pdsc *pdsc);
|
|||
int pdsc_dev_reinit(struct pdsc *pdsc);
|
||||
int pdsc_dev_init(struct pdsc *pdsc);
|
||||
|
||||
void pdsc_reset_prepare(struct pci_dev *pdev);
|
||||
void pdsc_reset_done(struct pci_dev *pdev);
|
||||
|
||||
int pdsc_intr_alloc(struct pdsc *pdsc, char *name,
|
||||
irq_handler_t handler, void *data);
|
||||
void pdsc_intr_free(struct pdsc *pdsc, int index);
|
||||
|
@ -309,4 +312,8 @@ irqreturn_t pdsc_adminq_isr(int irq, void *data);
|
|||
|
||||
int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
void pdsc_fw_down(struct pdsc *pdsc);
|
||||
void pdsc_fw_up(struct pdsc *pdsc);
|
||||
|
||||
#endif /* _PDSC_H_ */
|
||||
|
|
|
@ -42,6 +42,8 @@ int pdsc_err_to_errno(enum pds_core_status_code code)
|
|||
return -ERANGE;
|
||||
case PDS_RC_BAD_ADDR:
|
||||
return -EFAULT;
|
||||
case PDS_RC_BAD_PCI:
|
||||
return -ENXIO;
|
||||
case PDS_RC_EOPCODE:
|
||||
case PDS_RC_EINTR:
|
||||
case PDS_RC_DEV_CMD:
|
||||
|
@ -62,7 +64,7 @@ bool pdsc_is_fw_running(struct pdsc *pdsc)
|
|||
/* Firmware is useful only if the running bit is set and
|
||||
* fw_status != 0xff (bad PCI read)
|
||||
*/
|
||||
return (pdsc->fw_status != 0xff) &&
|
||||
return (pdsc->fw_status != PDS_RC_BAD_PCI) &&
|
||||
(pdsc->fw_status & PDS_CORE_FW_STS_F_RUNNING);
|
||||
}
|
||||
|
||||
|
@ -128,6 +130,7 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
|
|||
unsigned long max_wait;
|
||||
unsigned long duration;
|
||||
int timeout = 0;
|
||||
bool running;
|
||||
int done = 0;
|
||||
int err = 0;
|
||||
int status;
|
||||
|
@ -136,6 +139,10 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
|
|||
max_wait = start_time + (max_seconds * HZ);
|
||||
|
||||
while (!done && !timeout) {
|
||||
running = pdsc_is_fw_running(pdsc);
|
||||
if (!running)
|
||||
break;
|
||||
|
||||
done = pdsc_devcmd_done(pdsc);
|
||||
if (done)
|
||||
break;
|
||||
|
@ -152,7 +159,7 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
|
|||
dev_dbg(dev, "DEVCMD %d %s after %ld secs\n",
|
||||
opcode, pdsc_devcmd_str(opcode), duration / HZ);
|
||||
|
||||
if (!done || timeout) {
|
||||
if ((!done || timeout) && running) {
|
||||
dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n",
|
||||
opcode, pdsc_devcmd_str(opcode), done, timeout,
|
||||
max_seconds);
|
||||
|
|
|
@ -445,12 +445,62 @@ static void pdsc_remove(struct pci_dev *pdev)
|
|||
devlink_free(dl);
|
||||
}
|
||||
|
||||
void pdsc_reset_prepare(struct pci_dev *pdev)
|
||||
{
|
||||
struct pdsc *pdsc = pci_get_drvdata(pdev);
|
||||
|
||||
pdsc_fw_down(pdsc);
|
||||
|
||||
pci_free_irq_vectors(pdev);
|
||||
pdsc_unmap_bars(pdsc);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
void pdsc_reset_done(struct pci_dev *pdev)
|
||||
{
|
||||
struct pdsc *pdsc = pci_get_drvdata(pdev);
|
||||
struct device *dev = pdsc->dev;
|
||||
int err;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(dev, "Cannot enable PCI device: %pe\n", ERR_PTR(err));
|
||||
return;
|
||||
}
|
||||
pci_set_master(pdev);
|
||||
|
||||
if (!pdev->is_virtfn) {
|
||||
pcie_print_link_status(pdsc->pdev);
|
||||
|
||||
err = pci_request_regions(pdsc->pdev, PDS_CORE_DRV_NAME);
|
||||
if (err) {
|
||||
dev_err(pdsc->dev, "Cannot request PCI regions: %pe\n",
|
||||
ERR_PTR(err));
|
||||
return;
|
||||
}
|
||||
|
||||
err = pdsc_map_bars(pdsc);
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
|
||||
pdsc_fw_up(pdsc);
|
||||
}
|
||||
|
||||
static const struct pci_error_handlers pdsc_err_handler = {
|
||||
/* FLR handling */
|
||||
.reset_prepare = pdsc_reset_prepare,
|
||||
.reset_done = pdsc_reset_done,
|
||||
};
|
||||
|
||||
static struct pci_driver pdsc_driver = {
|
||||
.name = PDS_CORE_DRV_NAME,
|
||||
.id_table = pdsc_id_table,
|
||||
.probe = pdsc_probe,
|
||||
.remove = pdsc_remove,
|
||||
.sriov_configure = pdsc_sriov_configure,
|
||||
.err_handler = &pdsc_err_handler,
|
||||
};
|
||||
|
||||
void *pdsc_get_pf_struct(struct pci_dev *vf_pdev)
|
||||
|
|
|
@ -1487,7 +1487,7 @@ static int sunlance_sbus_probe(struct platform_device *op)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sunlance_sbus_remove(struct platform_device *op)
|
||||
static void sunlance_sbus_remove(struct platform_device *op)
|
||||
{
|
||||
struct lance_private *lp = platform_get_drvdata(op);
|
||||
struct net_device *net_dev = lp->dev;
|
||||
|
@ -1497,8 +1497,6 @@ static int sunlance_sbus_remove(struct platform_device *op)
|
|||
lance_free_hwresources(lp);
|
||||
|
||||
free_netdev(net_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sunlance_sbus_match[] = {
|
||||
|
@ -1516,7 +1514,7 @@ static struct platform_driver sunlance_sbus_driver = {
|
|||
.of_match_table = sunlance_sbus_match,
|
||||
},
|
||||
.probe = sunlance_sbus_probe,
|
||||
.remove = sunlance_sbus_remove,
|
||||
.remove_new = sunlance_sbus_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sunlance_sbus_driver);
|
||||
|
|
|
@ -512,7 +512,7 @@ err_alloc:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int xgbe_platform_remove(struct platform_device *pdev)
|
||||
static void xgbe_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
|
@ -521,8 +521,6 @@ static int xgbe_platform_remove(struct platform_device *pdev)
|
|||
platform_device_put(pdata->phy_platdev);
|
||||
|
||||
xgbe_free_pdata(pdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -615,7 +613,7 @@ static struct platform_driver xgbe_driver = {
|
|||
.pm = &xgbe_platform_pm_ops,
|
||||
},
|
||||
.probe = xgbe_platform_probe,
|
||||
.remove = xgbe_platform_remove,
|
||||
.remove_new = xgbe_platform_remove,
|
||||
};
|
||||
|
||||
int xgbe_platform_init(void)
|
||||
|
|
|
@ -690,7 +690,7 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int xge_remove(struct platform_device *pdev)
|
||||
static void xge_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xge_pdata *pdata;
|
||||
struct net_device *ndev;
|
||||
|
@ -706,8 +706,6 @@ static int xge_remove(struct platform_device *pdev)
|
|||
xge_mdio_remove(ndev);
|
||||
unregister_netdev(ndev);
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xge_shutdown(struct platform_device *pdev)
|
||||
|
@ -736,7 +734,7 @@ static struct platform_driver xge_driver = {
|
|||
.acpi_match_table = ACPI_PTR(xge_acpi_match),
|
||||
},
|
||||
.probe = xge_probe,
|
||||
.remove = xge_remove,
|
||||
.remove_new = xge_remove,
|
||||
.shutdown = xge_shutdown,
|
||||
};
|
||||
module_platform_driver(xge_driver);
|
||||
|
|
|
@ -2127,7 +2127,7 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int xgene_enet_remove(struct platform_device *pdev)
|
||||
static void xgene_enet_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xgene_enet_pdata *pdata;
|
||||
struct net_device *ndev;
|
||||
|
@ -2149,8 +2149,6 @@ static int xgene_enet_remove(struct platform_device *pdev)
|
|||
xgene_enet_delete_desc_rings(pdata);
|
||||
pdata->port_ops->shutdown(pdata);
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xgene_enet_shutdown(struct platform_device *pdev)
|
||||
|
@ -2174,7 +2172,7 @@ static struct platform_driver xgene_enet_driver = {
|
|||
.acpi_match_table = ACPI_PTR(xgene_enet_acpi_match),
|
||||
},
|
||||
.probe = xgene_enet_probe,
|
||||
.remove = xgene_enet_remove,
|
||||
.remove_new = xgene_enet_remove,
|
||||
.shutdown = xgene_enet_shutdown,
|
||||
};
|
||||
|
||||
|
|
|
@ -739,7 +739,7 @@ MODULE_LICENSE("GPL");
|
|||
MODULE_DESCRIPTION("Macintosh MACE ethernet driver");
|
||||
MODULE_ALIAS("platform:macmace");
|
||||
|
||||
static int mac_mace_device_remove(struct platform_device *pdev)
|
||||
static void mac_mace_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct mace_data *mp = netdev_priv(dev);
|
||||
|
@ -755,13 +755,11 @@ static int mac_mace_device_remove(struct platform_device *pdev)
|
|||
mp->tx_ring, mp->tx_ring_phys);
|
||||
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mac_mace_driver = {
|
||||
.probe = mace_probe,
|
||||
.remove = mac_mace_device_remove,
|
||||
.remove_new = mac_mace_device_remove,
|
||||
.driver = {
|
||||
.name = mac_mace_string,
|
||||
},
|
||||
|
|
|
@ -58,14 +58,12 @@ out_netdev:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int emac_arc_remove(struct platform_device *pdev)
|
||||
static void emac_arc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
|
||||
arc_emac_remove(ndev);
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id emac_arc_dt_ids[] = {
|
||||
|
@ -76,7 +74,7 @@ MODULE_DEVICE_TABLE(of, emac_arc_dt_ids);
|
|||
|
||||
static struct platform_driver emac_arc_driver = {
|
||||
.probe = emac_arc_probe,
|
||||
.remove = emac_arc_remove,
|
||||
.remove_new = emac_arc_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = emac_arc_dt_ids,
|
||||
|
|
|
@ -244,7 +244,7 @@ out_netdev:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int emac_rockchip_remove(struct platform_device *pdev)
|
||||
static void emac_rockchip_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct rockchip_priv_data *priv = netdev_priv(ndev);
|
||||
|
@ -260,12 +260,11 @@ static int emac_rockchip_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(priv->macclk);
|
||||
|
||||
free_netdev(ndev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver emac_rockchip_driver = {
|
||||
.probe = emac_rockchip_probe,
|
||||
.remove = emac_rockchip_remove,
|
||||
.remove_new = emac_rockchip_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = emac_rockchip_dt_ids,
|
||||
|
|
|
@ -1968,21 +1968,19 @@ err_put_clk:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ag71xx_remove(struct platform_device *pdev)
|
||||
static void ag71xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct ag71xx *ag;
|
||||
|
||||
if (!ndev)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
ag = netdev_priv(ndev);
|
||||
unregister_netdev(ndev);
|
||||
ag71xx_mdio_remove(ag);
|
||||
clk_disable_unprepare(ag->clk_eth);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u32 ar71xx_fifo_ar7100[] = {
|
||||
|
@ -2069,7 +2067,7 @@ static const struct of_device_id ag71xx_match[] = {
|
|||
|
||||
static struct platform_driver ag71xx_driver = {
|
||||
.probe = ag71xx_probe,
|
||||
.remove = ag71xx_remove,
|
||||
.remove_new = ag71xx_remove,
|
||||
.driver = {
|
||||
.name = "ag71xx",
|
||||
.of_match_table = ag71xx_match,
|
||||
|
|
|
@ -504,15 +504,12 @@ struct atl1c_rrd_ring {
|
|||
u16 next_to_use;
|
||||
u16 next_to_clean;
|
||||
struct napi_struct napi;
|
||||
struct page *rx_page;
|
||||
unsigned int rx_page_offset;
|
||||
};
|
||||
|
||||
/* board specific private data structure */
|
||||
struct atl1c_adapter {
|
||||
struct net_device *netdev;
|
||||
struct pci_dev *pdev;
|
||||
unsigned int rx_frag_size;
|
||||
struct atl1c_hw hw;
|
||||
struct atl1c_hw_stats hw_stats;
|
||||
struct mii_if_info mii; /* MII interface info */
|
||||
|
|
|
@ -483,15 +483,10 @@ static int atl1c_set_mac_addr(struct net_device *netdev, void *p)
|
|||
static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter,
|
||||
struct net_device *dev)
|
||||
{
|
||||
unsigned int head_size;
|
||||
int mtu = dev->mtu;
|
||||
|
||||
adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ?
|
||||
roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE;
|
||||
|
||||
head_size = SKB_DATA_ALIGN(adapter->rx_buffer_len + NET_SKB_PAD + NET_IP_ALIGN) +
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
adapter->rx_frag_size = roundup_pow_of_two(head_size);
|
||||
}
|
||||
|
||||
static netdev_features_t atl1c_fix_features(struct net_device *netdev,
|
||||
|
@ -964,7 +959,6 @@ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter)
|
|||
static void atl1c_free_ring_resources(struct atl1c_adapter *adapter)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
int i;
|
||||
|
||||
dma_free_coherent(&pdev->dev, adapter->ring_header.size,
|
||||
adapter->ring_header.desc, adapter->ring_header.dma);
|
||||
|
@ -977,12 +971,6 @@ static void atl1c_free_ring_resources(struct atl1c_adapter *adapter)
|
|||
kfree(adapter->tpd_ring[0].buffer_info);
|
||||
adapter->tpd_ring[0].buffer_info = NULL;
|
||||
}
|
||||
for (i = 0; i < adapter->rx_queue_count; ++i) {
|
||||
if (adapter->rrd_ring[i].rx_page) {
|
||||
put_page(adapter->rrd_ring[i].rx_page);
|
||||
adapter->rrd_ring[i].rx_page = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1754,48 +1742,11 @@ static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter,
|
|||
skb_checksum_none_assert(skb);
|
||||
}
|
||||
|
||||
static struct sk_buff *atl1c_alloc_skb(struct atl1c_adapter *adapter,
|
||||
u32 queue, bool napi_mode)
|
||||
{
|
||||
struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[queue];
|
||||
struct sk_buff *skb;
|
||||
struct page *page;
|
||||
|
||||
if (adapter->rx_frag_size > PAGE_SIZE) {
|
||||
if (likely(napi_mode))
|
||||
return napi_alloc_skb(&rrd_ring->napi,
|
||||
adapter->rx_buffer_len);
|
||||
else
|
||||
return netdev_alloc_skb_ip_align(adapter->netdev,
|
||||
adapter->rx_buffer_len);
|
||||
}
|
||||
|
||||
page = rrd_ring->rx_page;
|
||||
if (!page) {
|
||||
page = alloc_page(GFP_ATOMIC);
|
||||
if (unlikely(!page))
|
||||
return NULL;
|
||||
rrd_ring->rx_page = page;
|
||||
rrd_ring->rx_page_offset = 0;
|
||||
}
|
||||
|
||||
skb = build_skb(page_address(page) + rrd_ring->rx_page_offset,
|
||||
adapter->rx_frag_size);
|
||||
if (likely(skb)) {
|
||||
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
|
||||
rrd_ring->rx_page_offset += adapter->rx_frag_size;
|
||||
if (rrd_ring->rx_page_offset >= PAGE_SIZE)
|
||||
rrd_ring->rx_page = NULL;
|
||||
else
|
||||
get_page(page);
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, u32 queue,
|
||||
bool napi_mode)
|
||||
{
|
||||
struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[queue];
|
||||
struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[queue];
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct atl1c_buffer *buffer_info, *next_info;
|
||||
struct sk_buff *skb;
|
||||
|
@ -1814,13 +1765,27 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, u32 queue,
|
|||
while (next_info->flags & ATL1C_BUFFER_FREE) {
|
||||
rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use);
|
||||
|
||||
skb = atl1c_alloc_skb(adapter, queue, napi_mode);
|
||||
/* When DMA RX address is set to something like
|
||||
* 0x....fc0, it will be very likely to cause DMA
|
||||
* RFD overflow issue.
|
||||
*
|
||||
* To work around it, we apply rx skb with 64 bytes
|
||||
* longer space, and offset the address whenever
|
||||
* 0x....fc0 is detected.
|
||||
*/
|
||||
if (likely(napi_mode))
|
||||
skb = napi_alloc_skb(&rrd_ring->napi, adapter->rx_buffer_len + 64);
|
||||
else
|
||||
skb = netdev_alloc_skb(adapter->netdev, adapter->rx_buffer_len + 64);
|
||||
if (unlikely(!skb)) {
|
||||
if (netif_msg_rx_err(adapter))
|
||||
dev_warn(&pdev->dev, "alloc rx buffer failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (((unsigned long)skb->data & 0xfff) == 0xfc0)
|
||||
skb_reserve(skb, 64);
|
||||
|
||||
/*
|
||||
* Make buffer alignment 2 beyond a 16 byte boundary
|
||||
* this will result in a 16 byte aligned IP header after
|
||||
|
|
|
@ -1344,17 +1344,15 @@ of_put_exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bcmasp_remove(struct platform_device *pdev)
|
||||
static void bcmasp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcmasp_priv *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
priv->destroy_wol(priv);
|
||||
bcmasp_remove_intfs(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcmasp_shutdown(struct platform_device *pdev)
|
||||
|
@ -1428,7 +1426,7 @@ static SIMPLE_DEV_PM_OPS(bcmasp_pm_ops,
|
|||
|
||||
static struct platform_driver bcmasp_driver = {
|
||||
.probe = bcmasp_probe,
|
||||
.remove = bcmasp_remove,
|
||||
.remove_new = bcmasp_remove,
|
||||
.shutdown = bcmasp_shutdown,
|
||||
.driver = {
|
||||
.name = "brcm,asp-v2",
|
||||
|
|
|
@ -768,7 +768,7 @@ err_dma_free:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int bcm4908_enet_remove(struct platform_device *pdev)
|
||||
static void bcm4908_enet_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm4908_enet *enet = platform_get_drvdata(pdev);
|
||||
|
||||
|
@ -776,8 +776,6 @@ static int bcm4908_enet_remove(struct platform_device *pdev)
|
|||
netif_napi_del(&enet->rx_ring.napi);
|
||||
netif_napi_del(&enet->tx_ring.napi);
|
||||
bcm4908_enet_dma_free(enet);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm4908_enet_of_match[] = {
|
||||
|
@ -791,7 +789,7 @@ static struct platform_driver bcm4908_enet_driver = {
|
|||
.of_match_table = bcm4908_enet_of_match,
|
||||
},
|
||||
.probe = bcm4908_enet_probe,
|
||||
.remove = bcm4908_enet_remove,
|
||||
.remove_new = bcm4908_enet_remove,
|
||||
};
|
||||
module_platform_driver(bcm4908_enet_driver);
|
||||
|
||||
|
|
|
@ -1902,7 +1902,7 @@ out:
|
|||
/*
|
||||
* exit func, stops hardware and unregisters netdevice
|
||||
*/
|
||||
static int bcm_enet_remove(struct platform_device *pdev)
|
||||
static void bcm_enet_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm_enet_priv *priv;
|
||||
struct net_device *dev;
|
||||
|
@ -1932,12 +1932,11 @@ static int bcm_enet_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(priv->mac_clk);
|
||||
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bcm63xx_enet_driver = {
|
||||
.probe = bcm_enet_probe,
|
||||
.remove = bcm_enet_remove,
|
||||
.remove_new = bcm_enet_remove,
|
||||
.driver = {
|
||||
.name = "bcm63xx_enet",
|
||||
},
|
||||
|
@ -2739,7 +2738,7 @@ out:
|
|||
|
||||
|
||||
/* exit func, stops hardware and unregisters netdevice */
|
||||
static int bcm_enetsw_remove(struct platform_device *pdev)
|
||||
static void bcm_enetsw_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm_enet_priv *priv;
|
||||
struct net_device *dev;
|
||||
|
@ -2752,12 +2751,11 @@ static int bcm_enetsw_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(priv->mac_clk);
|
||||
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bcm63xx_enetsw_driver = {
|
||||
.probe = bcm_enetsw_probe,
|
||||
.remove = bcm_enetsw_remove,
|
||||
.remove_new = bcm_enetsw_remove,
|
||||
.driver = {
|
||||
.name = "bcm63xx_enetsw",
|
||||
},
|
||||
|
|
|
@ -2648,7 +2648,7 @@ err_free_netdev:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_sysport_remove(struct platform_device *pdev)
|
||||
static void bcm_sysport_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(&pdev->dev);
|
||||
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
||||
|
@ -2663,8 +2663,6 @@ static int bcm_sysport_remove(struct platform_device *pdev)
|
|||
of_phy_deregister_fixed_link(dn);
|
||||
free_netdev(dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
|
||||
|
@ -2901,7 +2899,7 @@ static SIMPLE_DEV_PM_OPS(bcm_sysport_pm_ops,
|
|||
|
||||
static struct platform_driver bcm_sysport_driver = {
|
||||
.probe = bcm_sysport_probe,
|
||||
.remove = bcm_sysport_remove,
|
||||
.remove_new = bcm_sysport_remove,
|
||||
.driver = {
|
||||
.name = "brcm-systemport",
|
||||
.of_match_table = bcm_sysport_of_match,
|
||||
|
|
|
@ -246,13 +246,11 @@ static int bgmac_probe(struct platform_device *pdev)
|
|||
return bgmac_enet_probe(bgmac);
|
||||
}
|
||||
|
||||
static int bgmac_remove(struct platform_device *pdev)
|
||||
static void bgmac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bgmac *bgmac = platform_get_drvdata(pdev);
|
||||
|
||||
bgmac_enet_remove(bgmac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -296,7 +294,7 @@ static struct platform_driver bgmac_enet_driver = {
|
|||
.pm = BGMAC_PM_OPS
|
||||
},
|
||||
.probe = bgmac_probe,
|
||||
.remove = bgmac_remove,
|
||||
.remove_new = bgmac_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bgmac_enet_driver);
|
||||
|
|
|
@ -4164,7 +4164,7 @@ err:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int bcmgenet_remove(struct platform_device *pdev)
|
||||
static void bcmgenet_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcmgenet_priv *priv = dev_to_priv(&pdev->dev);
|
||||
|
||||
|
@ -4172,8 +4172,6 @@ static int bcmgenet_remove(struct platform_device *pdev)
|
|||
unregister_netdev(priv->dev);
|
||||
bcmgenet_mii_exit(priv->dev);
|
||||
free_netdev(priv->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcmgenet_shutdown(struct platform_device *pdev)
|
||||
|
@ -4352,7 +4350,7 @@ MODULE_DEVICE_TABLE(acpi, genet_acpi_match);
|
|||
|
||||
static struct platform_driver bcmgenet_driver = {
|
||||
.probe = bcmgenet_probe,
|
||||
.remove = bcmgenet_remove,
|
||||
.remove_new = bcmgenet_remove,
|
||||
.shutdown = bcmgenet_shutdown,
|
||||
.driver = {
|
||||
.name = "bcmgenet",
|
||||
|
|
|
@ -2593,7 +2593,7 @@ out_out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sbmac_remove(struct platform_device *pldev)
|
||||
static void sbmac_remove(struct platform_device *pldev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pldev);
|
||||
struct sbmac_softc *sc = netdev_priv(dev);
|
||||
|
@ -2604,13 +2604,11 @@ static int sbmac_remove(struct platform_device *pldev)
|
|||
mdiobus_free(sc->mii_bus);
|
||||
iounmap(sc->sbm_base);
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sbmac_driver = {
|
||||
.probe = sbmac_probe,
|
||||
.remove = sbmac_remove,
|
||||
.remove_new = sbmac_remove,
|
||||
.driver = {
|
||||
.name = sbmac_string,
|
||||
},
|
||||
|
|
|
@ -5156,7 +5156,7 @@ err_disable_clocks:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int macb_remove(struct platform_device *pdev)
|
||||
static void macb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct macb *bp;
|
||||
|
@ -5181,8 +5181,6 @@ static int macb_remove(struct platform_device *pdev)
|
|||
phylink_destroy(bp->phylink);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused macb_suspend(struct device *dev)
|
||||
|
@ -5398,7 +5396,7 @@ static const struct dev_pm_ops macb_pm_ops = {
|
|||
|
||||
static struct platform_driver macb_driver = {
|
||||
.probe = macb_probe,
|
||||
.remove = macb_remove,
|
||||
.remove_new = macb_remove,
|
||||
.driver = {
|
||||
.name = "macb",
|
||||
.of_match_table = of_match_ptr(macb_dt_ids),
|
||||
|
|
|
@ -1820,7 +1820,7 @@ err_alloc:
|
|||
* changes the link status, releases the DMA descriptor rings,
|
||||
* unregisters the MDIO bus and unmaps the allocated memory.
|
||||
*/
|
||||
static int xgmac_remove(struct platform_device *pdev)
|
||||
static void xgmac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct xgmac_priv *priv = netdev_priv(ndev);
|
||||
|
@ -1840,8 +1840,6 @@ static int xgmac_remove(struct platform_device *pdev)
|
|||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -1921,7 +1919,7 @@ static struct platform_driver xgmac_driver = {
|
|||
.pm = &xgmac_pm_ops,
|
||||
},
|
||||
.probe = xgmac_probe,
|
||||
.remove = xgmac_remove,
|
||||
.remove_new = xgmac_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(xgmac_driver);
|
||||
|
|
|
@ -1521,7 +1521,7 @@ err:
|
|||
return result;
|
||||
}
|
||||
|
||||
static int octeon_mgmt_remove(struct platform_device *pdev)
|
||||
static void octeon_mgmt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
struct octeon_mgmt *p = netdev_priv(netdev);
|
||||
|
@ -1529,7 +1529,6 @@ static int octeon_mgmt_remove(struct platform_device *pdev)
|
|||
unregister_netdev(netdev);
|
||||
of_node_put(p->phy_np);
|
||||
free_netdev(netdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id octeon_mgmt_match[] = {
|
||||
|
@ -1546,7 +1545,7 @@ static struct platform_driver octeon_mgmt_driver = {
|
|||
.of_match_table = octeon_mgmt_match,
|
||||
},
|
||||
.probe = octeon_mgmt_probe,
|
||||
.remove = octeon_mgmt_remove,
|
||||
.remove_new = octeon_mgmt_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(octeon_mgmt_driver);
|
||||
|
|
|
@ -1879,7 +1879,7 @@ free:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int cs89x0_platform_remove(struct platform_device *pdev)
|
||||
static void cs89x0_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
|
@ -1889,7 +1889,6 @@ static int cs89x0_platform_remove(struct platform_device *pdev)
|
|||
*/
|
||||
unregister_netdev(dev);
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused cs89x0_match[] = {
|
||||
|
@ -1904,7 +1903,7 @@ static struct platform_driver cs89x0_driver = {
|
|||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(cs89x0_match),
|
||||
},
|
||||
.remove = cs89x0_platform_remove,
|
||||
.remove_new = cs89x0_platform_remove,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
|
||||
|
|
|
@ -757,7 +757,7 @@ static struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data)
|
|||
}
|
||||
|
||||
|
||||
static int ep93xx_eth_remove(struct platform_device *pdev)
|
||||
static void ep93xx_eth_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct ep93xx_priv *ep;
|
||||
|
@ -765,7 +765,7 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
|
|||
|
||||
dev = platform_get_drvdata(pdev);
|
||||
if (dev == NULL)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
ep = netdev_priv(dev);
|
||||
|
||||
|
@ -782,8 +782,6 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_eth_probe(struct platform_device *pdev)
|
||||
|
@ -862,7 +860,7 @@ err_out:
|
|||
|
||||
static struct platform_driver ep93xx_eth_driver = {
|
||||
.probe = ep93xx_eth_probe,
|
||||
.remove = ep93xx_eth_remove,
|
||||
.remove_new = ep93xx_eth_remove,
|
||||
.driver = {
|
||||
.name = "ep93xx-eth",
|
||||
},
|
||||
|
|
|
@ -556,19 +556,18 @@ static int set_mac_address(struct net_device *dev, void *addr)
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int mac89x0_device_remove(struct platform_device *pdev)
|
||||
static void mac89x0_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_netdev(dev);
|
||||
nubus_writew(0, dev->base_addr + ADD_PORT);
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mac89x0_platform_driver = {
|
||||
.probe = mac89x0_device_probe,
|
||||
.remove = mac89x0_device_remove,
|
||||
.remove_new = mac89x0_device_remove,
|
||||
.driver = {
|
||||
.name = "mac89x0",
|
||||
},
|
||||
|
|
|
@ -2518,13 +2518,11 @@ unprepare:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int gemini_ethernet_port_remove(struct platform_device *pdev)
|
||||
static void gemini_ethernet_port_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gemini_ethernet_port *port = platform_get_drvdata(pdev);
|
||||
|
||||
gemini_port_remove(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id gemini_ethernet_port_of_match[] = {
|
||||
|
@ -2541,7 +2539,7 @@ static struct platform_driver gemini_ethernet_port_driver = {
|
|||
.of_match_table = gemini_ethernet_port_of_match,
|
||||
},
|
||||
.probe = gemini_ethernet_port_probe,
|
||||
.remove = gemini_ethernet_port_remove,
|
||||
.remove_new = gemini_ethernet_port_remove,
|
||||
};
|
||||
|
||||
static int gemini_ethernet_probe(struct platform_device *pdev)
|
||||
|
@ -2583,14 +2581,12 @@ static int gemini_ethernet_probe(struct platform_device *pdev)
|
|||
return devm_of_platform_populate(dev);
|
||||
}
|
||||
|
||||
static int gemini_ethernet_remove(struct platform_device *pdev)
|
||||
static void gemini_ethernet_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gemini_ethernet *geth = platform_get_drvdata(pdev);
|
||||
|
||||
geth_cleanup_freeq(geth);
|
||||
geth->initialized = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id gemini_ethernet_of_match[] = {
|
||||
|
@ -2607,7 +2603,7 @@ static struct platform_driver gemini_ethernet_driver = {
|
|||
.of_match_table = gemini_ethernet_of_match,
|
||||
},
|
||||
.probe = gemini_ethernet_probe,
|
||||
.remove = gemini_ethernet_remove,
|
||||
.remove_new = gemini_ethernet_remove,
|
||||
};
|
||||
|
||||
static int __init gemini_ethernet_module_init(void)
|
||||
|
|
|
@ -1770,8 +1770,7 @@ static const struct dev_pm_ops dm9000_drv_pm_ops = {
|
|||
.resume = dm9000_drv_resume,
|
||||
};
|
||||
|
||||
static int
|
||||
dm9000_drv_remove(struct platform_device *pdev)
|
||||
static void dm9000_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct board_info *dm = to_dm9000_board(ndev);
|
||||
|
@ -1783,7 +1782,6 @@ dm9000_drv_remove(struct platform_device *pdev)
|
|||
regulator_disable(dm->power_supply);
|
||||
|
||||
dev_dbg(&pdev->dev, "released and freed device\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -1801,7 +1799,7 @@ static struct platform_driver dm9000_driver = {
|
|||
.of_match_table = of_match_ptr(dm9000_of_matches),
|
||||
},
|
||||
.probe = dm9000_probe,
|
||||
.remove = dm9000_drv_remove,
|
||||
.remove_new = dm9000_drv_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(dm9000_driver);
|
||||
|
|
|
@ -841,7 +841,7 @@ err_out_free_dev:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int dnet_remove(struct platform_device *pdev)
|
||||
static void dnet_remove(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
struct net_device *dev;
|
||||
|
@ -859,13 +859,11 @@ static int dnet_remove(struct platform_device *pdev)
|
|||
free_irq(dev->irq, dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dnet_driver = {
|
||||
.probe = dnet_probe,
|
||||
.remove = dnet_remove,
|
||||
.remove_new = dnet_remove,
|
||||
.driver = {
|
||||
.name = "dnet",
|
||||
},
|
||||
|
|
|
@ -2587,7 +2587,7 @@ mdio_init_failed:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int tsnep_remove(struct platform_device *pdev)
|
||||
static void tsnep_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tsnep_adapter *adapter = platform_get_drvdata(pdev);
|
||||
|
||||
|
@ -2603,8 +2603,6 @@ static int tsnep_remove(struct platform_device *pdev)
|
|||
mdiobus_unregister(adapter->mdiobus);
|
||||
|
||||
tsnep_disable_irq(adapter, ECM_INT_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tsnep_of_match[] = {
|
||||
|
@ -2619,7 +2617,7 @@ static struct platform_driver tsnep_driver = {
|
|||
.of_match_table = tsnep_of_match,
|
||||
},
|
||||
.probe = tsnep_probe,
|
||||
.remove = tsnep_remove,
|
||||
.remove_new = tsnep_remove,
|
||||
};
|
||||
module_platform_driver(tsnep_driver);
|
||||
|
||||
|
|
|
@ -1254,7 +1254,7 @@ out:
|
|||
* ethoc_remove - shutdown OpenCores ethernet MAC
|
||||
* @pdev: platform device
|
||||
*/
|
||||
static int ethoc_remove(struct platform_device *pdev)
|
||||
static void ethoc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
struct ethoc *priv = netdev_priv(netdev);
|
||||
|
@ -1271,8 +1271,6 @@ static int ethoc_remove(struct platform_device *pdev)
|
|||
unregister_netdev(netdev);
|
||||
free_netdev(netdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -1298,7 +1296,7 @@ MODULE_DEVICE_TABLE(of, ethoc_match);
|
|||
|
||||
static struct platform_driver ethoc_driver = {
|
||||
.probe = ethoc_probe,
|
||||
.remove = ethoc_remove,
|
||||
.remove_new = ethoc_remove,
|
||||
.suspend = ethoc_suspend,
|
||||
.resume = ethoc_resume,
|
||||
.driver = {
|
||||
|
|
|
@ -2012,7 +2012,7 @@ err_alloc_etherdev:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ftgmac100_remove(struct platform_device *pdev)
|
||||
static void ftgmac100_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *netdev;
|
||||
struct ftgmac100 *priv;
|
||||
|
@ -2040,7 +2040,6 @@ static int ftgmac100_remove(struct platform_device *pdev)
|
|||
|
||||
netif_napi_del(&priv->napi);
|
||||
free_netdev(netdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ftgmac100_of_match[] = {
|
||||
|
@ -2051,7 +2050,7 @@ MODULE_DEVICE_TABLE(of, ftgmac100_of_match);
|
|||
|
||||
static struct platform_driver ftgmac100_driver = {
|
||||
.probe = ftgmac100_probe,
|
||||
.remove = ftgmac100_remove,
|
||||
.remove_new = ftgmac100_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = ftgmac100_of_match,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue