From 2434ba2bc8518eafdb04852e70f5a2ba2ba1feee Mon Sep 17 00:00:00 2001 From: Eric Woudstra Date: Tue, 26 Mar 2024 17:23:04 +0100 Subject: [PATCH 01/18] dt-bindings: net: airoha,en8811h: Add en8811h Add the Airoha EN8811H 2.5 Gigabit PHY. The en8811h phy can be set with serdes polarity reversed on rx and/or tx. Signed-off-by: Eric Woudstra Reviewed-by: Krzysztof Kozlowski Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20240326162305.303598-2-ericwouds@gmail.com Signed-off-by: Jakub Kicinski --- .../bindings/net/airoha,en8811h.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/airoha,en8811h.yaml diff --git a/Documentation/devicetree/bindings/net/airoha,en8811h.yaml b/Documentation/devicetree/bindings/net/airoha,en8811h.yaml new file mode 100644 index 000000000000..ecb5149ec6b0 --- /dev/null +++ b/Documentation/devicetree/bindings/net/airoha,en8811h.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/airoha,en8811h.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN8811H PHY + +maintainers: + - Eric Woudstra + +description: + The Airoha EN8811H PHY has the ability to reverse polarity + on the lines to and/or from the MAC. It is reversed by + the booleans in the devicetree node of the phy. + +allOf: + - $ref: ethernet-phy.yaml# + +properties: + compatible: + enum: + - ethernet-phy-id03a2.a411 + + reg: + maxItems: 1 + + airoha,pnswap-rx: + type: boolean + description: + Reverse rx polarity of the SERDES. This is the receiving + side of the lines from the MAC towards the EN881H. + + airoha,pnswap-tx: + type: boolean + description: + Reverse tx polarity of SERDES. This is the transmitting + side of the lines from EN8811H towards the MAC. + +required: + - reg + +unevaluatedProperties: false + +examples: + - | + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.a411"; + reg = <1>; + airoha,pnswap-rx; + }; + }; From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001 From: Eric Woudstra Date: Tue, 26 Mar 2024 17:23:05 +0100 Subject: [PATCH 02/18] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports 100/1000/2500 Mbps with auto negotiation only. The driver uses two firmware files, for which updated versions are added to linux-firmware already. Note: At phy-address + 8 there is another device on the mdio bus, that belongs to the EN881H. While the original driver writes to it, Airoha has confirmed this is not needed. Therefore, communication with this device is not included in this driver. Signed-off-by: Eric Woudstra Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++ 3 files changed, 1092 insertions(+) create mode 100644 drivers/net/phy/air_en8811h.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 1df0595c5ba9..7fddc8306d82 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -76,6 +76,11 @@ config SFP comment "MII PHY device drivers" +config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" + help + Currently supports the Airoha EN8811H PHY. + config AMD_PHY tristate "AMD and Altima PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 197acfa0b412..202ed7f450da 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -34,6 +34,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) obj-$(CONFIG_ADIN_PHY) += adin.o obj-$(CONFIG_ADIN1100_PHY) += adin1100.o +obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ ifdef CONFIG_AX88796B_RUST_PHY diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c new file mode 100644 index 000000000000..720542a4fd82 --- /dev/null +++ b/drivers/net/phy/air_en8811h.c @@ -0,0 +1,1086 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * + * Limitations of the EN8811H: + * - Only full duplex supported + * - Forced speed (AN off) is not supported by hardware (100Mbps) + * + * Source originated from airoha's en8811h.c and en8811h.h v1.2.1 + * + * Copyright (C) 2023 Airoha Technology Corp. + */ + +#include +#include +#include +#include +#include + +#define EN8811H_PHY_ID 0x03a2a411 + +#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" +#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" + +#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DSP 0x00100000 + +/* MII Registers */ +#define AIR_AUX_CTRL_STATUS 0x1d +#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) +#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 +#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 +#define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc + +#define AIR_EXT_PAGE_ACCESS 0x1f +#define AIR_PHY_PAGE_STANDARD 0x0000 +#define AIR_PHY_PAGE_EXTENDED_4 0x0004 + +/* MII Registers Page 4*/ +#define AIR_BPBUS_MODE 0x10 +#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 +#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) +#define AIR_BPBUS_WR_ADDR_HIGH 0x11 +#define AIR_BPBUS_WR_ADDR_LOW 0x12 +#define AIR_BPBUS_WR_DATA_HIGH 0x13 +#define AIR_BPBUS_WR_DATA_LOW 0x14 +#define AIR_BPBUS_RD_ADDR_HIGH 0x15 +#define AIR_BPBUS_RD_ADDR_LOW 0x16 +#define AIR_BPBUS_RD_DATA_HIGH 0x17 +#define AIR_BPBUS_RD_DATA_LOW 0x18 + +/* Registers on MDIO_MMD_VEND1 */ +#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_READY 0x02 + +#define AIR_PHY_MCU_CMD_1 0x800c +#define AIR_PHY_MCU_CMD_1_MODE1 0x0 +#define AIR_PHY_MCU_CMD_2 0x800d +#define AIR_PHY_MCU_CMD_2_MODE1 0x0 +#define AIR_PHY_MCU_CMD_3 0x800e +#define AIR_PHY_MCU_CMD_3_MODE1 0x1101 +#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100 +#define AIR_PHY_MCU_CMD_4 0x800f +#define AIR_PHY_MCU_CMD_4_MODE1 0x0002 +#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4 + +/* Registers on MDIO_MMD_VEND2 */ +#define AIR_PHY_LED_BCR 0x021 +#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0) +#define AIR_PHY_LED_BCR_TIME_TEST BIT(2) +#define AIR_PHY_LED_BCR_CLK_EN BIT(3) +#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15) + +#define AIR_PHY_LED_DUR_ON 0x022 + +#define AIR_PHY_LED_DUR_BLINK 0x023 + +#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) +#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8)) +#define AIR_PHY_LED_ON_LINK1000 BIT(0) +#define AIR_PHY_LED_ON_LINK100 BIT(1) +#define AIR_PHY_LED_ON_LINK10 BIT(2) +#define AIR_PHY_LED_ON_LINKDOWN BIT(3) +#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +#define AIR_PHY_LED_ON_FORCE_ON BIT(6) +#define AIR_PHY_LED_ON_LINK2500 BIT(8) +#define AIR_PHY_LED_ON_POLARITY BIT(14) +#define AIR_PHY_LED_ON_ENABLE BIT(15) + +#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) +#define AIR_PHY_LED_BLINK_1000TX BIT(0) +#define AIR_PHY_LED_BLINK_1000RX BIT(1) +#define AIR_PHY_LED_BLINK_100TX BIT(2) +#define AIR_PHY_LED_BLINK_100RX BIT(3) +#define AIR_PHY_LED_BLINK_10TX BIT(4) +#define AIR_PHY_LED_BLINK_10RX BIT(5) +#define AIR_PHY_LED_BLINK_COLLISION BIT(6) +#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9) +#define AIR_PHY_LED_BLINK_2500TX BIT(10) +#define AIR_PHY_LED_BLINK_2500RX BIT(11) + +/* Registers on BUCKPBUS */ +#define EN8811H_2P5G_LPA 0x3b30 +#define EN8811H_2P5G_LPA_2P5G BIT(0) + +#define EN8811H_FW_VERSION 0x3b3c + +#define EN8811H_POLARITY 0xca0f8 +#define EN8811H_POLARITY_TX_NORMAL BIT(0) +#define EN8811H_POLARITY_RX_REVERSE BIT(1) + +#define EN8811H_GPIO_OUTPUT 0xcf8b8 +#define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5)) + +#define EN8811H_FW_CTRL_1 0x0f0018 +#define EN8811H_FW_CTRL_1_START 0x0 +#define EN8811H_FW_CTRL_1_FINISH 0x1 +#define EN8811H_FW_CTRL_2 0x800000 +#define EN8811H_FW_CTRL_2_LOADING BIT(11) + +/* Led definitions */ +#define EN8811H_LED_COUNT 3 + +/* Default LED setup: + * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx + * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps + * GPIO3 <-> LED2 On: Link detected at 2500 or 100 Mbps + */ +#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK) | \ + BIT(TRIGGER_NETDEV_RX) | \ + BIT(TRIGGER_NETDEV_TX)) +#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \ + BIT(TRIGGER_NETDEV_LINK_1000)) +#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \ + BIT(TRIGGER_NETDEV_LINK_100)) + +struct led { + unsigned long rules; + unsigned long state; +}; + +struct en8811h_priv { + u32 firmware_version; + bool mcu_needs_restart; + struct led led[EN8811H_LED_COUNT]; +}; + +enum { + AIR_PHY_LED_STATE_FORCE_ON, + AIR_PHY_LED_STATE_FORCE_BLINK, +}; + +enum { + AIR_PHY_LED_DUR_BLINK_32MS, + AIR_PHY_LED_DUR_BLINK_64MS, + AIR_PHY_LED_DUR_BLINK_128MS, + AIR_PHY_LED_DUR_BLINK_256MS, + AIR_PHY_LED_DUR_BLINK_512MS, + AIR_PHY_LED_DUR_BLINK_1024MS, +}; + +enum { + AIR_LED_DISABLE, + AIR_LED_ENABLE, +}; + +enum { + AIR_ACTIVE_LOW, + AIR_ACTIVE_HIGH, +}; + +enum { + AIR_LED_MODE_DISABLE, + AIR_LED_MODE_USER_DEFINE, +}; + +#define AIR_PHY_LED_DUR_UNIT 1024 +#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) + +static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) | + BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_1000) | + BIT(TRIGGER_NETDEV_LINK_2500) | + BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX); + +static int air_phy_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); +} + +static int air_phy_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); +} + +static int __air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ + int ret; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data)); + if (ret < 0) + return ret; + + return 0; +} + +static int air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_buckpbus_reg_write(phydev, pbus_address, + pbus_data); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} + +static int __air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ + int pbus_data_low, pbus_data_high; + int ret; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return ret; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return ret; + + *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +} + +static int air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} + +static int __air_buckpbus_reg_modify(struct phy_device *phydev, + u32 pbus_address, u32 mask, u32 set) +{ + int pbus_data_low, pbus_data_high; + u32 pbus_data_old, pbus_data_new; + int ret; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return ret; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return ret; + + pbus_data_old = pbus_data_low | (pbus_data_high << 16); + pbus_data_new = (pbus_data_old & ~mask) | set; + if (pbus_data_new == pbus_data_old) + return 0; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data_new)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data_new)); + if (ret < 0) + return ret; + + return 0; +} + +static int air_buckpbus_reg_modify(struct phy_device *phydev, + u32 pbus_address, u32 mask, u32 set) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, + set); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} + +static int __air_write_buf(struct phy_device *phydev, u32 address, + const struct firmware *fw) +{ + unsigned int offset; + int ret; + u16 val; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(address)); + if (ret < 0) + return ret; + + for (offset = 0; offset < fw->size; offset += 4) { + val = get_unaligned_le16(&fw->data[offset + 2]); + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val); + if (ret < 0) + return ret; + + val = get_unaligned_le16(&fw->data[offset]); + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int air_write_buf(struct phy_device *phydev, u32 address, + const struct firmware *fw) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_write_buf(phydev, address, fw); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} + +static int en8811h_wait_mcu_ready(struct phy_device *phydev) +{ + int ret, reg_value; + + /* Because of mdio-lock, may have to wait for multiple loads */ + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + EN8811H_PHY_FW_STATUS, reg_value, + reg_value == EN8811H_PHY_READY, + 20000, 7500000, true); + if (ret) { + phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value); + return -ENODEV; + } + + return 0; +} + +static int en8811h_load_firmware(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + const struct firmware *fw1, *fw2; + int ret; + + ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); + if (ret < 0) + return ret; + + ret = request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev); + if (ret < 0) + goto en8811h_load_firmware_rel1; + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, + EN8811H_FW_CTRL_2_LOADING); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_write_buf(phydev, AIR_FW_ADDR_DM, fw1); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, 0); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = en8811h_wait_mcu_ready(phydev); + + air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, + &priv->firmware_version); + phydev_info(phydev, "MD32 firmware version: %08x\n", + priv->firmware_version); + +en8811h_load_firmware_out: + release_firmware(fw2); + +en8811h_load_firmware_rel1: + release_firmware(fw1); + + if (ret < 0) + phydev_err(phydev, "Load firmware failed: %d\n", ret); + + return ret; +} + +static int en8811h_restart_mcu(struct phy_device *phydev) +{ + int ret; + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); + if (ret < 0) + return ret; + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); + if (ret < 0) + return ret; + + return en8811h_wait_mcu_ready(phydev); +} + +static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on) +{ + struct en8811h_priv *priv = phydev->priv; + bool changed; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + if (on) + changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_ON, + &priv->led[index].state); + else + changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_ON, + &priv->led[index].state); + + changed |= (priv->led[index].rules != 0); + + if (changed) + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + AIR_PHY_LED_ON(index), + AIR_PHY_LED_ON_MASK, + on ? AIR_PHY_LED_ON_FORCE_ON : 0); + + return 0; +} + +static int air_hw_led_blink_set(struct phy_device *phydev, u8 index, + bool blinking) +{ + struct en8811h_priv *priv = phydev->priv; + bool changed; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + if (blinking) + changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_BLINK, + &priv->led[index].state); + else + changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, + &priv->led[index].state); + + changed |= (priv->led[index].rules != 0); + + if (changed) + return phy_write_mmd(phydev, MDIO_MMD_VEND2, + AIR_PHY_LED_BLINK(index), + blinking ? + AIR_PHY_LED_BLINK_FORCE_BLINK : 0); + else + return 0; +} + +static int air_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct en8811h_priv *priv = phydev->priv; + bool blinking = false; + int err; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { + blinking = true; + *delay_on = 50; + *delay_off = 50; + } + + err = air_hw_led_blink_set(phydev, index, blinking); + if (err) + return err; + + /* led-blink set, so switch led-on off */ + err = air_hw_led_on_set(phydev, index, false); + if (err) + return err; + + /* hw-control is off*/ + if (!!test_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state)) + priv->led[index].rules = 0; + + return 0; +} + +static int air_led_brightness_set(struct phy_device *phydev, u8 index, + enum led_brightness value) +{ + struct en8811h_priv *priv = phydev->priv; + int err; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + /* led-on set, so switch led-blink off */ + err = air_hw_led_blink_set(phydev, index, false); + if (err) + return err; + + err = air_hw_led_on_set(phydev, index, (value != LED_OFF)); + if (err) + return err; + + /* hw-control is off */ + if (!!test_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state)) + priv->led[index].rules = 0; + + return 0; +} + +static int air_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + struct en8811h_priv *priv = phydev->priv; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + *rules = priv->led[index].rules; + + return 0; +}; + +static int air_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + struct en8811h_priv *priv = phydev->priv; + u16 on = 0, blink = 0; + int ret; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + priv->led[index].rules = rules; + + if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) + on |= AIR_PHY_LED_ON_FDX; + + if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) + on |= AIR_PHY_LED_ON_LINK10; + + if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) + on |= AIR_PHY_LED_ON_LINK100; + + if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) + on |= AIR_PHY_LED_ON_LINK1000; + + if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) + on |= AIR_PHY_LED_ON_LINK2500; + + if (rules & BIT(TRIGGER_NETDEV_RX)) { + blink |= AIR_PHY_LED_BLINK_10RX | + AIR_PHY_LED_BLINK_100RX | + AIR_PHY_LED_BLINK_1000RX | + AIR_PHY_LED_BLINK_2500RX; + } + + if (rules & BIT(TRIGGER_NETDEV_TX)) { + blink |= AIR_PHY_LED_BLINK_10TX | + AIR_PHY_LED_BLINK_100TX | + AIR_PHY_LED_BLINK_1000TX | + AIR_PHY_LED_BLINK_2500TX; + } + + if (blink || on) { + /* switch hw-control on, so led-on and led-blink are off */ + clear_bit(AIR_PHY_LED_STATE_FORCE_ON, + &priv->led[index].state); + clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, + &priv->led[index].state); + } else { + priv->led[index].rules = 0; + } + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), + AIR_PHY_LED_ON_MASK, on); + + if (ret < 0) + return ret; + + return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index), + blink); +}; + +static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) +{ + int val = 0; + int err; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + if (state == AIR_LED_ENABLE) + val |= AIR_PHY_LED_ON_ENABLE; + else + val &= ~AIR_PHY_LED_ON_ENABLE; + + if (pol == AIR_ACTIVE_HIGH) + val |= AIR_PHY_LED_ON_POLARITY; + else + val &= ~AIR_PHY_LED_ON_POLARITY; + + err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), + AIR_PHY_LED_ON_ENABLE | + AIR_PHY_LED_ON_POLARITY, val); + + if (err < 0) + return err; + + return 0; +} + +static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) +{ + struct en8811h_priv *priv = phydev->priv; + int ret, i; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, + dur); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, + dur >> 1); + if (ret < 0) + return ret; + + switch (mode) { + case AIR_LED_MODE_DISABLE: + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, + AIR_PHY_LED_BCR_EXT_CTRL | + AIR_PHY_LED_BCR_MODE_MASK, 0); + if (ret < 0) + return ret; + break; + case AIR_LED_MODE_USER_DEFINE: + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, + AIR_PHY_LED_BCR_EXT_CTRL | + AIR_PHY_LED_BCR_CLK_EN, + AIR_PHY_LED_BCR_EXT_CTRL | + AIR_PHY_LED_BCR_CLK_EN); + if (ret < 0) + return ret; + break; + default: + phydev_err(phydev, "LED mode %d is not supported\n", mode); + return -EINVAL; + } + + for (i = 0; i < num; ++i) { + ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); + if (ret < 0) { + phydev_err(phydev, "LED%d init failed: %d\n", i, ret); + return ret; + } + air_led_hw_control_set(phydev, i, priv->led[i].rules); + } + + return 0; +} + +static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + /* All combinations of the supported triggers are allowed */ + if (rules & ~en8811h_led_trig) + return -EOPNOTSUPP; + + return 0; +}; + +static int en8811h_probe(struct phy_device *phydev) +{ + struct en8811h_priv *priv; + int ret; + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + phydev->priv = priv; + + ret = en8811h_load_firmware(phydev); + if (ret < 0) + return ret; + + /* mcu has just restarted after firmware load */ + priv->mcu_needs_restart = false; + + priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; + priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; + priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; + + /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ + phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + + ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, + AIR_LED_MODE_DISABLE); + if (ret < 0) { + phydev_err(phydev, "Failed to disable leds: %d\n", ret); + return ret; + } + + /* Configure led gpio pins as output */ + ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, + EN8811H_GPIO_OUTPUT_345, + EN8811H_GPIO_OUTPUT_345); + if (ret < 0) + return ret; + + return 0; +} + +static int en8811h_config_init(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + u32 pbus_value; + int ret; + + /* If restart happened in .probe(), no need to restart now */ + if (priv->mcu_needs_restart) { + ret = en8811h_restart_mcu(phydev); + if (ret < 0) + return ret; + } else { + /* Next calls to .config_init() mcu needs to restart */ + priv->mcu_needs_restart = true; + } + + /* Select mode 1, the only mode supported. + * Configures the SerDes for 2500Base-X with rate adaptation + */ + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1, + AIR_PHY_MCU_CMD_1_MODE1); + if (ret < 0) + return ret; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2, + AIR_PHY_MCU_CMD_2_MODE1); + if (ret < 0) + return ret; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, + AIR_PHY_MCU_CMD_3_MODE1); + if (ret < 0) + return ret; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, + AIR_PHY_MCU_CMD_4_MODE1); + if (ret < 0) + return ret; + + /* Serdes polarity */ + pbus_value = 0; + if (device_property_read_bool(dev, "airoha,pnswap-rx")) + pbus_value |= EN8811H_POLARITY_RX_REVERSE; + else + pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; + if (device_property_read_bool(dev, "airoha,pnswap-tx")) + pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; + else + pbus_value |= EN8811H_POLARITY_TX_NORMAL; + ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, + EN8811H_POLARITY_RX_REVERSE | + EN8811H_POLARITY_TX_NORMAL, pbus_value); + if (ret < 0) + return ret; + + ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, + AIR_LED_MODE_USER_DEFINE); + if (ret < 0) { + phydev_err(phydev, "Failed to initialize leds: %d\n", ret); + return ret; + } + + return 0; +} + +static int en8811h_get_features(struct phy_device *phydev) +{ + linkmode_set_bit_array(phy_basic_ports_array, + ARRAY_SIZE(phy_basic_ports_array), + phydev->supported); + + return genphy_c45_pma_read_abilities(phydev); +} + +static int en8811h_get_rate_matching(struct phy_device *phydev, + phy_interface_t iface) +{ + return RATE_MATCH_PAUSE; +} + +static int en8811h_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + int ret; + u32 adv; + + if (phydev->autoneg == AUTONEG_DISABLE) { + phydev_warn(phydev, "Disabling autoneg is not supported\n"); + return -EINVAL; + } + + adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); + + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV2_5G, adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return __genphy_config_aneg(phydev, changed); +} + +static int en8811h_read_status(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + u32 pbus_value; + int ret, val; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + phydev->rate_matching = RATE_MATCH_PAUSE; + + ret = genphy_read_master_slave(phydev); + if (ret < 0) + return ret; + + ret = genphy_read_lpa(phydev); + if (ret < 0) + return ret; + + /* Get link partner 2.5GBASE-T ability from vendor register */ + ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value); + if (ret < 0) + return ret; + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->lp_advertising, + pbus_value & EN8811H_2P5G_LPA_2P5G); + + if (phydev->autoneg_complete) + phy_resolve_aneg_pause(phydev); + + if (!phydev->link) + return 0; + + /* Get real speed from vendor register */ + val = phy_read(phydev, AIR_AUX_CTRL_STATUS); + if (val < 0) + return val; + switch (val & AIR_AUX_CTRL_STATUS_SPEED_MASK) { + case AIR_AUX_CTRL_STATUS_SPEED_2500: + phydev->speed = SPEED_2500; + break; + case AIR_AUX_CTRL_STATUS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + case AIR_AUX_CTRL_STATUS_SPEED_100: + phydev->speed = SPEED_100; + break; + } + + /* Firmware before version 24011202 has no vendor register 2P5G_LPA. + * Assume link partner advertised it if connected at 2500Mbps. + */ + if (priv->firmware_version < 0x24011202) { + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->lp_advertising, + phydev->speed == SPEED_2500); + } + + /* Only supports full duplex */ + phydev->duplex = DUPLEX_FULL; + + return 0; +} + +static int en8811h_clear_intr(struct phy_device *phydev) +{ + int ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, + AIR_PHY_MCU_CMD_3_DOCMD); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, + AIR_PHY_MCU_CMD_4_INTCLR); + if (ret < 0) + return ret; + + return 0; +} + +static irqreturn_t en8811h_handle_interrupt(struct phy_device *phydev) +{ + int ret; + + ret = en8811h_clear_intr(phydev); + if (ret < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + phy_trigger_machine(phydev); + + return IRQ_HANDLED; +} + +static struct phy_driver en8811h_driver[] = { +{ + PHY_ID_MATCH_MODEL(EN8811H_PHY_ID), + .name = "Airoha EN8811H", + .probe = en8811h_probe, + .get_features = en8811h_get_features, + .config_init = en8811h_config_init, + .get_rate_matching = en8811h_get_rate_matching, + .config_aneg = en8811h_config_aneg, + .read_status = en8811h_read_status, + .config_intr = en8811h_clear_intr, + .handle_interrupt = en8811h_handle_interrupt, + .led_hw_is_supported = en8811h_led_hw_is_supported, + .read_page = air_phy_read_page, + .write_page = air_phy_write_page, + .led_blink_set = air_led_blink_set, + .led_brightness_set = air_led_brightness_set, + .led_hw_control_set = air_led_hw_control_set, + .led_hw_control_get = air_led_hw_control_get, +} }; + +module_phy_driver(en8811h_driver); + +static struct mdio_device_id __maybe_unused en8811h_tbl[] = { + { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, en8811h_tbl); +MODULE_FIRMWARE(EN8811H_MD32_DM); +MODULE_FIRMWARE(EN8811H_MD32_DSP); + +MODULE_DESCRIPTION("Airoha EN8811H PHY drivers"); +MODULE_AUTHOR("Airoha"); +MODULE_AUTHOR("Eric Woudstra "); +MODULE_LICENSE("GPL"); From b7b1c59069123368a939327d49009e8771e88d9c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 26 Mar 2024 19:58:36 +0200 Subject: [PATCH 03/18] nfc: st95hf: Switch to using gpiod API This updates the driver to gpiod API, and removes yet another use of of_get_named_gpio(). Signed-off-by: Andy Shevchenko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240326175836.1418718-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jakub Kicinski --- drivers/nfc/st95hf/core.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c index ed704bb77226..067e0ec31d2d 100644 --- a/drivers/nfc/st95hf/core.c +++ b/drivers/nfc/st95hf/core.c @@ -7,14 +7,13 @@ */ #include -#include +#include #include #include #include #include #include #include -#include #include #include #include @@ -196,7 +195,7 @@ struct st95_digital_cmd_complete_arg { * for spi communication between st95hf and host. * @ddev: nfc digital device object. * @nfcdev: nfc device object. - * @enable_gpio: gpio used to enable st95hf transceiver. + * @enable_gpiod: gpio used to enable st95hf transceiver. * @complete_cb_arg: structure to store various context information * that is passed from nfc requesting thread to the threaded ISR. * @st95hf_supply: regulator "consumer" for NFC device. @@ -219,7 +218,7 @@ struct st95hf_context { struct st95hf_spi_context spicontext; struct nfc_digital_dev *ddev; struct nfc_dev *nfcdev; - unsigned int enable_gpio; + struct gpio_desc *enable_gpiod; struct st95_digital_cmd_complete_arg complete_cb_arg; struct regulator *st95hf_supply; unsigned char sendrcv_trflag; @@ -451,19 +450,19 @@ static int st95hf_select_protocol(struct st95hf_context *stcontext, int type) static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con) { /* First make irq_in pin high */ - gpio_set_value(st95con->enable_gpio, HIGH); + gpiod_set_value(st95con->enable_gpiod, HIGH); /* wait for 1 milisecond */ usleep_range(1000, 2000); /* Make irq_in pin low */ - gpio_set_value(st95con->enable_gpio, LOW); + gpiod_set_value(st95con->enable_gpiod, LOW); /* wait for minimum interrupt pulse to make st95 active */ usleep_range(1000, 2000); /* At end make it high */ - gpio_set_value(st95con->enable_gpio, HIGH); + gpiod_set_value(st95con->enable_gpiod, HIGH); } /* @@ -1063,6 +1062,7 @@ MODULE_DEVICE_TABLE(of, st95hf_spi_of_match); static int st95hf_probe(struct spi_device *nfc_spi_dev) { + struct device *dev = &nfc_spi_dev->dev; int ret; struct st95hf_context *st95context; @@ -1108,19 +1108,14 @@ static int st95hf_probe(struct spi_device *nfc_spi_dev) */ dev_set_drvdata(&nfc_spi_dev->dev, spicontext); - st95context->enable_gpio = - of_get_named_gpio(nfc_spi_dev->dev.of_node, - "enable-gpio", - 0); - if (!gpio_is_valid(st95context->enable_gpio)) { + st95context->enable_gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(st95context->enable_gpiod)) { + ret = PTR_ERR(st95context->enable_gpiod); dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n"); - ret = st95context->enable_gpio; goto err_disable_regulator; } - ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio, - GPIOF_DIR_OUT | GPIOF_INIT_HIGH, - "enable_gpio"); + ret = gpiod_set_consumer_name(st95context->enable_gpiod, "enable_gpio"); if (ret) goto err_disable_regulator; From a0ad11fc2632903ec0fe9cd16b07d7dd2ade8341 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Tue, 26 Mar 2024 19:05:46 +0100 Subject: [PATCH 04/18] net: port TP_STORE_ADDR_PORTS_SKB macro to be tcp/udp independent This patch moves TP_STORE_ADDR_PORTS_SKB() to a common header and removes the TCP specific implementation details. Previously the macro assumed the skb passed as an argument is a TCP packet, the implementation now uses an argument to the L4 header and uses that to extract the source/destination ports, which happen to be named the same in "struct tcphdr" and "struct udphdr" Reviewed-by: Jason Xing Signed-off-by: Balazs Scheidler Link: https://lore.kernel.org/r/9e306f78260dfbbdc7353ba5f864cc027a409540.1711475011.git.balazs.scheidler@axoflow.com Signed-off-by: Jakub Kicinski --- include/trace/events/net_probe_common.h | 40 ++++++++++++++++++++++ include/trace/events/tcp.h | 45 ++----------------------- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/include/trace/events/net_probe_common.h b/include/trace/events/net_probe_common.h index b1f9a4d3ee13..5e33f91bdea3 100644 --- a/include/trace/events/net_probe_common.h +++ b/include/trace/events/net_probe_common.h @@ -70,4 +70,44 @@ TP_STORE_V4MAPPED(__entry, saddr, daddr) #endif +#define TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb, protoh) \ + do { \ + struct sockaddr_in *v4 = (void *)__entry->saddr; \ + \ + v4->sin_family = AF_INET; \ + v4->sin_port = protoh->source; \ + v4->sin_addr.s_addr = ip_hdr(skb)->saddr; \ + v4 = (void *)__entry->daddr; \ + v4->sin_family = AF_INET; \ + v4->sin_port = protoh->dest; \ + v4->sin_addr.s_addr = ip_hdr(skb)->daddr; \ + } while (0) + +#if IS_ENABLED(CONFIG_IPV6) + +#define TP_STORE_ADDR_PORTS_SKB(__entry, skb, protoh) \ + do { \ + const struct iphdr *iph = ip_hdr(skb); \ + \ + if (iph->version == 6) { \ + struct sockaddr_in6 *v6 = (void *)__entry->saddr; \ + \ + v6->sin6_family = AF_INET6; \ + v6->sin6_port = protoh->source; \ + v6->sin6_addr = ipv6_hdr(skb)->saddr; \ + v6 = (void *)__entry->daddr; \ + v6->sin6_family = AF_INET6; \ + v6->sin6_port = protoh->dest; \ + v6->sin6_addr = ipv6_hdr(skb)->daddr; \ + } else \ + TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb, protoh); \ + } while (0) + +#else + +#define TP_STORE_ADDR_PORTS_SKB(__entry, skb, protoh) \ + TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb, protoh) + +#endif + #endif diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 3c08a0846c47..1db95175c1e5 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -273,48 +273,6 @@ TRACE_EVENT(tcp_probe, __entry->skbaddr, __entry->skaddr) ); -#define TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb) \ - do { \ - const struct tcphdr *th = (const struct tcphdr *)skb->data; \ - struct sockaddr_in *v4 = (void *)__entry->saddr; \ - \ - v4->sin_family = AF_INET; \ - v4->sin_port = th->source; \ - v4->sin_addr.s_addr = ip_hdr(skb)->saddr; \ - v4 = (void *)__entry->daddr; \ - v4->sin_family = AF_INET; \ - v4->sin_port = th->dest; \ - v4->sin_addr.s_addr = ip_hdr(skb)->daddr; \ - } while (0) - -#if IS_ENABLED(CONFIG_IPV6) - -#define TP_STORE_ADDR_PORTS_SKB(__entry, skb) \ - do { \ - const struct iphdr *iph = ip_hdr(skb); \ - \ - if (iph->version == 6) { \ - const struct tcphdr *th = (const struct tcphdr *)skb->data; \ - struct sockaddr_in6 *v6 = (void *)__entry->saddr; \ - \ - v6->sin6_family = AF_INET6; \ - v6->sin6_port = th->source; \ - v6->sin6_addr = ipv6_hdr(skb)->saddr; \ - v6 = (void *)__entry->daddr; \ - v6->sin6_family = AF_INET6; \ - v6->sin6_port = th->dest; \ - v6->sin6_addr = ipv6_hdr(skb)->daddr; \ - } else \ - TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb); \ - } while (0) - -#else - -#define TP_STORE_ADDR_PORTS_SKB(__entry, skb) \ - TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb) - -#endif - /* * tcp event with only skb */ @@ -331,12 +289,13 @@ DECLARE_EVENT_CLASS(tcp_event_skb, ), TP_fast_assign( + const struct tcphdr *th = (const struct tcphdr *)skb->data; __entry->skbaddr = skb; memset(__entry->saddr, 0, sizeof(struct sockaddr_in6)); memset(__entry->daddr, 0, sizeof(struct sockaddr_in6)); - TP_STORE_ADDR_PORTS_SKB(__entry, skb); + TP_STORE_ADDR_PORTS_SKB(__entry, skb, th); ), TP_printk("skbaddr=%p src=%pISpc dest=%pISpc", From e9669a00bba79442dd4862c57761333d6a020c24 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Tue, 26 Mar 2024 19:05:47 +0100 Subject: [PATCH 05/18] net: udp: add IP/port data to the tracepoint udp/udp_fail_queue_rcv_skb The udp_fail_queue_rcv_skb() tracepoint lacks any details on the source and destination IP/port whereas this information can be critical in case of UDP/syslog. Signed-off-by: Balazs Scheidler Reviewed-by: Jason Xing Link: https://lore.kernel.org/r/0c8b3e33dbf679e190be6f4c6736603a76988a20.1711475011.git.balazs.scheidler@axoflow.com Signed-off-by: Jakub Kicinski --- include/trace/events/udp.h | 29 ++++++++++++++++++++++++----- net/ipv4/udp.c | 2 +- net/ipv6/udp.c | 3 ++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/trace/events/udp.h b/include/trace/events/udp.h index 336fe272889f..62bebe2a6ece 100644 --- a/include/trace/events/udp.h +++ b/include/trace/events/udp.h @@ -7,24 +7,43 @@ #include #include +#include TRACE_EVENT(udp_fail_queue_rcv_skb, - TP_PROTO(int rc, struct sock *sk), + TP_PROTO(int rc, struct sock *sk, struct sk_buff *skb), - TP_ARGS(rc, sk), + TP_ARGS(rc, sk, skb), TP_STRUCT__entry( __field(int, rc) - __field(__u16, lport) + + __field(__u16, sport) + __field(__u16, dport) + __field(__u16, family) + __array(__u8, saddr, sizeof(struct sockaddr_in6)) + __array(__u8, daddr, sizeof(struct sockaddr_in6)) ), TP_fast_assign( + const struct udphdr *uh = (const struct udphdr *)udp_hdr(skb); + __entry->rc = rc; - __entry->lport = inet_sk(sk)->inet_num; + + /* for filtering use */ + __entry->sport = ntohs(uh->source); + __entry->dport = ntohs(uh->dest); + __entry->family = sk->sk_family; + + memset(__entry->saddr, 0, sizeof(struct sockaddr_in6)); + memset(__entry->daddr, 0, sizeof(struct sockaddr_in6)); + + TP_STORE_ADDR_PORTS_SKB(__entry, skb, uh); ), - TP_printk("rc=%d port=%hu", __entry->rc, __entry->lport) + TP_printk("rc=%d family=%s src=%pISpc dest=%pISpc", __entry->rc, + show_family_name(__entry->family), + __entry->saddr, __entry->daddr) ); #endif /* _TRACE_UDP_H */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 661d0e0d273f..531882f321f2 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2049,8 +2049,8 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) drop_reason = SKB_DROP_REASON_PROTO_MEM; } UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); + trace_udp_fail_queue_rcv_skb(rc, sk, skb); kfree_skb_reason(skb, drop_reason); - trace_udp_fail_queue_rcv_skb(rc, sk); return -1; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 7c1e6469d091..2e4dc5e6137b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -658,8 +659,8 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) drop_reason = SKB_DROP_REASON_PROTO_MEM; } UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); + trace_udp_fail_queue_rcv_skb(rc, sk, skb); kfree_skb_reason(skb, drop_reason); - trace_udp_fail_queue_rcv_skb(rc, sk); return -1; } From b6694abcf5dffd2dac83a7fea067d27a49df0079 Mon Sep 17 00:00:00 2001 From: Radha Mohan Chintakuntla Date: Tue, 26 Mar 2024 11:45:14 -0700 Subject: [PATCH 06/18] octeontx2-af: Increase maximum BPID channels Any NIX interface type can have maximum 256 channels. So increased the backpressure ID count to 256 so that it can cover cn9k and cn10k SoCs that have different NIX interface types with varied maximum channels. Signed-off-by: Radha Mohan Chintakuntla Link: https://lore.kernel.org/r/20240326184514.1628284-1-radhac@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/mbox.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index eb2a20b5a0d0..3d801a1a4f70 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -1213,10 +1213,8 @@ struct nix_bp_cfg_req { /* bpid_per_chan = 1 assigns separate bp id for each channel */ }; -/* PF can be mapped to either CGX or LBK interface, - * so maximum 64 channels are possible. - */ -#define NIX_MAX_BPID_CHAN 64 +/* Maximum channels any single NIX interface can have */ +#define NIX_MAX_BPID_CHAN 256 struct nix_bp_cfg_rsp { struct mbox_msghdr hdr; u16 chan_bpid[NIX_MAX_BPID_CHAN]; /* Channel and bpid mapping */ From 9046d581ed586f3c715357638ca12c0e84402002 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 Mar 2024 23:38:01 +0100 Subject: [PATCH 07/18] enetc: avoid truncating error message As clang points out, the error message in enetc_setup_xdp_prog() still does not fit in the buffer and will be truncated: drivers/net/ethernet/freescale/enetc/enetc.c:2771:3: error: 'snprintf' will always be truncated; specified size is 80, but format string expands to at least 87 [-Werror,-Wformat-truncation] Replace it with an even shorter message that should fit. Fixes: f968c56417f0 ("net: enetc: shorten enetc_setup_xdp_prog() error message to fit NETLINK_MAX_FMTMSG_LEN") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240326223825.4084412-3-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 9f07f4947b63..5c45f42232d3 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -2769,7 +2769,7 @@ static int enetc_setup_xdp_prog(struct net_device *ndev, struct bpf_prog *prog, if (priv->min_num_stack_tx_queues + num_xdp_tx_queues > priv->num_tx_rings) { NL_SET_ERR_MSG_FMT_MOD(extack, - "Reserving %d XDP TXQs does not leave a minimum of %d for stack (total %d)", + "Reserving %d XDP TXQs leaves under %d for stack (total %d)", num_xdp_tx_queues, priv->min_num_stack_tx_queues, priv->num_tx_rings); From 954fd908f177604d4cce77e2a88cc50b29bad5ff Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 Mar 2024 23:38:02 +0100 Subject: [PATCH 08/18] qed: avoid truncating work queue length clang complains that the temporary string for the name passed into alloc_workqueue() is too short for its contents: drivers/net/ethernet/qlogic/qed/qed_main.c:1218:3: error: 'snprintf' will always be truncated; specified size is 16, but format string expands to at least 18 [-Werror,-Wformat-truncation] There is no need for a temporary buffer, and the actual name of a workqueue is 32 bytes (WQ_NAME_LEN), so just use the interface as intended to avoid the truncation. Fixes: 59ccf86fe69a ("qed: Add driver infrastucture for handling mfw requests.") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240326223825.4084412-4-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qed/qed_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 408da4312e01..17f284e9f06d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1205,7 +1205,6 @@ out: static int qed_slowpath_wq_start(struct qed_dev *cdev) { struct qed_hwfn *hwfn; - char name[NAME_SIZE]; int i; if (IS_VF(cdev)) @@ -1214,11 +1213,11 @@ static int qed_slowpath_wq_start(struct qed_dev *cdev) for_each_hwfn(cdev, i) { hwfn = &cdev->hwfns[i]; - snprintf(name, NAME_SIZE, "slowpath-%02x:%02x.%02x", - cdev->pdev->bus->number, - PCI_SLOT(cdev->pdev->devfn), hwfn->abs_pf_id); + hwfn->slowpath_wq = alloc_workqueue("slowpath-%02x:%02x.%02x", + 0, 0, cdev->pdev->bus->number, + PCI_SLOT(cdev->pdev->devfn), + hwfn->abs_pf_id); - hwfn->slowpath_wq = alloc_workqueue(name, 0, 0); if (!hwfn->slowpath_wq) { DP_NOTICE(hwfn, "Cannot create slowpath workqueue\n"); return -ENOMEM; From b324a960354b872431d25959ad384ab66a7116ec Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 Mar 2024 23:38:03 +0100 Subject: [PATCH 09/18] mlx5: avoid truncating error message clang warns that one error message is too long for its destination buffer: drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c:1876:4: error: 'snprintf' will always be truncated; specified size is 80, but format string expands to at least 94 [-Werror,-Wformat-truncation-non-kprintf] Reword it to be a bit shorter so it always fits. Fixes: 70f0302b3f20 ("net/mlx5: Bridge, implement mdb offload") Signed-off-by: Arnd Bergmann Reviewed-by: Subbaraya Sundeep Link: https://lore.kernel.org/r/20240326223825.4084412-5-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c index 1b9bc32efd6f..c5ea1d1d2b03 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c @@ -1874,7 +1874,7 @@ int mlx5_esw_bridge_port_mdb_add(struct net_device *dev, u16 vport_num, u16 esw_ "Failed to lookup bridge port vlan metadata to create MDB (MAC=%pM,vid=%u,vport=%u)\n", addr, vid, vport_num); NL_SET_ERR_MSG_FMT_MOD(extack, - "Failed to lookup bridge port vlan metadata to create MDB (MAC=%pM,vid=%u,vport=%u)\n", + "Failed to lookup vlan metadata for MDB (MAC=%pM,vid=%u,vport=%u)\n", addr, vid, vport_num); return -EINVAL; } From 730fffce4fd2eb7a0be2d0b6cd7e55e9194d76d5 Mon Sep 17 00:00:00 2001 From: Jian Wen Date: Wed, 27 Mar 2024 16:21:28 +0800 Subject: [PATCH 10/18] devlink: use kvzalloc() to allocate devlink instance resources During live migration of a virtual machine, the SR-IOV VF need to be re-registered. It may fail when the memory is badly fragmented. The related log is as follows. kernel: hv_netvsc 6045bdaa-c0d1-6045-bdaa-c0d16045bdaa eth0: VF slot 1 added ... kernel: kworker/0:0: page allocation failure: order:7, mode:0x40dc0(GFP_KERNEL|__GFP_COMP|__GFP_ZERO), nodemask=(null),cpuset=/,mems_allowed=0 kernel: CPU: 0 PID: 24006 Comm: kworker/0:0 Tainted: G E 5.4...x86_64 #1 kernel: Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS 090008 12/07/2018 kernel: Workqueue: events work_for_cpu_fn kernel: Call Trace: kernel: dump_stack+0x8b/0xc8 kernel: warn_alloc+0xff/0x170 kernel: __alloc_pages_slowpath+0x92c/0xb2b kernel: ? get_page_from_freelist+0x1d4/0x1140 kernel: __alloc_pages_nodemask+0x2f9/0x320 kernel: alloc_pages_current+0x6a/0xb0 kernel: kmalloc_order+0x1e/0x70 kernel: kmalloc_order_trace+0x26/0xb0 kernel: ? __switch_to_asm+0x34/0x70 kernel: __kmalloc+0x276/0x280 kernel: ? _raw_spin_unlock_irqrestore+0x1e/0x40 kernel: devlink_alloc+0x29/0x110 kernel: mlx5_devlink_alloc+0x1a/0x20 [mlx5_core] kernel: init_one+0x1d/0x650 [mlx5_core] kernel: local_pci_probe+0x46/0x90 kernel: work_for_cpu_fn+0x1a/0x30 kernel: process_one_work+0x16d/0x390 kernel: worker_thread+0x1d3/0x3f0 kernel: kthread+0x105/0x140 kernel: ? max_active_store+0x80/0x80 kernel: ? kthread_bind+0x20/0x20 kernel: ret_from_fork+0x3a/0x50 Signed-off-by: Jian Wen Link: https://lore.kernel.org/r/20240327082128.942818-1-wenjian1@xiaomi.com Signed-off-by: Jakub Kicinski --- net/devlink/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/devlink/core.c b/net/devlink/core.c index 7f0b093208d7..f49cd83f1955 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -314,7 +314,7 @@ static void devlink_release(struct work_struct *work) mutex_destroy(&devlink->lock); lockdep_unregister_key(&devlink->lock_key); put_device(devlink->dev); - kfree(devlink); + kvfree(devlink); } void devlink_put(struct devlink *devlink) @@ -420,7 +420,7 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops, if (!devlink_reload_actions_valid(ops)) return NULL; - devlink = kzalloc(sizeof(*devlink) + priv_size, GFP_KERNEL); + devlink = kvzalloc(struct_size(devlink, priv, priv_size), GFP_KERNEL); if (!devlink) return NULL; @@ -455,7 +455,7 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops, return devlink; err_xa_alloc: - kfree(devlink); + kvfree(devlink); return NULL; } EXPORT_SYMBOL_GPL(devlink_alloc_ns); From bd3ce405fecc6f2f9ce09c789386e326346899a0 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Thu, 28 Mar 2024 15:56:36 +0000 Subject: [PATCH 11/18] tools/net/ynl: Add extack policy attribute decoding The NLMSGERR_ATTR_POLICY extack attribute has been ignored by ynl up to now. Extend extack decoding to include _POLICY and the nested NL_POLICY_TYPE_ATTR_* attributes. For example: ./tools/net/ynl/cli.py \ --spec Documentation/netlink/specs/rt_link.yaml \ --create --do newlink --json '{ "ifname": "12345678901234567890", "linkinfo": {"kind": "bridge"} }' Netlink error: Numerical result out of range nl_len = 104 (88) nl_flags = 0x300 nl_type = 2 error: -34 extack: {'msg': 'Attribute failed policy validation', 'policy': {'max-length': 15, 'type': 'string'}, 'bad-attr': '.ifname'} Signed-off-by: Donald Hunter Link: https://lore.kernel.org/r/20240328155636.64688-1-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index e73b027c5624..82d3c98067aa 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause from collections import namedtuple +from enum import Enum import functools import os import random @@ -76,6 +77,25 @@ class Netlink: NLMSGERR_ATTR_MISS_TYPE = 5 NLMSGERR_ATTR_MISS_NEST = 6 + # Policy types + NL_POLICY_TYPE_ATTR_TYPE = 1 + NL_POLICY_TYPE_ATTR_MIN_VALUE_S = 2 + NL_POLICY_TYPE_ATTR_MAX_VALUE_S = 3 + NL_POLICY_TYPE_ATTR_MIN_VALUE_U = 4 + NL_POLICY_TYPE_ATTR_MAX_VALUE_U = 5 + NL_POLICY_TYPE_ATTR_MIN_LENGTH = 6 + NL_POLICY_TYPE_ATTR_MAX_LENGTH = 7 + NL_POLICY_TYPE_ATTR_POLICY_IDX = 8 + NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE = 9 + NL_POLICY_TYPE_ATTR_BITFIELD32_MASK = 10 + NL_POLICY_TYPE_ATTR_PAD = 11 + NL_POLICY_TYPE_ATTR_MASK = 12 + + AttrType = Enum('AttrType', ['flag', 'u8', 'u16', 'u32', 'u64', + 's8', 's16', 's32', 's64', + 'binary', 'string', 'nul-string', + 'nested', 'nested-array', + 'bitfield32', 'sint', 'uint']) class NlError(Exception): def __init__(self, nl_msg): @@ -198,6 +218,8 @@ class NlMsg: self.extack['miss-nest'] = extack.as_scalar('u32') elif extack.type == Netlink.NLMSGERR_ATTR_OFFS: self.extack['bad-attr-offs'] = extack.as_scalar('u32') + elif extack.type == Netlink.NLMSGERR_ATTR_POLICY: + self.extack['policy'] = self._decode_policy(extack.raw) else: if 'unknown' not in self.extack: self.extack['unknown'] = [] @@ -214,6 +236,30 @@ class NlMsg: desc += f" ({spec['doc']})" self.extack['miss-type'] = desc + def _decode_policy(self, raw): + policy = {} + for attr in NlAttrs(raw): + if attr.type == Netlink.NL_POLICY_TYPE_ATTR_TYPE: + type = attr.as_scalar('u32') + policy['type'] = Netlink.AttrType(type).name + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MIN_VALUE_S: + policy['min-value'] = attr.as_scalar('s64') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MAX_VALUE_S: + policy['max-value'] = attr.as_scalar('s64') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MIN_VALUE_U: + policy['min-value'] = attr.as_scalar('u64') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MAX_VALUE_U: + policy['max-value'] = attr.as_scalar('u64') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MIN_LENGTH: + policy['min-length'] = attr.as_scalar('u32') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MAX_LENGTH: + policy['max-length'] = attr.as_scalar('u32') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: + policy['bitfield32-mask'] = attr.as_scalar('u32') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MASK: + policy['mask'] = attr.as_scalar('u64') + return policy + def cmd(self): return self.nl_type From 648bb2bf444fd557192d6647945d6d8c8f7062e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Mar 2024 18:48:07 +0100 Subject: [PATCH 12/18] net: microchip: encx24j600: drop driver owner assignment Core in spi_register_driver() already sets the .owner, so driver does not need to. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240327174810.519676-1-krzysztof.kozlowski@linaro.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/encx24j600.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c index d7c8aa77ec75..cdc2872ace1b 100644 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -1112,7 +1112,6 @@ MODULE_DEVICE_TABLE(spi, encx24j600_spi_id_table); static struct spi_driver encx24j600_spi_net_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, .bus = &spi_bus_type, }, .probe = encx24j600_spi_probe, From 343941206138ed5b29e4e17834dc80f9f1bc9aaa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Mar 2024 18:48:08 +0100 Subject: [PATCH 13/18] net: wwan: mhi: drop driver owner assignment Core in mhi_driver_register() already sets the .owner, so driver does not need to. Signed-off-by: Krzysztof Kozlowski Acked-by: Sergey Ryazanov Link: https://lore.kernel.org/r/20240327174810.519676-2-krzysztof.kozlowski@linaro.org Signed-off-by: Jakub Kicinski --- drivers/net/wwan/mhi_wwan_mbim.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wwan/mhi_wwan_mbim.c b/drivers/net/wwan/mhi_wwan_mbim.c index 3f72ae943b29..f2aef84fc08d 100644 --- a/drivers/net/wwan/mhi_wwan_mbim.c +++ b/drivers/net/wwan/mhi_wwan_mbim.c @@ -648,7 +648,6 @@ static struct mhi_driver mhi_mbim_driver = { .id_table = mhi_mbim_id_table, .driver = { .name = "mhi_wwan_mbim", - .owner = THIS_MODULE, }, }; From e3c95d56190c97a214115881947d1a8f97927479 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Mar 2024 18:48:09 +0100 Subject: [PATCH 14/18] nfc: mrvl: spi: drop driver owner assignment Core in spi_register_driver() already sets the .owner, so driver does not need to. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240327174810.519676-3-krzysztof.kozlowski@linaro.org Signed-off-by: Jakub Kicinski --- drivers/nfc/nfcmrvl/spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c index ad3359a4942c..9c8cde1250fb 100644 --- a/drivers/nfc/nfcmrvl/spi.c +++ b/drivers/nfc/nfcmrvl/spi.c @@ -199,7 +199,6 @@ static struct spi_driver nfcmrvl_spi_driver = { .id_table = nfcmrvl_spi_id_table, .driver = { .name = "nfcmrvl_spi", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_nfcmrvl_spi_match), }, }; From e93af72286054e76d71aa5b7d721f33667ce0385 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Mar 2024 18:48:10 +0100 Subject: [PATCH 15/18] nfc: st95hf: drop driver owner assignment Core in spi_register_driver() already sets the .owner, so driver does not need to. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240327174810.519676-4-krzysztof.kozlowski@linaro.org Signed-off-by: Jakub Kicinski --- drivers/nfc/st95hf/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c index 067e0ec31d2d..ffe5b4eab457 100644 --- a/drivers/nfc/st95hf/core.c +++ b/drivers/nfc/st95hf/core.c @@ -1237,7 +1237,6 @@ static void st95hf_remove(struct spi_device *nfc_spi_dev) static struct spi_driver st95hf_driver = { .driver = { .name = "st95hf", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(st95hf_spi_of_match), }, .id_table = st95hf_id, From 1ab6fe64d220eaeb42a1130b3c31ea24c84cf5ad Mon Sep 17 00:00:00 2001 From: Su Hui Date: Thu, 28 Mar 2024 10:07:24 +0800 Subject: [PATCH 16/18] octeontx2-pf: remove unused variables req_hdr and rsp_hdr Clang static checker(scan-buid): drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:503:2: warning: Value stored to 'rsp_hdr' is never read [deadcode.DeadStores] Remove these unused variables to save some space. Signed-off-by: Su Hui Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20240328020723.4071539-1-suhui@nfschina.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index b40bd0e46751..5d8359b54098 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -450,7 +450,6 @@ static void otx2_pfvf_mbox_handler(struct work_struct *work) struct mbox_msghdr *msg = NULL; int offset, vf_idx, id, err; struct otx2_mbox_dev *mdev; - struct mbox_hdr *req_hdr; struct otx2_mbox *mbox; struct mbox *vf_mbox; struct otx2_nic *pf; @@ -461,9 +460,8 @@ static void otx2_pfvf_mbox_handler(struct work_struct *work) mbox = &pf->mbox_pfvf[0].mbox; mdev = &mbox->dev[vf_idx]; - req_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); - offset = ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN); + offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); for (id = 0; id < vf_mbox->num_msgs; id++) { msg = (struct mbox_msghdr *)(mdev->mbase + mbox->rx_start + @@ -494,7 +492,6 @@ static void otx2_pfvf_mbox_up_handler(struct work_struct *work) struct otx2_nic *pf = vf_mbox->pfvf; struct otx2_mbox_dev *mdev; int offset, id, vf_idx = 0; - struct mbox_hdr *rsp_hdr; struct mbox_msghdr *msg; struct otx2_mbox *mbox; @@ -502,8 +499,7 @@ static void otx2_pfvf_mbox_up_handler(struct work_struct *work) mbox = &pf->mbox_pfvf[0].mbox_up; mdev = &mbox->dev[vf_idx]; - rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); - offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + offset = mbox->rx_start + ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); for (id = 0; id < vf_mbox->up_num_msgs; id++) { msg = mdev->mbase + offset; From 06c2a5cd48fe50f24f8801dd10fcd2b6fd526566 Mon Sep 17 00:00:00 2001 From: Suraj Gupta Date: Thu, 28 Mar 2024 16:37:13 +0530 Subject: [PATCH 17/18] net: axienet: Fix kernel doc warnings Add description of mdio enable, mdio disable and mdio wait functions. Add description of skb pointer in axidma_bd data structure. Remove 'phy_node' description in axienet local data structure since it is not a valid struct member. Correct description of struct axienet_option. Fix below kernel-doc warnings in drivers/net/ethernet/xilinx/: 1) xilinx_axienet_mdio.c:1: warning: no structured comments found 2) xilinx_axienet.h:379: warning: Function parameter or struct member 'skb' not described in 'axidma_bd' 3) xilinx_axienet.h:538: warning: Excess struct member 'phy_node' description in 'axienet_local' 4) xilinx_axienet.h:1002: warning: expecting prototype for struct axiethernet_option. Prototype was for struct axienet_option instead Signed-off-by: Suraj Gupta Reviewed-by: Radhey Shyam Pandey Link: https://lore.kernel.org/r/20240328110713.12885-1-suraj.gupta2@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/xilinx/xilinx_axienet.h | 4 ++-- .../net/ethernet/xilinx/xilinx_axienet_mdio.c | 23 ++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 807ead678551..fa5500decc96 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -359,6 +359,7 @@ * @app2: MM2S/S2MM User Application Field 2. * @app3: MM2S/S2MM User Application Field 3. * @app4: MM2S/S2MM User Application Field 4. + * @skb: Pointer to SKB transferred using DMA */ struct axidma_bd { u32 next; /* Physical address of next buffer descriptor */ @@ -399,7 +400,6 @@ struct skbuf_dma_descriptor { * struct axienet_local - axienet private per device data * @ndev: Pointer for net_device to which it will be attached. * @dev: Pointer to device structure - * @phy_node: Pointer to device node structure * @phylink: Pointer to phylink instance * @phylink_config: phylink configuration settings * @pcs_phy: Reference to PCS/PMA PHY if used @@ -537,7 +537,7 @@ struct axienet_local { }; /** - * struct axiethernet_option - Used to set axi ethernet hardware options + * struct axienet_option - Used to set axi ethernet hardware options * @opt: Option to be set. * @reg: Register offset to be written for setting the option * @m_or: Mask to be ORed for setting the option in the register diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 2f07fde361aa..9ca2643c921e 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -20,7 +20,14 @@ #define DEFAULT_MDIO_FREQ 2500000 /* 2.5 MHz */ #define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */ -/* Wait till MDIO interface is ready to accept a new transaction.*/ +/** + * axienet_mdio_wait_until_ready - MDIO wait function + * @lp: Pointer to axienet local data structure. + * + * Return : 0 on success, Negative value on errors + * + * Wait till MDIO interface is ready to accept a new transaction. + */ static int axienet_mdio_wait_until_ready(struct axienet_local *lp) { u32 val; @@ -30,14 +37,24 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp) 1, 20000); } -/* Enable the MDIO MDC. Called prior to a read/write operation */ +/** + * axienet_mdio_mdc_enable - MDIO MDC enable function + * @lp: Pointer to axienet local data structure. + * + * Enable the MDIO MDC. Called prior to a read/write operation + */ static void axienet_mdio_mdc_enable(struct axienet_local *lp) { axienet_iow(lp, XAE_MDIO_MC_OFFSET, ((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK)); } -/* Disable the MDIO MDC. Called after a read/write operation*/ +/** + * axienet_mdio_mdc_disable - MDIO MDC disable function + * @lp: Pointer to axienet local data structure. + * + * Disable the MDIO MDC. Called after a read/write operation + */ static void axienet_mdio_mdc_disable(struct axienet_local *lp) { u32 mc_reg; From a5535e5336943b33689f558199366102387b7bbf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 28 Mar 2024 15:30:46 +0100 Subject: [PATCH 18/18] mlx5: stop warning for 64KB pages When building with 64KB pages, clang points out that xsk->chunk_size can never be PAGE_SIZE: drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c:19:22: error: result of comparison of constant 65536 with expression of type 'u16' (aka 'unsigned short') is always false [-Werror,-Wtautological-constant-out-of-range-compare] if (xsk->chunk_size > PAGE_SIZE || ~~~~~~~~~~~~~~~ ^ ~~~~~~~~~ In older versions of this code, using PAGE_SIZE was the only possibility, so this would have never worked on 64KB page kernels, but the patch apparently did not address this case completely. As Maxim Mikityanskiy suggested, 64KB chunks are really not all that useful, so just shut up the warning by adding a cast. Fixes: 282c0c798f8e ("net/mlx5e: Allow XSK frames smaller than a page") Link: https://lore.kernel.org/netdev/20211013150232.2942146-1-arnd@kernel.org/ Link: https://lore.kernel.org/lkml/a7b27541-0ebb-4f2d-bd06-270a4d404613@app.fastmail.com/ Signed-off-by: Arnd Bergmann Acked-by: Maxim Mikityanskiy Reviewed-by: Justin Stitt Reviewed-by: Tariq Toukan Link: https://lore.kernel.org/r/20240328143051.1069575-9-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c index 06592b9f0424..9240cfe25d10 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c @@ -28,8 +28,10 @@ bool mlx5e_validate_xsk_param(struct mlx5e_params *params, struct mlx5e_xsk_param *xsk, struct mlx5_core_dev *mdev) { - /* AF_XDP doesn't support frames larger than PAGE_SIZE. */ - if (xsk->chunk_size > PAGE_SIZE || xsk->chunk_size < MLX5E_MIN_XSK_CHUNK_SIZE) { + /* AF_XDP doesn't support frames larger than PAGE_SIZE, + * and xsk->chunk_size is limited to 65535 bytes. + */ + if ((size_t)xsk->chunk_size > PAGE_SIZE || xsk->chunk_size < MLX5E_MIN_XSK_CHUNK_SIZE) { mlx5_core_err(mdev, "XSK chunk size %u out of bounds [%u, %lu]\n", xsk->chunk_size, MLX5E_MIN_XSK_CHUNK_SIZE, PAGE_SIZE); return false;