Commit 8ef988b9 authored by David S. Miller's avatar David S. Miller

Merge branch 'NXP-SJA1105-DSA-driver'

Vladimir Oltean says:

====================
NXP SJA1105 DSA driver

This patchset adds a DSA driver for the SPI-controlled NXP SJA1105
switch.  Due to the hardware's unfriendliness, most of its state needs
to be shadowed in kernel memory by the driver. To support this and keep
a decent amount of cleanliness in the code, a new generic API for
converting between CPU-accessible ("unpacked") structures and
hardware-accessible ("packed") structures is proposed and used.

The driver is GPL-2.0 licensed. The source code files which are licensed
as BSD-3-Clause are hardware support files and derivative of the
userspace NXP sja1105-tool program, which is BSD-3-Clause licensed.

TODO items:
* Add support for traffic.
* Add full support for the P/Q/R/S series. The patches were mostly
  tested on a first-generation T device.
* Add timestamping support and PTP clock manipulation.
* Figure out how the tc-taprio hardware offload that was just proposed
  by Vinicius can be used to configure the switch's time-aware scheduler.
* Rework link state callbacks to use phylink once the SGMII port
  is supported.

Changes in v5:
1. Removed trailing empty lines at the end of files.
2. Moved the lib/packing.c file under a CONFIG_PACKING option instead of
   having it always built-in. The module is GPL licensed, which applies
   to its distribution in binary form, but the code is dual-licensed
   which means it can be used in projects with other licenses as well.
3. Made SJA1105 driver select CONFIG_PACKING and CONFIG_CRC32.

v4 patchset can be found at:
https://lwn.net/Articles/787077/

Changes in v4:
1. Previous patchset was broken apart, and for the moment the driver is
   configuring the switch as unmanaged. Support for regular and management
   traffic, as well as for PTP timestamping, will be submitted once the
   basic driver is accepted. Some core DSA patches were also broken out
   of the series, and are a dependency for this series:
   https://patchwork.ozlabs.org/project/netdev/list/?series=105069
2. Addressed Jiri Pirko's feedback about too generic function and macro
   naming.
3. Re-introduced ETH_P_DSA_8021Q.

v3 patchset can be found at:
https://lkml.org/lkml/2019/4/12/978

Changes in v3:
1. Removed the patch for a dedicated Ethertype to use with 802.1Q DSA
   tagging
2. Changed the SJA1105 switch tagging protocol sysfs label from
   "sja1105" to "8021q" to denote to users such as tcpdump that the
   structure is more generic.
3. Respun previous patch "net: dsa: Allow drivers to modulate between
   presence and absence of tagging". Current equivalent patch is called
   "net: dsa: Allow drivers to filter packets they can decode source
   port from" and at least allows reception of management traffic during
   the time when switch tagging is not enabled.
4. Added DSA-level fixes for the bridge core not unsetting
   vlan_filtering when ports leave. The global VLAN filtering is treated
   as a special case. Made the mt7530 driver use this. This patch
   benefits the SJA1105 because otherwise traffic in standalone mode
   would no longer work after removing the ports from a vlan_filtering
   bridge, since the driver and the hardware would be in an inconsistent
   state.
5. Restructured the documentation as rst. This depends upon the recently
   submitted "[PATCH net-next] Documentation: net: dsa: transition to
   the rst format": https://patchwork.ozlabs.org/patch/1084658/.

v2 patchset can be found at:
https://www.spinics.net/lists/netdev/msg563454.html

Changes in v2:
1. Device ID is no longer auto-detected but enforced based on explicit DT
   compatible string. This helps with stricter checking of DT bindings.
2. Group all device-specific operations into a sja1105_info structure and
   avoid using the IS_ET() and IS_PQRS() macros at runtime as much as possible.
3. Added more verbiage to commit messages and documentation.
4. Treat the case where RGMII internal delays are requested through DT bindings
   and return error.
5. Miscellaneous cosmetic cleanup in sja1105_clocking.c
6. Not advertising link features that are not supported, such as pause frames
   and the half duplex modes.
7. Fixed a mistake in previous patchset where the switch tagging was not
   actually enabled (lost during a rebase). This brought up another uncaught
   issue where switching at runtime between tagging and no-tagging was not
   supported by DSA. Fixed up the mistake in "net: dsa: sja1105: Add support
   for traffic through standalone ports", and added the new patch "net: dsa:
   Allow drivers to modulate between presence and absence of tagging" to
   address the other issue.
8. Added a workaround for switch resets cutting a frame in the middle of
   transmission, which would throw off some link partners.
9. Changed the TPID from ETH_P_EDSA (0xDADA) to a newly introduced one:
   ETH_P_DSA_8021Q (0xDADB). Uncovered another mistake in the previous patchset
   with a missing ntohs(), which was not caught because 0xDADA is
   endian-agnostic.
10. Made NET_DSA_TAG_8021Q select VLAN_8021Q
11. Renamed __dsa_port_vlan_add to dsa_port_vid_add and not to
    dsa_port_vlan_add_trans, as suggested, because the corresponding _del function
    does not have a transactional phase and the naming is more uniform this way.

v1 patchset can be found at:
https://www.spinics.net/lists/netdev/msg561589.html

Changes from RFC:
1. Removed the packing code for the static configuration tables that were
   not currently used
2. Removed the code for unpacking a static configuration structure from
   a memory buffer (not used)
3. Completely removed the SGMII stubs, since the configuration is not
   complete anyway.
4. Moved some code from the SJA1105 introduction commit into the patch
   that used it.
5. Made the code for checking global VLAN filtering generic and made b53
   driver use it.
6. Made mt7530 driver use the new generic dp->vlan_filtering
7. Fixed check for stringset in .get_sset_count
8. Minor cleanup in sja1105_clocking.c
9. Fixed a confusing typo in DSA

RFC can be found at:
https://www.mail-archive.com/netdev@vger.kernel.org/msg291717.html
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8b952747 013fe01d
NXP SJA1105 switch driver
=========================
Required properties:
- compatible:
Must be one of:
- "nxp,sja1105e"
- "nxp,sja1105t"
- "nxp,sja1105p"
- "nxp,sja1105q"
- "nxp,sja1105r"
- "nxp,sja1105s"
Although the device ID could be detected at runtime, explicit bindings
are required in order to be able to statically check their validity.
For example, SGMII can only be specified on port 4 of R and S devices,
and the non-SGMII devices, while pin-compatible, are not equal in terms
of support for RGMII internal delays (supported on P/Q/R/S, but not on
E/T).
Optional properties:
- sja1105,role-mac:
- sja1105,role-phy:
Boolean properties that can be assigned under each port node. By
default (unless otherwise specified) a port is configured as MAC if it
is driving a PHY (phy-handle is present) or as PHY if it is PHY-less
(fixed-link specified, presumably because it is connected to a MAC).
The effect of this property (in either its implicit or explicit form)
is:
- In the case of MII or RMII it specifies whether the SJA1105 port is a
clock source or sink for this interface (not applicable for RGMII
where there is a Tx and an Rx clock).
- In the case of RGMII it affects the behavior regarding internal
delays:
1. If sja1105,role-mac is specified, and the phy-mode property is one
of "rgmii-id", "rgmii-txid" or "rgmii-rxid", then the entity
designated to apply the delay/clock skew necessary for RGMII
is the PHY. The SJA1105 MAC does not apply any internal delays.
2. If sja1105,role-phy is specified, and the phy-mode property is one
of the above, the designated entity to apply the internal delays
is the SJA1105 MAC (if hardware-supported). This is only supported
by the second-generation (P/Q/R/S) hardware. On a first-generation
E or T device, it is an error to specify an RGMII phy-mode other
than "rgmii" for a port that is in fixed-link mode. In that case,
the clock skew must either be added by the MAC at the other end of
the fixed-link, or by PCB serpentine traces on the board.
These properties are required, for example, in the case where SJA1105
ports are at both ends of a MII/RMII PHY-less setup. One end would need
to have sja1105,role-mac, while the other sja1105,role-phy.
See Documentation/devicetree/bindings/net/dsa/dsa.txt for the list of standard
DSA required and optional properties.
Other observations
------------------
The SJA1105 SPI interface requires a CS-to-CLK time (t2 in UM10944) of at least
one half of t_CLK. At an SPI frequency of 1MHz, this means a minimum
cs_sck_delay of 500ns. Ensuring that this SPI timing requirement is observed
depends on the SPI bus master driver.
Example
-------
Ethernet switch connected via SPI to the host, CPU port wired to enet2:
arch/arm/boot/dts/ls1021a-tsn.dts:
/* SPI controller of the LS1021 */
&dspi0 {
sja1105@1 {
reg = <0x1>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "nxp,sja1105t";
spi-max-frequency = <4000000>;
fsl,spi-cs-sck-delay = <1000>;
fsl,spi-sck-cs-delay = <1000>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
/* ETH5 written on chassis */
label = "swp5";
phy-handle = <&rgmii_phy6>;
phy-mode = "rgmii-id";
reg = <0>;
/* Implicit "sja1105,role-mac;" */
};
port@1 {
/* ETH2 written on chassis */
label = "swp2";
phy-handle = <&rgmii_phy3>;
phy-mode = "rgmii-id";
reg = <1>;
/* Implicit "sja1105,role-mac;" */
};
port@2 {
/* ETH3 written on chassis */
label = "swp3";
phy-handle = <&rgmii_phy4>;
phy-mode = "rgmii-id";
reg = <2>;
/* Implicit "sja1105,role-mac;" */
};
port@3 {
/* ETH4 written on chassis */
phy-handle = <&rgmii_phy5>;
label = "swp4";
phy-mode = "rgmii-id";
reg = <3>;
/* Implicit "sja1105,role-mac;" */
};
port@4 {
/* Internal port connected to eth2 */
ethernet = <&enet2>;
phy-mode = "rgmii";
reg = <4>;
/* Implicit "sja1105,role-phy;" */
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
};
/* MDIO controller of the LS1021 */
&mdio0 {
/* BCM5464 */
rgmii_phy3: ethernet-phy@3 {
reg = <0x3>;
};
rgmii_phy4: ethernet-phy@4 {
reg = <0x4>;
};
rgmii_phy5: ethernet-phy@5 {
reg = <0x5>;
};
rgmii_phy6: ethernet-phy@6 {
reg = <0x6>;
};
};
/* Ethernet master port of the LS1021 */
&enet2 {
phy-connection-type = "rgmii";
status = "ok";
fixed-link {
speed = <1000>;
full-duplex;
};
};
......@@ -8,3 +8,4 @@ Distributed Switch Architecture
dsa
bcm_sf2
lan9303
sja1105
=========================
NXP SJA1105 switch driver
=========================
Overview
========
The NXP SJA1105 is a family of 6 devices:
- SJA1105E: First generation, no TTEthernet
- SJA1105T: First generation, TTEthernet
- SJA1105P: Second generation, no TTEthernet, no SGMII
- SJA1105Q: Second generation, TTEthernet, no SGMII
- SJA1105R: Second generation, no TTEthernet, SGMII
- SJA1105S: Second generation, TTEthernet, SGMII
These are SPI-managed automotive switches, with all ports being gigabit
capable, and supporting MII/RMII/RGMII and optionally SGMII on one port.
Being automotive parts, their configuration interface is geared towards
set-and-forget use, with minimal dynamic interaction at runtime. They
require a static configuration to be composed by software and packed
with CRC and table headers, and sent over SPI.
The static configuration is composed of several configuration tables. Each
table takes a number of entries. Some configuration tables can be (partially)
reconfigured at runtime, some not. Some tables are mandatory, some not:
============================= ================== =============================
Table Mandatory Reconfigurable
============================= ================== =============================
Schedule no no
Schedule entry points if Scheduling no
VL Lookup no no
VL Policing if VL Lookup no
VL Forwarding if VL Lookup no
L2 Lookup no no
L2 Policing yes no
VLAN Lookup yes yes
L2 Forwarding yes partially (fully on P/Q/R/S)
MAC Config yes partially (fully on P/Q/R/S)
Schedule Params if Scheduling no
Schedule Entry Points Params if Scheduling no
VL Forwarding Params if VL Forwarding no
L2 Lookup Params no partially (fully on P/Q/R/S)
L2 Forwarding Params yes no
Clock Sync Params no no
AVB Params no no
General Params yes partially
Retagging no yes
xMII Params yes no
SGMII no yes
============================= ================== =============================
Also the configuration is write-only (software cannot read it back from the
switch except for very few exceptions).
The driver creates a static configuration at probe time, and keeps it at
all times in memory, as a shadow for the hardware state. When required to
change a hardware setting, the static configuration is also updated.
If that changed setting can be transmitted to the switch through the dynamic
reconfiguration interface, it is; otherwise the switch is reset and
reprogrammed with the updated static configuration.
Switching features
==================
The driver supports the configuration of L2 forwarding rules in hardware for
port bridging. The forwarding, broadcast and flooding domain between ports can
be restricted through two methods: either at the L2 forwarding level (isolate
one bridge's ports from another's) or at the VLAN port membership level
(isolate ports within the same bridge). The final forwarding decision taken by
the hardware is a logical AND of these two sets of rules.
The hardware tags all traffic internally with a port-based VLAN (pvid), or it
decodes the VLAN information from the 802.1Q tag. Advanced VLAN classification
is not possible. Once attributed a VLAN tag, frames are checked against the
port's membership rules and dropped at ingress if they don't match any VLAN.
This behavior is available when switch ports are enslaved to a bridge with
``vlan_filtering 1``.
Normally the hardware is not configurable with respect to VLAN awareness, but
by changing what TPID the switch searches 802.1Q tags for, the semantics of a
bridge with ``vlan_filtering 0`` can be kept (accept all traffic, tagged or
untagged), and therefore this mode is also supported.
Segregating the switch ports in multiple bridges is supported (e.g. 2 + 2), but
all bridges should have the same level of VLAN awareness (either both have
``vlan_filtering`` 0, or both 1). Also an inevitable limitation of the fact
that VLAN awareness is global at the switch level is that once a bridge with
``vlan_filtering`` enslaves at least one switch port, the other un-bridged
ports are no longer available for standalone traffic termination.
Device Tree bindings and board design
=====================================
This section references ``Documentation/devicetree/bindings/net/dsa/sja1105.txt``
and aims to showcase some potential switch caveats.
RMII PHY role and out-of-band signaling
---------------------------------------
In the RMII spec, the 50 MHz clock signals are either driven by the MAC or by
an external oscillator (but not by the PHY).
But the spec is rather loose and devices go outside it in several ways.
Some PHYs go against the spec and may provide an output pin where they source
the 50 MHz clock themselves, in an attempt to be helpful.
On the other hand, the SJA1105 is only binary configurable - when in the RMII
MAC role it will also attempt to drive the clock signal. To prevent this from
happening it must be put in RMII PHY role.
But doing so has some unintended consequences.
In the RMII spec, the PHY can transmit extra out-of-band signals via RXD[1:0].
These are practically some extra code words (/J/ and /K/) sent prior to the
preamble of each frame. The MAC does not have this out-of-band signaling
mechanism defined by the RMII spec.
So when the SJA1105 port is put in PHY role to avoid having 2 drivers on the
clock signal, inevitably an RMII PHY-to-PHY connection is created. The SJA1105
emulates a PHY interface fully and generates the /J/ and /K/ symbols prior to
frame preambles, which the real PHY is not expected to understand. So the PHY
simply encodes the extra symbols received from the SJA1105-as-PHY onto the
100Base-Tx wire.
On the other side of the wire, some link partners might discard these extra
symbols, while others might choke on them and discard the entire Ethernet
frames that follow along. This looks like packet loss with some link partners
but not with others.
The take-away is that in RMII mode, the SJA1105 must be let to drive the
reference clock if connected to a PHY.
RGMII fixed-link and internal delays
------------------------------------
As mentioned in the bindings document, the second generation of devices has
tunable delay lines as part of the MAC, which can be used to establish the
correct RGMII timing budget.
When powered up, these can shift the Rx and Tx clocks with a phase difference
between 73.8 and 101.7 degrees.
The catch is that the delay lines need to lock onto a clock signal with a
stable frequency. This means that there must be at least 2 microseconds of
silence between the clock at the old vs at the new frequency. Otherwise the
lock is lost and the delay lines must be reset (powered down and back up).
In RGMII the clock frequency changes with link speed (125 MHz at 1000 Mbps, 25
MHz at 100 Mbps and 2.5 MHz at 10 Mbps), and link speed might change during the
AN process.
In the situation where the switch port is connected through an RGMII fixed-link
to a link partner whose link state life cycle is outside the control of Linux
(such as a different SoC), then the delay lines would remain unlocked (and
inactive) until there is manual intervention (ifdown/ifup on the switch port).
The take-away is that in RGMII mode, the switch's internal delays are only
reliable if the link partner never changes link speeds, or if it does, it does
so in a way that is coordinated with the switch port (practically, both ends of
the fixed-link are under control of the same Linux system).
As to why would a fixed-link interface ever change link speeds: there are
Ethernet controllers out there which come out of reset in 100 Mbps mode, and
their driver inevitably needs to change the speed and clock frequency if it's
required to work at gigabit.
MDIO bus and PHY management
---------------------------
The SJA1105 does not have an MDIO bus and does not perform in-band AN either.
Therefore there is no link state notification coming from the switch device.
A board would need to hook up the PHYs connected to the switch to any other
MDIO bus available to Linux within the system (e.g. to the DSA master's MDIO
bus). Link state management then works by the driver manually keeping in sync
(over SPI commands) the MAC link speed with the settings negotiated by the PHY.
================================================
Generic bitfield packing and unpacking functions
================================================
Problem statement
-----------------
When working with hardware, one has to choose between several approaches of
interfacing with it.
One can memory-map a pointer to a carefully crafted struct over the hardware
device's memory region, and access its fields as struct members (potentially
declared as bitfields). But writing code this way would make it less portable,
due to potential endianness mismatches between the CPU and the hardware device.
Additionally, one has to pay close attention when translating register
definitions from the hardware documentation into bit field indices for the
structs. Also, some hardware (typically networking equipment) tends to group
its register fields in ways that violate any reasonable word boundaries
(sometimes even 64 bit ones). This creates the inconvenience of having to
define "high" and "low" portions of register fields within the struct.
A more robust alternative to struct field definitions would be to extract the
required fields by shifting the appropriate number of bits. But this would
still not protect from endianness mismatches, except if all memory accesses
were performed byte-by-byte. Also the code can easily get cluttered, and the
high-level idea might get lost among the many bit shifts required.
Many drivers take the bit-shifting approach and then attempt to reduce the
clutter with tailored macros, but more often than not these macros take
shortcuts that still prevent the code from being truly portable.
The solution
------------
This API deals with 2 basic operations:
- Packing a CPU-usable number into a memory buffer (with hardware
constraints/quirks)
- Unpacking a memory buffer (which has hardware constraints/quirks)
into a CPU-usable number.
The API offers an abstraction over said hardware constraints and quirks,
over CPU endianness and therefore between possible mismatches between
the two.
The basic unit of these API functions is the u64. From the CPU's
perspective, bit 63 always means bit offset 7 of byte 7, albeit only
logically. The question is: where do we lay this bit out in memory?
The following examples cover the memory layout of a packed u64 field.
The byte offsets in the packed buffer are always implicitly 0, 1, ... 7.
What the examples show is where the logical bytes and bits sit.
1. Normally (no quirks), we would do it like this:
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
7 6 5 4
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
3 2 1 0
That is, the MSByte (7) of the CPU-usable u64 sits at memory offset 0, and the
LSByte (0) of the u64 sits at memory offset 7.
This corresponds to what most folks would regard to as "big endian", where
bit i corresponds to the number 2^i. This is also referred to in the code
comments as "logical" notation.
2. If QUIRK_MSB_ON_THE_RIGHT is set, we do it like this:
56 57 58 59 60 61 62 63 48 49 50 51 52 53 54 55 40 41 42 43 44 45 46 47 32 33 34 35 36 37 38 39
7 6 5 4
24 25 26 27 28 29 30 31 16 17 18 19 20 21 22 23 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7
3 2 1 0
That is, QUIRK_MSB_ON_THE_RIGHT does not affect byte positioning, but
inverts bit offsets inside a byte.
3. If QUIRK_LITTLE_ENDIAN is set, we do it like this:
39 38 37 36 35 34 33 32 47 46 45 44 43 42 41 40 55 54 53 52 51 50 49 48 63 62 61 60 59 58 57 56
4 5 6 7
7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 24
0 1 2 3
Therefore, QUIRK_LITTLE_ENDIAN means that inside the memory region, every
byte from each 4-byte word is placed at its mirrored position compared to
the boundary of that word.
4. If QUIRK_MSB_ON_THE_RIGHT and QUIRK_LITTLE_ENDIAN are both set, we do it
like this:
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
4 5 6 7
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0 1 2 3
5. If just QUIRK_LSW32_IS_FIRST is set, we do it like this:
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
3 2 1 0
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
7 6 5 4
In this case the 8 byte memory region is interpreted as follows: first
4 bytes correspond to the least significant 4-byte word, next 4 bytes to
the more significant 4-byte word.
6. If QUIRK_LSW32_IS_FIRST and QUIRK_MSB_ON_THE_RIGHT are set, we do it like
this:
24 25 26 27 28 29 30 31 16 17 18 19 20 21 22 23 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7
3 2 1 0
56 57 58 59 60 61 62 63 48 49 50 51 52 53 54 55 40 41 42 43 44 45 46 47 32 33 34 35 36 37 38 39
7 6 5 4
7. If QUIRK_LSW32_IS_FIRST and QUIRK_LITTLE_ENDIAN are set, it looks like
this:
7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 24
0 1 2 3
39 38 37 36 35 34 33 32 47 46 45 44 43 42 41 40 55 54 53 52 51 50 49 48 63 62 61 60 59 58 57 56
4 5 6 7
8. If QUIRK_LSW32_IS_FIRST, QUIRK_LITTLE_ENDIAN and QUIRK_MSB_ON_THE_RIGHT
are set, it looks like this:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0 1 2 3
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
4 5 6 7
We always think of our offsets as if there were no quirk, and we translate
them afterwards, before accessing the memory region.
Intended use
------------
Drivers that opt to use this API first need to identify which of the above 3
quirk combinations (for a total of 8) match what the hardware documentation
describes. Then they should wrap the packing() function, creating a new
xxx_packing() that calls it using the proper QUIRK_* one-hot bits set.
The packing() function returns an int-encoded error code, which protects the
programmer against incorrect API use. The errors are not expected to occur
durring runtime, therefore it is reasonable for xxx_packing() to return void
and simply swallow those errors. Optionally it can dump stack or print the
error description.
......@@ -11120,6 +11120,12 @@ S: Maintained
F: Documentation/devicetree/bindings/sound/sgtl5000.txt
F: sound/soc/codecs/sgtl5000*
NXP SJA1105 ETHERNET SWITCH DRIVER
M: Vladimir Oltean <olteanv@gmail.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/net/dsa/sja1105
NXP TDA998X DRM DRIVER
M: Russell King <linux@armlinux.org.uk>
S: Maintained
......@@ -11673,6 +11679,14 @@ L: linux-i2c@vger.kernel.org
S: Orphan
F: drivers/i2c/busses/i2c-pasemi.c
PACKING
M: Vladimir Oltean <olteanv@gmail.com>
L: netdev@vger.kernel.org
S: Supported
F: lib/packing.c
F: include/linux/packing.h
F: Documentation/packing.txt
PADATA PARALLEL EXECUTION MECHANISM
M: Steffen Klassert <steffen.klassert@secunet.com>
L: linux-crypto@vger.kernel.org
......
......@@ -51,6 +51,8 @@ source "drivers/net/dsa/microchip/Kconfig"
source "drivers/net/dsa/mv88e6xxx/Kconfig"
source "drivers/net/dsa/sja1105/Kconfig"
config NET_DSA_QCA8K
tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
depends on NET_DSA
......
......@@ -18,3 +18,4 @@ obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx.o
obj-y += b53/
obj-y += microchip/
obj-y += mv88e6xxx/
obj-y += sja1105/
config NET_DSA_SJA1105
tristate "NXP SJA1105 Ethernet switch family support"
depends on NET_DSA && SPI
select PACKING
select CRC32
help
This is the driver for the NXP SJA1105 automotive Ethernet switch
family. These are 5-port devices and are managed over an SPI
interface. Probing is handled based on OF bindings and so is the
linkage to phylib. The driver supports the following revisions:
- SJA1105E (Gen. 1, No TT-Ethernet)
- SJA1105T (Gen. 1, TT-Ethernet)
- SJA1105P (Gen. 2, No SGMII, No TT-Ethernet)
- SJA1105Q (Gen. 2, No SGMII, TT-Ethernet)
- SJA1105R (Gen. 2, SGMII, No TT-Ethernet)
- SJA1105S (Gen. 2, SGMII, TT-Ethernet)
obj-$(CONFIG_NET_DSA_SJA1105) += sja1105.o
sja1105-objs := \
sja1105_spi.o \
sja1105_main.o \
sja1105_ethtool.o \
sja1105_clocking.o \
sja1105_static_config.o \
sja1105_dynamic_config.o \
/* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018, Sensor-Technik Wiedemann GmbH
* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
*/
#ifndef _SJA1105_H
#define _SJA1105_H
#include <linux/dsa/sja1105.h>
#include <net/dsa.h>
#include "sja1105_static_config.h"
#define SJA1105_NUM_PORTS 5
#define SJA1105_NUM_TC 8
#define SJA1105ET_FDB_BIN_SIZE 4
/* The hardware value is in multiples of 10 ms.
* The passed parameter is in multiples of 1 ms.
*/
#define SJA1105_AGEING_TIME_MS(ms) ((ms) / 10)
/* Keeps the different addresses between E/T and P/Q/R/S */
struct sja1105_regs {
u64 device_id;
u64 prod_id;
u64 status;
u64 port_control;
u64 rgu;
u64 config;
u64 rmii_pll1;
u64 pad_mii_tx[SJA1105_NUM_PORTS];
u64 cgu_idiv[SJA1105_NUM_PORTS];
u64 rgmii_pad_mii_tx[SJA1105_NUM_PORTS];
u64 mii_tx_clk[SJA1105_NUM_PORTS];
u64 mii_rx_clk[SJA1105_NUM_PORTS];
u64 mii_ext_tx_clk[SJA1105_NUM_PORTS];
u64 mii_ext_rx_clk[SJA1105_NUM_PORTS];
u64 rgmii_tx_clk[SJA1105_NUM_PORTS];
u64 rmii_ref_clk[SJA1105_NUM_PORTS];
u64 rmii_ext_tx_clk[SJA1105_NUM_PORTS];
u64 mac[SJA1105_NUM_PORTS];
u64 mac_hl1[SJA1105_NUM_PORTS];
u64 mac_hl2[SJA1105_NUM_PORTS];
u64 qlevel[SJA1105_NUM_PORTS];
};
struct sja1105_info {
u64 device_id;
/* Needed for distinction between P and R, and between Q and S
* (since the parts with/without SGMII share the same
* switch core and device_id)
*/
u64 part_no;
const struct sja1105_dynamic_table_ops *dyn_ops;
const struct sja1105_table_ops *static_ops;
const struct sja1105_regs *regs;
int (*reset_cmd)(const void *ctx, const void *data);
int (*setup_rgmii_delay)(const void *ctx, int port);
const char *name;
};
struct sja1105_private {
struct sja1105_static_config static_config;
bool rgmii_rx_delay[SJA1105_NUM_PORTS];
bool rgmii_tx_delay[SJA1105_NUM_PORTS];
const struct sja1105_info *info;
struct gpio_desc *reset_gpio;
struct spi_device *spidev;
struct dsa_switch *ds;
};
#include "sja1105_dynamic_config.h"
struct sja1105_spi_message {
u64 access;
u64 read_count;
u64 address;
};
typedef enum {
SPI_READ = 0,
SPI_WRITE = 1,
} sja1105_spi_rw_mode_t;
/* From sja1105_spi.c */
int sja1105_spi_send_packed_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr,
void *packed_buf, size_t size_bytes);
int sja1105_spi_send_int(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr,
u64 *value, u64 size_bytes);
int sja1105_spi_send_long_packed_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 base_addr,
void *packed_buf, u64 buf_len);
int sja1105_static_config_upload(struct sja1105_private *priv);
extern struct sja1105_info sja1105e_info;
extern struct sja1105_info sja1105t_info;
extern struct sja1105_info sja1105p_info;
extern struct sja1105_info sja1105q_info;
extern struct sja1105_info sja1105r_info;
extern struct sja1105_info sja1105s_info;
/* From sja1105_clocking.c */
typedef enum {
XMII_MAC = 0,
XMII_PHY = 1,
} sja1105_mii_role_t;
typedef enum {
XMII_MODE_MII = 0,
XMII_MODE_RMII = 1,
XMII_MODE_RGMII = 2,
} sja1105_phy_interface_t;
typedef enum {
SJA1105_SPEED_10MBPS = 3,
SJA1105_SPEED_100MBPS = 2,
SJA1105_SPEED_1000MBPS = 1,
SJA1105_SPEED_AUTO = 0,
} sja1105_speed_t;
int sja1105_clocking_setup_port(struct sja1105_private *priv, int port);
int sja1105_clocking_setup(struct sja1105_private *priv);
/* From sja1105_ethtool.c */
void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data);
void sja1105_get_strings(struct dsa_switch *ds, int port,
u32 stringset, u8 *data);
int sja1105_get_sset_count(struct dsa_switch *ds, int port, int sset);
/* From sja1105_dynamic_config.c */
int sja1105_dynamic_config_read(struct sja1105_private *priv,
enum sja1105_blk_idx blk_idx,
int index, void *entry);
int sja1105_dynamic_config_write(struct sja1105_private *priv,
enum sja1105_blk_idx blk_idx,
int index, void *entry, bool keep);
u8 sja1105_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid);
/* Common implementations for the static and dynamic configs */
size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
enum packing_op op);
size_t sja1105pqrs_l2_lookup_entry_packing(void *buf, void *entry_ptr,
enum packing_op op);
size_t sja1105et_l2_lookup_entry_packing(void *buf, void *entry_ptr,
enum packing_op op);
size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
enum packing_op op);
size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,
enum packing_op op);
#endif
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
*/
#ifndef _SJA1105_DYNAMIC_CONFIG_H
#define _SJA1105_DYNAMIC_CONFIG_H
#include "sja1105.h"
#include <linux/packing.h>
struct sja1105_dyn_cmd {
u64 valid;
u64 rdwrset;
u64 errors;
u64 valident;
u64 index;
};
struct sja1105_dynamic_table_ops {
/* This returns size_t just to keep same prototype as the
* static config ops, of which we are reusing some functions.
*/
size_t (*entry_packing)(void *buf, void *entry_ptr, enum packing_op op);
void (*cmd_packing)(void *buf, struct sja1105_dyn_cmd *cmd,
enum packing_op op);
size_t max_entry_count;
size_t packed_size;
u64 addr;
u8 access;
};
struct sja1105_mgmt_entry {
u64 tsreg;
u64 takets;
u64 macaddr;
u64 destports;
u64 enfport;
u64 index;
};
extern struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN];
extern struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN];
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) 2016-2018, NXP Semiconductors
* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
*/
#ifndef _SJA1105_STATIC_CONFIG_H
#define _SJA1105_STATIC_CONFIG_H
#include <linux/packing.h>
#include <linux/types.h>
#include <asm/types.h>
#define SJA1105_SIZE_DEVICE_ID 4
#define SJA1105_SIZE_TABLE_HEADER 12
#define SJA1105_SIZE_L2_POLICING_ENTRY 8
#define SJA1105_SIZE_VLAN_LOOKUP_ENTRY 8
#define SJA1105_SIZE_L2_FORWARDING_ENTRY 8
#define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY 12
#define SJA1105_SIZE_XMII_PARAMS_ENTRY 4
#define SJA1105ET_SIZE_L2_LOOKUP_ENTRY 12
#define SJA1105ET_SIZE_MAC_CONFIG_ENTRY 28
#define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_ENTRY 4
#define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY 40
#define SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY 20
#define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY 32
#define SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY 16
#define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY 44
/* UM10944.pdf Page 11, Table 2. Configuration Blocks */
enum {
BLKID_L2_LOOKUP = 0x05,
BLKID_L2_POLICING = 0x06,
BLKID_VLAN_LOOKUP = 0x07,
BLKID_L2_FORWARDING = 0x08,
BLKID_MAC_CONFIG = 0x09,
BLKID_L2_LOOKUP_PARAMS = 0x0D,
BLKID_L2_FORWARDING_PARAMS = 0x0E,
BLKID_GENERAL_PARAMS = 0x11,
BLKID_XMII_PARAMS = 0x4E,
};
enum sja1105_blk_idx {
BLK_IDX_L2_LOOKUP = 0,
BLK_IDX_L2_POLICING,
BLK_IDX_VLAN_LOOKUP,
BLK_IDX_L2_FORWARDING,
BLK_IDX_MAC_CONFIG,
BLK_IDX_L2_LOOKUP_PARAMS,
BLK_IDX_L2_FORWARDING_PARAMS,
BLK_IDX_GENERAL_PARAMS,
BLK_IDX_XMII_PARAMS,
BLK_IDX_MAX,
/* Fake block indices that are only valid for dynamic access */
BLK_IDX_MGMT_ROUTE,
BLK_IDX_MAX_DYN,
BLK_IDX_INVAL = -1,
};
#define SJA1105_MAX_L2_LOOKUP_COUNT 1024
#define SJA1105_MAX_L2_POLICING_COUNT 45
#define SJA1105_MAX_VLAN_LOOKUP_COUNT 4096
#define SJA1105_MAX_L2_FORWARDING_COUNT 13
#define SJA1105_MAX_MAC_CONFIG_COUNT 5
#define SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT 1
#define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT 1
#define SJA1105_MAX_GENERAL_PARAMS_COUNT 1
#define SJA1105_MAX_XMII_PARAMS_COUNT 1
#define SJA1105_MAX_FRAME_MEMORY 929
#define SJA1105E_DEVICE_ID 0x9C00000Cull
#define SJA1105T_DEVICE_ID 0x9E00030Eull
#define SJA1105PR_DEVICE_ID 0xAF00030Eull
#define SJA1105QS_DEVICE_ID 0xAE00030Eull
#define SJA1105ET_PART_NO 0x9A83
#define SJA1105P_PART_NO 0x9A84
#define SJA1105Q_PART_NO 0x9A85
#define SJA1105R_PART_NO 0x9A86
#define SJA1105S_PART_NO 0x9A87
struct sja1105_general_params_entry {
u64 vllupformat;
u64 mirr_ptacu;
u64 switchid;
u64 hostprio;
u64 mac_fltres1;
u64 mac_fltres0;
u64 mac_flt1;
u64 mac_flt0;
u64 incl_srcpt1;
u64 incl_srcpt0;
u64 send_meta1;
u64 send_meta0;
u64 casc_port;
u64 host_port;
u64 mirr_port;
u64 vlmarker;
u64 vlmask;
u64 tpid;
u64 ignore2stf;
u64 tpid2;
/* P/Q/R/S only */
u64 queue_ts;
u64 egrmirrvid;
u64 egrmirrpcp;
u64 egrmirrdei;
u64 replay_port;
};
struct sja1105_vlan_lookup_entry {
u64 ving_mirr;
u64 vegr_mirr;
u64 vmemb_port;
u64 vlan_bc;
u64 tag_port;
u64 vlanid;
};
struct sja1105_l2_lookup_entry {
u64 vlanid;
u64 macaddr;
u64 destports;
u64 enfport;
u64 index;
};
struct sja1105_l2_lookup_params_entry {
u64 maxage; /* Shared */
u64 dyn_tbsz; /* E/T only */
u64 poly; /* E/T only */
u64 shared_learn; /* Shared */
u64 no_enf_hostprt; /* Shared */
u64 no_mgmt_learn; /* Shared */
};
struct sja1105_l2_forwarding_entry {
u64 bc_domain;
u64 reach_port;
u64 fl_domain;
u64 vlan_pmap[8];
};
struct sja1105_l2_forwarding_params_entry {
u64 max_dynp;
u64 part_spc[8];
};
struct sja1105_l2_policing_entry {
u64 sharindx;
u64 smax;
u64 rate;
u64 maxlen;
u64 partition;
};
struct sja1105_mac_config_entry {
u64 top[8];
u64 base[8];
u64 enabled[8];
u64 ifg;
u64 speed;
u64 tp_delin;
u64 tp_delout;
u64 maxage;
u64 vlanprio;
u64 vlanid;
u64 ing_mirr;
u64 egr_mirr;
u64 drpnona664;
u64 drpdtag;
u64 drpuntag;
u64 retag;
u64 dyn_learn;
u64 egress;
u64 ingress;
};
struct sja1105_xmii_params_entry {
u64 phy_mac[5];
u64 xmii_mode[5];
};
struct sja1105_table_header {
u64 block_id;
u64 len;
u64 crc;
};
struct sja1105_table_ops {
size_t (*packing)(void *buf, void *entry_ptr, enum packing_op op);
size_t unpacked_entry_size;
size_t packed_entry_size;
size_t max_entry_count;
};
struct sja1105_table {
const struct sja1105_table_ops *ops;
size_t entry_count;
void *entries;
};
struct sja1105_static_config {
u64 device_id;
struct sja1105_table tables[BLK_IDX_MAX];
};
extern struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX];
extern struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX];
extern struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX];
extern struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX];
extern struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX];
extern struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX];
size_t sja1105_table_header_packing(void *buf, void *hdr, enum packing_op op);
void
sja1105_table_header_pack_with_crc(void *buf, struct sja1105_table_header *hdr);
size_t
sja1105_static_config_get_length(const struct sja1105_static_config *config);
typedef enum {
SJA1105_CONFIG_OK = 0,
SJA1105_MISSING_L2_POLICING_TABLE,
SJA1105_MISSING_L2_FORWARDING_TABLE,
SJA1105_MISSING_L2_FORWARDING_PARAMS_TABLE,
SJA1105_MISSING_GENERAL_PARAMS_TABLE,
SJA1105_MISSING_VLAN_TABLE,
SJA1105_MISSING_XMII_TABLE,
SJA1105_MISSING_MAC_TABLE,
SJA1105_OVERCOMMITTED_FRAME_MEMORY,
} sja1105_config_valid_t;
extern const char *sja1105_static_config_error_msg[];
sja1105_config_valid_t
sja1105_static_config_check_valid(const struct sja1105_static_config *config);
void
sja1105_static_config_pack(void *buf, struct sja1105_static_config *config);
int sja1105_static_config_init(struct sja1105_static_config *config,
const struct sja1105_table_ops *static_ops,
u64 device_id);
void sja1105_static_config_free(struct sja1105_static_config *config);
int sja1105_table_delete_entry(struct sja1105_table *table, int i);
int sja1105_table_resize(struct sja1105_table *table, size_t new_count);
u32 sja1105_crc32(const void *buf, size_t len);
void sja1105_pack(void *buf, const u64 *val, int start, int end, size_t len);
void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len);
void sja1105_packing(void *buf, u64 *val, int start, int end,
size_t len, enum packing_op op);
#endif
/* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
*/
/* Included by drivers/net/dsa/sja1105/sja1105.h */
#ifndef _NET_DSA_SJA1105_H
#define _NET_DSA_SJA1105_H
#include <linux/etherdevice.h>
#define ETH_P_SJA1105 ETH_P_DSA_8021Q
/* The switch can only be convinced to stay in unmanaged mode and not trap any
* link-local traffic by actually telling it to filter frames sent at the
* 00:00:00:00:00:00 destination MAC.
*/
#define SJA1105_LINKLOCAL_FILTER_A 0x000000000000ull
#define SJA1105_LINKLOCAL_FILTER_A_MASK 0xFFFFFFFFFFFFull
#define SJA1105_LINKLOCAL_FILTER_B 0x000000000000ull
#define SJA1105_LINKLOCAL_FILTER_B_MASK 0xFFFFFFFFFFFFull
#endif /* _NET_DSA_SJA1105_H */
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) 2016-2018, NXP Semiconductors
* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
*/
#ifndef _LINUX_PACKING_H
#define _LINUX_PACKING_H
#include <linux/types.h>
#include <linux/bitops.h>
#define QUIRK_MSB_ON_THE_RIGHT BIT(0)
#define QUIRK_LITTLE_ENDIAN BIT(1)
#define QUIRK_LSW32_IS_FIRST BIT(2)
enum packing_op {
PACK,
UNPACK,
};
/**
* packing - Convert numbers (currently u64) between a packed and an unpacked
* format. Unpacked means laid out in memory in the CPU's native
* understanding of integers, while packed means anything else that
* requires translation.
*
* @pbuf: Pointer to a buffer holding the packed value.
* @uval: Pointer to an u64 holding the unpacked value.
* @startbit: The index (in logical notation, compensated for quirks) where
* the packed value starts within pbuf. Must be larger than, or
* equal to, endbit.
* @endbit: The index (in logical notation, compensated for quirks) where
* the packed value ends within pbuf. Must be smaller than, or equal
* to, startbit.
* @op: If PACK, then uval will be treated as const pointer and copied (packed)
* into pbuf, between startbit and endbit.
* If UNPACK, then pbuf will be treated as const pointer and the logical
* value between startbit and endbit will be copied (unpacked) to uval.
* @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
* QUIRK_MSB_ON_THE_RIGHT.
*
* Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
* correct usage, return code may be discarded.
* If op is PACK, pbuf is modified.
* If op is UNPACK, uval is modified.
*/
int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
enum packing_op op, u8 quirks);
#endif
......@@ -109,6 +109,7 @@
#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */
#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
......
......@@ -18,6 +18,23 @@ config RAID6_PQ_BENCHMARK
Benchmark all available RAID6 PQ functions on init and choose the
fastest one.
config PACKING
bool "Generic bitfield packing and unpacking"
default n
help
This option provides the packing() helper function, which permits
converting bitfields between a CPU-usable representation and a
memory representation that can have any combination of these quirks:
- Is little endian (bytes are reversed within a 32-bit group)
- The least-significant 32-bit word comes first (within a 64-bit
group)
- The most significant bit of a byte is at its right (bit 0 of a
register description is numerically 2^7).
Drivers may use these helpers to match the bit indices as described
in the data sheets of the peripherals they are in control of.
When in doubt, say N.
config BITREVERSE
tristate
......
......@@ -108,6 +108,7 @@ obj-$(CONFIG_DEBUG_LIST) += list_debug.o
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
obj-$(CONFIG_BITREVERSE) += bitrev.o
obj-$(CONFIG_PACKING) += packing.o
obj-$(CONFIG_RATIONAL) += rational.o
obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
obj-$(CONFIG_CRC16) += crc16.o
......
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2016-2018, NXP Semiconductors
* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
*/
#include <linux/packing.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/types.h>
static int get_le_offset(int offset)
{
int closest_multiple_of_4;
closest_multiple_of_4 = (offset / 4) * 4;
offset -= closest_multiple_of_4;
return closest_multiple_of_4 + (3 - offset);
}
static int get_reverse_lsw32_offset(int offset, size_t len)
{
int closest_multiple_of_4;
int word_index;
word_index = offset / 4;
closest_multiple_of_4 = word_index * 4;
offset -= closest_multiple_of_4;
word_index = (len / 4) - word_index - 1;
return word_index * 4 + offset;
}
static u64 bit_reverse(u64 val, unsigned int width)
{
u64 new_val = 0;
unsigned int bit;
unsigned int i;
for (i = 0; i < width; i++) {
bit = (val & (1 << i)) != 0;
new_val |= (bit << (width - i - 1));
}
return new_val;
}
static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
int *box_end_bit, u8 *box_mask)
{
int box_bit_width = *box_start_bit - *box_end_bit + 1;
int new_box_start_bit, new_box_end_bit;
*to_write >>= *box_end_bit;
*to_write = bit_reverse(*to_write, box_bit_width);
*to_write <<= *box_end_bit;
new_box_end_bit = box_bit_width - *box_start_bit - 1;
new_box_start_bit = box_bit_width - *box_end_bit - 1;
*box_mask = GENMASK_ULL(new_box_start_bit, new_box_end_bit);
*box_start_bit = new_box_start_bit;
*box_end_bit = new_box_end_bit;
}
/**
* packing - Convert numbers (currently u64) between a packed and an unpacked
* format. Unpacked means laid out in memory in the CPU's native
* understanding of integers, while packed means anything else that
* requires translation.
*
* @pbuf: Pointer to a buffer holding the packed value.
* @uval: Pointer to an u64 holding the unpacked value.
* @startbit: The index (in logical notation, compensated for quirks) where
* the packed value starts within pbuf. Must be larger than, or
* equal to, endbit.
* @endbit: The index (in logical notation, compensated for quirks) where
* the packed value ends within pbuf. Must be smaller than, or equal
* to, startbit.
* @op: If PACK, then uval will be treated as const pointer and copied (packed)
* into pbuf, between startbit and endbit.
* If UNPACK, then pbuf will be treated as const pointer and the logical
* value between startbit and endbit will be copied (unpacked) to uval.
* @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
* QUIRK_MSB_ON_THE_RIGHT.
*
* Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
* correct usage, return code may be discarded.
* If op is PACK, pbuf is modified.
* If op is UNPACK, uval is modified.
*/
int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
enum packing_op op, u8 quirks)
{
/* Number of bits for storing "uval"
* also width of the field to access in the pbuf
*/
u64 value_width;
/* Logical byte indices corresponding to the
* start and end of the field.
*/
int plogical_first_u8, plogical_last_u8, box;
/* startbit is expected to be larger than endbit */
if (startbit < endbit)
/* Invalid function call */
return -EINVAL;
value_width = startbit - endbit + 1;
if (value_width > 64)
return -ERANGE;
/* Check if "uval" fits in "value_width" bits.
* If value_width is 64, the check will fail, but any
* 64-bit uval will surely fit.
*/
if (op == PACK && value_width < 64 && (*uval >= (1ull << value_width)))
/* Cannot store "uval" inside "value_width" bits.
* Truncating "uval" is most certainly not desirable,
* so simply erroring out is appropriate.
*/
return -ERANGE;
/* Initialize parameter */
if (op == UNPACK)
*uval = 0;
/* Iterate through an idealistic view of the pbuf as an u64 with
* no quirks, u8 by u8 (aligned at u8 boundaries), from high to low
* logical bit significance. "box" denotes the current logical u8.
*/
plogical_first_u8 = startbit / 8;
plogical_last_u8 = endbit / 8;
for (box = plogical_first_u8; box >= plogical_last_u8; box--) {
/* Bit indices into the currently accessed 8-bit box */
int box_start_bit, box_end_bit, box_addr;
u8 box_mask;
/* Corresponding bits from the unpacked u64 parameter */
int proj_start_bit, proj_end_bit;
u64 proj_mask;
/* This u8 may need to be accessed in its entirety
* (from bit 7 to bit 0), or not, depending on the
* input arguments startbit and endbit.
*/
if (box == plogical_first_u8)
box_start_bit = startbit % 8;
else
box_start_bit = 7;
if (box == plogical_last_u8)
box_end_bit = endbit % 8;
else
box_end_bit = 0;
/* We have determined the box bit start and end.
* Now we calculate where this (masked) u8 box would fit
* in the unpacked (CPU-readable) u64 - the u8 box's
* projection onto the unpacked u64. Though the
* box is u8, the projection is u64 because it may fall
* anywhere within the unpacked u64.
*/
proj_start_bit = ((box * 8) + box_start_bit) - endbit;
proj_end_bit = ((box * 8) + box_end_bit) - endbit;
proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit);
box_mask = GENMASK_ULL(box_start_bit, box_end_bit);
/* Determine the offset of the u8 box inside the pbuf,
* adjusted for quirks. The adjusted box_addr will be used for
* effective addressing inside the pbuf (so it's not
* logical any longer).
*/
box_addr = pbuflen - box - 1;
if (quirks & QUIRK_LITTLE_ENDIAN)
box_addr = get_le_offset(box_addr);
if (quirks & QUIRK_LSW32_IS_FIRST)
box_addr = get_reverse_lsw32_offset(box_addr,
pbuflen);
if (op == UNPACK) {
u64 pval;
/* Read from pbuf, write to uval */
pval = ((u8 *)pbuf)[box_addr] & box_mask;
if (quirks & QUIRK_MSB_ON_THE_RIGHT)
adjust_for_msb_right_quirk(&pval,
&box_start_bit,
&box_end_bit,
&box_mask);
pval >>= box_end_bit;
pval <<= proj_end_bit;
*uval &= ~proj_mask;
*uval |= pval;
} else {
u64 pval;
/* Write to pbuf, read from uval */
pval = (*uval) & proj_mask;
pval >>= proj_end_bit;
if (quirks & QUIRK_MSB_ON_THE_RIGHT)
adjust_for_msb_right_quirk(&pval,
&box_start_bit,
&box_end_bit,
&box_mask);
pval <<= box_end_bit;
((u8 *)pbuf)[box_addr] &= ~box_mask;
((u8 *)pbuf)[box_addr] |= pval;
}
}
return 0;
}
EXPORT_SYMBOL(packing);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Generic bitfield packing and unpacking");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment