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:
Huacai Chen 2023-11-01 10:55:00 +08:00
commit a6bdc082ad
558 changed files with 44939 additions and 4929 deletions

View file

@ -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:

View file

@ -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`` | |
+ +----------------------------------------+----------------------------------+-----------+

View file

@ -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.

View file

@ -22,6 +22,7 @@ properties:
- mediatek,mt7622-wed
- mediatek,mt7981-wed
- mediatek,mt7986-wed
- mediatek,mt7988-wed
- const: syscon
reg:

View file

@ -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

View file

@ -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

View 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>;
};
};
};

View 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>;
};
};
};

View file

@ -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:

View file

@ -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:

View file

@ -20,6 +20,7 @@ properties:
items:
- enum:
- mediatek,mt7986-wo-ccif
- mediatek,mt7988-wo-ccif
- const: syscon
reg:

View 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``.

View file

@ -114,6 +114,7 @@ available subsections can be seen below.
zorro
hte/index
wmi
dpll
.. only:: subproject and html

View 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

View file

@ -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
-

View file

@ -32,6 +32,7 @@ Contents:
intel/e1000
intel/e1000e
intel/fm10k
intel/idpf
intel/igb
intel/igbvf
intel/ixgbe

View 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.

View file

@ -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
-------

View file

@ -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

View file

@ -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
=======

View file

@ -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>

View file

@ -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;

View file

@ -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)

View file

@ -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];

View file

@ -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;
}

View file

@ -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");
}

View file

@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
source "drivers/cdx/Kconfig"
source "drivers/dpll/Kconfig"
endmenu

View file

@ -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
View 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
View 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
View 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(&reg->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(&reg->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(&reg->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(&reg->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(&reg->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(&reg->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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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 */

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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)

View 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);
}

View file

@ -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;

View file

@ -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

View file

@ -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",

View file

@ -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 };

View file

@ -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);

View file

@ -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",

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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",

View file

@ -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,
};

View file

@ -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);

View file

@ -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 = {

View file

@ -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);

View file

@ -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);

View file

@ -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,
};

View file

@ -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 = {

View file

@ -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",
},

View file

@ -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:

View file

@ -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_ */

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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,
};

View file

@ -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,
},

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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 */

View file

@ -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

View file

@ -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",

View file

@ -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);

View file

@ -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",
},

View file

@ -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,

View file

@ -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);

View file

@ -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",

View file

@ -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,
},

View file

@ -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),

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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",
},

View file

@ -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",
},

View file

@ -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)

View file

@ -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);

View file

@ -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",
},

View file

@ -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);

View file

@ -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 = {

View file

@ -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