diff options
author | David S. Miller <davem@davemloft.net> | 2019-05-03 10:49:18 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-05-03 10:49:18 -0400 |
commit | 8ef988b914bd449458eb2174febb67b0f137b33c (patch) | |
tree | 278b2afbb922ef8e9fe83be37b92e1a058e29deb /lib/packing.c | |
parent | 8b952747844526cef50fa2e0ae903f586e3cb2e4 (diff) | |
parent | 013fe01d45ed095189fd746e45fdf75016fec1f6 (diff) |
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: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib/packing.c')
-rw-r--r-- | lib/packing.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/lib/packing.c b/lib/packing.c new file mode 100644 index 000000000000..50d1e9f2f5a7 --- /dev/null +++ b/lib/packing.c @@ -0,0 +1,213 @@ +// 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"); |