* Re: [PATCH net] net/stmmac: Set Rx queue page_pool to NULL when freeing DMA resources
From: Maxime Chevallier @ 2026-07-02 9:24 UTC (permalink / raw)
To: Jakub Raczynski, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
linux-kernel, k.tegowski, k.domagalski, Yashwant Varur
In-Reply-To: <20260630100953.747868-1-j.raczynski@samsung.com>
Hi,
On 6/30/26 12:09, Jakub Raczynski wrote:
> When freeing RX descriptor resources, there is standard clearing of
> descriptor page_pool via page_pool_destroy() which does destroy
> page but does not set its pointer to NULL, which must be done by driver
> calling this function.
> It is not done in __free_dma_rx_desc_resources() when stopping interface,
> which is generally not an issue, because __alloc_dma_rx_desc_resources() does
> setup this regardless of previous state.
> But above is true assuming reinitialization is successful.
>
> In case of failure of page_pool_create() in __alloc_dma_rx_desc_resources(),
> all non-NULL pages will be freed, including those already cleared.
> So there is possible kernel panic due to wrong paging request at address.
>
> Fix this by assigning NULL to page_pool pointer on free
>
> Fixes: da5ec7f22a0f1 ("net: stmmac: refactor stmmac_init_rx_buffers for stmmac_reinit_rx_buffers")
> Signed-off-by: Yashwant Varur <yashwant.v@samsung.com>
> Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
> ---
> drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 3591755ea30b..4b8f2814d3b5 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -2172,8 +2172,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
> xdp_rxq_info_unreg(&rx_q->xdp_rxq);
>
> kfree(rx_q->buf_pool);
> - if (rx_q->page_pool)
> + if (rx_q->page_pool) {
> page_pool_destroy(rx_q->page_pool);
> + rx_q->page_pool = NULL;
> + }
page_pool_destroy can be passed NULL, as you're fixing this I think you might as well
drop the if(rx_q->page_pool) at the same time :) I agree with the change otherwise.
with that changed,
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Maxime
^ permalink raw reply
* Re: [PATCH net-next v11 1/7] dt-bindings: phy: document the serdes PHY on sa8255p
From: Geert Uytterhoeven @ 2026-07-02 9:16 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Vinod Koul, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Coquelin,
Alexandre Torgue, Giuseppe Cavallaro, Chen-Yu Tsai,
Jernej Skrabec, Neil Armstrong, Kevin Hilman, Jerome Brunet,
Shawn Guo, Fabio Estevam, Jan Petrous, s32, Mohd Ayaan Anwar,
Romain Gantois, Magnus Damm, Maxime Ripard, Christophe Roullier,
Radu Rendec, linux-arm-msm, devicetree, linux-kernel, netdev,
linux-stm32, linux-arm-kernel, Drew Fustini, linux-sunxi,
linux-amlogic, linux-mips, imx, linux-renesas-soc, linux-rockchip,
sophgo, linux-riscv, Bartosz Golaszewski, Bartosz Golaszewski
In-Reply-To: <CAMRc=MfBcOhbj=ETxy2Xz9o_nyzykrEhKAc3jYi6V5_jMZEE3Q@mail.gmail.com>
Hi Bartosz,
On Thu, 2 Jul 2026 at 11:12, Bartosz Golaszewski <brgl@kernel.org> wrote:
> On Tue, 30 Jun 2026 12:23:16 +0200, Vinod Koul <vkoul@kernel.org> said:
> > On 29-06-26, 16:51, Geert Uytterhoeven wrote:
> >> > Russell King asked me to put the PHY logic for SCMI pm domains into the PHY
> >> > driver instead of the MAC driver where it was previously. Instead of cramming
> >> > both HLOS and firmware handling into the same driver, I figured it makes more
> >> > sense to have a dedicated, cleaner driver as the two share very little code (if
> >> > any).
> >>
> >> I think you are mixing up DT bindings and driver implementation?
> >
> > Should the bindings change if we have different driver and firmware
> > implementations? Isn't binding supposed to be agnostic of
> > implementations..?
>
> I've thought about it some more and I believe this question is philosophical in
> nature.
>
> sa8775p and sa8255p are *the same* hardware. I can flash different firmware on
> the same Lemans Ride board and it becomes one or the other. Yet they are not
> described by the same DTS and the bindings differ as well. I don't see why we
> wouldn't allow the same approach for the this PHY.
>
> We treat it as different HW variant when it's managed by firmware - just like
> we do with the rest of the SoC.
DT describes hardware, not software policy.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH net-next v2] ionic: Change list definition method
From: patchwork-bot+netdevbpf @ 2026-07-02 9:20 UTC (permalink / raw)
To: zhulei; +Cc: brett.creeley, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev
In-Reply-To: <20260630065457.160081-1-zhulei_szu@163.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 14:54:57 +0800 you wrote:
> From: Lei Zhu <zhulei@kylinos.cn>
>
> The LIST_HEAD macro can both define a linked list and initialize
> it in one step. To simplify code, we replace the separate operations
> of linked list definition and manual initialization with the LIST_HEAD
> macro.
>
> [...]
Here is the summary with links:
- [net-next,v2] ionic: Change list definition method
https://git.kernel.org/netdev/net-next/c/09cfee6a8004
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next v1] ixgbe: E610: force phy link to get down when interface is down
From: Jagielski, Jedrzej @ 2026-07-02 9:13 UTC (permalink / raw)
To: Paul Menzel
Cc: intel-wired-lan@lists.osuosl.org, Nguyen, Anthony L,
netdev@vger.kernel.org, Loktionov, Aleksandr
In-Reply-To: <fb13640b-0171-457a-84c5-f6dbdd6d7471@molgen.mpg.de>
From: Paul Menzel <pmenzel@molgen.mpg.de>
Sent: Wednesday, July 1, 2026 2:27 PM
>Dear Jedrzej,
>
>
>Thank you for your patch.
>
>Am 01.07.26 um 13:35 schrieb Jedrzej Jagielski:
>> For the E610 family, similarly to the E8xx adapters, the default behavior
>> is for the PHY link to remain up even when the corresponding OS interface
>> is down.
>>
>> Add function setting down the PHY config IXGBE_ACI_PHY_ENA_LINK bit
>> what leads to disabling PHY link.
>
>It’d extend it a little:
>
>… by factoring the code out into ixgbe_handle_link_down(), and call it
>in ixgbe_close().
Hi Paul
thanks for suggestions,
sure, the commit msg will be extended.
>
>> Align functionality with the implementation of the ice driver.
>
>Please add a paragraph detailing regression potential. Are there users
>that might depend on the current default, as uncommon it might be?
There's no regression potential imho
changing behavior is volountary and is done via ethtool
i will rephrase it if that's unclear from the commit msg
>
>> Let user to configure link-down-on-close enablement through ethtool.
>
>Please provide examples, and how to test your change. Doing this you can
>also paste the new log messages.
>
>> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
>> Signed-off-by: Jedrzej Jagielski <jedrzej.jagielski@intel.com>
>> ---
>> drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 +
>> drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c | 35 ++++++++++++++++++-
>> drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h | 1 +
>> .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 15 ++++++++
>> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 27 +++++++++++---
>> 5 files changed, 73 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
>> index 30f62174acf2..7bbb82dd962c 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
>> @@ -685,6 +685,7 @@ struct ixgbe_adapter {
>> #define IXGBE_FLAG2_MOD_POWER_UNSUPPORTED BIT(22)
>> #define IXGBE_FLAG2_API_MISMATCH BIT(23)
>> #define IXGBE_FLAG2_FW_ROLLBACK BIT(24)
>> +#define IXGBE_FLAG2_LINK_DOWN_ON_CLOSE BIT(25)
>>
>> /* Tx fast path data */
>> int num_tx_queues;
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c
>> index da445fb673fc..46d8a3ea86b8 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c
>> @@ -1923,6 +1923,33 @@ void ixgbe_fc_autoneg_e610(struct ixgbe_hw *hw)
>> hw->fc.current_mode = hw->fc.requested_mode;
>> }
>>
>> +/**
>> + * ixgbe_disable_phy_link - force phy link to get down
>> + * @hw: pointer to hardware structure
>> + *
>> + * Send 0x0601 with the IXGBE_ACI_PHY_ENA_LINK bit set down.
>> + *
>> + * Return: the exit code of the operation.
>
>At least for me it’s not that helpful. Shouldn’t the return values be
>listed? What is success? What is failure?
Yeah i know that's not ideal but i also don't think mentioning all possible
retvals of ixgbe_aci_get_phy_caps and ixgbe_aci_set_phy_cfg is the best way
>
>> + */
>> +int ixgbe_disable_phy_link(struct ixgbe_hw *hw)
>> +{
>> + struct ixgbe_aci_cmd_get_phy_caps_data pcaps = {};
>> + struct ixgbe_aci_cmd_set_phy_cfg_data pcfg = {};
>> + int err;
>> +
>> + err = ixgbe_aci_get_phy_caps(hw, false, IXGBE_ACI_REPORT_ACTIVE_CFG,
>> + &pcaps);
>> + if (err)
>> + return err;
>> +
>> + ixgbe_copy_phy_caps_to_cfg(&pcaps, &pcfg);
>> +
>> + pcfg.caps &= ~IXGBE_ACI_PHY_ENA_LINK;
>> + pcfg.caps |= IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT;
>> +
>> + return ixgbe_aci_set_phy_cfg(hw, &pcfg);
>> +}
>> +
>> /**
>> * ixgbe_disable_rx_e610 - Disable RX unit
>> * @hw: pointer to hardware structure
>> @@ -2207,6 +2234,7 @@ int ixgbe_setup_phy_link_e610(struct ixgbe_hw *hw)
>> u8 rmode = IXGBE_ACI_REPORT_TOPO_CAP_MEDIA;
>> u64 sup_phy_type_low, sup_phy_type_high;
>> u64 phy_type_low = 0, phy_type_high = 0;
>> + bool force_on_required;
>> int err;
>>
>> err = ixgbe_aci_get_link_info(hw, false, NULL);
>> @@ -2272,6 +2300,11 @@ int ixgbe_setup_phy_link_e610(struct ixgbe_hw *hw)
>> phy_type_high |= IXGBE_PHY_TYPE_HIGH_10G_USXGMII;
>> }
>>
>> + /* If IXGBE_ACI_PHY_ENA_LINK has been explicitly disabled that means
>> + * we need to force interface enablement after reaching that point
>
>It’d be great, if you rephrased “that point”.
ok, will try
>
>> + */
>> + force_on_required = !(pcfg.caps & IXGBE_ACI_PHY_ENA_LINK);
>> +
>> /* Mask the set values to avoid requesting unsupported link types. */
>> phy_type_low &= sup_phy_type_low;
>> pcfg.phy_type_low = cpu_to_le64(phy_type_low);
>> @@ -2280,7 +2313,7 @@ int ixgbe_setup_phy_link_e610(struct ixgbe_hw *hw)
>>
>> if (pcfg.phy_type_high != pcaps.phy_type_high ||
>> pcfg.phy_type_low != pcaps.phy_type_low ||
>> - pcfg.caps != pcaps.caps) {
>> + pcfg.caps != pcaps.caps || force_on_required) {
>> pcfg.caps |= IXGBE_ACI_PHY_ENA_LINK;
>> pcfg.caps |= IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT;
>>
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h
>> index 2cb76a3d30ae..59044d67ebeb 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h
>> @@ -50,6 +50,7 @@ int ixgbe_cfg_phy_fc(struct ixgbe_hw *hw,
>> enum ixgbe_fc_mode req_mode);
>> int ixgbe_setup_fc_e610(struct ixgbe_hw *hw);
>> void ixgbe_fc_autoneg_e610(struct ixgbe_hw *hw);
>> +int ixgbe_disable_phy_link(struct ixgbe_hw *hw);
>> void ixgbe_disable_rx_e610(struct ixgbe_hw *hw);
>> int ixgbe_init_phy_ops_e610(struct ixgbe_hw *hw);
>> int ixgbe_identify_phy_e610(struct ixgbe_hw *hw);
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
>> index 4dfae53b4ea1..0fcb9d738984 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
>> @@ -139,6 +139,8 @@ static const char ixgbe_priv_flags_strings[][ETH_GSTRING_LEN] = {
>> "vf-ipsec",
>> #define IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF BIT(2)
>> "mdd-disable-vf",
>> +#define IXGBE_PRIV_LINK_DOWN_ON_CLOSE BIT(3)
>> + "link-down-on-close",
>> };
>>
>> #define IXGBE_PRIV_FLAGS_STR_LEN ARRAY_SIZE(ixgbe_priv_flags_strings)
>> @@ -3842,6 +3844,9 @@ static u32 ixgbe_get_priv_flags(struct net_device *netdev)
>> if (adapter->flags2 & IXGBE_FLAG2_AUTO_DISABLE_VF)
>> priv_flags |= IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF;
>>
>> + if (adapter->flags2 & IXGBE_FLAG2_LINK_DOWN_ON_CLOSE)
>> + priv_flags |= IXGBE_PRIV_LINK_DOWN_ON_CLOSE;
>> +
>> return priv_flags;
>> }
>>
>> @@ -3879,6 +3884,16 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags)
>> }
>> }
>>
>> + flags2 &= ~IXGBE_FLAG2_LINK_DOWN_ON_CLOSE;
>> + if (priv_flags & IXGBE_PRIV_LINK_DOWN_ON_CLOSE) {
>> + if (adapter->hw.mac.type == ixgbe_mac_e610) {
>> + flags2 |= IXGBE_FLAG2_LINK_DOWN_ON_CLOSE;
>> + } else {
>> + e_info(probe, "Cannot set private flags: Unsupported hardware\n");
>
>Please print hw.mac.type, and mention, that it’s only supported on E610.
yeah why not
>
>> + return -EOPNOTSUPP;
>> + }
>> + }
>> +
>> if (flags2 != adapter->flags2) {
>> adapter->flags2 = flags2;
>>
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> index 62c2d83e1577..58ee4a186039 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> @@ -7544,6 +7544,17 @@ static void ixgbe_close_suspend(struct ixgbe_adapter *adapter)
>> ixgbe_free_all_rx_resources(adapter);
>> }
>>
>> +static void ixgbe_handle_link_down(struct ixgbe_adapter *adapter)
>> +{
>> + struct net_device *netdev = adapter->netdev;
>> +
>> + if (test_bit(__IXGBE_PTP_RUNNING, &adapter->state))
>> + ixgbe_ptp_start_cyclecounter(adapter);
>> +
>> + e_info(drv, "NIC Link is Down\n");
>> + netif_carrier_off(netdev);
>> +}
>> +
>> /**
>> * ixgbe_close - Disables a network interface
>> * @netdev: network interface device structure
>> @@ -7566,6 +7577,16 @@ int ixgbe_close(struct net_device *netdev)
>>
>> ixgbe_fdir_filter_exit(adapter);
>>
>> + if (adapter->flags2 & IXGBE_FLAG2_LINK_DOWN_ON_CLOSE) {
>> + int err;
>> +
>> + err = ixgbe_disable_phy_link(&adapter->hw);
>> + if (err)
>> + e_warn(drv, "Cannot set PHY link down\n");
>
>Log the error?
you mean to change the log lvl?
Thanks for your review!
>
>> +
>> + ixgbe_handle_link_down(adapter);
>> + }
>> +
>> ixgbe_release_hw_control(adapter);
>>
>> return 0;
>> @@ -8244,11 +8265,7 @@ static void ixgbe_watchdog_link_is_down(struct ixgbe_adapter *adapter)
>> if (ixgbe_is_sfp(hw) && hw->mac.type == ixgbe_mac_82598EB)
>> adapter->flags2 |= IXGBE_FLAG2_SEARCH_FOR_SFP;
>>
>> - if (test_bit(__IXGBE_PTP_RUNNING, &adapter->state))
>> - ixgbe_ptp_start_cyclecounter(adapter);
>> -
>> - e_info(drv, "NIC Link is Down\n");
>> - netif_carrier_off(netdev);
>> + ixgbe_handle_link_down(adapter);
>> }
>>
>> static bool ixgbe_ring_tx_pending(struct ixgbe_adapter *adapter)
>
>
>Kind regards,
>
>Paul
^ permalink raw reply
* Re: [PATCH net-next v11 1/7] dt-bindings: phy: document the serdes PHY on sa8255p
From: Bartosz Golaszewski @ 2026-07-02 9:12 UTC (permalink / raw)
To: Vinod Koul
Cc: Bartosz Golaszewski, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Coquelin,
Alexandre Torgue, Giuseppe Cavallaro, Chen-Yu Tsai,
Jernej Skrabec, Neil Armstrong, Kevin Hilman, Jerome Brunet,
Shawn Guo, Fabio Estevam, Jan Petrous, s32, Mohd Ayaan Anwar,
Romain Gantois, Magnus Damm, Maxime Ripard, Christophe Roullier,
Radu Rendec, linux-arm-msm, devicetree, linux-kernel, netdev,
linux-stm32, linux-arm-kernel, Drew Fustini, linux-sunxi,
linux-amlogic, linux-mips, imx, linux-renesas-soc, linux-rockchip,
sophgo, linux-riscv, Bartosz Golaszewski, Bartosz Golaszewski,
Geert Uytterhoeven
In-Reply-To: <akOZFIowVvprnAMf@vaman>
On Tue, 30 Jun 2026 12:23:16 +0200, Vinod Koul <vkoul@kernel.org> said:
> On 29-06-26, 16:51, Geert Uytterhoeven wrote:
>> > Russell King asked me to put the PHY logic for SCMI pm domains into the PHY
>> > driver instead of the MAC driver where it was previously. Instead of cramming
>> > both HLOS and firmware handling into the same driver, I figured it makes more
>> > sense to have a dedicated, cleaner driver as the two share very little code (if
>> > any).
>>
>> I think you are mixing up DT bindings and driver implementation?
>
> Should the bindings change if we have different driver and firmware
> implementations? Isn't binding supposed to be agnostic of
> implementations..?
>
I've thought about it some more and I believe this question is philosophical in
nature.
sa8775p and sa8255p are *the same* hardware. I can flash different firmware on
the same Lemans Ride board and it becomes one or the other. Yet they are not
described by the same DTS and the bindings differ as well. I don't see why we
wouldn't allow the same approach for the this PHY.
We treat it as different HW variant when it's managed by firmware - just like
we do with the rest of the SoC.
Bart
^ permalink raw reply
* Re: [PATCH net-next v6 4/6] pds_core: add PLDM component info display
From: Paolo Abeni @ 2026-07-02 9:11 UTC (permalink / raw)
To: nikhil.rao
Cc: netdev, kuba, brett.creeley, eric.joyner, andrew+netdev, davem,
edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260629230200.82092-5-nikhil.rao@amd.com>
From: AI Reviewer <ai@example.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
pds_core: add PLDM component info display
This change adds detailed PLDM component info to devlink info on
identity v2 devices, introduces a pdsc_fw_components_invalidate()
helper used during firmware recovery and after firmware update, and
maps PDS_CORE_FW_TYPE_MAIN to the generic "fw" name in the PLDM
flash path.
> diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
> index 38a2446571af..4d9ab8b74ec6 100644
> --- a/drivers/net/ethernet/amd/pds_core/core.c
> +++ b/drivers/net/ethernet/amd/pds_core/core.c
> @@ -580,6 +580,8 @@ void pdsc_fw_up(struct pdsc *pdsc)
> return;
> }
>
> + pdsc_fw_components_invalidate(pdsc);
> +
> err = pdsc_setup(pdsc, PDSC_SETUP_RECOVERY);
> if (err)
> goto err_out;
[Medium]
Can concurrent access to pdsc->fw_components race here?
pdsc_fw_up() runs from pdsc_health_thread (workqueue, holding only
pdsc->config_lock) and from pdsc_reset_done (PCI reset handler, holding
neither), so pdsc_fw_components_invalidate() writes num_components = 0
without the devlink instance lock.
Meanwhile, pdsc_dl_component_info_get() reads pdsc->fw_components from
the devlink info_get callback under only the devlink lock, never
config_lock. The two domains do not exclude each other.
In pdsc_dl_component_info_get():
if (!pdsc->fw_components.num_components) {
err = pdsc_get_component_info(pdsc);
if (err || !pdsc->fw_components.num_components)
return pdsc_dl_report_fw_ver(req,
pdsc->dev_info.fw_version);
}
list_info = &pdsc->fw_components;
num_components = min_t(u16, list_info->num_components,
le16_to_cpu(pdsc->dev_ident.max_fw_slots));
The reader can observe num_components > 0 on the first load, then
re-read it inside min_t() and observe 0 after pdsc_fw_up() invalidates
the cache, producing zero iterations and no fallback. Conversely, a
stale non-zero count could be iterated over info[] entries describing
the previous firmware.
No READ_ONCE/WRITE_ONCE or memory barriers are used. Would a lock or
RCU be a better fit here?
> diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
> index 3b763ee1715e..bd234c3c50b2 100644
> --- a/drivers/net/ethernet/amd/pds_core/devlink.c
> +++ b/drivers/net/ethernet/amd/pds_core/devlink.c
> @@ -93,14 +93,110 @@ int pdsc_dl_flash_update(struct devlink *dl,
> return pdsc_firmware_update(pdsc, params, extack);
> }
>
> +static int pdsc_dl_report_component(struct devlink_info_req *req,
> + struct pds_core_fw_component_info *info)
> +{
> + enum devlink_info_version_type ver_type;
> + u16 flags = le16_to_cpu(info->flags);
> + char *ver = info->version;
> + const char *name;
> + char buf[32];
> +
> + /* Main firmware is reported as generic "fw" */
> + if (info->component_type == PDS_CORE_FW_TYPE_MAIN) {
> + if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
> + snprintf(buf, sizeof(buf), "fw.gold");
> + else
> + snprintf(buf, sizeof(buf), "fw");
> + } else {
> + name = pdsc_fw_type_to_name(info->component_type);
> + if (!name)
> + return 0;
> +
> + if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
> + snprintf(buf, sizeof(buf), "fw.%s.gold", name);
> + else
> + snprintf(buf, sizeof(buf), "fw.%s", name);
> + }
[Medium]
For PDS_CORE_FW_TYPE_MAIN, only PDS_CORE_FW_SLOT_GOLD is special-cased.
PDS_CORE_FW_SLOT_INVALID, PDS_CORE_FW_SLOT_A, and PDS_CORE_FW_SLOT_B all
end up emitting the bare name "fw".
The legacy v1 path used distinct names fw.mainfwa and fw.mainfwb for
these two slots, and devlink_info_version_*_put() does not deduplicate
names. If firmware reports two MAIN components (which the v1
pds_core_fw_list_info already exposed as A/B), will userspace see two
entries both named "fw" with no way to distinguish them?
[Medium]
Are the new component names documented anywhere? The driver introduces
fw.bootloader, fw.cpld, fw.secure, fw.fpga, fw.suc, fw.suc.bootloader,
fw.uboot, plus a .gold variant of each, and fw.gold.
Documentation/networking/devlink/devlink-info.rst says:
It is expected that drivers use the following generic names ... As
last resort, if a component is truly unique, using driver-specific
names is allowed, but these should be documented in the driver-specific
file.
Of those names only fw and fw.bootloader correspond to documented
generic suffixes; the rest appear to be driver-invented, and there is
no Documentation/networking/devlink/pds_core.rst introduced by this
patch. The .gold suffix in particular is not part of the generic
vocabulary (different copies of the same component are normally handled
via bundle_id or by reporting the same name under both running and
stored). Composite names like fw.suc.bootloader.gold do not have any
documented dot-separation semantics either.
> +
> + ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;
> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)
> + ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT;
> +
> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) {
> + int err;
> +
> + err = devlink_info_version_fixed_put(req, buf, ver);
> + if (err)
> + return err;
> + }
> +
> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) {
> + int err;
> +
> + err = devlink_info_version_running_put_ext(req, buf,
> + ver, ver_type);
> + if (err)
> + return err;
> + }
> +
> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) {
> + int err;
> +
> + err = devlink_info_version_stored_put_ext(req, buf,
> + ver, ver_type);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
[Medium]
Is the generic running "fw" entry still guaranteed on the v2 path?
It is produced only as a side effect of pdsc_dl_report_component()
seeing component_type == PDS_CORE_FW_TYPE_MAIN with
PDS_CORE_FW_COMPONENT_INFO_F_RUNNING set.
The v1 path always called:
devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
pdsc->dev_info.fw_version);
unconditionally. If firmware reports MAIN only with F_STARTUP/F_FIXED
(no F_RUNNING), or reports no MAIN component at all, does the user
silently lose the generic running fw version that monitoring tools rely
on to identify the active firmware?
> +
> +static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver)
> +{
> + return devlink_info_version_running_put(req,
> + DEVLINK_INFO_VERSION_GENERIC_FW,
> + fw_ver);
> +}
> +
> +static int pdsc_dl_component_info_get(struct devlink *dl,
> + struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> +{
> + struct pds_core_component_list_info *list_info;
> + struct pdsc *pdsc = devlink_priv(dl);
> + u8 num_components;
> + int err;
> + int i;
> +
> + if (!pdsc->fw_components.num_components) {
> + err = pdsc_get_component_info(pdsc);
> + if (err || !pdsc->fw_components.num_components)
> + return pdsc_dl_report_fw_ver(req,
> + pdsc->dev_info.fw_version);
> + }
[High]
Can devlink info polling accumulate deferred DMA buffers when the
firmware is hung?
pdsc_dl_component_info_get() is reachable from any devlink dev info
request. On -EAGAIN or -ETIMEDOUT, pdsc_get_component_info() calls
pdsc_deferred_dma_add() and returns the error without setting
pdsc->fw_components.num_components:
err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
if (err == -ETIMEDOUT || err == -EAGAIN) {
pdsc_deferred_dma_add(pdsc, deferred, dma_addr, list_info,
PDS_PAGE_SIZE, DMA_FROM_DEVICE);
return err;
}
The cached count therefore stays 0, so the next devlink info request
re-enters the same path and queues another PDS_PAGE_SIZE allocation
plus DMA mapping. The deferred list is only drained by
pdsc_deferred_dma_free() in __pdsc_devcmd_locked() on a non-EAGAIN
return.
Before this patch, pdsc_get_component_info() was reachable only from
the privileged flash path. Is it intentional that any monitoring agent
polling devlink dev info while firmware is wedged can grow the deferred
DMA list unbounded?
[Medium]
On the v2 branch, the function falls back to a single
pdsc_dl_report_fw_ver() call whenever pdsc_get_component_info() returns
an error or num_components is still 0 (which also happens when the
firmware reports comp.get_component_info.ver == 0 and the function
returns 0).
Before this patch, pdsc_dl_info_get() unconditionally invoked
PDS_CORE_FW_GET_LIST and emitted fw.goldfw, fw.mainfwa, fw.mainfwb.
On v2 devices that branch is unreachable, since pdsc_dl_fw_list_info_get()
is only called from pdsc_dl_info_get_v1().
For transitional firmware that advertises identity v2 but does not yet
support PLDM/GET_COMPONENT_INFO, would calling pdsc_dl_fw_list_info_get()
as a v2 fallback preserve the previous four-version output rather than
silently collapsing it to one?
> +
> + list_info = &pdsc->fw_components;
> + num_components = min_t(u16, list_info->num_components,
> + le16_to_cpu(pdsc->dev_ident.max_fw_slots));
> + for (i = 0; i < num_components; i++) {
> + err = pdsc_dl_report_component(req, &list_info->info[i]);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
[Medium]
What happens if dev_ident.max_fw_slots is 0? pdsc_get_component_info()
populates pdsc->fw_components.num_components from the firmware list
(bounded by PDS_CORE_FW_COMPONENT_LIST_LEN), but the loop bound clamps
that against the separate max_fw_slots identity field:
num_components = min_t(u16, list_info->num_components,
le16_to_cpu(pdsc->dev_ident.max_fw_slots));
If firmware advertises identity v2 but reports max_fw_slots == 0, the
earlier !pdsc->fw_components.num_components check does not fire (the
count is non-zero), min_t() yields 0, the loop body is skipped, and
pdsc_dl_info_get() proceeds straight to ASIC ID/REV/serial. Would
adding a fallback to pdsc_dl_report_fw_ver() in this case avoid
emitting devlink info with no fw entry at all?
> +
> static char *fw_slotnames[] = {
> "fw.goldfw",
> "fw.mainfwa",
> "fw.mainfwb",
> };
>
> -int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
> - struct netlink_ext_ack *extack)
> +static int pdsc_dl_fw_list_info_get(struct devlink *dl,
> + struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> {
[ ... ]
> @@ -134,12 +230,41 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
> return err;
> }
>
> - err = devlink_info_version_running_put(req,
> - DEVLINK_INFO_VERSION_GENERIC_FW,
> - pdsc->dev_info.fw_version);
> + return 0;
> +}
> +
> +static int pdsc_dl_info_get_v1(struct devlink *dl,
> + struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> +{
> + struct pdsc *pdsc = devlink_priv(dl);
> + int err;
> +
> + err = pdsc_dl_fw_list_info_get(dl, req, extack);
> if (err)
> return err;
>
> + /* Version 1: report fw from dev_info (running only) */
> + return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version);
> +}
> +
> +int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> +{
> + struct pdsc *pdsc = devlink_priv(dl);
> + char buf[32];
> + int err;
> +
> + if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) {
> + err = pdsc_dl_component_info_get(dl, req, extack);
> + if (err)
> + return err;
> + } else {
> + err = pdsc_dl_info_get_v1(dl, req, extack);
> + if (err)
> + return err;
> + }
> +
[Medium]
Is the devlink-info naming change across the identity-version boundary
intentional, and is it captured in the commit message or a Documentation
update?
Before this patch every device emitted stored slot names from
fw_slotnames[] (fw.goldfw, fw.mainfwa, fw.mainfwb) via the unconditional
PDS_CORE_FW_GET_LIST devcmd. After this patch, devices that advertise
identity >= PDS_CORE_IDENTITY_VERSION_2 take the
pdsc_dl_component_info_get() branch and emit a different set of names
(fw, fw.gold, fw.bootloader, fw.uboot, fw.cpld, ...), with no alias for
the old names.
A firmware-only upgrade can therefore silently rename the user-visible
stored entries. Should the commit message call out this UAPI rename,
and should the legacy names remain emitted as aliases for compatibility
with existing fleet tooling that parses devlink dev info output?
--
This is an AI-generated review.
^ permalink raw reply
* Re: [PATCH net-next v6 3/6] pds_core: add PLDM firmware update support via devlink flash
From: Paolo Abeni @ 2026-07-02 9:11 UTC (permalink / raw)
To: nikhil.rao
Cc: netdev, kuba, brett.creeley, eric.joyner, andrew+netdev, davem,
edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260629230200.82092-4-nikhil.rao@amd.com>
From: AI Reviewer <ai@example.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
pds_core: add PLDM firmware update support via devlink flash
Implements PLDM firmware update via devlink flash using the upstream
pldmfw API. Adds whole-package and per-component update paths, and
introduces a deferred-DMA list that releases buffers after timed-out
devcmds. A few questions inline.
> diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
> index b7fe9ad73349..c686f0bbbaeb 100644
> --- a/drivers/net/ethernet/amd/pds_core/core.h
> +++ b/drivers/net/ethernet/amd/pds_core/core.h
> @@ -23,6 +23,14 @@
> #define PDSC_SETUP_RECOVERY false
> #define PDSC_SETUP_INIT true
>
> +struct pdsc_deferred_dma {
> + struct list_head list;
> + dma_addr_t dma_addr;
> + void *va;
> + size_t size;
> + enum dma_data_direction dir;
> +};
> +
[ ... ]
> diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
> index 5c0ca3d0b000..6082b28915db 100644
> --- a/drivers/net/ethernet/amd/pds_core/dev.c
> +++ b/drivers/net/ethernet/amd/pds_core/dev.c
> @@ -206,15 +206,53 @@ static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
> else
> memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
>
> + if (err != -ETIMEDOUT && err != -EAGAIN)
> + pdsc_deferred_dma_free(pdsc);
> +
> return err;
> }
[High]
Can the deferred-DMA scheme actually guarantee that the device has
finished accessing the buffer before it is unmapped and freed?
The trigger for releasing a deferred entry is "the next devcmd returned
something other than -ETIMEDOUT/-EAGAIN", plus pdsc_remove() and
pdsc_reset_prepare(). None of these provide a hardware-level fence that
the previous in-flight DMA has drained. In the remove/reset paths the
free runs after pci_disable_device(), which clears bus-master but does
not synchronously drain pending transactions.
The DMA_FROM_DEVICE case in pdsc_get_component_info() is particularly
exposed because the buffer is plain kernel slab memory: a late device
write after kfree would corrupt unrelated allocations.
Also note that pdsc_send_package_data(), pdsc_get_component_info(), and
pdsc_flash_component_chunk() only defer on -EAGAIN/-ETIMEDOUT and
immediately unmap+kfree on any other error (-EIO, -ENXIO, ...). Is the
device guaranteed not to have started any DMA in those cases?
> +void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
> + dma_addr_t dma_addr, void *va, size_t size,
> + enum dma_data_direction dir)
> +{
> + entry->dma_addr = dma_addr;
> + entry->va = va;
> + entry->size = size;
> + entry->dir = dir;
> +
> + spin_lock(&pdsc->deferred_dma_lock);
> + list_add_tail(&entry->list, &pdsc->deferred_dma_list);
> + spin_unlock(&pdsc->deferred_dma_lock);
> +}
> +
> +void pdsc_deferred_dma_free(struct pdsc *pdsc)
> +{
> + struct pdsc_deferred_dma *entry, *tmp;
> +
> + spin_lock(&pdsc->deferred_dma_lock);
> + list_for_each_entry_safe(entry, tmp, &pdsc->deferred_dma_list, list) {
> + dma_unmap_single(pdsc->dev, entry->dma_addr,
> + entry->size, entry->dir);
> + kfree(entry->va);
> + list_del(&entry->list);
> + kfree(entry);
> + }
> + spin_unlock(&pdsc->deferred_dma_lock);
> +}
[Medium]
Is it intentional to hold deferred_dma_lock across dma_unmap_single()
and two kfree()s per entry? This runs from __pdsc_devcmd_locked() on
every successful devcmd while devcmd_lock is also held, and
dma_unmap_single() can trigger IOMMU IOTLB invalidations. Would
list_splice_init() under the lock followed by iteration on a detached
list scale better when many entries have accumulated?
[ ... ]
> diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
> index fa626719e68d..433b0a8df4c1 100644
> --- a/drivers/net/ethernet/amd/pds_core/fw.c
> +++ b/drivers/net/ethernet/amd/pds_core/fw.c
[ ... ]
> +#define PDSC_FW_COMPONENT_PREFIX "fw."
> +#define PDSC_FW_COMPONENT_FULL_NAME_BUFLEN \
> + (sizeof(PDSC_FW_COMPONENT_PREFIX) + PDS_CORE_FW_COMPONENT_NAME_BUFLEN)
> +
> +/* Driver-defined component type to name mapping.
> + * PDS_CORE_FW_TYPE_MAIN is NULL - handled specially as "fw" without prefix.
> + */
> +static const char * const pdsc_fw_type_names[] = {
> + [PDS_CORE_FW_TYPE_MAIN] = NULL,
> + [PDS_CORE_FW_TYPE_BOOT] = "bootloader",
> + [PDS_CORE_FW_TYPE_CPLD] = "cpld",
> + [PDS_CORE_FW_TYPE_SECURE] = "secure",
> + [PDS_CORE_FW_TYPE_FPGA] = "fpga",
> + [PDS_CORE_FW_TYPE_SUC_MAIN] = "suc",
> + [PDS_CORE_FW_TYPE_SUC_BOOT] = "suc.bootloader",
> + [PDS_CORE_FW_TYPE_UBOOT] = "uboot",
> +};
[Low, Low]
The documentation table added in this same patch lists fw.gold as a
driver-defined component name, and the kerneldoc on
enum pds_core_fw_component_type says gold variants are "reported with
a .gold suffix (e.g., fw.gold)". The table above has no gold entry, so
pdsc_name_to_fw_type("fw.gold") returns 0 and pdsc_pldm_firmware_update()
rejects the request with -ENOENT. Should fw.gold either be wired up or
the docs clarify that gold is read-only?
PDSC_FW_COMPONENT_FULL_NAME_BUFLEN is defined here but never used;
pdsc_flash_component() uses an ad-hoc sizeof(PDSC_FW_COMPONENT_PREFIX) +
16 instead. Should this macro be wired up or dropped?
[ ... ]
> +int pdsc_get_component_info(struct pdsc *pdsc)
> +{
> + union pds_core_dev_cmd cmd = {
> + .get_component_info.opcode = PDS_CORE_CMD_GET_COMPONENT_INFO,
> + .get_component_info.ver = 1,
> + };
[ ... ]
> + if (comp.get_component_info.ver == 0) {
> + /* Don't support backward compatibility as version 0 has
> + * alignment issues, so give a hint to users to update
> + * their firmware
> + */
> + dev_warn_once(pdsc->dev,
> + "Incompatible get_component_info version %u reported by firmware\n",
> + comp.get_component_info.ver);
> + err = 0;
> + goto out;
> + }
[Low]
This branch returns 0 without populating pdsc->fw_components. The caller
pdsc_pldm_firmware_update() uses:
if (!pdsc->fw_components.num_components) {
err = pdsc_get_component_info(pdsc);
if (err) { ... return err; }
}
if (params->component) {
u8 type = pdsc_name_to_fw_type(params->component);
if (!type || !pdsc_component_type_exists(pdsc, type))
return -ENOENT;
}
so on a device that reports ver == 0, the cache never populates and
every per-component flash returns -ENOENT. Should this branch return an
explicit error instead, or otherwise prevent the per-component lookup
from running?
[ ... ]
> +static int pdsc_devcmd_send_component(struct pdsc *pdsc,
> + struct pds_core_flash_component *info,
> + u16 info_sz, dma_addr_t addr, u32 length,
> + u32 offset, u16 slot_id,
> + union pds_core_dev_comp *comp)
> +{
> + union pds_core_dev_cmd cmd = {
> + .send_component.opcode = PDS_CORE_CMD_SEND_COMPONENT,
> + .send_component.ver = 1,
> + .send_component.operation = PDS_CORE_SEND_COMPONENT_START,
> + .send_component.data_pa = cpu_to_le64(addr),
> + .send_component.data_len = cpu_to_le32(length),
> + .send_component.offset = cpu_to_le32(offset),
> + .send_component.slot_id = slot_id,
> + };
> + unsigned long timeout = 300 * HZ;
> + unsigned long start_time;
> + unsigned long end_time;
> + int err;
> +
> + start_time = jiffies;
> + end_time = start_time + timeout;
> + do {
> + /* prevent noisy/benign devcmd failures */
> + err = pdsc_devcmd_with_data_nomsg(pdsc, &cmd, info, info_sz,
> + comp, 60);
> + if (err != -EAGAIN)
> + break;
> +
> + /* if required, subsequent commands check status of
> + * PDS_CORE_CMD_SEND_COMPONENT command, which returns
> + * EAGAIN while the command is still running,
> + * else we get the final command status.
> + */
> + cmd.send_component.operation = PDS_CORE_SEND_COMPONENT_STATUS;
> + msleep(20);
> + } while (time_before(jiffies, end_time));
[Low]
In the STATUS retry phase, only cmd.send_component.operation changes
between iterations; data_pa, data_len, offset, and slot_id remain set
to the original START values, and pdsc_devcmd_with_data_nomsg() also
re-issues memcpy_toio(&pdsc->cmd_regs->data, info, info_sz) on every
poll (potentially up to ~15000 iterations over the 300s timeout at 20ms
cadence). Does the firmware ignore data_pa/data_len on the STATUS
opcode, or could it re-initiate DMA from the same buffer? Could the
status poll send just the operation field?
[ ... ]
> +static int pdsc_finalize_update(struct pldmfw *context)
> +{
[ ... ]
> + start_time = jiffies;
> + end_time = start_time + (PDSC_FW_INSTALL_TIMEOUT * HZ);
> + do {
> + err = pdsc_devcmd_finalize_update(pdsc);
> + if (!err || err != -EAGAIN)
> + break;
> +
> + dev_dbg(dev, "retrying finalize_update: %pe\n", ERR_PTR(err));
> + msleep(20);
> + } while (time_before(jiffies, end_time) && err == -EAGAIN);
[Low]
Is the !err in if (!err || err != -EAGAIN) intentional? err == 0 already
satisfies err != -EAGAIN, so the condition reduces to err != -EAGAIN.
[ ... ]
> +static int pdsc_pldm_firmware_update(struct pdsc *pdsc,
> + struct devlink_flash_update_params *params,
> + struct netlink_ext_ack *extack,
> + const struct firmware *fw)
> +{
> + struct pds_core_fwu_priv priv = {};
> + int err;
> +
> + if (!pdsc->fw_components.num_components) {
> + err = pdsc_get_component_info(pdsc);
> + if (err) {
> + dev_err(pdsc->dev,
> + "Failed to get component info: %pe\n",
> + ERR_PTR(err));
> + return err;
> + }
> + }
> +
> + if (params->component) {
> + u8 type = pdsc_name_to_fw_type(params->component);
> +
> + if (!type || !pdsc_component_type_exists(pdsc, type))
> + return -ENOENT;
> + }
> +
> + INIT_LIST_HEAD(&priv.components);
> + priv.context.ops = &pdsc_pldmfw_ops;
> + priv.context.dev = pdsc->dev;
> + priv.params = params;
> + priv.pdsc = pdsc;
> +
> + err = pldmfw_flash_image(&priv.context, fw);
> + pdsc_free_fwu_priv(&priv);
> +
> + return err;
> +}
[Medium]
The extack parameter is accepted here and struct pds_core_fwu_priv
declares a netlink_ext_ack *extack field, but priv.extack is never
assigned, and none of the PLDM callbacks (pdsc_match_record_descs,
pdsc_send_package_data, pdsc_send_component_table, pdsc_flash_component,
pdsc_finalize_update) call NL_SET_ERR_MSG_MOD(). The legacy path uses
extack at multiple call sites ("Firmware wait timed out", "Segment
download failed", ...). Was the intent to propagate extack diagnostics
on the PLDM path too, including for the -ENOENT returned above?
> +int pdsc_firmware_update(struct pdsc *pdsc,
> + struct devlink_flash_update_params *params,
> + struct netlink_ext_ack *extack)
> +{
> + int err;
> +
> + if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2 &&
> + pdsc->dev_ident.capabilities &
> + cpu_to_le64(PDS_CORE_DEV_CAP_PLDM_FW_UPDATE))
> + err = pdsc_pldm_firmware_update(pdsc, params, extack,
> + params->fw);
> + else
> + err = pdsc_legacy_firmware_update(pdsc, params->fw, extack);
> +
> + /* Invalidate cached component info so next info_get refreshes */
> + pdsc->fw_components.num_components = 0;
> +
> + return err;
> +}
[Medium]
What happens here when a user runs:
devlink dev flash pci/0000:b5:00.0 file foo component fw.cpld
against a device that does not advertise PDS_CORE_DEV_CAP_PLDM_FW_UPDATE?
The dispatcher falls through to pdsc_legacy_firmware_update(pdsc,
params->fw, extack), which has no notion of components and proceeds to
flash the entire image.
The commit message says:
Not all components support per-component update - devlink will
reject the request if the specified component cannot be updated.
Should the dispatcher return -EOPNOTSUPP (or similar) when
params->component != NULL on the legacy path, to match that promise?
> diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
> index 619186f26b5b..5a1fafaccf20 100644
> --- a/include/linux/pds/pds_core_if.h
> +++ b/include/linux/pds/pds_core_if.h
> @@ -279,11 +299,20 @@ enum pds_core_fw_control_oper {
> PDS_CORE_FW_GET_LIST = 7,
> };
>
> +/**
> + * enum pds_core_fw_slot - Firmware slot identifiers
> + * @PDS_CORE_FW_SLOT_INVALID: Let firmware select slot based on package metadata
> + * @PDS_CORE_FW_SLOT_A: Primary firmware slot A
> + * @PDS_CORE_FW_SLOT_B: Primary firmware slot B
> + * @PDS_CORE_FW_SLOT_GOLD: Gold/recovery firmware slot
> + * @PDS_CORE_FW_SLOT_MAX: Sentinel value indicating no slot resolved
> + */
> enum pds_core_fw_slot {
> PDS_CORE_FW_SLOT_INVALID = 0,
> PDS_CORE_FW_SLOT_A = 1,
> PDS_CORE_FW_SLOT_B = 2,
> PDS_CORE_FW_SLOT_GOLD = 3,
> + PDS_CORE_FW_SLOT_MAX = 0xff,
> };
[Low]
PDS_CORE_FW_SLOT_MAX is added with value 0xff to this wire-protocol
enum but has no references anywhere. Should it be wired up to something
or dropped before the enum value is locked in by firmware?
--
This is an AI-generated review.
^ permalink raw reply
* Re: [PATCH net-next v4] net: usb: rtl8150: handle link status read failures
From: patchwork-bot+netdevbpf @ 2026-07-02 9:10 UTC (permalink / raw)
To: Yousef Alhouseen
Cc: petkan, andrew+netdev, davem, edumazet, kuba, pabeni, linux-usb,
netdev, linux-kernel, syzbot+9db6c624635564ad813c
In-Reply-To: <20260630101216.10365-1-alhouseenyousef@gmail.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 12:12:16 +0200 you wrote:
> set_carrier() ignores the result of the USB control transfer and tests
> the stack variable supplied as its receive buffer. If the device rejects
> or aborts the request, that variable remains uninitialized and the driver
> chooses an arbitrary carrier state.
>
> Leave the existing carrier state unchanged when the link status cannot be
> read. A transient USB error should not be treated as link loss.
>
> [...]
Here is the summary with links:
- [net-next,v4] net: usb: rtl8150: handle link status read failures
https://git.kernel.org/netdev/net-next/c/8c9c5b9a6896
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH net-next] selftests/net/openvswitch: add SCTP flow key test
From: Minxi Hou @ 2026-07-02 9:09 UTC (permalink / raw)
To: netdev
Cc: Minxi Hou, aconole, echaudro, i.maximets, davem, edumazet, kuba,
pabeni, horms, shuah, dev, linux-kselftest
Register OVS_KEY_ATTR_SCTP in the flow key parser so that sctp()
can be used in flow specifications. The ovs_key_sctp class already
exists (with src/dst fields matching the TCP/UDP siblings) but was
not wired into the parser, so the token was silently dropped and the
kernel rejected the flow.
Add test_sctp_connect_v4 exercising the SCTP flow key with
port-specific matching: sctp(dst=4443) for client-to-server and
sctp(src=4443) for server-to-client, mirroring the SCTP four-way
handshake direction. The test verifies connectivity with flows
installed, confirms failure after flow removal, then reinstalls
and verifies recovery.
Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
.../selftests/net/openvswitch/openvswitch.sh | 92 +++++++++++++++++++
.../selftests/net/openvswitch/ovs-dpctl.py | 5 +
2 files changed, 97 insertions(+)
diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh
index 2954245129a2..dd4d0b3bcc23 100755
--- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
+++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh
@@ -32,6 +32,7 @@ tests="
dec_ttl ttl: dec_ttl decrements IP TTL
flow_set flow-set: Flow modify
action_set set: SET action rewrites fields
+ sctp_connect_v4 sctp: SCTP flow key matching
psample psample: Sampling packets with psample"
info() {
@@ -443,6 +444,97 @@ test_action_set() {
return 0
}
+# sctp_connect_v4 test
+# - sctp(dst=4443) matches client-to-server INIT
+# - sctp(src=4443) matches server-to-client INIT-ACK
+# - remove flows and verify connection fails, reinstall and recover
+test_sctp_connect_v4() {
+ local t="test_sctp_connect_v4"
+
+ which ncat >/dev/null 2>&1 || return $ksft_skip
+ modprobe -q sctp 2>/dev/null || return $ksft_skip
+
+ sbx_add "$t" || return $?
+ ovs_add_dp "$t" sctp4 || return 1
+
+ info "create namespaces"
+ for ns in client server; do
+ ovs_add_netns_and_veths "$t" "sctp4" "$ns" \
+ "${ns:0:1}0" "${ns:0:1}1" || return 1
+ done
+
+ ip netns exec client ip addr add 172.31.110.10/24 dev c1
+ ip netns exec client ip link set c1 up
+ ip netns exec server ip addr add 172.31.110.20/24 dev s1
+ ip netns exec server ip link set s1 up
+
+ # ARP forwarding
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0806),arp()' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0806),arp()' \
+ '1' || return 1
+
+ # SCTP port matching: dst for request, src for reply
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0800),ipv4(proto=132),sctp(src=4443)' \
+ '1' || return 1
+
+ echo "server" | \
+ ovs_netns_spawn_daemon "$t" "server" \
+ ncat --sctp -l 172.31.110.20 -vn 4443
+ sleep 0.1
+
+ info "verify SCTP association with port-keyed flows"
+ ovs_sbx "$t" ip netns exec client \
+ ncat --sctp -i 1 -zv 172.31.110.20 4443 \
+ || return 1
+
+ ovs_del_flows "$t" sctp4
+
+ info "verify connection fails without flows"
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0806),arp()' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0806),arp()' \
+ '1' || return 1
+
+ echo "server2" | \
+ ovs_netns_spawn_daemon "$t" "server" \
+ ncat --sctp -l 172.31.110.20 -vn 4443
+ sleep 0.1
+
+ ovs_sbx "$t" ip netns exec client \
+ ncat --sctp -w 2 -zv 172.31.110.20 4443 \
+ >/dev/null 2>&1 \
+ && { info "FAIL: connection should fail without flows"
+ return 1; }
+
+ info "reinstall flows and verify recovery"
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0800),ipv4(proto=132),sctp(src=4443)' \
+ '1' || return 1
+
+ echo "server3" | \
+ ovs_netns_spawn_daemon "$t" "server" \
+ ncat --sctp -l 172.31.110.20 -vn 4443
+ sleep 0.1
+
+ ovs_sbx "$t" ip netns exec client \
+ ncat --sctp -i 1 -zv 172.31.110.20 4443 \
+ || return 1
+
+ return 0
+}
+
# psample test
# - use psample to observe packets
test_psample() {
diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index e1ecfad2c03e..7cfc29ec7e59 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -1982,6 +1982,11 @@ class ovskey(nla):
"icmp",
ovskey.ovs_key_icmp,
),
+ (
+ "OVS_KEY_ATTR_SCTP",
+ "sctp",
+ ovskey.ovs_key_sctp,
+ ),
(
"OVS_KEY_ATTR_TCP_FLAGS",
"tcp_flags",
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 10/10] net: dsa: microchip: move the drive strength config out of the common section
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
The drive strength configuration is done during the setup of all
switches through a common function that then has specific behavior
depending on the switch identity.
Split the common configuration in two functions: one is dedicated to the
KSZ8 family, the other is dedicated to the KSZ9477 family.
Remove the drive strength configuration from the lan937x_setup since
the LAN937x family doesn't support it.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 127 ++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz9477.c | 54 +++++++++-
drivers/net/dsa/microchip/ksz_common.c | 171 ++-----------------------------
drivers/net/dsa/microchip/ksz_common.h | 32 +++++-
drivers/net/dsa/microchip/lan937x_main.c | 4 -
5 files changed, 218 insertions(+), 170 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 472cc62ea747..c4c769028a20 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -36,6 +36,15 @@
#include "ksz8_reg.h"
#include "ksz8.h"
+/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, ..
+ * variants.
+ * This values are documented in KSZ8873 and KSZ8863 datasheets.
+ */
+static const struct ksz_drive_strength ksz88x3_drive_strengths[] = {
+ { 0, 8000 },
+ { KSZ8873_DRIVE_STRENGTH_16MA, 16000 },
+};
+
struct ksz88xx_stats_raw {
u64 rx;
u64 rx_hi;
@@ -2291,6 +2300,122 @@ static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port)
spin_unlock(&mib->stats64_lock);
}
+/**
+ * ksz88x3_drive_strength_write() - Set the drive strength configuration for
+ * KSZ8863 compatible chip variants.
+ * @dev: ksz device
+ * @props: Array of drive strength properties to be set
+ * @num_props: Number of properties in the array
+ *
+ * This function applies the specified drive strength settings to KSZ88X3 chip
+ * variants (KSZ8873, KSZ8863).
+ * It ensures the configurations align with what the chip variant supports and
+ * warns or errors out on unsupported settings.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int ksz88x3_drive_strength_write(struct ksz_device *dev,
+ struct ksz_driver_strength_prop *props,
+ int num_props)
+{
+ size_t array_size = ARRAY_SIZE(ksz88x3_drive_strengths);
+ int microamp;
+ int i, ret;
+
+ for (i = 0; i < num_props; i++) {
+ if (props[i].value == -1 || i == KSZ_DRIVER_STRENGTH_IO)
+ continue;
+
+ dev_warn(dev->dev, "%s is not supported by this chip variant\n",
+ props[i].name);
+ }
+
+ microamp = props[KSZ_DRIVER_STRENGTH_IO].value;
+ ret = ksz_drive_strength_to_reg(ksz88x3_drive_strengths, array_size,
+ microamp);
+ if (ret < 0) {
+ ksz_drive_strength_error(dev, ksz88x3_drive_strengths,
+ array_size, microamp);
+ return ret;
+ }
+
+ return ksz_rmw8(dev, KSZ8873_REG_GLOBAL_CTRL_12,
+ KSZ8873_DRIVE_STRENGTH_16MA, ret);
+}
+
+/**
+ * ksz8_parse_drive_strength() - Extract and apply drive strength configurations
+ * from device tree properties.
+ * @dev: ksz device
+ *
+ * This function reads the specified drive strength properties from the
+ * device tree, validates against the supported chip variants, and sets
+ * them accordingly. An error should be critical here, as the drive strength
+ * settings are crucial for EMI compliance.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int ksz8_parse_drive_strength(struct ksz_device *dev)
+{
+ struct ksz_driver_strength_prop of_props[] = {
+ [KSZ_DRIVER_STRENGTH_HI] = {
+ .name = "microchip,hi-drive-strength-microamp",
+ .offset = SW_HI_SPEED_DRIVE_STRENGTH_S,
+ .value = -1,
+ },
+ [KSZ_DRIVER_STRENGTH_LO] = {
+ .name = "microchip,lo-drive-strength-microamp",
+ .offset = SW_LO_SPEED_DRIVE_STRENGTH_S,
+ .value = -1,
+ },
+ [KSZ_DRIVER_STRENGTH_IO] = {
+ .name = "microchip,io-drive-strength-microamp",
+ .offset = 0, /* don't care */
+ .value = -1,
+ },
+ };
+ struct device_node *np = dev->dev->of_node;
+ bool have_any_prop = false;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(of_props); i++) {
+ ret = of_property_read_u32(np, of_props[i].name,
+ &of_props[i].value);
+ if (ret && ret != -EINVAL)
+ dev_warn(dev->dev, "Failed to read %s\n",
+ of_props[i].name);
+ if (ret)
+ continue;
+
+ have_any_prop = true;
+ }
+
+ if (!have_any_prop)
+ return 0;
+
+ switch (dev->chip_id) {
+ case KSZ88X3_CHIP_ID:
+ return ksz88x3_drive_strength_write(dev, of_props,
+ ARRAY_SIZE(of_props));
+ case KSZ8795_CHIP_ID:
+ case KSZ8794_CHIP_ID:
+ case KSZ8765_CHIP_ID:
+ return ksz_drive_strength_write(dev, of_props,
+ ARRAY_SIZE(of_props));
+ default:
+ /* KSZ8864, KSZ8895 */
+ for (i = 0; i < ARRAY_SIZE(of_props); i++) {
+ if (of_props[i].value == -1)
+ continue;
+
+ dev_warn(dev->dev, "%s is not supported by this chip variant\n",
+ of_props[i].name);
+ }
+ }
+
+ return 0;
+}
+
static int ksz8_setup(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -2313,7 +2438,7 @@ static int ksz8_setup(struct dsa_switch *ds)
return ret;
}
- ret = ksz_parse_drive_strength(dev);
+ ret = ksz8_parse_drive_strength(dev);
if (ret)
return ret;
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 691b9b18c707..3ee995545c57 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -1615,6 +1615,58 @@ int ksz9477_enable_stp_addr(struct ksz_device *dev)
return 0;
}
+/**
+ * ksz9477_parse_drive_strength() - Extract and apply drive strength
+ * configurations from device tree properties.
+ * @dev: ksz device
+ *
+ * This function reads the specified drive strength properties from the
+ * device tree, validates against the supported chip variants, and sets
+ * them accordingly. An error should be critical here, as the drive strength
+ * settings are crucial for EMI compliance.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int ksz9477_parse_drive_strength(struct ksz_device *dev)
+{
+ struct ksz_driver_strength_prop of_props[] = {
+ [KSZ_DRIVER_STRENGTH_HI] = {
+ .name = "microchip,hi-drive-strength-microamp",
+ .offset = SW_HI_SPEED_DRIVE_STRENGTH_S,
+ .value = -1,
+ },
+ [KSZ_DRIVER_STRENGTH_LO] = {
+ .name = "microchip,lo-drive-strength-microamp",
+ .offset = SW_LO_SPEED_DRIVE_STRENGTH_S,
+ .value = -1,
+ },
+ [KSZ_DRIVER_STRENGTH_IO] = {
+ .name = "microchip,io-drive-strength-microamp",
+ .offset = 0, /* don't care */
+ .value = -1,
+ },
+ };
+ struct device_node *np = dev->dev->of_node;
+ bool have_any_prop = false;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(of_props); i++) {
+ ret = of_property_read_u32(np, of_props[i].name,
+ &of_props[i].value);
+ if (ret && ret != -EINVAL)
+ dev_warn(dev->dev, "Failed to read %s\n",
+ of_props[i].name);
+ if (ret)
+ continue;
+
+ have_any_prop = true;
+ }
+
+ if (!have_any_prop)
+ return 0;
+
+ return ksz_drive_strength_write(dev, of_props, ARRAY_SIZE(of_props));
+}
static int ksz9477_setup(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -1637,7 +1689,7 @@ static int ksz9477_setup(struct dsa_switch *ds)
return ret;
}
- ret = ksz_parse_drive_strength(dev);
+ ret = ksz9477_parse_drive_strength(dev);
if (ret)
return ret;
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 15ed139564cb..67ab6ddb9e53 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -113,28 +113,6 @@ static const struct ksz_mib_names ksz9477_mib_names[] = {
{ 0x83, "tx_discards" },
};
-struct ksz_driver_strength_prop {
- const char *name;
- int offset;
- int value;
-};
-
-enum ksz_driver_strength_type {
- KSZ_DRIVER_STRENGTH_HI,
- KSZ_DRIVER_STRENGTH_LO,
- KSZ_DRIVER_STRENGTH_IO,
-};
-
-/**
- * struct ksz_drive_strength - drive strength mapping
- * @reg_val: register value
- * @microamp: microamp value
- */
-struct ksz_drive_strength {
- u32 reg_val;
- u32 microamp;
-};
-
/* ksz9477_drive_strengths - Drive strength mapping for KSZ9477 variants
*
* This values are not documented in KSZ9477 variants but confirmed by
@@ -170,15 +148,6 @@ static const struct ksz_drive_strength ksz9477_drive_strengths[] = {
{ SW_DRIVE_STRENGTH_28MA, 28000 },
};
-/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, ..
- * variants.
- * This values are documented in KSZ8873 and KSZ8863 datasheets.
- */
-static const struct ksz_drive_strength ksz88x3_drive_strengths[] = {
- { 0, 8000 },
- { KSZ8873_DRIVE_STRENGTH_16MA, 16000 },
-};
-
/**
* ksz_phylink_mac_disable_tx_lpi() - Callback to signal LPI support (Dummy)
* @config: phylink config structure
@@ -3785,8 +3754,8 @@ static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
* Returns: If found, the corresponding register value for that drive strength
* is returned. Otherwise, -EINVAL is returned indicating an invalid value.
*/
-static int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array,
- size_t array_size, int microamp)
+int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array,
+ size_t array_size, int microamp)
{
int i;
@@ -3809,9 +3778,9 @@ static int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array,
* is detected. It lists out all the supported drive strength values for
* reference in the error message.
*/
-static void ksz_drive_strength_error(struct ksz_device *dev,
- const struct ksz_drive_strength *array,
- size_t array_size, int microamp)
+void ksz_drive_strength_error(struct ksz_device *dev,
+ const struct ksz_drive_strength *array,
+ size_t array_size, int microamp)
{
char supported_values[100];
size_t remaining_size;
@@ -3850,9 +3819,9 @@ static void ksz_drive_strength_error(struct ksz_device *dev,
*
* Return: 0 on successful configuration, a negative error code on failure.
*/
-static int ksz_drive_strength_write(struct ksz_device *dev,
- struct ksz_driver_strength_prop *props,
- int num_props)
+int ksz_drive_strength_write(struct ksz_device *dev,
+ struct ksz_driver_strength_prop *props,
+ int num_props)
{
size_t array_size = ARRAY_SIZE(ksz9477_drive_strengths);
int i, ret, reg;
@@ -3889,130 +3858,6 @@ static int ksz_drive_strength_write(struct ksz_device *dev,
return ksz_rmw8(dev, reg, mask, val);
}
-/**
- * ksz88x3_drive_strength_write() - Set the drive strength configuration for
- * KSZ8863 compatible chip variants.
- * @dev: ksz device
- * @props: Array of drive strength properties to be set
- * @num_props: Number of properties in the array
- *
- * This function applies the specified drive strength settings to KSZ88X3 chip
- * variants (KSZ8873, KSZ8863).
- * It ensures the configurations align with what the chip variant supports and
- * warns or errors out on unsupported settings.
- *
- * Return: 0 on success, error code otherwise
- */
-static int ksz88x3_drive_strength_write(struct ksz_device *dev,
- struct ksz_driver_strength_prop *props,
- int num_props)
-{
- size_t array_size = ARRAY_SIZE(ksz88x3_drive_strengths);
- int microamp;
- int i, ret;
-
- for (i = 0; i < num_props; i++) {
- if (props[i].value == -1 || i == KSZ_DRIVER_STRENGTH_IO)
- continue;
-
- dev_warn(dev->dev, "%s is not supported by this chip variant\n",
- props[i].name);
- }
-
- microamp = props[KSZ_DRIVER_STRENGTH_IO].value;
- ret = ksz_drive_strength_to_reg(ksz88x3_drive_strengths, array_size,
- microamp);
- if (ret < 0) {
- ksz_drive_strength_error(dev, ksz88x3_drive_strengths,
- array_size, microamp);
- return ret;
- }
-
- return ksz_rmw8(dev, KSZ8873_REG_GLOBAL_CTRL_12,
- KSZ8873_DRIVE_STRENGTH_16MA, ret);
-}
-
-/**
- * ksz_parse_drive_strength() - Extract and apply drive strength configurations
- * from device tree properties.
- * @dev: ksz device
- *
- * This function reads the specified drive strength properties from the
- * device tree, validates against the supported chip variants, and sets
- * them accordingly. An error should be critical here, as the drive strength
- * settings are crucial for EMI compliance.
- *
- * Return: 0 on success, error code otherwise
- */
-int ksz_parse_drive_strength(struct ksz_device *dev)
-{
- struct ksz_driver_strength_prop of_props[] = {
- [KSZ_DRIVER_STRENGTH_HI] = {
- .name = "microchip,hi-drive-strength-microamp",
- .offset = SW_HI_SPEED_DRIVE_STRENGTH_S,
- .value = -1,
- },
- [KSZ_DRIVER_STRENGTH_LO] = {
- .name = "microchip,lo-drive-strength-microamp",
- .offset = SW_LO_SPEED_DRIVE_STRENGTH_S,
- .value = -1,
- },
- [KSZ_DRIVER_STRENGTH_IO] = {
- .name = "microchip,io-drive-strength-microamp",
- .offset = 0, /* don't care */
- .value = -1,
- },
- };
- struct device_node *np = dev->dev->of_node;
- bool have_any_prop = false;
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(of_props); i++) {
- ret = of_property_read_u32(np, of_props[i].name,
- &of_props[i].value);
- if (ret && ret != -EINVAL)
- dev_warn(dev->dev, "Failed to read %s\n",
- of_props[i].name);
- if (ret)
- continue;
-
- have_any_prop = true;
- }
-
- if (!have_any_prop)
- return 0;
-
- switch (dev->chip_id) {
- case KSZ88X3_CHIP_ID:
- return ksz88x3_drive_strength_write(dev, of_props,
- ARRAY_SIZE(of_props));
- case KSZ8795_CHIP_ID:
- case KSZ8794_CHIP_ID:
- case KSZ8765_CHIP_ID:
- case KSZ8563_CHIP_ID:
- case KSZ8567_CHIP_ID:
- case KSZ9477_CHIP_ID:
- case KSZ9563_CHIP_ID:
- case KSZ9567_CHIP_ID:
- case KSZ9893_CHIP_ID:
- case KSZ9896_CHIP_ID:
- case KSZ9897_CHIP_ID:
- case LAN9646_CHIP_ID:
- return ksz_drive_strength_write(dev, of_props,
- ARRAY_SIZE(of_props));
- default:
- for (i = 0; i < ARRAY_SIZE(of_props); i++) {
- if (of_props[i].value == -1)
- continue;
-
- dev_warn(dev->dev, "%s is not supported by this chip variant\n",
- of_props[i].name);
- }
- }
-
- return 0;
-}
-
static int ksz8463_configure_straps_spi(struct ksz_device *dev)
{
struct pinctrl *pinctrl;
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 029080838237..acaf70e6f393 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -511,7 +511,37 @@ int ksz_mdio_register(struct ksz_device *dev);
int ksz_pirq_setup(struct ksz_device *dev, u8 p);
int ksz_girq_setup(struct ksz_device *dev);
void ksz_irq_free(struct ksz_irq *kirq);
-int ksz_parse_drive_strength(struct ksz_device *dev);
+
+struct ksz_driver_strength_prop {
+ const char *name;
+ int offset;
+ int value;
+};
+
+enum ksz_driver_strength_type {
+ KSZ_DRIVER_STRENGTH_HI,
+ KSZ_DRIVER_STRENGTH_LO,
+ KSZ_DRIVER_STRENGTH_IO,
+};
+
+/**
+ * struct ksz_drive_strength - drive strength mapping
+ * @reg_val: register value
+ * @microamp: microamp value
+ */
+struct ksz_drive_strength {
+ u32 reg_val;
+ u32 microamp;
+};
+
+void ksz_drive_strength_error(struct ksz_device *dev,
+ const struct ksz_drive_strength *array,
+ size_t array_size, int microamp);
+int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array,
+ size_t array_size, int microamp);
+int ksz_drive_strength_write(struct ksz_device *dev,
+ struct ksz_driver_strength_prop *props,
+ int num_props);
/* Common register access functions */
static inline struct regmap *ksz_regmap_8(struct ksz_device *dev)
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index f060fbc4c4f4..86ce3a86705f 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -787,10 +787,6 @@ static int lan937x_setup(struct dsa_switch *ds)
return ret;
}
- ret = ksz_parse_drive_strength(dev);
- if (ret)
- return ret;
-
/* set broadcast storm protection 10% rate */
storm_mask = BROADCAST_STORM_RATE;
storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 09/10] net: dsa: microchip: rename ksz9477_drive_strength_write()
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
ksz9477_drive_strength_write() isn't used for the KSZ9477-family only.
It's also used for the KSZ87xx chip variants. This function name is
misleading.
Rename it ksz_drive_strength_write().
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_common.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 4f6f5526c26b..15ed139564cb 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -3838,8 +3838,8 @@ static void ksz_drive_strength_error(struct ksz_device *dev,
}
/**
- * ksz9477_drive_strength_write() - Set the drive strength for specific KSZ9477
- * chip variants.
+ * ksz_drive_strength_write() - Set the drive strength for specific KSZ9477
+ * and the KSZ87xx chip variants.
* @dev: ksz device
* @props: Array of drive strength properties to be applied
* @num_props: Number of properties in the array
@@ -3850,9 +3850,9 @@ static void ksz_drive_strength_error(struct ksz_device *dev,
*
* Return: 0 on successful configuration, a negative error code on failure.
*/
-static int ksz9477_drive_strength_write(struct ksz_device *dev,
- struct ksz_driver_strength_prop *props,
- int num_props)
+static int ksz_drive_strength_write(struct ksz_device *dev,
+ struct ksz_driver_strength_prop *props,
+ int num_props)
{
size_t array_size = ARRAY_SIZE(ksz9477_drive_strengths);
int i, ret, reg;
@@ -3998,8 +3998,8 @@ int ksz_parse_drive_strength(struct ksz_device *dev)
case KSZ9896_CHIP_ID:
case KSZ9897_CHIP_ID:
case LAN9646_CHIP_ID:
- return ksz9477_drive_strength_write(dev, of_props,
- ARRAY_SIZE(of_props));
+ return ksz_drive_strength_write(dev, of_props,
+ ARRAY_SIZE(of_props));
default:
for (i = 0; i < ARRAY_SIZE(of_props); i++) {
if (of_props[i].value == -1)
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 08/10] net: dsa: microchip: move ksz9477_set_default_prio_queue_mapping() to ksz9477.c
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
ksz9477_set_default_prio_queue_mapping() dictates a KSZ9477-specific
behavior but is defined in the common section of the code.
Move its definition to the KSZ9477-specific area.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz9477.c | 23 +++++++++++++++++++++++
drivers/net/dsa/microchip/ksz9477.h | 1 +
drivers/net/dsa/microchip/ksz_common.c | 22 ----------------------
drivers/net/dsa/microchip/ksz_common.h | 1 -
4 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index a831eb884ce4..691b9b18c707 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -15,6 +15,7 @@
#include <linux/if_hsr.h>
#include <linux/if_vlan.h>
#include <net/dsa.h>
+#include <net/ieee8021q.h>
#include <net/switchdev.h>
#include "ksz9477_reg.h"
@@ -1410,6 +1411,28 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
ksz_pwrite8(dev, port, regs[REG_PORT_PME_CTRL], 0);
}
+int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port)
+{
+ u32 queue_map = 0;
+ int ipm;
+
+ for (ipm = 0; ipm < dev->info->num_ipms; ipm++) {
+ int queue;
+
+ /* Traffic Type (TT) is corresponding to the Internal Priority
+ * Map (IPM) in the switch. Traffic Class (TC) is
+ * corresponding to the queue in the switch.
+ */
+ queue = ieee8021q_tt_to_tc(ipm, dev->info->num_tx_queues);
+ if (queue < 0)
+ return queue;
+
+ queue_map |= queue << (ipm * KSZ9477_PORT_TC_MAP_S);
+ }
+
+ return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map);
+}
+
static int ksz9477_dsa_port_setup(struct dsa_switch *ds, int port)
{
struct ksz_device *dev = ds->priv;
diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h
index 962174a922a0..a84c000000e6 100644
--- a/drivers/net/dsa/microchip/ksz9477.h
+++ b/drivers/net/dsa/microchip/ksz9477.h
@@ -44,6 +44,7 @@ int ksz9477_mdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb, struct dsa_db db);
int ksz9477_enable_stp_addr(struct ksz_device *dev);
void ksz9477_port_queue_split(struct ksz_device *dev, int port);
+int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port);
int ksz9477_port_acl_init(struct ksz_device *dev, int port);
void ksz9477_port_acl_free(struct ksz_device *dev, int port);
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 2846041d97a5..4f6f5526c26b 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2753,28 +2753,6 @@ void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
*/
}
-int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port)
-{
- u32 queue_map = 0;
- int ipm;
-
- for (ipm = 0; ipm < dev->info->num_ipms; ipm++) {
- int queue;
-
- /* Traffic Type (TT) is corresponding to the Internal Priority
- * Map (IPM) in the switch. Traffic Class (TC) is
- * corresponding to the queue in the switch.
- */
- queue = ieee8021q_tt_to_tc(ipm, dev->info->num_tx_queues);
- if (queue < 0)
- return queue;
-
- queue_map |= queue << (ipm * KSZ9477_PORT_TC_MAP_S);
- }
-
- return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map);
-}
-
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
{
struct ksz_device *dev = ds->priv;
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 7aa4a69d06ef..029080838237 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -512,7 +512,6 @@ int ksz_pirq_setup(struct ksz_device *dev, u8 p);
int ksz_girq_setup(struct ksz_device *dev);
void ksz_irq_free(struct ksz_irq *kirq);
int ksz_parse_drive_strength(struct ksz_device *dev);
-int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port);
/* Common register access functions */
static inline struct regmap *ksz_regmap_8(struct ksz_device *dev)
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 07/10] net: dsa: microchip: handle KSZ8-specific tc setup in ksz8.c
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
The setup of QDISC_ETS isn't the same for the KSZ8 switches than for
the other switches. It leads to is_ksz8() branches in the common code.
Move the KSZ8-specific portions into ksz8.c by creating two setup_tc()
functions, one dedicated to the KSZ87xx family that only handles the
TC_SETUP_QDISC_CBS case, one for the rest of the KSZ8 that handles both
TC_SETUP_QDISC_CBS and TC_SETUP_QDISC_ETS cases.
It remains some is_kszXXXX() branches because inside the ksz88xx family,
only the ksz88x3 switches support QDISC_ETS.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 159 ++++++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_common.c | 124 ++-----------------------
drivers/net/dsa/microchip/ksz_common.h | 7 ++
3 files changed, 171 insertions(+), 119 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index ad7c7a6e1d31..472cc62ea747 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -1782,6 +1782,159 @@ static void ksz8_port_mirror_del(struct dsa_switch *ds, int port,
PORT_MIRROR_SNIFFER, false);
}
+static u8 ksz8463_tc_ctrl(int port, int queue)
+{
+ u8 reg;
+
+ reg = 0xC8 + port * 4;
+ reg += ((3 - queue) / 2) * 2;
+ reg++;
+ reg -= (queue & 1);
+ return reg;
+}
+
+/**
+ * ksz88x3_tc_ets_add - Configure ETS (Enhanced Transmission Selection)
+ * for a port on KSZ88x3 switch
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number to configure
+ * @p: Pointer to offload replace parameters describing ETS bands and mapping
+ *
+ * The KSZ88x3 supports two scheduling modes: Strict Priority and
+ * Weighted Fair Queuing (WFQ). Both modes have fixed behavior:
+ * - No configurable queue-to-priority mapping
+ * - No weight adjustment in WFQ mode
+ *
+ * This function configures the switch to use strict priority mode by
+ * clearing the WFQ enable bit for all queues associated with ETS bands.
+ * If strict priority is not explicitly requested, the switch will default
+ * to WFQ mode.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz88x3_tc_ets_add(struct ksz_device *dev, int port,
+ struct tc_ets_qopt_offload_replace_params *p)
+{
+ int ret, band;
+
+ /* Only strict priority mode is supported for now.
+ * WFQ is implicitly enabled when strict mode is disabled.
+ */
+ for (band = 0; band < p->bands; band++) {
+ int queue = ksz_ets_band_to_queue(p, band);
+ u8 reg;
+
+ /* Calculate TXQ Split Control register address for this
+ * port/queue
+ */
+ reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
+ if (ksz_is_ksz8463(dev))
+ reg = ksz8463_tc_ctrl(port, queue);
+
+ /* Clear WFQ enable bit to select strict priority scheduling */
+ ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ksz88x3_tc_ets_del - Reset ETS (Enhanced Transmission Selection) config
+ * for a port on KSZ88x3 switch
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number to reset
+ *
+ * The KSZ88x3 supports only fixed scheduling modes: Strict Priority or
+ * Weighted Fair Queuing (WFQ), with no reconfiguration of weights or
+ * queue mapping. This function resets the port’s scheduling mode to
+ * the default, which is WFQ, by enabling the WFQ bit for all queues.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz88x3_tc_ets_del(struct ksz_device *dev, int port)
+{
+ int ret, queue;
+
+ /* Iterate over all transmit queues for this port */
+ for (queue = 0; queue < dev->info->num_tx_queues; queue++) {
+ u8 reg;
+
+ /* Calculate TXQ Split Control register address for this
+ * port/queue
+ */
+ reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
+ if (ksz_is_ksz8463(dev))
+ reg = ksz8463_tc_ctrl(port, queue);
+
+ /* Set WFQ enable bit to revert back to default scheduling
+ * mode
+ */
+ ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE,
+ KSZ8873_TXQ_WFQ_ENABLE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ksz8_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
+ struct tc_ets_qopt_offload *qopt)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret;
+
+ if (!(ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)))
+ return -EOPNOTSUPP;
+
+ if (qopt->parent != TC_H_ROOT) {
+ dev_err(dev->dev, "Parent should be \"root\"\n");
+ return -EOPNOTSUPP;
+ }
+
+ switch (qopt->command) {
+ case TC_ETS_REPLACE:
+ ret = ksz_tc_ets_validate(dev, port, &qopt->replace_params);
+ if (ret)
+ return ret;
+
+ return ksz88x3_tc_ets_add(dev, port, &qopt->replace_params);
+ case TC_ETS_DESTROY:
+ return ksz88x3_tc_ets_del(dev, port);
+ case TC_ETS_STATS:
+ case TC_ETS_GRAFT:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int ksz87xx_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type, void *type_data)
+{
+ switch (type) {
+ case TC_SETUP_QDISC_CBS:
+ return ksz_setup_tc_cbs(ds, port, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ksz8_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type, void *type_data)
+{
+ switch (type) {
+ case TC_SETUP_QDISC_CBS:
+ return ksz_setup_tc_cbs(ds, port, type_data);
+ case TC_SETUP_QDISC_ETS:
+ return ksz8_tc_setup_qdisc_ets(ds, port, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port)
{
struct ksz_port *p = &dev->ports[port];
@@ -2645,7 +2798,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.port_hwtstamp_set = ksz_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
- .port_setup_tc = ksz_setup_tc,
+ .port_setup_tc = ksz8_setup_tc,
.port_get_default_prio = ksz_port_get_default_prio,
.port_set_default_prio = ksz_port_set_default_prio,
.port_get_dscp_prio = ksz_port_get_dscp_prio,
@@ -2695,7 +2848,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = {
.port_hwtstamp_set = ksz_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
- .port_setup_tc = ksz_setup_tc,
+ .port_setup_tc = ksz87xx_setup_tc,
.port_get_default_prio = ksz_port_get_default_prio,
.port_set_default_prio = ksz_port_set_default_prio,
.port_get_dscp_prio = ksz_port_get_dscp_prio,
@@ -2748,7 +2901,7 @@ const struct dsa_switch_ops ksz88xx_switch_ops = {
.port_hwtstamp_set = ksz_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
- .port_setup_tc = ksz_setup_tc,
+ .port_setup_tc = ksz8_setup_tc,
.port_get_default_prio = ksz_port_get_default_prio,
.port_set_default_prio = ksz_port_set_default_prio,
.port_get_dscp_prio = ksz_port_get_dscp_prio,
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index b54aea92b67d..2846041d97a5 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -3097,8 +3097,8 @@ static int ksz_setup_tc_mode(struct ksz_device *dev, int port, u8 scheduler,
FIELD_PREP(MTI_SHAPING_M, shaper));
}
-static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port,
- struct tc_cbs_qopt_offload *qopt)
+int ksz_setup_tc_cbs(struct dsa_switch *ds, int port,
+ struct tc_cbs_qopt_offload *qopt)
{
struct ksz_device *dev = ds->priv;
int ret;
@@ -3163,8 +3163,8 @@ static int ksz_disable_egress_rate_limit(struct ksz_device *dev, int port)
return 0;
}
-static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p,
- int band)
+int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p,
+ int band)
{
/* Compared to queues, bands prioritize packets differently. In strict
* priority mode, the lowest priority is assigned to Queue 0 while the
@@ -3173,104 +3173,6 @@ static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p,
return p->bands - 1 - band;
}
-static u8 ksz8463_tc_ctrl(int port, int queue)
-{
- u8 reg;
-
- reg = 0xC8 + port * 4;
- reg += ((3 - queue) / 2) * 2;
- reg++;
- reg -= (queue & 1);
- return reg;
-}
-
-/**
- * ksz88x3_tc_ets_add - Configure ETS (Enhanced Transmission Selection)
- * for a port on KSZ88x3 switch
- * @dev: Pointer to the KSZ switch device structure
- * @port: Port number to configure
- * @p: Pointer to offload replace parameters describing ETS bands and mapping
- *
- * The KSZ88x3 supports two scheduling modes: Strict Priority and
- * Weighted Fair Queuing (WFQ). Both modes have fixed behavior:
- * - No configurable queue-to-priority mapping
- * - No weight adjustment in WFQ mode
- *
- * This function configures the switch to use strict priority mode by
- * clearing the WFQ enable bit for all queues associated with ETS bands.
- * If strict priority is not explicitly requested, the switch will default
- * to WFQ mode.
- *
- * Return: 0 on success, or a negative error code on failure
- */
-static int ksz88x3_tc_ets_add(struct ksz_device *dev, int port,
- struct tc_ets_qopt_offload_replace_params *p)
-{
- int ret, band;
-
- /* Only strict priority mode is supported for now.
- * WFQ is implicitly enabled when strict mode is disabled.
- */
- for (band = 0; band < p->bands; band++) {
- int queue = ksz_ets_band_to_queue(p, band);
- u8 reg;
-
- /* Calculate TXQ Split Control register address for this
- * port/queue
- */
- reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
- if (ksz_is_ksz8463(dev))
- reg = ksz8463_tc_ctrl(port, queue);
-
- /* Clear WFQ enable bit to select strict priority scheduling */
- ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, 0);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * ksz88x3_tc_ets_del - Reset ETS (Enhanced Transmission Selection) config
- * for a port on KSZ88x3 switch
- * @dev: Pointer to the KSZ switch device structure
- * @port: Port number to reset
- *
- * The KSZ88x3 supports only fixed scheduling modes: Strict Priority or
- * Weighted Fair Queuing (WFQ), with no reconfiguration of weights or
- * queue mapping. This function resets the port’s scheduling mode to
- * the default, which is WFQ, by enabling the WFQ bit for all queues.
- *
- * Return: 0 on success, or a negative error code on failure
- */
-static int ksz88x3_tc_ets_del(struct ksz_device *dev, int port)
-{
- int ret, queue;
-
- /* Iterate over all transmit queues for this port */
- for (queue = 0; queue < dev->info->num_tx_queues; queue++) {
- u8 reg;
-
- /* Calculate TXQ Split Control register address for this
- * port/queue
- */
- reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
- if (ksz_is_ksz8463(dev))
- reg = ksz8463_tc_ctrl(port, queue);
-
- /* Set WFQ enable bit to revert back to default scheduling
- * mode
- */
- ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE,
- KSZ8873_TXQ_WFQ_ENABLE);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
static int ksz_queue_set_strict(struct ksz_device *dev, int port, int queue)
{
int ret;
@@ -3363,8 +3265,8 @@ static int ksz_tc_ets_del(struct ksz_device *dev, int port)
return ksz9477_set_default_prio_queue_mapping(dev, port);
}
-static int ksz_tc_ets_validate(struct ksz_device *dev, int port,
- struct tc_ets_qopt_offload_replace_params *p)
+int ksz_tc_ets_validate(struct ksz_device *dev, int port,
+ struct tc_ets_qopt_offload_replace_params *p)
{
int band;
@@ -3405,9 +3307,6 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
struct ksz_device *dev = ds->priv;
int ret;
- if (is_ksz8(dev) && !(ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)))
- return -EOPNOTSUPP;
-
if (qopt->parent != TC_H_ROOT) {
dev_err(dev->dev, "Parent should be \"root\"\n");
return -EOPNOTSUPP;
@@ -3419,16 +3318,9 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
if (ret)
return ret;
- if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
- return ksz88x3_tc_ets_add(dev, port,
- &qopt->replace_params);
- else
- return ksz_tc_ets_add(dev, port, &qopt->replace_params);
+ return ksz_tc_ets_add(dev, port, &qopt->replace_params);
case TC_ETS_DESTROY:
- if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
- return ksz88x3_tc_ets_del(dev, port);
- else
- return ksz_tc_ets_del(dev, port);
+ return ksz_tc_ets_del(dev, port);
case TC_ETS_STATS:
case TC_ETS_GRAFT:
return -EOPNOTSUPP;
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 245329b6e0e9..7aa4a69d06ef 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -14,6 +14,7 @@
#include <linux/phy.h>
#include <linux/regmap.h>
#include <net/dsa.h>
+#include <net/pkt_cls.h>
#include <linux/irq.h>
#include <linux/platform_data/microchip-ksz.h>
@@ -481,6 +482,12 @@ void ksz_phylink_mac_link_down(struct phylink_config *config,
int ksz_set_mac_eee(struct dsa_switch *ds, int port,
struct ethtool_keee *e);
+int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p,
+ int band);
+int ksz_setup_tc_cbs(struct dsa_switch *ds, int port,
+ struct tc_cbs_qopt_offload *qopt);
+int ksz_tc_ets_validate(struct ksz_device *dev, int port,
+ struct tc_ets_qopt_offload_replace_params *p);
int ksz_setup_tc(struct dsa_switch *ds, int port,
enum tc_setup_type type, void *type_data);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 06/10] net: dsa: microchip: move KSZ9477 errata handling to ksz9477.c
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
The KSZ9477 PHY errata is handled from the common ksz_r_mib_stat64().
This errata clearly belongs to the KSZ9477 family so it should be handled
from the ksz9477-specific portion of the driver.
Create a ksz9477-specific r_mib_stat64() implementation that handles
this errata.
Remove the errata handling from the common ksz_r_mib_stat64().
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz9477.c | 24 +++++++++++++++---
drivers/net/dsa/microchip/ksz9477.h | 2 --
drivers/net/dsa/microchip/ksz_common.c | 46 ----------------------------------
drivers/net/dsa/microchip/ksz_common.h | 39 ++++++++++++++++++++++++++++
4 files changed, 60 insertions(+), 51 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index ad6748905e08..a831eb884ce4 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -483,8 +483,8 @@ static int ksz9477_half_duplex_monitor(struct ksz_device *dev, int port,
return ret;
}
-int ksz9477_errata_monitor(struct ksz_device *dev, int port,
- u64 tx_late_col)
+static int ksz9477_errata_monitor(struct ksz_device *dev, int port,
+ u64 tx_late_col)
{
u8 status;
int ret;
@@ -502,6 +502,24 @@ int ksz9477_errata_monitor(struct ksz_device *dev, int port,
return ret;
}
+static void ksz9477_r_mib_stats64(struct ksz_device *dev, int port)
+{
+ struct ksz_stats_raw *raw;
+ struct ksz_port_mib *mib;
+ int ret;
+
+ ksz_r_mib_stats64(dev, port);
+
+ if (dev->info->phy_errata_9477 && !ksz_is_sgmii_port(dev, port)) {
+ mib = &dev->ports[port].mib;
+ raw = (struct ksz_stats_raw *)mib->counters;
+
+ ret = ksz9477_errata_monitor(dev, port, raw->tx_late_col);
+ if (ret)
+ dev_err(dev->dev, "Failed to monitor transmission halt\n");
+ }
+};
+
void ksz9477_port_init_cnt(struct ksz_device *dev, int port)
{
struct ksz_port_mib *mib = &dev->ports[port].mib;
@@ -2109,7 +2127,7 @@ const struct ksz_dev_ops ksz9477_dev_ops = {
.cfg_port_member = ksz9477_cfg_port_member,
.r_mib_cnt = ksz9477_r_mib_cnt,
.r_mib_pkt = ksz9477_r_mib_pkt,
- .r_mib_stat64 = ksz_r_mib_stats64,
+ .r_mib_stat64 = ksz9477_r_mib_stats64,
.freeze_mib = ksz9477_freeze_mib,
.port_init_cnt = ksz9477_port_init_cnt,
.pme_write8 = ksz_write8,
diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h
index 25b74d0af6c5..962174a922a0 100644
--- a/drivers/net/dsa/microchip/ksz9477.h
+++ b/drivers/net/dsa/microchip/ksz9477.h
@@ -32,8 +32,6 @@ int ksz9477_port_mirror_add(struct dsa_switch *ds, int port,
bool ingress, struct netlink_ext_ack *extack);
void ksz9477_port_mirror_del(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror);
-int ksz9477_errata_monitor(struct ksz_device *dev, int port,
- u64 tx_late_col);
int ksz9477_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data);
int ksz9477_fdb_add(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 92fcbd9605c7..b54aea92b67d 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -37,45 +37,6 @@
#define MIB_COUNTER_NUM 0x20
-struct ksz_stats_raw {
- u64 rx_hi;
- u64 rx_undersize;
- u64 rx_fragments;
- u64 rx_oversize;
- u64 rx_jabbers;
- u64 rx_symbol_err;
- u64 rx_crc_err;
- u64 rx_align_err;
- u64 rx_mac_ctrl;
- u64 rx_pause;
- u64 rx_bcast;
- u64 rx_mcast;
- u64 rx_ucast;
- u64 rx_64_or_less;
- u64 rx_65_127;
- u64 rx_128_255;
- u64 rx_256_511;
- u64 rx_512_1023;
- u64 rx_1024_1522;
- u64 rx_1523_2000;
- u64 rx_2001;
- u64 tx_hi;
- u64 tx_late_col;
- u64 tx_pause;
- u64 tx_bcast;
- u64 tx_mcast;
- u64 tx_ucast;
- u64 tx_deferred;
- u64 tx_total_col;
- u64 tx_exc_col;
- u64 tx_single_col;
- u64 tx_mult_col;
- u64 rx_total;
- u64 tx_total;
- u64 rx_discards;
- u64 tx_discards;
-};
-
static const struct ksz_mib_names ksz88xx_mib_names[] = {
{ 0x00, "rx" },
{ 0x01, "rx_hi" },
@@ -1976,7 +1937,6 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port)
struct rtnl_link_stats64 *stats;
struct ksz_stats_raw *raw;
struct ksz_port_mib *mib;
- int ret;
mib = &dev->ports[port].mib;
stats = &mib->stats64;
@@ -2018,12 +1978,6 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port)
pstats->rx_pause_frames = raw->rx_pause;
spin_unlock(&mib->stats64_lock);
-
- if (dev->info->phy_errata_9477 && !ksz_is_sgmii_port(dev, port)) {
- ret = ksz9477_errata_monitor(dev, port, raw->tx_late_col);
- if (ret)
- dev_err(dev->dev, "Failed to monitor transmission halt\n");
- }
}
void ksz_get_stats64(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 2c7716d8cf28..245329b6e0e9 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -40,6 +40,45 @@ struct vlan_table {
u32 table[3];
};
+struct ksz_stats_raw {
+ u64 rx_hi;
+ u64 rx_undersize;
+ u64 rx_fragments;
+ u64 rx_oversize;
+ u64 rx_jabbers;
+ u64 rx_symbol_err;
+ u64 rx_crc_err;
+ u64 rx_align_err;
+ u64 rx_mac_ctrl;
+ u64 rx_pause;
+ u64 rx_bcast;
+ u64 rx_mcast;
+ u64 rx_ucast;
+ u64 rx_64_or_less;
+ u64 rx_65_127;
+ u64 rx_128_255;
+ u64 rx_256_511;
+ u64 rx_512_1023;
+ u64 rx_1024_1522;
+ u64 rx_1523_2000;
+ u64 rx_2001;
+ u64 tx_hi;
+ u64 tx_late_col;
+ u64 tx_pause;
+ u64 tx_bcast;
+ u64 tx_mcast;
+ u64 tx_ucast;
+ u64 tx_deferred;
+ u64 tx_total_col;
+ u64 tx_exc_col;
+ u64 tx_single_col;
+ u64 tx_mult_col;
+ u64 rx_total;
+ u64 tx_total;
+ u64 rx_discards;
+ u64 tx_discards;
+};
+
struct ksz_port_mib {
struct mutex cnt_mutex; /* structure access */
u8 cnt_ptr;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 05/10] net: dsa: microchip: move ksz_get_gbit() and ksz_get_xmii() to ksz9477.c
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
ksz_get_gbit() and ksz_get_xmii() are defined in the ksz_common while
they are only used by the ksz9477 driver.
Move their definition into ksz9477.c
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz9477.c | 56 ++++++++++++++++++++++++++++++++--
drivers/net/dsa/microchip/ksz_common.c | 51 -------------------------------
drivers/net/dsa/microchip/ksz_common.h | 2 --
3 files changed, 54 insertions(+), 55 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 0b08d2175f17..ad6748905e08 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -1181,6 +1181,58 @@ void ksz9477_port_mirror_del(struct dsa_switch *ds, int port,
PORT_MIRROR_SNIFFER, false);
}
+static bool ksz9477_get_gbit(struct ksz_device *dev, int port)
+{
+ const u8 *bitval = dev->info->xmii_ctrl1;
+ const u16 *regs = dev->info->regs;
+ bool gbit = false;
+ u8 data8;
+ bool val;
+
+ ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8);
+
+ val = FIELD_GET(P_GMII_1GBIT_M, data8);
+
+ if (val == bitval[P_GMII_1GBIT])
+ gbit = true;
+
+ return gbit;
+}
+
+static phy_interface_t ksz9477_get_xmii(struct ksz_device *dev, int port,
+ bool gbit)
+{
+ const u8 *bitval = dev->info->xmii_ctrl1;
+ const u16 *regs = dev->info->regs;
+ phy_interface_t interface;
+ u8 data8;
+ u8 val;
+
+ ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8);
+
+ val = FIELD_GET(P_MII_SEL_M, data8);
+
+ if (val == bitval[P_MII_SEL]) {
+ if (gbit)
+ interface = PHY_INTERFACE_MODE_GMII;
+ else
+ interface = PHY_INTERFACE_MODE_MII;
+ } else if (val == bitval[P_RMII_SEL]) {
+ interface = PHY_INTERFACE_MODE_RMII;
+ } else {
+ interface = PHY_INTERFACE_MODE_RGMII;
+ if (data8 & P_RGMII_ID_EG_ENABLE)
+ interface = PHY_INTERFACE_MODE_RGMII_TXID;
+ if (data8 & P_RGMII_ID_IG_ENABLE) {
+ interface = PHY_INTERFACE_MODE_RGMII_RXID;
+ if (data8 & P_RGMII_ID_EG_ENABLE)
+ interface = PHY_INTERFACE_MODE_RGMII_ID;
+ }
+ }
+
+ return interface;
+}
+
static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port)
{
phy_interface_t interface;
@@ -1189,9 +1241,9 @@ static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port)
if (dev->info->internal_phy[port])
return PHY_INTERFACE_MODE_NA;
- gbit = ksz_get_gbit(dev, port);
+ gbit = ksz9477_get_gbit(dev, port);
- interface = ksz_get_xmii(dev, port, gbit);
+ interface = ksz9477_get_xmii(dev, port, gbit);
return interface;
}
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 56dd4c27b182..92fcbd9605c7 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2966,39 +2966,6 @@ void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface)
ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8);
}
-phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit)
-{
- const u8 *bitval = dev->info->xmii_ctrl1;
- const u16 *regs = dev->info->regs;
- phy_interface_t interface;
- u8 data8;
- u8 val;
-
- ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8);
-
- val = FIELD_GET(P_MII_SEL_M, data8);
-
- if (val == bitval[P_MII_SEL]) {
- if (gbit)
- interface = PHY_INTERFACE_MODE_GMII;
- else
- interface = PHY_INTERFACE_MODE_MII;
- } else if (val == bitval[P_RMII_SEL]) {
- interface = PHY_INTERFACE_MODE_RMII;
- } else {
- interface = PHY_INTERFACE_MODE_RGMII;
- if (data8 & P_RGMII_ID_EG_ENABLE)
- interface = PHY_INTERFACE_MODE_RGMII_TXID;
- if (data8 & P_RGMII_ID_IG_ENABLE) {
- interface = PHY_INTERFACE_MODE_RGMII_RXID;
- if (data8 & P_RGMII_ID_EG_ENABLE)
- interface = PHY_INTERFACE_MODE_RGMII_ID;
- }
- }
-
- return interface;
-}
-
bool ksz_phylink_need_config(struct phylink_config *config,
unsigned int mode)
{
@@ -3034,24 +3001,6 @@ void ksz_phylink_mac_config(struct phylink_config *config,
ksz_set_xmii(dev, port, state->interface);
}
-bool ksz_get_gbit(struct ksz_device *dev, int port)
-{
- const u8 *bitval = dev->info->xmii_ctrl1;
- const u16 *regs = dev->info->regs;
- bool gbit = false;
- u8 data8;
- bool val;
-
- ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8);
-
- val = FIELD_GET(P_GMII_1GBIT_M, data8);
-
- if (val == bitval[P_GMII_1GBIT])
- gbit = true;
-
- return gbit;
-}
-
static int ksz_switch_detect(struct ksz_device *dev)
{
u8 id1, id2, id4;
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 347787ef1f00..2c7716d8cf28 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -395,8 +395,6 @@ void ksz_teardown(struct dsa_switch *ds);
void ksz_init_mib_timer(struct ksz_device *dev);
void ksz_r_mib_stats64(struct ksz_device *dev, int port);
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
-bool ksz_get_gbit(struct ksz_device *dev, int port);
-phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit);
extern const struct ksz_chip_data ksz_switch_chips[];
int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 04/10] net: dsa: microchip: move ksz88xx stats handling in ksz8.c
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
ksz88xx_r_mib_stats64() is defined in ksz_common while it's clearly
ksz88xx-specific.
Move its definition in ksz8.c
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 86 ++++++++++++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_common.c | 86 ----------------------------------
drivers/net/dsa/microchip/ksz_common.h | 1 -
3 files changed, 86 insertions(+), 87 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 2b4720c4c87e..ad7c7a6e1d31 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -36,6 +36,43 @@
#include "ksz8_reg.h"
#include "ksz8.h"
+struct ksz88xx_stats_raw {
+ u64 rx;
+ u64 rx_hi;
+ u64 rx_undersize;
+ u64 rx_fragments;
+ u64 rx_oversize;
+ u64 rx_jabbers;
+ u64 rx_symbol_err;
+ u64 rx_crc_err;
+ u64 rx_align_err;
+ u64 rx_mac_ctrl;
+ u64 rx_pause;
+ u64 rx_bcast;
+ u64 rx_mcast;
+ u64 rx_ucast;
+ u64 rx_64_or_less;
+ u64 rx_65_127;
+ u64 rx_128_255;
+ u64 rx_256_511;
+ u64 rx_512_1023;
+ u64 rx_1024_1522;
+ u64 tx;
+ u64 tx_hi;
+ u64 tx_late_col;
+ u64 tx_pause;
+ u64 tx_bcast;
+ u64 tx_mcast;
+ u64 tx_ucast;
+ u64 tx_deferred;
+ u64 tx_total_col;
+ u64 tx_exc_col;
+ u64 tx_single_col;
+ u64 tx_mult_col;
+ u64 rx_discards;
+ u64 tx_discards;
+};
+
static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
{
ksz_rmw8(dev, addr, bits, set ? bits : 0);
@@ -2052,6 +2089,55 @@ static int ksz8_enable_stp_addr(struct ksz_device *dev)
return ksz8_w_sta_mac_table(dev, 0, &alu);
}
+static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port)
+{
+ struct ethtool_pause_stats *pstats;
+ struct rtnl_link_stats64 *stats;
+ struct ksz88xx_stats_raw *raw;
+ struct ksz_port_mib *mib;
+
+ mib = &dev->ports[port].mib;
+ stats = &mib->stats64;
+ pstats = &mib->pause_stats;
+ raw = (struct ksz88xx_stats_raw *)mib->counters;
+
+ spin_lock(&mib->stats64_lock);
+
+ stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast +
+ raw->rx_pause;
+ stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast +
+ raw->tx_pause;
+
+ /* HW counters are counting bytes + FCS which is not acceptable
+ * for rtnl_link_stats64 interface
+ */
+ stats->rx_bytes = raw->rx + raw->rx_hi - stats->rx_packets * ETH_FCS_LEN;
+ stats->tx_bytes = raw->tx + raw->tx_hi - stats->tx_packets * ETH_FCS_LEN;
+
+ stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments +
+ raw->rx_oversize;
+
+ stats->rx_crc_errors = raw->rx_crc_err;
+ stats->rx_frame_errors = raw->rx_align_err;
+ stats->rx_dropped = raw->rx_discards;
+ stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
+ stats->rx_frame_errors + stats->rx_dropped;
+
+ stats->tx_window_errors = raw->tx_late_col;
+ stats->tx_fifo_errors = raw->tx_discards;
+ stats->tx_aborted_errors = raw->tx_exc_col;
+ stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors +
+ stats->tx_aborted_errors;
+
+ stats->multicast = raw->rx_mcast;
+ stats->collisions = raw->tx_total_col;
+
+ pstats->tx_pause_frames = raw->tx_pause;
+ pstats->rx_pause_frames = raw->rx_pause;
+
+ spin_unlock(&mib->stats64_lock);
+}
+
static int ksz8_setup(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index be7738ae1f2a..56dd4c27b182 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -76,43 +76,6 @@ struct ksz_stats_raw {
u64 tx_discards;
};
-struct ksz88xx_stats_raw {
- u64 rx;
- u64 rx_hi;
- u64 rx_undersize;
- u64 rx_fragments;
- u64 rx_oversize;
- u64 rx_jabbers;
- u64 rx_symbol_err;
- u64 rx_crc_err;
- u64 rx_align_err;
- u64 rx_mac_ctrl;
- u64 rx_pause;
- u64 rx_bcast;
- u64 rx_mcast;
- u64 rx_ucast;
- u64 rx_64_or_less;
- u64 rx_65_127;
- u64 rx_128_255;
- u64 rx_256_511;
- u64 rx_512_1023;
- u64 rx_1024_1522;
- u64 tx;
- u64 tx_hi;
- u64 tx_late_col;
- u64 tx_pause;
- u64 tx_bcast;
- u64 tx_mcast;
- u64 tx_ucast;
- u64 tx_deferred;
- u64 tx_total_col;
- u64 tx_exc_col;
- u64 tx_single_col;
- u64 tx_mult_col;
- u64 rx_discards;
- u64 tx_discards;
-};
-
static const struct ksz_mib_names ksz88xx_mib_names[] = {
{ 0x00, "rx" },
{ 0x01, "rx_hi" },
@@ -2063,55 +2026,6 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port)
}
}
-void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port)
-{
- struct ethtool_pause_stats *pstats;
- struct rtnl_link_stats64 *stats;
- struct ksz88xx_stats_raw *raw;
- struct ksz_port_mib *mib;
-
- mib = &dev->ports[port].mib;
- stats = &mib->stats64;
- pstats = &mib->pause_stats;
- raw = (struct ksz88xx_stats_raw *)mib->counters;
-
- spin_lock(&mib->stats64_lock);
-
- stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast +
- raw->rx_pause;
- stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast +
- raw->tx_pause;
-
- /* HW counters are counting bytes + FCS which is not acceptable
- * for rtnl_link_stats64 interface
- */
- stats->rx_bytes = raw->rx + raw->rx_hi - stats->rx_packets * ETH_FCS_LEN;
- stats->tx_bytes = raw->tx + raw->tx_hi - stats->tx_packets * ETH_FCS_LEN;
-
- stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments +
- raw->rx_oversize;
-
- stats->rx_crc_errors = raw->rx_crc_err;
- stats->rx_frame_errors = raw->rx_align_err;
- stats->rx_dropped = raw->rx_discards;
- stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
- stats->rx_frame_errors + stats->rx_dropped;
-
- stats->tx_window_errors = raw->tx_late_col;
- stats->tx_fifo_errors = raw->tx_discards;
- stats->tx_aborted_errors = raw->tx_exc_col;
- stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors +
- stats->tx_aborted_errors;
-
- stats->multicast = raw->rx_mcast;
- stats->collisions = raw->tx_total_col;
-
- pstats->tx_pause_frames = raw->tx_pause;
- pstats->rx_pause_frames = raw->rx_pause;
-
- spin_unlock(&mib->stats64_lock);
-}
-
void ksz_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s)
{
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index f367d6f96fa2..347787ef1f00 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -394,7 +394,6 @@ void ksz_teardown(struct dsa_switch *ds);
void ksz_init_mib_timer(struct ksz_device *dev);
void ksz_r_mib_stats64(struct ksz_device *dev, int port);
-void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port);
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
bool ksz_get_gbit(struct ksz_device *dev, int port);
phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 03/10] net: dsa: microchip: make ksz_is_port_mac_global_usable() static
From: Bastien Curutchet (Schneider Electric) @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
ksz_is_port_mac_global_usable() is exposed in ksz_common.h while it's
only used internally.
Make ksz_is_port_mac_global_usable() static.
Move its definition above its first call.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_common.c | 56 +++++++++++++++++-----------------
drivers/net/dsa/microchip/ksz_common.h | 1 -
2 files changed, 28 insertions(+), 29 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 686bdbfe8b6a..be7738ae1f2a 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -3670,6 +3670,34 @@ int ksz_handle_wake_reason(struct ksz_device *dev, int port)
pme_status);
}
+/**
+ * ksz_is_port_mac_global_usable - Check if the MAC address on a given port
+ * can be used as a global address.
+ * @ds: Pointer to the DSA switch structure.
+ * @port: The port number on which the MAC address is to be checked.
+ *
+ * This function examines the MAC address set on the specified port and
+ * determines if it can be used as a global address for the switch.
+ *
+ * Return: true if the port's MAC address can be used as a global address, false
+ * otherwise.
+ */
+static bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port)
+{
+ struct net_device *user = dsa_to_port(ds, port)->user;
+ const unsigned char *addr = user->dev_addr;
+ struct ksz_switch_macaddr *switch_macaddr;
+ struct ksz_device *dev = ds->priv;
+
+ ASSERT_RTNL();
+
+ switch_macaddr = dev->switch_macaddr;
+ if (switch_macaddr && !ether_addr_equal(switch_macaddr->addr, addr))
+ return false;
+
+ return true;
+}
+
/**
* ksz_get_wol - Get Wake-on-LAN settings for a specified port.
* @ds: The dsa_switch structure.
@@ -3863,34 +3891,6 @@ int ksz_port_set_mac_address(struct dsa_switch *ds, int port,
return 0;
}
-/**
- * ksz_is_port_mac_global_usable - Check if the MAC address on a given port
- * can be used as a global address.
- * @ds: Pointer to the DSA switch structure.
- * @port: The port number on which the MAC address is to be checked.
- *
- * This function examines the MAC address set on the specified port and
- * determines if it can be used as a global address for the switch.
- *
- * Return: true if the port's MAC address can be used as a global address, false
- * otherwise.
- */
-bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port)
-{
- struct net_device *user = dsa_to_port(ds, port)->user;
- const unsigned char *addr = user->dev_addr;
- struct ksz_switch_macaddr *switch_macaddr;
- struct ksz_device *dev = ds->priv;
-
- ASSERT_RTNL();
-
- switch_macaddr = dev->switch_macaddr;
- if (switch_macaddr && !ether_addr_equal(switch_macaddr->addr, addr))
- return false;
-
- return true;
-}
-
/**
* ksz_switch_macaddr_get - Program the switch's MAC address register.
* @ds: DSA switch instance.
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index f276f2452684..f367d6f96fa2 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -393,7 +393,6 @@ int ksz_switch_resume(struct device *dev);
void ksz_teardown(struct dsa_switch *ds);
void ksz_init_mib_timer(struct ksz_device *dev);
-bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port);
void ksz_r_mib_stats64(struct ksz_device *dev, int port);
void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port);
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 02/10] net: dsa: microchip: split port_max_mtu() implementation
From: Bastien Curutchet @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric),
Vladimir Oltean
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
From: Vladimir Oltean <vladimir.oltean@nxp.com>
ksz_max_mtu() is a bit cluttered. It would be good for developers and
reviewers if they didn't need to look at a common function for hardware
they likely don't have, and which is vastly different, when they are
interested in only a specific chip.
Benefit from the fact that all families listed here have their own
dsa_switch_ops, and provide separate implementations for the
port_max_mtu() method.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 16 ++++++++++++---
drivers/net/dsa/microchip/ksz9477.c | 7 ++++++-
drivers/net/dsa/microchip/ksz9477.h | 1 +
drivers/net/dsa/microchip/ksz_common.c | 34 --------------------------------
drivers/net/dsa/microchip/ksz_common.h | 2 --
drivers/net/dsa/microchip/lan937x_main.c | 2 +-
6 files changed, 21 insertions(+), 41 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c351179f6a60..2b4720c4c87e 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -202,6 +202,16 @@ static int ksz87xx_change_mtu(struct dsa_switch *ds, int port, int mtu)
return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2);
}
+static int ksz87xx_max_mtu(struct dsa_switch *ds, int port)
+{
+ return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
+}
+
+static int ksz88xx_max_mtu(struct dsa_switch *ds, int port)
+{
+ return KSZ8863_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
+}
+
static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
{
u8 mask_4q, mask_2q;
@@ -2541,7 +2551,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz88xx_change_mtu,
- .port_max_mtu = ksz_max_mtu,
+ .port_max_mtu = ksz88xx_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
.get_ts_info = ksz_get_ts_info,
@@ -2591,7 +2601,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = {
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz87xx_change_mtu,
- .port_max_mtu = ksz_max_mtu,
+ .port_max_mtu = ksz87xx_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
.get_ts_info = ksz_get_ts_info,
@@ -2642,7 +2652,7 @@ const struct dsa_switch_ops ksz88xx_switch_ops = {
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz88xx_change_mtu,
- .port_max_mtu = ksz_max_mtu,
+ .port_max_mtu = ksz88xx_max_mtu,
.get_wol = ksz_get_wol,
.set_wol = ksz_set_wol,
.suspend = ksz_suspend,
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index f3f0c98dfb5a..0b08d2175f17 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -60,6 +60,11 @@ static int ksz9477_change_mtu(struct dsa_switch *ds, int port, int mtu)
REG_SW_MTU_MASK, frame_size);
}
+int ksz9477_max_mtu(struct dsa_switch *ds, int port)
+{
+ return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
+}
+
static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev)
{
unsigned int val;
@@ -2098,7 +2103,7 @@ const struct dsa_switch_ops ksz9477_switch_ops = {
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz9477_change_mtu,
- .port_max_mtu = ksz_max_mtu,
+ .port_max_mtu = ksz9477_max_mtu,
.get_wol = ksz_get_wol,
.set_wol = ksz_set_wol,
.suspend = ksz_suspend,
diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h
index 92a1d889224d..25b74d0af6c5 100644
--- a/drivers/net/dsa/microchip/ksz9477.h
+++ b/drivers/net/dsa/microchip/ksz9477.h
@@ -21,6 +21,7 @@ void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze);
void ksz9477_port_init_cnt(struct ksz_device *dev, int port);
int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
bool flag, struct netlink_ext_ack *extack);
+int ksz9477_max_mtu(struct dsa_switch *ds, int port);
int ksz9477_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack);
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index d1726778bb48..686bdbfe8b6a 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2984,40 +2984,6 @@ int ksz_port_bridge_flags(struct dsa_switch *ds, int port,
return 0;
}
-int ksz_max_mtu(struct dsa_switch *ds, int port)
-{
- struct ksz_device *dev = ds->priv;
-
- switch (dev->chip_id) {
- case KSZ8795_CHIP_ID:
- case KSZ8794_CHIP_ID:
- case KSZ8765_CHIP_ID:
- return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
- case KSZ8463_CHIP_ID:
- case KSZ88X3_CHIP_ID:
- case KSZ8864_CHIP_ID:
- case KSZ8895_CHIP_ID:
- return KSZ8863_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
- case KSZ8563_CHIP_ID:
- case KSZ8567_CHIP_ID:
- case KSZ9477_CHIP_ID:
- case KSZ9563_CHIP_ID:
- case KSZ9567_CHIP_ID:
- case KSZ9893_CHIP_ID:
- case KSZ9896_CHIP_ID:
- case KSZ9897_CHIP_ID:
- case LAN9370_CHIP_ID:
- case LAN9371_CHIP_ID:
- case LAN9372_CHIP_ID:
- case LAN9373_CHIP_ID:
- case LAN9374_CHIP_ID:
- case LAN9646_CHIP_ID:
- return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
- }
-
- return -EOPNOTSUPP;
-}
-
int ksz_set_mac_eee(struct dsa_switch *ds, int port,
struct ethtool_keee *e)
{
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index b4a5673ba365..f276f2452684 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -443,8 +443,6 @@ void ksz_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface);
-int ksz_max_mtu(struct dsa_switch *ds, int port);
-
int ksz_set_mac_eee(struct dsa_switch *ds, int port,
struct ethtool_keee *e);
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 8eb5337b0c10..f060fbc4c4f4 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -983,7 +983,7 @@ const struct dsa_switch_ops lan937x_switch_ops = {
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = lan937x_change_mtu,
- .port_max_mtu = ksz_max_mtu,
+ .port_max_mtu = ksz9477_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
.get_ts_info = ksz_get_ts_info,
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 01/10] net: dsa: microchip: split ksz8_change_mtu()
From: Bastien Curutchet @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric),
Vladimir Oltean
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Even among the ksz8 family, there are big differences in the MTU change
procedure between KSZ87xx and KSZ88xx (KSZ8463 is like KSZ88xx here).
Since we have 3 separate dsa_switch_ops for what constitutes "KSZ8", we
can split those procedures into separate functions.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 49 ++++++++++++++++------------------------
1 file changed, 19 insertions(+), 30 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 586916570a84..c351179f6a60 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -158,10 +158,17 @@ static int ksz8_reset_switch(struct ksz_device *dev)
return 0;
}
-static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size)
+static int ksz88xx_change_mtu(struct dsa_switch *ds, int port, int mtu)
{
+ struct ksz_device *dev = ds->priv;
+ int frame_size;
u8 ctrl2 = 0;
+ if (!dsa_is_cpu_port(dev->ds, port))
+ return 0;
+
+ frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+
if (frame_size <= KSZ8_LEGAL_PACKET_SIZE)
ctrl2 |= KSZ8863_LEGAL_PACKET_ENABLE;
else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE)
@@ -171,11 +178,18 @@ static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size)
KSZ8863_HUGE_PACKET_ENABLE, ctrl2);
}
-static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size)
+static int ksz87xx_change_mtu(struct dsa_switch *ds, int port, int mtu)
{
+ struct ksz_device *dev = ds->priv;
u8 ctrl1 = 0, ctrl2 = 0;
+ u16 frame_size;
int ret;
+ if (!dsa_is_cpu_port(dev->ds, port))
+ return 0;
+
+ frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+
if (frame_size > KSZ8_LEGAL_PACKET_SIZE)
ctrl2 |= SW_LEGAL_PACKET_DISABLE;
if (frame_size > KSZ8863_NORMAL_PACKET_SIZE)
@@ -188,31 +202,6 @@ static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size)
return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2);
}
-static int ksz8_change_mtu(struct dsa_switch *ds, int port, int mtu)
-{
- struct ksz_device *dev = ds->priv;
- u16 frame_size;
-
- if (!dsa_is_cpu_port(dev->ds, port))
- return 0;
-
- frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
-
- switch (dev->chip_id) {
- case KSZ8795_CHIP_ID:
- case KSZ8794_CHIP_ID:
- case KSZ8765_CHIP_ID:
- return ksz8795_change_mtu(dev, frame_size);
- case KSZ8463_CHIP_ID:
- case KSZ88X3_CHIP_ID:
- case KSZ8864_CHIP_ID:
- case KSZ8895_CHIP_ID:
- return ksz8863_change_mtu(dev, frame_size);
- }
-
- return -EOPNOTSUPP;
-}
-
static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
{
u8 mask_4q, mask_2q;
@@ -2551,7 +2540,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.port_mirror_del = ksz8_port_mirror_del,
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
- .port_change_mtu = ksz8_change_mtu,
+ .port_change_mtu = ksz88xx_change_mtu,
.port_max_mtu = ksz_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
@@ -2601,7 +2590,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = {
.port_mirror_del = ksz8_port_mirror_del,
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
- .port_change_mtu = ksz8_change_mtu,
+ .port_change_mtu = ksz87xx_change_mtu,
.port_max_mtu = ksz_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
@@ -2652,7 +2641,7 @@ const struct dsa_switch_ops ksz88xx_switch_ops = {
.port_mirror_del = ksz8_port_mirror_del,
.get_stats64 = ksz_get_stats64,
.get_pause_stats = ksz_get_pause_stats,
- .port_change_mtu = ksz8_change_mtu,
+ .port_change_mtu = ksz88xx_change_mtu,
.port_max_mtu = ksz_max_mtu,
.get_wol = ksz_get_wol,
.set_wol = ksz_set_wol,
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 00/10] net: dsa: microchip: move non-common function out of ksz_common.c
From: Bastien Curutchet @ 2026-07-02 9:07 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric),
Vladimir Oltean
Hi all,
This series continues the rework of the KSZ driver initiated by three
previous series (see [1], [2] & [3]).
The KSZ driver handles more than 20 switches split in several families.
This was previously handled through a common set of dsa_switch_ops
operations that used device-specific ksz_dev_ops callbacks. The three
previous series have split this common struct dsa_switch_ops into 5
to connect the ksz_dev_ops's implentations directly to the new
dsa_swicth ops.
This series continues in the same vein and moves out of ksz_common.c all
the functions that aren't truly common to all the families.
On top of this on-going rework I added PTP and periodic output support for
the KSZ8463 (which was my first goal). There are still more than 13 patches
left for all this so this series will be followed by two others and if you
want to see the full picture we can check my github ([4]).
FYI, I only have a KSZ8463 so, unfortunately, I can't test other switches.
The next series is going to add PTP support for the KSZ8463. The final one
will add periodic output support for the KSZ8463.
[1]: https://lore.kernel.org/r/20260505-clean-ksz-driver-v1-0-05d70fa42461@bootlin.com
[2]: https://lore.kernel.org/r/20260521-clean-ksz-2nd-series-v3-0-75c38971c19a@bootlin.com
[3]: https://lore.kernel.org/r/20260608-clean-ksz-3rd-v2-0-6e61b7be23c4@bootlin.com
[4]: https://github.com/bastien-curutchet/linux/tree/ksz_rework
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
Bastien Curutchet (Schneider Electric) (8):
net: dsa: microchip: make ksz_is_port_mac_global_usable() static
net: dsa: microchip: move ksz88xx stats handling in ksz8.c
net: dsa: microchip: move ksz_get_gbit() and ksz_get_xmii() to ksz9477.c
net: dsa: microchip: move KSZ9477 errata handling to ksz9477.c
net: dsa: microchip: handle KSZ8-specific tc setup in ksz8.c
net: dsa: microchip: move ksz9477_set_default_prio_queue_mapping() to ksz9477.c
net: dsa: microchip: rename ksz9477_drive_strength_write()
net: dsa: microchip: move the drive strength config out of the common section
Vladimir Oltean (2):
net: dsa: microchip: split ksz8_change_mtu()
net: dsa: microchip: split port_max_mtu() implementation
drivers/net/dsa/microchip/ksz8.c | 429 ++++++++++++++++++++--
drivers/net/dsa/microchip/ksz9477.c | 164 ++++++++-
drivers/net/dsa/microchip/ksz9477.h | 4 +-
drivers/net/dsa/microchip/ksz_common.c | 594 +++----------------------------
drivers/net/dsa/microchip/ksz_common.h | 85 ++++-
drivers/net/dsa/microchip/lan937x_main.c | 6 +-
6 files changed, 679 insertions(+), 603 deletions(-)
---
base-commit: 1c664ec4b9ea827b609d296921ed5bad8a40a158
change-id: 20260615-clean-ksz-4th-1652867e943e
Best regards,
--
Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
^ permalink raw reply
* Re: [PATCH rdma-next v8] RDMA: Change capability fields in ib_device_attr from int to u32
From: Andy Shevchenko @ 2026-07-02 8:59 UTC (permalink / raw)
To: Erni Sri Satya Vennela
Cc: Jason Gunthorpe, Leon Romanovsky, mkalderon, zyjzyj2000, sagi,
mgurtovoy, haris.iqbal, jinpu.wang, bvanassche, kbusch,
Jens Axboe, Christoph Hellwig, kch, smfrench, linkinjeon, metze,
tom, trondmy, anna, chuck.lever, jlayton, neil, okorniev, Dai.Ngo,
achender, davem, edumazet, kuba, pabeni, horms, kees, markzhang,
ebadger, linux-rdma, linux-kernel, target-devel, linux-nvme,
linux-cifs, samba-technical, linux-nfs, netdev, rds-devel,
Jason Gunthorpe
In-Reply-To: <akX/P/0TiSQ38YdS@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
On Wed, Jul 01, 2026 at 11:03:43PM -0700, Erni Sri Satya Vennela wrote:
> On Fri, Jun 19, 2026 at 01:30:39PM -0700, Erni Sri Satya Vennela wrote:
> > The capability counter fields in struct ib_device_attr are declared
> > as signed int, but these values are inherently non-negative. Drivers
> > maintain their cached caps as u32 and assign them directly into these
> > int fields; if a cap exceeds INT_MAX the implicit narrowing yields a
> > negative value visible to the IB core.
> >
> > Change the signed int capability fields to u32 to match the
> > underlying nature of the data. Also update consumers across the IB
> > core, ULPs, NVMe-oF target, RDS, and NFS/RDMA so the new u32 values
> > are not forced back through signed int or u8 via min()/min_t() or
> > narrowing local variables.
>
> Just a friendly follow-up on this patch. The Sashiko review mentioned a
> low-priority item, and I'd appreciate any guidance on whether the change
> is needed.
As I read this it needs to be fixed, the problem is that there is
a potential of a weird case (not sure they are IRL) when somebody can use
INT_MIN (in representation of signed number) to actually mean 0x80000000
size.
> https://sashiko.dev/#/patchset/20260619203107.606359-1-ernis%40linux.microsoft.com
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH RFC 0/8] clk: sunxi-ng: Add support for Allwinner A733 CCU and PRCM
From: Andre Przywara @ 2026-07-02 8:57 UTC (permalink / raw)
To: Enzo Adriano, Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Brian Masney, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jerome Brunet, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran,
linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-riscv, netdev
In-Reply-To: <20260701160055.320000-a733-ccu-status-enzo.adriano.code@gmail.com>
Hi Enzo,
On 7/1/26 18:07, Enzo Adriano wrote:
> Hi Junhui,
>
> Thanks for the A733 CCU/PRCM RFC v1. I've been reading through the
> series and the review feedback, including the NSI clock/reset handling,
> the binding naming and ordering comments, the SDM macro cleanup, and the
> question around modeled but otherwise-unused clocks such as the GIC clock.
that's great, but please tell the list, by replying to the respective
patches. There are still a few patches left that haven't been checked
against the manual or otherwise reviewed. I will probably do this at
some point in time, but we could use any help here.
You don't need to be an expert for this: just comparing the numbers in
the struct (register offsets and bit positions) against the manual is
already very helpful, and can be done by most people, just with some
endurance ;-)
Thanks,
Andre
> I do not see a v2 on the list yet, so I wanted to check in: are you still
> planning to take this series forward? No rush at all, and I am happy to
> leave it entirely with you if so.
>
> If you have moved on to other things, I would be glad to help carry the
> series forward and address the review comments, keeping your authorship
> and prior work intact. I have A733 hardware here and can help test the
> changes.
>
> Either way, please let me know what works best for you.
>
> Thanks,
> Enzo
^ permalink raw reply
* Re: [PATCH net-next v4] net: mana: Add Interrupt Moderation support
From: Paolo Abeni @ 2026-07-02 8:56 UTC (permalink / raw)
To: Haiyang Zhang, linux-hyperv, netdev, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Konstantin Taranov,
Simon Horman, Erni Sri Satya Vennela, Dipayaan Roy, Aditya Garg,
Breno Leitao, linux-kernel, linux-rdma
Cc: paulros
In-Reply-To: <20260629213652.11682-1-haiyangz@linux.microsoft.com>
On 6/29/26 11:36 PM, Haiyang Zhang wrote:
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index 7438ea6b3f26..9391e9564605 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> @@ -1591,6 +1591,9 @@ int mana_create_wq_obj(struct mana_port_context *apc,
>
> mana_gd_init_req_hdr(&req.hdr, MANA_CREATE_WQ_OBJ,
> sizeof(req), sizeof(resp));
> +
> + req.hdr.req.msg_version = GDMA_MESSAGE_V3;
> + req.hdr.resp.msg_version = GDMA_MESSAGE_V2;
Double checking the above is intentional; it feels strange to me that
request and reply use different versions. Possibly a comment for future
memory would make sense.
> req.vport = vport;
> req.wq_type = wq_type;
> req.wq_gdma_region = wq_spec->gdma_region;
> @@ -1599,6 +1602,9 @@ int mana_create_wq_obj(struct mana_port_context *apc,
> req.cq_size = cq_spec->queue_size;
> req.cq_moderation_ctx_id = cq_spec->modr_ctx_id;
> req.cq_parent_qid = cq_spec->attached_eq;
> + req.req_cq_moderation = cq_spec->req_cq_moderation;
> + req.cq_moderation_comp = cq_spec->cq_moderation_comp;
> + req.cq_moderation_usec = cq_spec->cq_moderation_usec;
>
> err = mana_send_request(apc->ac, &req, sizeof(req), &resp,
> sizeof(resp));
> @@ -1856,6 +1862,7 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
> struct gdma_posted_wqe_info *wqe_info;
> unsigned int pkt_transmitted = 0;
> unsigned int wqe_unit_cnt = 0;
> + unsigned int tx_bytes = 0;
> struct mana_txq *txq = cq->txq;
> struct mana_port_context *apc;
> struct netdev_queue *net_txq;
> @@ -1937,6 +1944,8 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
>
> mana_unmap_skb(skb, apc);
>
> + tx_bytes += skb->len;
> +
> napi_consume_skb(skb, cq->budget);
>
> pkt_transmitted++;
> @@ -1967,6 +1976,10 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
> if (atomic_sub_return(pkt_transmitted, &txq->pending_sends) < 0)
> WARN_ON_ONCE(1);
>
> + /* Feed DIM with the completion rate observed here, in NAPI context. */
> + cq->tx_dim_pkts += pkt_transmitted;
> + cq->tx_dim_bytes += tx_bytes;
> +
> cq->work_done = pkt_transmitted;
> }
>
> @@ -2318,6 +2331,119 @@ static void mana_poll_rx_cq(struct mana_cq *cq)
> xdp_do_flush();
> }
>
> +static void mana_rx_dim_work(struct work_struct *work)
> +{
> + struct dim *dim = container_of(work, struct dim, work);
> + struct dim_cq_moder cur_moder;
> + struct mana_cq *cq;
> +
> + cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
> + cq = container_of(dim, struct mana_cq, dim);
> +
> + cur_moder.usec = min_t(u16, cur_moder.usec, MANA_INTR_MODR_USEC_MAX);
> + cur_moder.pkts = min_t(u16, cur_moder.pkts, MANA_INTR_MODR_COMP_MAX);
> +
> + mana_gd_ring_dim(cq->gdma_cq, cur_moder.usec, true,
> + cur_moder.pkts, true);
> +
> + dim->state = DIM_START_MEASURE;
> +}
> +
> +static void mana_tx_dim_work(struct work_struct *work)
> +{
> + struct dim *dim = container_of(work, struct dim, work);
> + struct dim_cq_moder cur_moder;
> + struct mana_cq *cq;
> +
> + cur_moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix);
> + cq = container_of(dim, struct mana_cq, dim);
> +
> + cur_moder.usec = min_t(u16, cur_moder.usec, MANA_INTR_MODR_USEC_MAX);
> + cur_moder.pkts = min_t(u16, cur_moder.pkts, MANA_INTR_MODR_COMP_MAX);
> +
> + mana_gd_ring_dim(cq->gdma_cq, cur_moder.usec, true,
> + cur_moder.pkts, true);
> +
> + dim->state = DIM_START_MEASURE;
> +}
> +
> +/* The caller must update apc->rx/tx_dim_enabled before disabling and
> + * after enabling. And synchronize_net() before draining the DIM work,
> + * so that NAPI cannot observe a stale flag.
> + */
> +int mana_dim_change(struct mana_cq *cq, bool enable)
This always return 0, and the return value is not checked by the
callers; return type should likelly changed to void
/P
^ permalink raw reply
* Re: [PATCH net 1/2] vsock/virtio: collapse receive queue under memory pressure
From: Stefano Garzarella @ 2026-07-02 8:56 UTC (permalink / raw)
To: Bobby Eshleman
Cc: netdev, Jason Wang, Jakub Kicinski, Paolo Abeni,
Michael S. Tsirkin, kvm, virtualization, Xuan Zhuo, Eric Dumazet,
Simon Horman, linux-kernel, Stefan Hajnoczi, David S. Miller,
Eugenio Pérez, stable, Brien Oberstein
In-Reply-To: <akVBmydgSd0Eb46/@devvm29614.prn0.facebook.com>
On Wed, Jul 01, 2026 at 09:34:35AM -0700, Bobby Eshleman wrote:
>On Fri, Jun 26, 2026 at 03:48:22PM +0200, Stefano Garzarella wrote:
[...]
>> +out:
>> + if (new_skb)
>> + __skb_queue_tail(&new_queue, new_skb);
>> +
>> + skb_queue_splice(&new_queue, &vvs->rx_queue);
>
>I think the new skbs will also need skb_set_owner_sk_safe(skb, sk)
>when adding to rx_queue?
IIRC we added it in the rx path, mainily for loopback to pass the
ownership from the tx socket to the rx socket, but here we are already
in the rx path, so the skb will never leave this socket.
Maybe it's necessary for the eBPF path?
In any case, I can add it, but if you can help me better understand what
it prevents, that will also help me add a comment above it.
Thanks,
Stefano
^ permalink raw reply
* Re: [PATCH net-next 0/2] net: remove the orphaned IBM eHEA driver
From: patchwork-bot+netdevbpf @ 2026-07-02 8:50 UTC (permalink / raw)
To: David Christensen
Cc: davem, edumazet, kuba, pabeni, mpe, npiggin, christophe.leroy,
netdev, linuxppc-dev, linux-kernel
In-Reply-To: <20260629211343.3712775-1-drc@linux.ibm.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Mon, 29 Jun 2026 16:13:41 -0500 you wrote:
> The IBM eHEA (Ethernet Host Ethernet Adapter) driver has been orphaned
> since April 2024 with no active maintainer stepping forward. This series
> removes the driver and associated references from the kernel tree.
>
> The driver was marked as an Orphan on April 18, 2024:
>
> commit 97ec32b583bb ("MAINTAINERS: eth: mark IBM eHEA as an Orphan")
>
> [...]
Here is the summary with links:
- [net-next,1/2] ehea: remove the ehea driver
https://git.kernel.org/netdev/net-next/c/eb56577ae9a5
- [net-next,2/2] powerpc: remove ehea driver references
https://git.kernel.org/netdev/net-next/c/4bbb6f594070
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox