* Re: [PATCH net] net: phy: qcom: at803x: Use the correct bit to disable extended next page
From: Andrew Lunn @ 2026-04-11 14:10 UTC (permalink / raw)
To: Maxime Chevallier
Cc: Jakub Kicinski, davem, Eric Dumazet, Paolo Abeni, Simon Horman,
Russell King, thomas.petazzoni, netdev, linux-kernel,
linux-arm-msm
In-Reply-To: <20260410171021.1277138-1-maxime.chevallier@bootlin.com>
On Fri, Apr 10, 2026 at 07:10:20PM +0200, Maxime Chevallier wrote:
> As noted in the blamed commit, the AR8035 and other PHYs from this
> family advertise the Extended Next Page support by default, which may be
> understood by some partners as this PHY being multi-gig capable.
>
> The fix is to disable XNP advertising, which is done by setting bit 12
> of the Auto-Negotiation Advertisement Register (MII_ADVERTISE).
>
> The blamed commit incorrectly uses MDIO_AN_CTRL1_XNP, which is bit 13 as per
> 802.3 : 45.2.7.1 AN control register (Register 7.0)
>
> BIT 12 in MII_ADVERTISE is wrapped by ADVERTISE_RESV, used by some
> drivers such as the aquantia one. 802.3 Clause 28 defines bit 12 as
> Extended Next Page ability, at least in recent versions of the standard.
> Let's add a define for it and use it in the at803x driver.
I agree with this, it defines the C22 4.12 bit. And this is what the
at803x driver is using it for.
> static void at803x_link_change_notify(struct phy_device *phydev)
> diff --git a/include/uapi/linux/mii.h b/include/uapi/linux/mii.h
> index 39f7c44baf53..61d6edad4b94 100644
> --- a/include/uapi/linux/mii.h
> +++ b/include/uapi/linux/mii.h
> @@ -82,7 +82,8 @@
> #define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
> #define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */
> #define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */
> -#define ADVERTISE_RESV 0x1000 /* Unused... */
> +#define ADVERTISE_XNP 0x1000 /* Extended Next Page */
> +#define ADVERTISE_RESV ADVERTISE_XNP /* Used to be reserved */
Should we keep ADVERTISE_RESV?
45.2.7.6 AN advertisement register
If the Auto-Negotiation advertisement register (register 4) is
present, (see 28.2.4.1.3), then this register is a copy of the
Auto-Negotiation advertisement register (register 4). In this case,
reads to the AN advertisement register (7.16) report the value of
the Auto-Negotiation advertisement register (register 4); writes to
the AN advertisement register (7.16) cause a write to occur to the
Auto-Negotiation advertisement register.
So MDIO_MMD_AN:MDIO_AN_ADVERTISE is a straight copy of MII_ADVERTISE.
ef4_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, adv);
So ADVERTISE_XNP is just as valid in the other two drivers using
ADVERTISE_RESV. I think we should change those as well to
ADVERTISE_XNP and remove ADVERTISE_RESV?
Andrew
^ permalink raw reply
* Re: [PATCH v2 6/6] bus: mhi: host: mhi_phc: Add support for PHC over MHI
From: Jie Gan @ 2026-04-11 13:50 UTC (permalink / raw)
To: Krishna Chaitanya Chundru, Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev, Imran Shaik, Taniya Das
In-Reply-To: <20260411-tsc_timesync-v2-6-6f25f72987b3@oss.qualcomm.com>
On 4/11/2026 4:12 PM, Krishna Chaitanya Chundru wrote:
> From: Imran Shaik <imran.shaik@oss.qualcomm.com>
>
> This patch introduces the MHI PHC (PTP Hardware Clock) driver, which
> registers a PTP (Precision Time Protocol) clock and communicates with
> the MHI core to get the device side timestamps. These timestamps are
> then exposed to the PTP subsystem, enabling precise time synchronization
> between the host and the device.
>
> The following diagram illustrates the architecture and data flow:
>
> +-------------+ +--------------------+ +--------------+
> |Userspace App|<-->|Kernel PTP framework|<-->|MHI PHC Driver|
> +-------------+ +--------------------+ +--------------+
> |
> v
> +-------------------------------+ +-----------------+
> | MHI Device (Timestamp source) |<------->| MHI Core Driver |
> +-------------------------------+ +-----------------+
>
> - User space applications use the standard Linux PTP interface.
> - The PTP subsystem routes IOCTLs to the MHI PHC driver.
> - The MHI PHC driver communicates with the MHI core to fetch timestamps.
> - The MHI core interacts with the device to retrieve accurate time data.
>
> Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
> Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
> Signed-off-by: Imran Shaik <imran.shaik@oss.qualcomm.com>
> ---
> drivers/bus/mhi/host/Kconfig | 8 ++
> drivers/bus/mhi/host/Makefile | 1 +
> drivers/bus/mhi/host/mhi_phc.c | 150 +++++++++++++++++++++++++++++++++++++
> drivers/bus/mhi/host/mhi_phc.h | 28 +++++++
> drivers/bus/mhi/host/pci_generic.c | 23 ++++++
> 5 files changed, 210 insertions(+)
>
> diff --git a/drivers/bus/mhi/host/Kconfig b/drivers/bus/mhi/host/Kconfig
> index da5cd0c9fc620ab595e742c422f1a22a2a84c7b9..b4eabf3e5c56907de93232f02962040e979c3110 100644
> --- a/drivers/bus/mhi/host/Kconfig
> +++ b/drivers/bus/mhi/host/Kconfig
> @@ -29,3 +29,11 @@ config MHI_BUS_PCI_GENERIC
> This driver provides MHI PCI controller driver for devices such as
> Qualcomm SDX55 based PCIe modems.
>
> +config MHI_BUS_PHC
> + bool "MHI PHC driver"
> + depends on MHI_BUS_PCI_GENERIC
> + help
> + This driver provides Precision Time Protocol (PTP) clock and
> + communicates with MHI PCI driver to get the device side timestamp,
> + which enables precise time synchronization between the host and
> + the device.
> diff --git a/drivers/bus/mhi/host/Makefile b/drivers/bus/mhi/host/Makefile
> index 859c2f38451c669b3d3014c374b2b957c99a1cfe..5ba244fe7d596834ea535797efd3428963ba0ed0 100644
> --- a/drivers/bus/mhi/host/Makefile
> +++ b/drivers/bus/mhi/host/Makefile
> @@ -4,3 +4,4 @@ mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o
>
> obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
> mhi_pci_generic-y += pci_generic.o
> +mhi_pci_generic-$(CONFIG_MHI_BUS_PHC) += mhi_phc.o
> diff --git a/drivers/bus/mhi/host/mhi_phc.c b/drivers/bus/mhi/host/mhi_phc.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..fa04eb7f6025fa281d86c0a45b5f7d3e61f5ce12
> --- /dev/null
> +++ b/drivers/bus/mhi/host/mhi_phc.c
> @@ -0,0 +1,150 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mhi.h>
> +#include <linux/ptp_clock_kernel.h>
> +#include "mhi_phc.h"
> +
> +#define NSEC 1000000000ULL
> +
> +/**
> + * struct mhi_phc_dev - MHI PHC device
> + * @ptp_clock: associated PTP clock
> + * @ptp_clock_info: PTP clock information
> + * @mhi_dev: associated mhi device object
> + * @lock: spinlock
> + * @enabled: Flag to track the state of the MHI device
> + */
> +struct mhi_phc_dev {
> + struct ptp_clock *ptp_clock;
> + struct ptp_clock_info ptp_clock_info;
> + struct mhi_device *mhi_dev;
> + spinlock_t lock;
> + bool enabled;
> +};
> +
> +static int qcom_ptp_gettimex64(struct ptp_clock_info *ptp, struct timespec64 *ts,
> + struct ptp_system_timestamp *sts)
> +{
> + struct mhi_phc_dev *phc_dev = container_of(ptp, struct mhi_phc_dev, ptp_clock_info);
> + struct mhi_timesync_info time;
> + ktime_t ktime_cur;
> + unsigned long flags;
> + int ret;
> +
> + spin_lock_irqsave(&phc_dev->lock, flags);
> + if (!phc_dev->enabled) {
> + ret = -ENODEV;
> + goto err;
> + }
> +
> + ret = mhi_get_remote_tsc_time_sync(phc_dev->mhi_dev, &time);
mhi_get_remote_tsc_time_sync -> mhi_device_get_sync ->
__mhi_device_get_sync -> wait_event_timeout (can sleep with spinlock)
Be careful to use the spinlock here.
Thanks,
Jie
> + if (ret)
> + goto err;
> +
> + ktime_cur = time.t_dev_hi * NSEC + time.t_dev_lo;
> + *ts = ktime_to_timespec64(ktime_cur);
> +
> + dev_dbg(&phc_dev->mhi_dev->dev, "TSC time stamps sec:%u nsec:%u current:%lld\n",
> + time.t_dev_hi, time.t_dev_lo, ktime_cur);
> +
> + /* Update pre and post timestamps for PTP_SYS_OFFSET_EXTENDED*/
> + if (sts != NULL) {
> + sts->pre_ts = ktime_to_timespec64(time.t_host_pre);
> + sts->post_ts = ktime_to_timespec64(time.t_host_post);
> + dev_dbg(&phc_dev->mhi_dev->dev, "pre:%lld post:%lld\n",
> + time.t_host_pre, time.t_host_post);
> + }
> +
> +err:
> + spin_unlock_irqrestore(&phc_dev->lock, flags);
> +
> + return ret;
> +}
> +
> +int mhi_phc_start(struct mhi_controller *mhi_cntrl)
> +{
> + struct mhi_phc_dev *phc_dev = dev_get_drvdata(&mhi_cntrl->mhi_dev->dev);
> + unsigned long flags;
> +
> + if (!phc_dev) {
> + dev_err(&mhi_cntrl->mhi_dev->dev, "Driver data is NULL\n");
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&phc_dev->lock, flags);
> + phc_dev->enabled = true;
> + spin_unlock_irqrestore(&phc_dev->lock, flags);
> +
> + return 0;
> +}
> +
> +int mhi_phc_stop(struct mhi_controller *mhi_cntrl)
> +{
> + struct mhi_phc_dev *phc_dev = dev_get_drvdata(&mhi_cntrl->mhi_dev->dev);
> + unsigned long flags;
> +
> + if (!phc_dev) {
> + dev_err(&mhi_cntrl->mhi_dev->dev, "Driver data is NULL\n");
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&phc_dev->lock, flags);
> + phc_dev->enabled = false;
> + spin_unlock_irqrestore(&phc_dev->lock, flags);
> +
> + return 0;
> +}
> +
> +static struct ptp_clock_info qcom_ptp_clock_info = {
> + .owner = THIS_MODULE,
> + .gettimex64 = qcom_ptp_gettimex64,
> +};
> +
> +int mhi_phc_init(struct mhi_controller *mhi_cntrl)
> +{
> + struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
> + struct mhi_phc_dev *phc_dev;
> + int ret;
> +
> + phc_dev = devm_kzalloc(&mhi_dev->dev, sizeof(*phc_dev), GFP_KERNEL);
> + if (!phc_dev)
> + return -ENOMEM;
> +
> + phc_dev->mhi_dev = mhi_dev;
> +
> + phc_dev->ptp_clock_info = qcom_ptp_clock_info;
> + strscpy(phc_dev->ptp_clock_info.name, mhi_dev->name, PTP_CLOCK_NAME_LEN);
> +
> + spin_lock_init(&phc_dev->lock);
> +
> + phc_dev->ptp_clock = ptp_clock_register(&phc_dev->ptp_clock_info, &mhi_dev->dev);
> + if (IS_ERR(phc_dev->ptp_clock)) {
> + ret = PTR_ERR(phc_dev->ptp_clock);
> + dev_err(&mhi_dev->dev, "Failed to register PTP clock\n");
> + phc_dev->ptp_clock = NULL;
> + return ret;
> + }
> +
> + dev_set_drvdata(&mhi_dev->dev, phc_dev);
> +
> + dev_dbg(&mhi_dev->dev, "probed MHI PHC dev: %s\n", mhi_dev->name);
> + return 0;
> +};
> +
> +void mhi_phc_exit(struct mhi_controller *mhi_cntrl)
> +{
> + struct mhi_phc_dev *phc_dev = dev_get_drvdata(&mhi_cntrl->mhi_dev->dev);
> +
> + if (!phc_dev)
> + return;
> +
> + /* disable the node */
> + ptp_clock_unregister(phc_dev->ptp_clock);
> + phc_dev->enabled = false;
> +}
> diff --git a/drivers/bus/mhi/host/mhi_phc.h b/drivers/bus/mhi/host/mhi_phc.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..e6b0866bc768ba5a8ac3e4c40a99aa2050db1389
> --- /dev/null
> +++ b/drivers/bus/mhi/host/mhi_phc.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifdef CONFIG_MHI_BUS_PHC
> +int mhi_phc_init(struct mhi_controller *mhi_cntrl);
> +int mhi_phc_start(struct mhi_controller *mhi_cntrl);
> +int mhi_phc_stop(struct mhi_controller *mhi_cntrl);
> +void mhi_phc_exit(struct mhi_controller *mhi_cntrl);
> +#else
> +static inline int mhi_phc_init(struct mhi_controller *mhi_cntrl)
> +{
> + return 0;
> +}
> +
> +static inline int mhi_phc_start(struct mhi_controller *mhi_cntrl)
> +{
> + return 0;
> +}
> +
> +static inline int mhi_phc_stop(struct mhi_controller *mhi_cntrl)
> +{
> + return 0;
> +}
> +
> +static inline void mhi_phc_exit(struct mhi_controller *mhi_cntrl) {}
> +#endif
> diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
> index b1122c7224bdd469406d96af6d3df342040e1002..6cba5cecd1adb40396bba30c9b2a551898dce871 100644
> --- a/drivers/bus/mhi/host/pci_generic.c
> +++ b/drivers/bus/mhi/host/pci_generic.c
> @@ -16,6 +16,7 @@
> #include <linux/pm_runtime.h>
> #include <linux/timer.h>
> #include <linux/workqueue.h>
> +#include "mhi_phc.h"
>
> #define MHI_PCI_DEFAULT_BAR_NUM 0
>
> @@ -1044,6 +1045,7 @@ struct mhi_pci_device {
> struct timer_list health_check_timer;
> unsigned long status;
> bool reset_on_remove;
> + bool mhi_phc_init_done;
> };
>
> #ifdef readq
> @@ -1084,6 +1086,7 @@ static void mhi_pci_status_cb(struct mhi_controller *mhi_cntrl,
> enum mhi_callback cb)
> {
> struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
> + struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
>
> /* Nothing to do for now */
> switch (cb) {
> @@ -1091,9 +1094,21 @@ static void mhi_pci_status_cb(struct mhi_controller *mhi_cntrl,
> case MHI_CB_SYS_ERROR:
> dev_warn(&pdev->dev, "firmware crashed (%u)\n", cb);
> pm_runtime_forbid(&pdev->dev);
> + /* Stop PHC */
> + if (mhi_cntrl->tsc_timesync)
> + mhi_phc_stop(mhi_cntrl);
> break;
> case MHI_CB_EE_MISSION_MODE:
> pm_runtime_allow(&pdev->dev);
> + /* Start PHC */
> + if (mhi_cntrl->tsc_timesync) {
> + if (!mhi_pdev->mhi_phc_init_done) {
> + mhi_phc_init(mhi_cntrl);
> + mhi_pdev->mhi_phc_init_done = true;
> + }
> +
> + mhi_phc_start(mhi_cntrl);
> + }
> break;
> default:
> break;
> @@ -1236,6 +1251,10 @@ static void mhi_pci_recovery_work(struct work_struct *work)
>
> pm_runtime_forbid(&pdev->dev);
>
> + /* Stop PHC */
> + if (mhi_cntrl->tsc_timesync)
> + mhi_phc_stop(mhi_cntrl);
> +
> /* Clean up MHI state */
> if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
> mhi_power_down(mhi_cntrl, false);
> @@ -1457,6 +1476,10 @@ static void mhi_pci_remove(struct pci_dev *pdev)
> timer_delete_sync(&mhi_pdev->health_check_timer);
> cancel_work_sync(&mhi_pdev->recovery_work);
>
> + /* Remove PHC */
> + if (mhi_cntrl->tsc_timesync)
> + mhi_phc_exit(mhi_cntrl);
> +
> if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
> mhi_power_down(mhi_cntrl, true);
> mhi_unprepare_after_power_down(mhi_cntrl);
>
^ permalink raw reply
* Re: [PATCH net-next] net: phy: call phy_init_hw() in phy resume path
From: Andrew Lunn @ 2026-04-11 13:50 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Biju Das, biju.das.au, Heiner Kallweit, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Ovidiu Panait,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Geert Uytterhoeven, Prabhakar Mahadev Lad,
linux-renesas-soc@vger.kernel.org
In-Reply-To: <admMethCSjOQhu8g@shell.armlinux.org.uk>
> So, I question whether any of the functions in this driver actually
> have a valid reason to take phydev->lock - looks to me like a not
> very well written driver.
>
> In cases like this, I don't think we should make things more
> difficult in the core just because we have a lockdep splat when that
> can be avoided by killing off unnecessary locking.
Agreed. This patchset should cleanup these locks.
We also need to look at lan937x_dsp_workaround(). I also don't see
what that mutex lock/unlock is protecting. Accessing bank registers
need to be protected, so doing one additional access within that
should not need additional protection.
Andrew
^ permalink raw reply
* Re: [PATCH v2 2/6] bus: mhi: host: Add support for non-posted TSC timesync feature
From: Jie Gan @ 2026-04-11 13:43 UTC (permalink / raw)
To: Krishna Chaitanya Chundru, Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev, Vivek Pernamitta
In-Reply-To: <20260411-tsc_timesync-v2-2-6f25f72987b3@oss.qualcomm.com>
On 4/11/2026 4:12 PM, Krishna Chaitanya Chundru wrote:
> From: Vivek Pernamitta <quic_vpernami@quicinc.com>
>
> Implement non-posted time synchronization as described in section 5.1.1
> of the MHI v1.2 specification. The host disables low-power link states
> to minimize latency, reads the local time, issues a MMIO read to the
> device's TIME register.
>
> Add support for initializing this feature and export a function to be
> used by the drivers which does the time synchronization.
>
> MHI reads the device time registers in the MMIO address space pointed to
> by the capability register after disabling all low power modes and keeping
> MHI in M0. Before and after MHI reads, the local time is captured
> and shared for processing.
>
> Signed-off-by: Vivek Pernamitta <quic_vpernami@quicinc.com>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> ---
> drivers/bus/mhi/common.h | 4 +++
> drivers/bus/mhi/host/init.c | 28 ++++++++++++++++
> drivers/bus/mhi/host/internal.h | 9 +++++
> drivers/bus/mhi/host/main.c | 74 +++++++++++++++++++++++++++++++++++++++++
> include/linux/mhi.h | 37 +++++++++++++++++++++
> 5 files changed, 152 insertions(+)
>
> diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
> index 4c316f3d5a68beb01f15cf575b03747096fdcf2c..64f9b2b94387a112bb6b5e20c634c3ba8d6bc78e 100644
> --- a/drivers/bus/mhi/common.h
> +++ b/drivers/bus/mhi/common.h
> @@ -118,6 +118,10 @@
> #define CAP_CAPID_MASK GENMASK(31, 24)
> #define CAP_NEXT_CAP_MASK GENMASK(23, 12)
>
> +/* MHI TSC Timesync */
> +#define TSC_TIMESYNC_TIME_LOW_OFFSET (0x8)
> +#define TSC_TIMESYNC_TIME_HIGH_OFFSET (0xC)
> +
> /* Command Ring Element macros */
> /* No operation command */
> #define MHI_TRE_CMD_NOOP_PTR 0
> diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c
> index c2162aa04e810e45ccfbedd20aaa62f892420d31..eb720f671726d919646cbc450cd54bda655a1060 100644
> --- a/drivers/bus/mhi/host/init.c
> +++ b/drivers/bus/mhi/host/init.c
> @@ -498,6 +498,30 @@ static int mhi_find_capability(struct mhi_controller *mhi_cntrl, u32 capability)
> return 0;
> }
>
> +static int mhi_init_tsc_timesync(struct mhi_controller *mhi_cntrl)
> +{
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> + struct mhi_timesync *mhi_tsc_tsync;
> + u32 time_offset;
> + int ret;
> +
> + time_offset = mhi_find_capability(mhi_cntrl, MHI_CAP_ID_TSC_TIME_SYNC);
> + if (!time_offset)
> + return -ENXIO;
> +
> + mhi_tsc_tsync = devm_kzalloc(dev, sizeof(*mhi_tsc_tsync), GFP_KERNEL);
> + if (!mhi_tsc_tsync)
> + return -ENOMEM;
> +
> + mhi_cntrl->tsc_timesync = mhi_tsc_tsync;
> + mutex_init(&mhi_tsc_tsync->ts_mutex);
> +
> + /* save time_offset for obtaining time via MMIO register reads */
> + mhi_tsc_tsync->time_reg = mhi_cntrl->regs + time_offset;
> +
> + return 0;
> +}
> +
> int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
> {
> u32 val;
> @@ -635,6 +659,10 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
> return ret;
> }
>
> + ret = mhi_init_tsc_timesync(mhi_cntrl);
> + if (ret)
> + dev_dbg(dev, "TSC Time synchronization init failure\n");
> +
> return 0;
> }
>
> diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
> index 7b0ee5e3a12dd585064169b7b884750bf4d8c8db..a0e729e7a1198c1b82c70b6bfe3bc2ee24331229 100644
> --- a/drivers/bus/mhi/host/internal.h
> +++ b/drivers/bus/mhi/host/internal.h
> @@ -15,6 +15,15 @@ extern const struct bus_type mhi_bus_type;
> #define MHI_SOC_RESET_REQ_OFFSET 0xb0
> #define MHI_SOC_RESET_REQ BIT(0)
>
> +/*
> + * With ASPM enabled, the link may enter a low power state, requiring
> + * a wake-up sequence. Use a short burst of back-to-back reads to
> + * transition the link to the active state. Based on testing,
> + * 4 iterations are necessary to ensure reliable wake-up without
> + * excess latency.
> + */
> +#define MHI_NUM_BACK_TO_BACK_READS 4
> +
> struct mhi_ctxt {
> struct mhi_event_ctxt *er_ctxt;
> struct mhi_chan_ctxt *chan_ctxt;
> diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
> index 53c0ffe300702bcc3caa8fd9ea8086203c75b186..b7a727b1a5d1f20b570c62707a991ec5b85bfec7 100644
> --- a/drivers/bus/mhi/host/main.c
> +++ b/drivers/bus/mhi/host/main.c
> @@ -1626,3 +1626,77 @@ int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_
> return 0;
> }
> EXPORT_SYMBOL_GPL(mhi_get_channel_doorbell_offset);
> +
> +static int mhi_get_remote_time(struct mhi_controller *mhi_cntrl, struct mhi_timesync *mhi_tsync,
> + struct mhi_timesync_info *time)
> +{
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> + int ret, i;
> +
> + if (!mhi_tsync && !mhi_tsync->time_reg) {
if (!mhi_tsync || !mhi_tsync->time_reg) {
> + dev_err(dev, "Time sync is not supported\n");
> + return -EINVAL;
> + }
> +
> + if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
> + dev_err(dev, "MHI is not in active state, pm_state:%s\n",
> + to_mhi_pm_state_str(mhi_cntrl->pm_state));
> + return -EIO;
> + }
> +
> + /* bring to M0 state */
> + ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
> + if (ret)
> + return ret;
> +
> + guard(mutex)(&mhi_tsync->ts_mutex);
> + mhi_cntrl->runtime_get(mhi_cntrl);
> +
> + /*
> + * time critical code to fetch device time, delay between these two steps
> + * should be deterministic as possible.
> + */
> + preempt_disable();
> + local_irq_disable();
> +
> + time->t_host_pre = ktime_get_real();
> +
> + /*
> + * To ensure the PCIe link is in L0 when ASPM is enabled, perform series
> + * of back-to-back reads. This is necessary because the link may be in a
> + * low-power state (e.g., L1 or L1ss), and need to be forced it to
> + * transition to L0.
> + */
> + for (i = 0; i < MHI_NUM_BACK_TO_BACK_READS; i++) {
> + ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
> + TSC_TIMESYNC_TIME_LOW_OFFSET, &time->t_dev_lo);
> +
> + ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
> + TSC_TIMESYNC_TIME_HIGH_OFFSET, &time->t_dev_hi);
ret is assigned but no where checked.
Thanks,
Jie
> + }
> +
> + time->t_host_post = ktime_get_real();
> +
> + local_irq_enable();
> + preempt_enable();
> +
> + mhi_cntrl->runtime_put(mhi_cntrl);
> +
> + mhi_device_put(mhi_cntrl->mhi_dev);
> +
> + return 0;
> +}
> +
> +int mhi_get_remote_tsc_time_sync(struct mhi_device *mhi_dev, struct mhi_timesync_info *time)
> +{
> + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
> + struct mhi_timesync *mhi_tsc_tsync = mhi_cntrl->tsc_timesync;
> + int ret;
> +
> + ret = mhi_get_remote_time(mhi_cntrl, mhi_tsc_tsync, time);
> + if (ret)
> + dev_err(&mhi_dev->dev, "Failed to get TSC Time Sync value:%d\n", ret);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(mhi_get_remote_tsc_time_sync);
> diff --git a/include/linux/mhi.h b/include/linux/mhi.h
> index 88ccb3e14f481d6b85c2a314eb74ba960c2d4c81..f39c8ca7c251954f2d83c1227d206b600b88c75f 100644
> --- a/include/linux/mhi.h
> +++ b/include/linux/mhi.h
> @@ -286,6 +286,30 @@ struct mhi_controller_config {
> bool m2_no_db;
> };
>
> +/**
> + * struct mhi_timesync - MHI time synchronization structure
> + * @time_reg: Points to address of Timesync register
> + * @ts_mutex: Mutex for synchronization
> + */
> +struct mhi_timesync {
> + void __iomem *time_reg;
> + struct mutex ts_mutex;
> +};
> +
> +/**
> + * struct mhi_timesync_info - MHI time sync info structure
> + * @t_host_pre: Pre host soc time
> + * @t_host_post: Post host soc time
> + * @t_dev_lo: Mhi device time of lower dword
> + * @t_dev_hi: Mhi device time of higher dword
> + */
> +struct mhi_timesync_info {
> + ktime_t t_host_pre;
> + ktime_t t_host_post;
> + u32 t_dev_lo;
> + u32 t_dev_hi;
> +};
> +
> /**
> * struct mhi_controller - Master MHI controller structure
> * @name: Device name of the MHI controller
> @@ -323,6 +347,7 @@ struct mhi_controller_config {
> * @mhi_event: MHI event ring configurations table
> * @mhi_cmd: MHI command ring configurations table
> * @mhi_ctxt: MHI device context, shared memory between host and device
> + * @tsc_timesync: MHI TSC timesync
> * @pm_mutex: Mutex for suspend/resume operation
> * @pm_lock: Lock for protecting MHI power management state
> * @timeout_ms: Timeout in ms for state transitions
> @@ -401,6 +426,8 @@ struct mhi_controller {
> struct mhi_cmd *mhi_cmd;
> struct mhi_ctxt *mhi_ctxt;
>
> + struct mhi_timesync *tsc_timesync;
> +
> struct mutex pm_mutex;
> rwlock_t pm_lock;
> u32 timeout_ms;
> @@ -795,4 +822,14 @@ bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
> */
> int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset);
>
> +/**
> + * mhi_get_remote_tsc_time_sync - get external soc time relative to local soc
> + * time pre and post using MMIO method.
> + * @mhi_dev: Device associated with the channels
> + * @time: mhi_timesync_info to get device time details
> + *
> + * Returns:
> + * 0 for success, error code for failure
> + */
> +int mhi_get_remote_tsc_time_sync(struct mhi_device *mhi_dev, struct mhi_timesync_info *time);
> #endif /* _MHI_H_ */
>
^ permalink raw reply
* Re: [PATCH net] openvswitch: fix kernel panic from oversized vport upcall PID arrays
From: David Laight @ 2026-04-11 12:23 UTC (permalink / raw)
To: Weiming Shi
Cc: Aaron Conole, Eelco Chaudron, Ilya Maximets, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Pravin B Shelar, Flavio Leitner, Mark Gray, netdev, dev,
linux-kernel, Xiang Mei
In-Reply-To: <20260411055915.1224902-2-bestswngs@gmail.com>
On Fri, 10 Apr 2026 22:59:16 -0700
Weiming Shi <bestswngs@gmail.com> wrote:
> The vport netlink reply helpers allocate a fixed-size skb with
> nlmsg_new(NLMSG_DEFAULT_SIZE, ...) but serialize the full upcall PID
> array via ovs_vport_get_upcall_portids(). Since
> ovs_vport_set_upcall_portids() accepts any non-zero multiple of
> sizeof(u32) with no upper bound, a CAP_NET_ADMIN user can install a
> PID array large enough to overflow the reply buffer. When the
> subsequent nla_put() fails with -EMSGSIZE, five BUG_ON(err < 0) sites
> fire and panic the kernel. On systems with unprivileged user namespaces
> enabled (e.g., Ubuntu default), this is reachable via unshare -Urn.
>
> kernel BUG at net/openvswitch/datapath.c:2414!
> Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
> CPU: 1 UID: 0 PID: 65 Comm: poc Not tainted 7.0.0-rc7-00195-geb216e422044 #1
> RIP: 0010:ovs_vport_cmd_set (net/openvswitch/datapath.c:2414 (discriminator 1))
> Call Trace:
> <TASK>
> genl_family_rcv_msg_doit (net/netlink/genetlink.c:1116)
> genl_rcv_msg (net/netlink/genetlink.c:1194 net/netlink/genetlink.c:1209)
> netlink_rcv_skb (net/netlink/af_netlink.c:2550)
> genl_rcv (net/netlink/genetlink.c:1219)
> netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1344)
> netlink_sendmsg (net/netlink/af_netlink.c:1894)
> __sys_sendto (net/socket.c:2206 (discriminator 1))
> __x64_sys_sendto (net/socket.c:2209)
> do_syscall_64 (arch/x86/entry/syscall_64.c:63 (discriminator 1))
> entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
> </TASK>
> Kernel panic - not syncing: Fatal exception
>
> Dynamically compute the reply skb size based on the vport's actual PID
> array length instead of using a fixed NLMSG_DEFAULT_SIZE, and replace
> the BUG_ON() calls with WARN_ON_ONCE() plus graceful error returns.
IIRC WARN_ON_ONCE() will still panic all the systems with panic-on_warn set.
David
^ permalink raw reply
* Re: [PATCH] net: Optimize flush calculation in inet_gro_receive()
From: David Laight @ 2026-04-11 12:09 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: deller, davem, dsahern, linux-kernel, linux-parisc, netdev,
edumazet
In-Reply-To: <20260411052037.2013228-1-kuniyu@google.com>
On Sat, 11 Apr 2026 05:19:35 +0000
Kuniyuki Iwashima <kuniyu@google.com> wrote:
> From: Helge Deller <deller@kernel.org>
> Date: Fri, 10 Apr 2026 16:43:54 +0200
> > For the calculation of the flush variable, use the get_unaligned_xxx() helpers
> > to access only relevant bits of the IP header.
> >
> > Note: Since I don't know the network details, I'm not sure if "& ~IP_DF"
> > (& ~0x4000) is correct, or if "& IP_OFFSET" (& 0x1FFF) should be used instead
>
> ~IP_DF is correct (MF bit needs to be checked), see
>
> commit db8caf3dbc77599dc90f4ea0a803cd1d97116f30
> Author: Eric Dumazet <edumazet@google.com>
> Date: Fri May 31 11:18:10 2013
>
> gro: should aggregate frames without DF
>
>
> > (which I believe would be more correct). Instead of possibly breaking things I
> > left it as is, but maybe some expert can check?
> >
> > Signed-off-by: Helge Deller <deller@gmx.de>
> >
> > diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
> > index c7731e300a44..58cad2687c2c 100644
> > --- a/net/ipv4/af_inet.c
> > +++ b/net/ipv4/af_inet.c
> > @@ -1479,7 +1479,7 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
> > struct sk_buff *p;
> > unsigned int hlen;
> > unsigned int off;
> > - int flush = 1;
> > + u16 flush = 1;
> > int proto;
> >
> > off = skb_gro_offset(skb);
> > @@ -1504,7 +1504,8 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
> > goto out;
> >
> > NAPI_GRO_CB(skb)->proto = proto;
> > - flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (ntohl(*(__be32 *)&iph->id) & ~IP_DF));
> > + flush = (get_unaligned_be16(&iph->tot_len) ^ skb_gro_len(skb)) |
> > + (get_unaligned_be16(&iph->frag_off) & ~IP_DF);
>
> I think here we intentionally use 32-bit loads:
>
> commit 1075f3f65d0e0f49351b7d4310e9f94483972a51
> Author: Herbert Xu <herbert@gondor.apana.org.au>
> Date: Tue May 26 18:50:29 2009
>
> ipv4: Use 32-bit loads for ID and length in GRO
>
>
> Before your patch, 32-bit load + bswap are used while
> 16-bit load + rol 8 after the change.
>
> I feel the 4-byte aligned load + bswap is faster than
> misaligned access + 8 times shift (Is this internally
> optimised like xchg for a single word size ?)
>
> Do you have some numbers ?
Check on some architecture that doesn't support misaligned loads.
Actually, aren't the accesses aligned??
Also on ones without 32bit byteswap (some do have byteswapping
memory reads).
Also you may not want to change 'flush' to u16.
On non-x86 it may force the compiler add extra masking instructions.
David
>
>
> Before:
> flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb))
> mov edx,DWORD PTR [rcx]
> bswap edx
> return skb->len - NAPI_GRO_CB(skb)->data_offset;
> mov r8d,DWORD PTR [rsi+0x38]
> mov r9d,DWORD PTR [rsi+0x70]
> sub r9d,r8d
> xor r9d,edx
> | (ntohl(*(__be32 *)&iph->id) & ~IP_DF));
> mov ebp,0xffbfffff
> and ebp,DWORD PTR [rcx+0x4]
> bswap ebp
> or ebp,r9d
>
>
> After:
> flush = (get_unaligned_be16(&iph->tot_len) ^ skb_gro_len(skb))
> movzx edx,WORD PTR [rcx+0x2]
> rol dx,0x8
> return skb->len - NAPI_GRO_CB(skb)->data_offset;
> mov r8d,DWORD PTR [rsi+0x38]
> mov r9d,DWORD PTR [rsi+0x70]
> sub r9d,r8d
> xor r9d,edx
> | (get_unaligned_be16(&iph->frag_off) & ~IP_DF);
> movzx ebp,WORD PTR [rcx+0x6]
> and ebp,0xffffffbf
> rol bp,0x8
> or ebp,r9d
>
^ permalink raw reply
* [PATCH net v2] atm: mpoa: keep mpc->dev referenced across mpoad restart
From: Shuvam Pandey @ 2026-04-11 11:59 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, netdev
Cc: linux-kernel, syzbot+5ec223ccb83b24ef982f, shuvampandey1
atm: mpoa: keep mpc->dev referenced across mpoad restart
syzbot reported a netdevice refcount warning:
refcount_t: decrement hit 0; leaking memory.
WARNING: lib/refcount.c:31 at refcount_warn_saturate+0x70/0x110
...
dev_put include/linux/netdevice.h:4466 [inline]
mpoad_close+0x1fc/0x3e0 net/atm/mpc.c:889
mpoad_close() drops the reference held in mpc->dev, but the mpoa_client
itself stays alive and keeps the same device pointer.
When mpoad is attached again, atm_mpoa_mpoad_attach() reuses the existing
mpoa_client and its mpc->dev without reacquiring that reference, so the
next close can hit the netdevice refcount warning.
This reference is owned by the mpoa_client/LEC association rather than a
single mpoad open/close cycle. It is acquired when the client gets its
LEC device and is released later from mpoa_event_listener() on
NETDEV_UNREGISTER. Fix the imbalance by removing the dev_put() from
mpoad_close().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+5ec223ccb83b24ef982f@syzkaller.appspotmail.com
Link: https://groups.google.com/g/syzkaller-bugs/c/qhZ5MJfLBOE/m/UnotmgRdAQAJ
Signed-off-by: Shuvam Pandey <shuvampandey1@gmail.com>
---
Changes in v2:
- drop the atm_mpoa_cleanup() dev_put()/NULL hunk
- add the syzbot warning excerpt
- add a Fixes tag
- clarify that the final dev_put() comes from the notifier path
net/atm/mpc.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/net/atm/mpc.c b/net/atm/mpc.c
index ce8e9780373b9..90ab8f2889734 100644
--- a/net/atm/mpc.c
+++ b/net/atm/mpc.c
@@ -886,7 +886,6 @@ static void mpoad_close(struct atm_vcc *vcc)
struct lec_priv *priv = netdev_priv(mpc->dev);
priv->lane2_ops->associate_indicator = NULL;
stop_mpc(mpc);
- dev_put(mpc->dev);
}
mpc->in_ops->destroy_cache(mpc);
^ permalink raw reply related
* Re: [PATCH] net: phy: realtek: use LEDCR page number define on RTL8211F
From: Daniel Golle @ 2026-04-11 11:49 UTC (permalink / raw)
To: Aleksander Jan Bajkowski
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
vladimir.oltean, michael, daniel.braunwarth, ih, rmk+kernel,
netdev, linux-kernel
In-Reply-To: <20260411105150.184577-1-olek2@wp.pl>
On Sat, Apr 11, 2026 at 12:51:45PM +0200, Aleksander Jan Bajkowski wrote:
> Replace the magic number with an existing define for the LEDCR
> register page number on the RTL8211F.
>
> Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl>
Reviewed-by: Daniel Golle <daniel@makrotopia.org>
^ permalink raw reply
* Re: [PATCH v3] net/mlx5: Fix OOB access and stack information leak in PTP event handling
From: Carolina Jubran @ 2026-04-11 11:35 UTC (permalink / raw)
To: Prathamesh Deshpande
Cc: Richard Cochran, Tariq Toukan, Mark Bloch, netdev, linux-rdma,
linux-kernel, Leon Romanovsky, Saeed Mahameed
In-Reply-To: <20260410015336.7353-1-prathameshdeshpande7@gmail.com>
On 10/04/2026 4:53, Prathamesh Deshpande wrote:
> In mlx5_pps_event(), several critical issues were identified during
> review by Sashiko:
>
> 1. The 'pin' index from the hardware event was used without bounds
> checking to index 'pin_config' and 'pps_info->start', leading to
> potential out-of-bounds memory access.
> 2. 'ptp_event' was not zero-initialized. Since it contains a union,
> assigning a timestamp partially leaves the 'ts_raw' field with
> uninitialized stack memory, which can leak kernel data or
> corrupt time sync logic in hardpps().
> 3. A NULL 'pin_config' could be dereferenced if initialization failed.
> 4. 'clock->ptp' could be NULL if ptp_clock_register() failed.
>
> Fix these by zero-initializing the event struct, adding a bounds
> check against n_pins, and adding appropriate NULL guards.
>
> Fixes: 7c39afb394c7 ("net/mlx5: PTP code migration to driver core section")
> Suggested-by: Carolina Jubran <cjubran@nvidia.com>
> Signed-off-by: Prathamesh Deshpande <prathameshdeshpande7@gmail.com>
> ---
> v3:
> - Fix union corruption by using a local timestamp variable [Sashiko].
> - Validate pin index against n_pins with WARN_ON_ONCE [Carolina].
> - Remove redundant pin < 0 check and cleanup TODO comment.
> v2:
> - Zero-initialize ptp_event to prevent stack information leak [Sashiko].
> - Add bounds check for hardware pin index to prevent OOB access [Sashiko].
> - Add NULL guard for pin_config to handle initialization failures [Sashiko].
> - Add NULL check for clock->ptp as originally intended.
>
> .../net/ethernet/mellanox/mlx5/core/lib/clock.c | 17 ++++++++++++-----
> 1 file changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
> index bd4e042077af..674dd048a6b8 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
> @@ -1164,16 +1164,22 @@ static int mlx5_pps_event(struct notifier_block *nb,
> pps_nb);
> struct mlx5_core_dev *mdev = clock_state->mdev;
> struct mlx5_clock *clock = mdev->clock;
> - struct ptp_clock_event ptp_event;
> + struct ptp_clock_event ptp_event = {};
> struct mlx5_eqe *eqe = data;
> int pin = eqe->data.pps.pin;
> unsigned long flags;
> u64 ns;
>
> + if (!clock->ptp_info.pin_config)
> + return NOTIFY_OK;
> +
> + if (WARN_ON_ONCE(pin >= clock->ptp_info.n_pins))
> + return NOTIFY_OK;
Sorry if my previous comment wasn't clear enough.
The firmware will never report a pin higher than n_pins, thats not the
concern
here. if future hardware reports n_pins > 8, checking against n_pins
would still
allow OOB access on those arrays. The check should compare against
MAX_PIN_NUM
instead, since thats the actual hard limit of the driver's data
structures. and if a new
device supports more than 8 pins, the WARN_ON_ONCE would let us know we need
to update the driver.
Thanks,
Carolina
^ permalink raw reply
* Re: [PATCH net] xfrm_user: fix info leak in build_mapping()
From: Greg Kroah-Hartman @ 2026-04-11 11:26 UTC (permalink / raw)
To: Steffen Klassert
Cc: Jakub Kicinski, netdev, linux-kernel, Herbert Xu, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman
In-Reply-To: <adYgIr5vlORzWqi7@secunet.com>
On Wed, Apr 08, 2026 at 11:30:10AM +0200, Steffen Klassert wrote:
> On Tue, Apr 07, 2026 at 06:12:11PM -0700, Jakub Kicinski wrote:
> > On Tue, 7 Apr 2026 07:51:15 +0200 Greg Kroah-Hartman wrote:
> > > > > I guess nlmsg_append() would work? It tries to do some zeroing out for
> > > > > alignment for some reason...
> > > > >
> > > > > Want me to do that? I don't have a way to test any of this, I just
> > > > > found it using some static code analysis tools that looked at holes in
> > > > > structures.
> > > >
> > > > Do you have any more Netlink leaks in the queue? If you do let's do it,
> > > > if you don't we can wait until the next victi^w patch to arrive.
> > >
> > > I do not have any more, sorry. So is it worth it for just these 2?
> > > Your call :)
> >
> > These are fine. I would have applied but I think Steffen will take them
> > via the ipsec tree first (LMK if that's not the plan, Steffen)
>
> Both patches are now applied to the ipsec tree. Thanks a lot!
Wonderful, thanks!
^ permalink raw reply
* [PATCH net] net: sched: em_text: require NUL-terminated algo name
From: Greg Kroah-Hartman @ 2026-04-11 11:10 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, Greg Kroah-Hartman, Jamal Hadi Salim, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, stable
em_text_change() copies the user-supplied tcf_em_text struct from
netlink and passes conf->algo straight to textsearch_prepare(), which
forwards it to lookup_ts_algo() (strcmp) and request_module() (vsnprintf
%s). But the algo[16] field is never validated to be NULL-terminated,
so a fully populated array reads past it into the adjacent
from_offset/to_offset/pattern_len fields and the trailing pattern bytes
during the string operations.
This type of pattern is properly checked in the string_mt_check() for
xt_string netfilter matching function, but for some reason was not added
here, so fix this up by doing the same exact thing.
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>
Fixes: d675c989ed2d ("[PKT_SCHED]: Packet classification based on textsearch (ematch)")
Cc: stable <stable@kernel.org>
Assisted-by: gregkh_clanker_t1000
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
Note, my tools flagged this, so I fixed this up the same way that
string_mt_check() did, but if there is some other way that this should
be resolved, or I got this totally wrong that this isn't an issue,
please let me know, thanks!
net/sched/em_text.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/sched/em_text.c b/net/sched/em_text.c
index 343f1aebeec2..24a8aa21971d 100644
--- a/net/sched/em_text.c
+++ b/net/sched/em_text.c
@@ -58,6 +58,9 @@ static int em_text_change(struct net *net, void *data, int len,
if (len < sizeof(*conf) || len < (sizeof(*conf) + conf->pattern_len))
return -EINVAL;
+ if (conf->algo[sizeof(conf->algo) - 1] != '\0')
+ return -EINVAL;
+
if (conf->from_layer > conf->to_layer)
return -EINVAL;
--
2.53.0
^ permalink raw reply related
* [PATCH net] net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()
From: Greg Kroah-Hartman @ 2026-04-11 11:01 UTC (permalink / raw)
To: linux-usb, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, stable
A malicious USB device claiming to be a CDC Phonet modem can overflow
the skb_shared_info->frags[] array by sending an unbounded sequence of
full-page bulk transfers.
Drop the skb and increment the length error when the frag limit is
reached. This matches the same fix that commit f0813bcd2d9d ("net:
wwan: t7xx: fix potential skb->frags overflow in RX path") did for the
t7xx driver.
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: stable <stable@kernel.org>
Assisted-by: gregkh_clanker_t1000
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/net/usb/cdc-phonet.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index ad5121e9cf5d..165650ecef64 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -157,11 +157,16 @@ static void rx_complete(struct urb *req)
PAGE_SIZE);
page = NULL;
}
- } else {
+ } else if (skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS) {
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
page, 0, req->actual_length,
PAGE_SIZE);
page = NULL;
+ } else {
+ dev_kfree_skb_any(skb);
+ pnd->rx_skb = NULL;
+ skb = NULL;
+ dev->stats.rx_length_errors++;
}
if (req->actual_length < PAGE_SIZE)
pnd->rx_skb = NULL; /* Last fragment */
--
2.53.0
^ permalink raw reply related
* [PATCH net] net: usb: cdc_ncm: reject negative chained NDP offsets
From: Greg Kroah-Hartman @ 2026-04-11 10:53 UTC (permalink / raw)
To: linux-usb, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Oliver Neukum, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
stable
cdc_ncm_rx_fixup() reads dwNextNdpIndex from each NDP32 to chain to the
next one. The 32-bit value from the device is stored into the signed
int ndpoffset so that means values with the high bit set become
negative. The first time this is read, the value is properly tested for
a negative value BUT the next time through the loop, this type of check
is missed entirely.
Fix this up by checking for a negative value when dwNextNdpIndex is read
again in the bottom of the loop to match the top check.
Commit 8d2b1a1ec9f5 ("CDC-NCM: avoid overflow in sanity checking") fixed
a similar signed-overflow issue in the datagram offset checks of the
same function.
Cc: Oliver Neukum <oliver@neukum.org>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Fixes: 0fa81b304a79 ("cdc_ncm: Implement the 32-bit version of NCM Transfer Block")
Cc: stable <stable@kernel.org>
Assisted-by: gregkh_clanker_t1000
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/net/usb/cdc_ncm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index bb9929727eb9..b2d08c4aae54 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1835,7 +1835,7 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
else
ndpoffset = le32_to_cpu(ndp.ndp32->dwNextNdpIndex);
- if (ndpoffset && loopcount--)
+ if (ndpoffset > 0 && loopcount--)
goto next_ndp;
/* update stats */
--
2.53.0
^ permalink raw reply related
* [PATCH] net: phy: realtek: use LEDCR page number define on RTL8211F
From: Aleksander Jan Bajkowski @ 2026-04-11 10:51 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, daniel,
vladimir.oltean, michael, olek2, daniel.braunwarth, ih,
rmk+kernel, netdev, linux-kernel
Replace the magic number with an existing define for the LEDCR
register page number on the RTL8211F.
Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl>
---
drivers/net/phy/realtek/realtek_main.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 75565fbdbf6d..536cbb3820eb 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -874,7 +874,7 @@ static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index,
if (index >= RTL8211x_LED_COUNT)
return -EINVAL;
- val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR);
+ val = phy_read_paged(phydev, RTL8211F_LEDCR_PAGE, RTL8211F_LEDCR);
if (val < 0)
return val;
@@ -936,7 +936,8 @@ static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index,
reg <<= RTL8211F_LEDCR_SHIFT * index;
reg |= RTL8211F_LEDCR_MODE; /* Mode B */
- return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg);
+ return phy_modify_paged(phydev, RTL8211F_LEDCR_PAGE, RTL8211F_LEDCR,
+ mask, reg);
}
static int rtl8211e_led_hw_control_get(struct phy_device *phydev, u8 index,
--
2.51.0
^ permalink raw reply related
* [PATCH net] bnge: return after auxiliary_device_uninit() in error path
From: Greg Kroah-Hartman @ 2026-04-11 10:45 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, Greg Kroah-Hartman, Vikas Gupta, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
stable
When auxiliary_device_add() fails, the error block calls
auxiliary_device_uninit() but does not return. The uninit drops the
last reference and synchronously runs bnge_aux_dev_release(), which sets
bd->auxr_dev = NULL and frees the underlying object. The subsequent
bd->auxr_dev->net = bd->netdev then dereferences NULL, which is not a
good thing to have happen when trying to clean up from an error.
Add the missing return, as the auxiliary bus documentation states is a
requirement (seems that LLM tools read documentation better than humans
do...)
Cc: Vikas Gupta <vikas.gupta@broadcom.com>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Fixes: 8ac050ec3b1c ("bng_en: Add RoCE aux device support")
Cc: stable <stable@kernel.org>
Assisted-by: gregkh_clanker_t1000
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/net/ethernet/broadcom/bnge/bnge_auxr.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
index b942076762ef..67e93e17d4d9 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
@@ -194,6 +194,7 @@ void bnge_rdma_aux_device_add(struct bnge_dev *bd)
dev_warn(bd->dev, "Failed to add auxiliary device for ROCE\n");
auxiliary_device_uninit(aux_dev);
bd->flags &= ~BNGE_EN_ROCE;
+ return;
}
bd->auxr_dev->net = bd->netdev;
--
2.53.0
^ permalink raw reply related
* [PATCH net] idpf: fix double free and use-after-free in aux device error paths
From: Greg Kroah-Hartman @ 2026-04-11 10:12 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, linux-kernel, Greg Kroah-Hartman, Tony Nguyen,
Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, stable
When auxiliary_device_add() fails in idpf_plug_vport_aux_dev() or
idpf_plug_core_aux_dev(), the err_aux_dev_add label calls
auxiliary_device_uninit() and falls through to err_aux_dev_init. The
uninit call will trigger put_device(), which invokes the release
callback (idpf_vport_adev_release / idpf_core_adev_release) that frees
iadev. The fall-through then reads adev->id from the freed iadev for
ida_free() and double-frees iadev with kfree().
Free the IDA slot and clear the back-pointer before uninit, while adev
is still valid, then return immediately.
Commit 65637c3a1811 65637c3a1811 ("idpf: fix UAF in RDMA core aux dev
deinitialization") fixed the same use-after-free in the matching unplug
path in this file but missed both probe error paths.
Cc: Tony Nguyen <anthony.l.nguyen@intel.com>
Cc: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: stable <stable@kernel.org>
Fixes: be91128c579c ("idpf: implement RDMA vport auxiliary dev create, init, and destroy")
Fixes: f4312e6bfa2a ("idpf: implement core RDMA auxiliary dev create, init, and destroy")
Assisted-by: gregkh_clanker_t1000
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
Note, these cleanup paths are messy, but I couldn't see a simpler way
without a lot more rework, so I choose the simple way :)
drivers/net/ethernet/intel/idpf/idpf_idc.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_idc.c b/drivers/net/ethernet/intel/idpf/idpf_idc.c
index 7e4f4ac92653..b7d6b08fc89e 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_idc.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_idc.c
@@ -90,7 +90,10 @@ static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info,
return 0;
err_aux_dev_add:
+ ida_free(&idpf_idc_ida, adev->id);
+ vdev_info->adev = NULL;
auxiliary_device_uninit(adev);
+ return ret;
err_aux_dev_init:
ida_free(&idpf_idc_ida, adev->id);
err_ida_alloc:
@@ -228,7 +231,10 @@ static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info)
return 0;
err_aux_dev_add:
+ ida_free(&idpf_idc_ida, adev->id);
+ cdev_info->adev = NULL;
auxiliary_device_uninit(adev);
+ return ret;
err_aux_dev_init:
ida_free(&idpf_idc_ida, adev->id);
err_ida_alloc:
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net] netrom: do some basic forms of validation on incoming frames
From: Greg KH @ 2026-04-11 8:58 UTC (permalink / raw)
To: hugh
Cc: Kuniyuki Iwashima, kuba, davem, edumazet, horms, linux-hams,
linux-kernel, netdev, pabeni, stable, workflows, yizhe
In-Reply-To: <b9355e3c-9252-4dd7-b7ed-dff13e3b2c8b@blemings.org>
On Sat, Apr 11, 2026 at 05:24:17PM +1000, Hugh Blemings wrote:
>
> On 11/4/2026 15:50, Greg KH wrote:
> > On Sat, Apr 11, 2026 at 08:25:19AM +1000, Hugh Blemings wrote:
> > > On 11/4/2026 08:11, Kuniyuki Iwashima wrote:
> > > > From: Jakub Kicinski <kuba@kernel.org>
> > > > Date: Fri, 10 Apr 2026 14:54:48 -0700
> > > > > On Fri, 10 Apr 2026 14:30:42 -0700 Jakub Kicinski wrote:
> > > > > > On Fri, 10 Apr 2026 07:24:36 +0200 Greg Kroah-Hartman wrote:
> > > > > > > On Thu, Apr 09, 2026 at 08:32:35PM -0700, Jakub Kicinski wrote:
> > > > > > > > Or for simplicity we could also be testing against skb_headlen()
> > > > > > > > since we don't expect any legit non-linear frames here? Dunno.
> > > > > > > I'll be glad to change this either way, your call. Given that this is
> > > > > > > an obsolete protocol that seems to only be a target for drive-by fuzzers
> > > > > > > to attack, whatever the simplest thing to do to quiet them up I'll be
> > > > > > > glad to implement.
> > > > > > >
> > > > > > > Or can we just delete this stuff entirely? :)
> > > > > > Yes.
> > > > > >
> > > > > > My thinking is to delete hamradio, nfc, atm, caif.. [more to come]
> > > > > > Create GH repos which provide them as OOT modules.
> > > > > > Hopefully we can convince any existing users to switch to that.
> > > > > >
> > > > > > The only thing stopping me is the concern that this is just the softest
> > > > > > target and the LLMs will find something else to focus on which we can't
> > > > > > delete. I suspect any PCIe driver can be flooded with "aren't you
> > > > > > trusting the HW to provide valid responses here?" bullshit.
> > > > > >
> > > > > > But hey, let's try. I'll post a patch nuking all of hamradio later
> > > > > > today.
> > > > > Well, either we "expunge" this code to OOT repos, or we mark it
> > > > > as broken and tell everyone that we don't take security fixes
> > > > > for anything that depends on BROKEN. I'd personally rather expunge.
> > > > +1 for "expunge" to prevent LLM-based patch flood.
> > > >
> > > > IIRC, we did that recently for one driver only used by OpenWRT ?
> > > >
> > > >
> > > If the main concern here is ongoing maintenance of these Ham Radio related
> > > protocols/drivers, can we pause for a moment on anything as dramatic as
> > > removing from the tree entirely ?
> > Sure, but:
> >
> > > There is a good cohort of capable kernel folks that either are or were ham
> > > radio operators who I believe, upon realising that things have got to this
> > > point, will be happy to redouble efforts to ensure this code maintained and
> > > tested to a satisfactory standard.
> > We need this code to be maintained, because as is being shown, there are
> > reported problems with it that will affect these devices/networks that
> > you all are using. So all we need is a maintainer for this to be able
> > to take reports that we get and fix things up as needed. I know you
> > have that experience, want to come back to kernel development, we've
> > missed you :)
>
> That's most kind Greg, thank you, have missed all you cool kids too :)
>
> More seriously though - I'd be up for doing it, but I think there may be
> others better placed than I who haven't yet realised we have this conundrum.
> I'm nudging a few folks offline on this front.
The main "conundrum" is, is that this protocol completly trusts the
hardware to give the kernel the "correct" data. So if you trust the
hardware to work properly, it will be fine, but as the fuzzing tools are
finding, if the data from the hardware modems is a bit out-of-spec,
"bad" things can happen.
I don't know how well controlled the data is from these devices, if it's
just a "pass through" from what they get off the "wire" or if the
devices always ensure the protocol packets are sane before passing them
off to the kernel. That's going to be something you all with the
hardware is going to have to determine in order to keep this a working
system over time. Especially given that this is a wireless protcol
where you "have" to trust the remote end.
thanks,
greg k-h
^ permalink raw reply
* [PATCH] net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
From: Pavitra Jha @ 2026-04-11 8:39 UTC (permalink / raw)
To: w; +Cc: chandrashekar.devegowda, linux-wwan, netdev, stable, Pavitra Jha
t7xx_port_enum_msg_handler() uses the modem-supplied port_count field as
a loop bound over port_msg->data[] without checking that the message buffer
contains sufficient data. A modem sending port_count=65535 in a 12-byte
buffer triggers a slab-out-of-bounds read of up to 262140 bytes.
Add a struct_size() check after extracting port_count and before the loop.
Pass msg_len from both call sites: skb->len at the DPMAIF path after
skb_pull(), and the captured rt_feature->data_len at the handshake path.
Fixes: 1e3e8eb9b6e3 ("net: wwan: t7xx: Add control DMA interface")
Cc: stable@vger.kernel.org
Reported-by: Pavitra Jha <jhapavitra98@gmail.com>
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
---
drivers/net/wwan/t7xx/t7xx_modem_ops.c | 14 +++++++-------
drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c | 12 +++++++++---
drivers/net/wwan/t7xx/t7xx_port_proxy.h | 2 +-
3 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
index 7968e208d..d0559fe16 100644
--- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c
+++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
@@ -453,25 +453,25 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
{
enum mtk_feature_support_type ft_spt_st, ft_spt_cfg;
struct mtk_runtime_feature *rt_feature;
+ size_t feat_data_len;
int i, offset;
offset = sizeof(struct feature_query);
for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) {
rt_feature = data + offset;
- offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len);
-
+ feat_data_len = le32_to_cpu(rt_feature->data_len);
+ offset += sizeof(*rt_feature) + feat_data_len;
ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]);
if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED)
continue;
-
ft_spt_st = FIELD_GET(FEATURE_MSK, rt_feature->support_info);
if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED)
return -EINVAL;
-
- if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM)
- t7xx_port_enum_msg_handler(ctl->md, rt_feature->data);
+ if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) {
+ t7xx_port_enum_msg_handler(ctl->md, rt_feature->data,
+ feat_data_len);
+ }
}
-
return 0;
}
diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
index ae632ef96..d984a688d 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
@@ -124,7 +124,7 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
* * 0 - Success.
* * -EFAULT - Message check failure.
*/
-int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
+int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len)
{
struct device *dev = &md->t7xx_dev->pdev->dev;
unsigned int version, port_count, i;
@@ -141,6 +141,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
}
port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info));
+
+ if (msg_len < struct_size(port_msg, data, port_count)) {
+ dev_err(dev, "Port enum msg too short: need %zu, have %zu\n",
+ struct_size(port_msg, data, port_count), msg_len);
+ return -EINVAL;
+ }
+
for (i = 0; i < port_count; i++) {
u32 port_info = le32_to_cpu(port_msg->data[i]);
unsigned int ch_id;
@@ -154,7 +161,6 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
return 0;
}
-
static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
{
const struct t7xx_port_conf *port_conf = port->port_conf;
@@ -191,7 +197,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
case CTL_ID_PORT_ENUM:
skb_pull(skb, sizeof(*ctrl_msg_h));
- ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data);
+ ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len);
if (!ret)
ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0);
else
diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
index f0918b36e..7c3190bf0 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h
+++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
@@ -103,7 +103,7 @@ void t7xx_port_proxy_reset(struct port_proxy *port_prox);
void t7xx_port_proxy_uninit(struct port_proxy *port_prox);
int t7xx_port_proxy_init(struct t7xx_modem *md);
void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state);
-int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg);
+int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len);
int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id,
bool en_flag);
void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id);
--
2.51.0
^ permalink raw reply related
* Re: [PATCH net-next 1/3] psp: add crypt-offset and spi-threshold get/set attributes
From: Akhilesh Samineni @ 2026-04-11 8:38 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Willem de Bruijn, davem, edumazet, pabeni, andrew+netdev, horms,
willemb, daniel.zahka, netdev, linux-kernel,
jayakrishnan.udayavarma, ajit.khaparde, kiran.kella, sachin.suman
In-Reply-To: <20260410135736.4bc7ed46@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 1125 bytes --]
On Sat, Apr 11, 2026 at 2:27 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Sat, 11 Apr 2026 01:06:06 +0530 Akhilesh Samineni wrote:
> > On Wed, Apr 8, 2026 at 6:34 AM Jakub Kicinski <kuba@kernel.org> wrote:
> > > On Tue, 07 Apr 2026 17:37:41 -0400 Willem de Bruijn wrote:
> > > > PSP defines a 6-bit field in 4 octet units. Does this need bounds checking?
> > >
> > > More fundamentally, were we to support this -- is it a device property
> > > or an assoc property?
> >
> > It's a device property. All associations under the device will share
> > the same crypt-offset.
>
> I don't think there's anything in the spec that says the crypto
> offset is device level.
Yes, the spec didn’t mention the device level. We currently chose the
per-device approach based on the hardware implementation.
> At the very least every L4 proto may want to have a different offset.
We agree that per-protocol granularity would be more flexible because
L4 protocol header sizes differ.
> We should probably hold off adding this until a real user appears.
Sure we will hold off this change till that time.
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4211 bytes --]
^ permalink raw reply
* Re: [PATCH] net/ipv6: repeat route lookup with saddr set for ECMP
From: Maximilian Moehl @ 2026-04-11 8:18 UTC (permalink / raw)
To: Maximilian Moehl, Paolo Abeni, David S. Miller, David Ahern,
Eric Dumazet, Jakub Kicinski, Simon Horman
Cc: netdev, linux-kernel
In-Reply-To: <DHGZBEMZ25SW.3LE7U694ND0L0@moehl.eu>
On Tue Mar 31, 2026 at 2:50 PM CEST, Maximilian Moehl wrote:
> On Mon Mar 30, 2026 at 9:56 AM CEST, Paolo Abeni wrote:
>> On 3/29/26 11:12 AM, Maximilian Moehl wrote:
>>> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
>>> index 8e2a6b28cea7..465fce51d017 100644
>>> --- a/net/ipv6/ip6_output.c
>>> +++ b/net/ipv6/ip6_output.c
>>> @@ -1148,6 +1148,18 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
>>> *dst = NULL;
>>> }
>>>
>>> + /* If ECMP was involved the initial hash was calculted
>>> + * with saddr=:: which can result in instability
>>> + * when it is later re-calculated with the selected
>>> + * saddr. Lookup the route again with the chosen
>>> + * saddr to get a stable result.
>>> + */
>>> + if (fl6->mp_hash) {
>>> + fl6->mp_hash = 0;
>>> + dst_release(*dst);
>>> + *dst = NULL;
>>> + }
>>> +
>>> if (fl6->flowi6_oif)
>>> flags |= RT6_LOOKUP_F_IFACE;
>>> }
>>
>> This apparently breaks ipv6 fib tests (fib_tests.sh):
>>
>> # IPv6 multipath load balance test
>> # TEST: IPv6 multipath loadbalance [FAIL]
>>
>> see
>> https://github.com/linux-netdev/nipa/wiki/How-to-run-netdev-selftests-CI-style
>> on how to reproduce the tests.
>>
>> Also this would deserve additional testcases.
>
> Thank you for the pointer, I will look into the tests.
I've investigated the test failure. The logic I introduced causes the
packet to leave interface 1 with the address of interface 3 and is
therefore not picked up by the TC counter causing the test failure.
IPv4 does not have this issue, neither does it have the issue I'm
trying to fix for IPv6.
>> Without diving much inside the code I have the feeling this change is
>> plugged into the wrong place: multipath selection logic should be
>> encapsulated by fib6_select_path().
I further looked into how IPv4 prevents this issue from occurring.
Initially I thought it was because it does more than one route lookup,
but if I got it right now, it's because of the scoring logic in
fib_select_multipath. It adds one point for a matching hash bucket and
two points for a matching source address. After initially selecting an
outgoing interface, and with that a source address, the flow stays
bound to the initially selected interface, no matter what the hash is
(unless there's a second interface with the same address where the
hash matches, but in that case switching interfaces is probably
fine?).
I will prepare a new patch that addresses this difference so that IPv6
also prefers the outgoing interface with a matching source address
over the hash bucket it would select otherwise.
--
Max
^ permalink raw reply
* [PATCH v2 6/6] bus: mhi: host: mhi_phc: Add support for PHC over MHI
From: Krishna Chaitanya Chundru @ 2026-04-11 8:12 UTC (permalink / raw)
To: Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev,
Krishna Chaitanya Chundru, Imran Shaik, Taniya Das
In-Reply-To: <20260411-tsc_timesync-v2-0-6f25f72987b3@oss.qualcomm.com>
From: Imran Shaik <imran.shaik@oss.qualcomm.com>
This patch introduces the MHI PHC (PTP Hardware Clock) driver, which
registers a PTP (Precision Time Protocol) clock and communicates with
the MHI core to get the device side timestamps. These timestamps are
then exposed to the PTP subsystem, enabling precise time synchronization
between the host and the device.
The following diagram illustrates the architecture and data flow:
+-------------+ +--------------------+ +--------------+
|Userspace App|<-->|Kernel PTP framework|<-->|MHI PHC Driver|
+-------------+ +--------------------+ +--------------+
|
v
+-------------------------------+ +-----------------+
| MHI Device (Timestamp source) |<------->| MHI Core Driver |
+-------------------------------+ +-----------------+
- User space applications use the standard Linux PTP interface.
- The PTP subsystem routes IOCTLs to the MHI PHC driver.
- The MHI PHC driver communicates with the MHI core to fetch timestamps.
- The MHI core interacts with the device to retrieve accurate time data.
Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Imran Shaik <imran.shaik@oss.qualcomm.com>
---
drivers/bus/mhi/host/Kconfig | 8 ++
drivers/bus/mhi/host/Makefile | 1 +
drivers/bus/mhi/host/mhi_phc.c | 150 +++++++++++++++++++++++++++++++++++++
drivers/bus/mhi/host/mhi_phc.h | 28 +++++++
drivers/bus/mhi/host/pci_generic.c | 23 ++++++
5 files changed, 210 insertions(+)
diff --git a/drivers/bus/mhi/host/Kconfig b/drivers/bus/mhi/host/Kconfig
index da5cd0c9fc620ab595e742c422f1a22a2a84c7b9..b4eabf3e5c56907de93232f02962040e979c3110 100644
--- a/drivers/bus/mhi/host/Kconfig
+++ b/drivers/bus/mhi/host/Kconfig
@@ -29,3 +29,11 @@ config MHI_BUS_PCI_GENERIC
This driver provides MHI PCI controller driver for devices such as
Qualcomm SDX55 based PCIe modems.
+config MHI_BUS_PHC
+ bool "MHI PHC driver"
+ depends on MHI_BUS_PCI_GENERIC
+ help
+ This driver provides Precision Time Protocol (PTP) clock and
+ communicates with MHI PCI driver to get the device side timestamp,
+ which enables precise time synchronization between the host and
+ the device.
diff --git a/drivers/bus/mhi/host/Makefile b/drivers/bus/mhi/host/Makefile
index 859c2f38451c669b3d3014c374b2b957c99a1cfe..5ba244fe7d596834ea535797efd3428963ba0ed0 100644
--- a/drivers/bus/mhi/host/Makefile
+++ b/drivers/bus/mhi/host/Makefile
@@ -4,3 +4,4 @@ mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o
obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
mhi_pci_generic-y += pci_generic.o
+mhi_pci_generic-$(CONFIG_MHI_BUS_PHC) += mhi_phc.o
diff --git a/drivers/bus/mhi/host/mhi_phc.c b/drivers/bus/mhi/host/mhi_phc.c
new file mode 100644
index 0000000000000000000000000000000000000000..fa04eb7f6025fa281d86c0a45b5f7d3e61f5ce12
--- /dev/null
+++ b/drivers/bus/mhi/host/mhi_phc.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mhi.h>
+#include <linux/ptp_clock_kernel.h>
+#include "mhi_phc.h"
+
+#define NSEC 1000000000ULL
+
+/**
+ * struct mhi_phc_dev - MHI PHC device
+ * @ptp_clock: associated PTP clock
+ * @ptp_clock_info: PTP clock information
+ * @mhi_dev: associated mhi device object
+ * @lock: spinlock
+ * @enabled: Flag to track the state of the MHI device
+ */
+struct mhi_phc_dev {
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_clock_info;
+ struct mhi_device *mhi_dev;
+ spinlock_t lock;
+ bool enabled;
+};
+
+static int qcom_ptp_gettimex64(struct ptp_clock_info *ptp, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct mhi_phc_dev *phc_dev = container_of(ptp, struct mhi_phc_dev, ptp_clock_info);
+ struct mhi_timesync_info time;
+ ktime_t ktime_cur;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&phc_dev->lock, flags);
+ if (!phc_dev->enabled) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = mhi_get_remote_tsc_time_sync(phc_dev->mhi_dev, &time);
+ if (ret)
+ goto err;
+
+ ktime_cur = time.t_dev_hi * NSEC + time.t_dev_lo;
+ *ts = ktime_to_timespec64(ktime_cur);
+
+ dev_dbg(&phc_dev->mhi_dev->dev, "TSC time stamps sec:%u nsec:%u current:%lld\n",
+ time.t_dev_hi, time.t_dev_lo, ktime_cur);
+
+ /* Update pre and post timestamps for PTP_SYS_OFFSET_EXTENDED*/
+ if (sts != NULL) {
+ sts->pre_ts = ktime_to_timespec64(time.t_host_pre);
+ sts->post_ts = ktime_to_timespec64(time.t_host_post);
+ dev_dbg(&phc_dev->mhi_dev->dev, "pre:%lld post:%lld\n",
+ time.t_host_pre, time.t_host_post);
+ }
+
+err:
+ spin_unlock_irqrestore(&phc_dev->lock, flags);
+
+ return ret;
+}
+
+int mhi_phc_start(struct mhi_controller *mhi_cntrl)
+{
+ struct mhi_phc_dev *phc_dev = dev_get_drvdata(&mhi_cntrl->mhi_dev->dev);
+ unsigned long flags;
+
+ if (!phc_dev) {
+ dev_err(&mhi_cntrl->mhi_dev->dev, "Driver data is NULL\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&phc_dev->lock, flags);
+ phc_dev->enabled = true;
+ spin_unlock_irqrestore(&phc_dev->lock, flags);
+
+ return 0;
+}
+
+int mhi_phc_stop(struct mhi_controller *mhi_cntrl)
+{
+ struct mhi_phc_dev *phc_dev = dev_get_drvdata(&mhi_cntrl->mhi_dev->dev);
+ unsigned long flags;
+
+ if (!phc_dev) {
+ dev_err(&mhi_cntrl->mhi_dev->dev, "Driver data is NULL\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&phc_dev->lock, flags);
+ phc_dev->enabled = false;
+ spin_unlock_irqrestore(&phc_dev->lock, flags);
+
+ return 0;
+}
+
+static struct ptp_clock_info qcom_ptp_clock_info = {
+ .owner = THIS_MODULE,
+ .gettimex64 = qcom_ptp_gettimex64,
+};
+
+int mhi_phc_init(struct mhi_controller *mhi_cntrl)
+{
+ struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
+ struct mhi_phc_dev *phc_dev;
+ int ret;
+
+ phc_dev = devm_kzalloc(&mhi_dev->dev, sizeof(*phc_dev), GFP_KERNEL);
+ if (!phc_dev)
+ return -ENOMEM;
+
+ phc_dev->mhi_dev = mhi_dev;
+
+ phc_dev->ptp_clock_info = qcom_ptp_clock_info;
+ strscpy(phc_dev->ptp_clock_info.name, mhi_dev->name, PTP_CLOCK_NAME_LEN);
+
+ spin_lock_init(&phc_dev->lock);
+
+ phc_dev->ptp_clock = ptp_clock_register(&phc_dev->ptp_clock_info, &mhi_dev->dev);
+ if (IS_ERR(phc_dev->ptp_clock)) {
+ ret = PTR_ERR(phc_dev->ptp_clock);
+ dev_err(&mhi_dev->dev, "Failed to register PTP clock\n");
+ phc_dev->ptp_clock = NULL;
+ return ret;
+ }
+
+ dev_set_drvdata(&mhi_dev->dev, phc_dev);
+
+ dev_dbg(&mhi_dev->dev, "probed MHI PHC dev: %s\n", mhi_dev->name);
+ return 0;
+};
+
+void mhi_phc_exit(struct mhi_controller *mhi_cntrl)
+{
+ struct mhi_phc_dev *phc_dev = dev_get_drvdata(&mhi_cntrl->mhi_dev->dev);
+
+ if (!phc_dev)
+ return;
+
+ /* disable the node */
+ ptp_clock_unregister(phc_dev->ptp_clock);
+ phc_dev->enabled = false;
+}
diff --git a/drivers/bus/mhi/host/mhi_phc.h b/drivers/bus/mhi/host/mhi_phc.h
new file mode 100644
index 0000000000000000000000000000000000000000..e6b0866bc768ba5a8ac3e4c40a99aa2050db1389
--- /dev/null
+++ b/drivers/bus/mhi/host/mhi_phc.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifdef CONFIG_MHI_BUS_PHC
+int mhi_phc_init(struct mhi_controller *mhi_cntrl);
+int mhi_phc_start(struct mhi_controller *mhi_cntrl);
+int mhi_phc_stop(struct mhi_controller *mhi_cntrl);
+void mhi_phc_exit(struct mhi_controller *mhi_cntrl);
+#else
+static inline int mhi_phc_init(struct mhi_controller *mhi_cntrl)
+{
+ return 0;
+}
+
+static inline int mhi_phc_start(struct mhi_controller *mhi_cntrl)
+{
+ return 0;
+}
+
+static inline int mhi_phc_stop(struct mhi_controller *mhi_cntrl)
+{
+ return 0;
+}
+
+static inline void mhi_phc_exit(struct mhi_controller *mhi_cntrl) {}
+#endif
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index b1122c7224bdd469406d96af6d3df342040e1002..6cba5cecd1adb40396bba30c9b2a551898dce871 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -16,6 +16,7 @@
#include <linux/pm_runtime.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
+#include "mhi_phc.h"
#define MHI_PCI_DEFAULT_BAR_NUM 0
@@ -1044,6 +1045,7 @@ struct mhi_pci_device {
struct timer_list health_check_timer;
unsigned long status;
bool reset_on_remove;
+ bool mhi_phc_init_done;
};
#ifdef readq
@@ -1084,6 +1086,7 @@ static void mhi_pci_status_cb(struct mhi_controller *mhi_cntrl,
enum mhi_callback cb)
{
struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
+ struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
/* Nothing to do for now */
switch (cb) {
@@ -1091,9 +1094,21 @@ static void mhi_pci_status_cb(struct mhi_controller *mhi_cntrl,
case MHI_CB_SYS_ERROR:
dev_warn(&pdev->dev, "firmware crashed (%u)\n", cb);
pm_runtime_forbid(&pdev->dev);
+ /* Stop PHC */
+ if (mhi_cntrl->tsc_timesync)
+ mhi_phc_stop(mhi_cntrl);
break;
case MHI_CB_EE_MISSION_MODE:
pm_runtime_allow(&pdev->dev);
+ /* Start PHC */
+ if (mhi_cntrl->tsc_timesync) {
+ if (!mhi_pdev->mhi_phc_init_done) {
+ mhi_phc_init(mhi_cntrl);
+ mhi_pdev->mhi_phc_init_done = true;
+ }
+
+ mhi_phc_start(mhi_cntrl);
+ }
break;
default:
break;
@@ -1236,6 +1251,10 @@ static void mhi_pci_recovery_work(struct work_struct *work)
pm_runtime_forbid(&pdev->dev);
+ /* Stop PHC */
+ if (mhi_cntrl->tsc_timesync)
+ mhi_phc_stop(mhi_cntrl);
+
/* Clean up MHI state */
if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
mhi_power_down(mhi_cntrl, false);
@@ -1457,6 +1476,10 @@ static void mhi_pci_remove(struct pci_dev *pdev)
timer_delete_sync(&mhi_pdev->health_check_timer);
cancel_work_sync(&mhi_pdev->recovery_work);
+ /* Remove PHC */
+ if (mhi_cntrl->tsc_timesync)
+ mhi_phc_exit(mhi_cntrl);
+
if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
mhi_power_down(mhi_cntrl, true);
mhi_unprepare_after_power_down(mhi_cntrl);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 5/6] bus: mhi: host: Update the Time sync logic to read 64 bit register value
From: Krishna Chaitanya Chundru @ 2026-04-11 8:12 UTC (permalink / raw)
To: Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev,
Krishna Chaitanya Chundru
In-Reply-To: <20260411-tsc_timesync-v2-0-6f25f72987b3@oss.qualcomm.com>
Instead of reading low and high of the mhi registers twice use 64 bit
register value to avoid any time penality.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/bus/mhi/host/main.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index 99917593e1da06f1dece7b5b0037c2485953410f..e853419a0195dff4a18123631cb1f74242ab4428 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -1643,6 +1643,7 @@ static int mhi_get_remote_time(struct mhi_controller *mhi_cntrl, struct mhi_time
struct mhi_timesync_info *time)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ u64 val = U64_MAX;
int ret, i;
if (!mhi_tsync && !mhi_tsync->time_reg) {
@@ -1680,15 +1681,25 @@ static int mhi_get_remote_time(struct mhi_controller *mhi_cntrl, struct mhi_time
* transition to L0.
*/
for (i = 0; i < MHI_NUM_BACK_TO_BACK_READS; i++) {
- ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
- TSC_TIMESYNC_TIME_LOW_OFFSET, &time->t_dev_lo);
-
- ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
- TSC_TIMESYNC_TIME_HIGH_OFFSET, &time->t_dev_hi);
+ if (mhi_cntrl->read_reg64) {
+ ret = mhi_read_reg64(mhi_cntrl, mhi_tsync->time_reg,
+ TSC_TIMESYNC_TIME_LOW_OFFSET, &val);
+ } else {
+ ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
+ TSC_TIMESYNC_TIME_LOW_OFFSET, &time->t_dev_lo);
+
+ ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
+ TSC_TIMESYNC_TIME_HIGH_OFFSET, &time->t_dev_hi);
+ }
}
time->t_host_post = ktime_get_real();
+ if (mhi_cntrl->read_reg64) {
+ time->t_dev_lo = (u32)val;
+ time->t_dev_hi = (u32)(val >> 32);
+ }
+
local_irq_enable();
preempt_enable();
--
2.34.1
^ permalink raw reply related
* [PATCH v2 4/6] bus: mhi: pci_generic: Add support for 64 bit register read & write
From: Krishna Chaitanya Chundru @ 2026-04-11 8:12 UTC (permalink / raw)
To: Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev,
Krishna Chaitanya Chundru
In-Reply-To: <20260411-tsc_timesync-v2-0-6f25f72987b3@oss.qualcomm.com>
Add functions which does read and write on 64 bit registers.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/bus/mhi/host/pci_generic.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index 0884a384b77fc3f56fa62a12351933132ffc9293..b1122c7224bdd469406d96af6d3df342040e1002 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -1046,6 +1046,27 @@ struct mhi_pci_device {
bool reset_on_remove;
};
+#ifdef readq
+static int mhi_pci_read_reg64(struct mhi_controller *mhi_cntrl,
+ void __iomem *addr, u64 *out)
+{
+ *out = readq(addr);
+ return 0;
+}
+#else
+#define mhi_pci_read_reg64 NULL
+#endif
+
+#ifdef writeq
+static void mhi_pci_write_reg64(struct mhi_controller *mhi_cntrl,
+ void __iomem *addr, u64 val)
+{
+ writeq(val, addr);
+}
+#else
+#define mhi_pci_write_reg64 NULL
+#endif
+
static int mhi_pci_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *addr, u32 *out)
{
@@ -1347,6 +1368,8 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_cntrl->read_reg = mhi_pci_read_reg;
mhi_cntrl->write_reg = mhi_pci_write_reg;
+ mhi_cntrl->read_reg64 = mhi_pci_read_reg64;
+ mhi_cntrl->write_reg64 = mhi_pci_write_reg64;
mhi_cntrl->status_cb = mhi_pci_status_cb;
mhi_cntrl->runtime_get = mhi_pci_runtime_get;
mhi_cntrl->runtime_put = mhi_pci_runtime_put;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 3/6] bus: mhi: host: Add support for 64bit register reads and writes
From: Krishna Chaitanya Chundru @ 2026-04-11 8:12 UTC (permalink / raw)
To: Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev,
Krishna Chaitanya Chundru
In-Reply-To: <20260411-tsc_timesync-v2-0-6f25f72987b3@oss.qualcomm.com>
Some mhi registers are of 64 bit size, instead of reading high value
and low value separately provide a new function op to read & write to
64 bit register.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/bus/mhi/host/main.c | 12 ++++++++++++
include/linux/mhi.h | 6 ++++++
2 files changed, 18 insertions(+)
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index b7a727b1a5d1f20b570c62707a991ec5b85bfec7..99917593e1da06f1dece7b5b0037c2485953410f 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -67,6 +67,18 @@ void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
mhi_cntrl->write_reg(mhi_cntrl, base + offset, val);
}
+static int __must_check mhi_read_reg64(struct mhi_controller *mhi_cntrl,
+ void __iomem *base, u32 offset, u64 *out)
+{
+ return mhi_cntrl->read_reg64(mhi_cntrl, base + offset, out);
+}
+
+static void __maybe_unused mhi_write_reg64(struct mhi_controller *mhi_cntrl, void __iomem *base,
+ u32 offset, u64 val)
+{
+ mhi_cntrl->write_reg64(mhi_cntrl, base + offset, val);
+}
+
int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 val)
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index f39c8ca7c251954f2d83c1227d206b600b88c75f..8e7257a9c907fb03571a86e29db5534f492678c7 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -376,6 +376,8 @@ struct mhi_timesync_info {
* @unmap_single: CB function to destroy TRE buffer
* @read_reg: Read a MHI register via the physical link (required)
* @write_reg: Write a MHI register via the physical link (required)
+ * @read_reg64: Read a 64 bit MHI register via the physical link (optional)
+ * @write_reg64: Write a 64 bit MHI register via the physical link (optional)
* @reset: Controller specific reset function (optional)
* @edl_trigger: CB function to trigger EDL mode (optional)
* @buffer_len: Bounce buffer length
@@ -462,6 +464,10 @@ struct mhi_controller {
u32 *out);
void (*write_reg)(struct mhi_controller *mhi_cntrl, void __iomem *addr,
u32 val);
+ int (*read_reg64)(struct mhi_controller *mhi_cntrl, void __iomem *addr,
+ u64 *out);
+ void (*write_reg64)(struct mhi_controller *mhi_cntrl, void __iomem *addr,
+ u64 val);
void (*reset)(struct mhi_controller *mhi_cntrl);
int (*edl_trigger)(struct mhi_controller *mhi_cntrl);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 2/6] bus: mhi: host: Add support for non-posted TSC timesync feature
From: Krishna Chaitanya Chundru @ 2026-04-11 8:12 UTC (permalink / raw)
To: Manivannan Sadhasivam, Richard Cochran
Cc: mhi, linux-arm-msm, linux-kernel, netdev,
Krishna Chaitanya Chundru, Vivek Pernamitta
In-Reply-To: <20260411-tsc_timesync-v2-0-6f25f72987b3@oss.qualcomm.com>
From: Vivek Pernamitta <quic_vpernami@quicinc.com>
Implement non-posted time synchronization as described in section 5.1.1
of the MHI v1.2 specification. The host disables low-power link states
to minimize latency, reads the local time, issues a MMIO read to the
device's TIME register.
Add support for initializing this feature and export a function to be
used by the drivers which does the time synchronization.
MHI reads the device time registers in the MMIO address space pointed to
by the capability register after disabling all low power modes and keeping
MHI in M0. Before and after MHI reads, the local time is captured
and shared for processing.
Signed-off-by: Vivek Pernamitta <quic_vpernami@quicinc.com>
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/bus/mhi/common.h | 4 +++
drivers/bus/mhi/host/init.c | 28 ++++++++++++++++
drivers/bus/mhi/host/internal.h | 9 +++++
drivers/bus/mhi/host/main.c | 74 +++++++++++++++++++++++++++++++++++++++++
include/linux/mhi.h | 37 +++++++++++++++++++++
5 files changed, 152 insertions(+)
diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
index 4c316f3d5a68beb01f15cf575b03747096fdcf2c..64f9b2b94387a112bb6b5e20c634c3ba8d6bc78e 100644
--- a/drivers/bus/mhi/common.h
+++ b/drivers/bus/mhi/common.h
@@ -118,6 +118,10 @@
#define CAP_CAPID_MASK GENMASK(31, 24)
#define CAP_NEXT_CAP_MASK GENMASK(23, 12)
+/* MHI TSC Timesync */
+#define TSC_TIMESYNC_TIME_LOW_OFFSET (0x8)
+#define TSC_TIMESYNC_TIME_HIGH_OFFSET (0xC)
+
/* Command Ring Element macros */
/* No operation command */
#define MHI_TRE_CMD_NOOP_PTR 0
diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c
index c2162aa04e810e45ccfbedd20aaa62f892420d31..eb720f671726d919646cbc450cd54bda655a1060 100644
--- a/drivers/bus/mhi/host/init.c
+++ b/drivers/bus/mhi/host/init.c
@@ -498,6 +498,30 @@ static int mhi_find_capability(struct mhi_controller *mhi_cntrl, u32 capability)
return 0;
}
+static int mhi_init_tsc_timesync(struct mhi_controller *mhi_cntrl)
+{
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ struct mhi_timesync *mhi_tsc_tsync;
+ u32 time_offset;
+ int ret;
+
+ time_offset = mhi_find_capability(mhi_cntrl, MHI_CAP_ID_TSC_TIME_SYNC);
+ if (!time_offset)
+ return -ENXIO;
+
+ mhi_tsc_tsync = devm_kzalloc(dev, sizeof(*mhi_tsc_tsync), GFP_KERNEL);
+ if (!mhi_tsc_tsync)
+ return -ENOMEM;
+
+ mhi_cntrl->tsc_timesync = mhi_tsc_tsync;
+ mutex_init(&mhi_tsc_tsync->ts_mutex);
+
+ /* save time_offset for obtaining time via MMIO register reads */
+ mhi_tsc_tsync->time_reg = mhi_cntrl->regs + time_offset;
+
+ return 0;
+}
+
int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
{
u32 val;
@@ -635,6 +659,10 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
return ret;
}
+ ret = mhi_init_tsc_timesync(mhi_cntrl);
+ if (ret)
+ dev_dbg(dev, "TSC Time synchronization init failure\n");
+
return 0;
}
diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
index 7b0ee5e3a12dd585064169b7b884750bf4d8c8db..a0e729e7a1198c1b82c70b6bfe3bc2ee24331229 100644
--- a/drivers/bus/mhi/host/internal.h
+++ b/drivers/bus/mhi/host/internal.h
@@ -15,6 +15,15 @@ extern const struct bus_type mhi_bus_type;
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
#define MHI_SOC_RESET_REQ BIT(0)
+/*
+ * With ASPM enabled, the link may enter a low power state, requiring
+ * a wake-up sequence. Use a short burst of back-to-back reads to
+ * transition the link to the active state. Based on testing,
+ * 4 iterations are necessary to ensure reliable wake-up without
+ * excess latency.
+ */
+#define MHI_NUM_BACK_TO_BACK_READS 4
+
struct mhi_ctxt {
struct mhi_event_ctxt *er_ctxt;
struct mhi_chan_ctxt *chan_ctxt;
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index 53c0ffe300702bcc3caa8fd9ea8086203c75b186..b7a727b1a5d1f20b570c62707a991ec5b85bfec7 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -1626,3 +1626,77 @@ int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_
return 0;
}
EXPORT_SYMBOL_GPL(mhi_get_channel_doorbell_offset);
+
+static int mhi_get_remote_time(struct mhi_controller *mhi_cntrl, struct mhi_timesync *mhi_tsync,
+ struct mhi_timesync_info *time)
+{
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ int ret, i;
+
+ if (!mhi_tsync && !mhi_tsync->time_reg) {
+ dev_err(dev, "Time sync is not supported\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
+ dev_err(dev, "MHI is not in active state, pm_state:%s\n",
+ to_mhi_pm_state_str(mhi_cntrl->pm_state));
+ return -EIO;
+ }
+
+ /* bring to M0 state */
+ ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&mhi_tsync->ts_mutex);
+ mhi_cntrl->runtime_get(mhi_cntrl);
+
+ /*
+ * time critical code to fetch device time, delay between these two steps
+ * should be deterministic as possible.
+ */
+ preempt_disable();
+ local_irq_disable();
+
+ time->t_host_pre = ktime_get_real();
+
+ /*
+ * To ensure the PCIe link is in L0 when ASPM is enabled, perform series
+ * of back-to-back reads. This is necessary because the link may be in a
+ * low-power state (e.g., L1 or L1ss), and need to be forced it to
+ * transition to L0.
+ */
+ for (i = 0; i < MHI_NUM_BACK_TO_BACK_READS; i++) {
+ ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
+ TSC_TIMESYNC_TIME_LOW_OFFSET, &time->t_dev_lo);
+
+ ret = mhi_read_reg(mhi_cntrl, mhi_tsync->time_reg,
+ TSC_TIMESYNC_TIME_HIGH_OFFSET, &time->t_dev_hi);
+ }
+
+ time->t_host_post = ktime_get_real();
+
+ local_irq_enable();
+ preempt_enable();
+
+ mhi_cntrl->runtime_put(mhi_cntrl);
+
+ mhi_device_put(mhi_cntrl->mhi_dev);
+
+ return 0;
+}
+
+int mhi_get_remote_tsc_time_sync(struct mhi_device *mhi_dev, struct mhi_timesync_info *time)
+{
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
+ struct mhi_timesync *mhi_tsc_tsync = mhi_cntrl->tsc_timesync;
+ int ret;
+
+ ret = mhi_get_remote_time(mhi_cntrl, mhi_tsc_tsync, time);
+ if (ret)
+ dev_err(&mhi_dev->dev, "Failed to get TSC Time Sync value:%d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mhi_get_remote_tsc_time_sync);
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index 88ccb3e14f481d6b85c2a314eb74ba960c2d4c81..f39c8ca7c251954f2d83c1227d206b600b88c75f 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -286,6 +286,30 @@ struct mhi_controller_config {
bool m2_no_db;
};
+/**
+ * struct mhi_timesync - MHI time synchronization structure
+ * @time_reg: Points to address of Timesync register
+ * @ts_mutex: Mutex for synchronization
+ */
+struct mhi_timesync {
+ void __iomem *time_reg;
+ struct mutex ts_mutex;
+};
+
+/**
+ * struct mhi_timesync_info - MHI time sync info structure
+ * @t_host_pre: Pre host soc time
+ * @t_host_post: Post host soc time
+ * @t_dev_lo: Mhi device time of lower dword
+ * @t_dev_hi: Mhi device time of higher dword
+ */
+struct mhi_timesync_info {
+ ktime_t t_host_pre;
+ ktime_t t_host_post;
+ u32 t_dev_lo;
+ u32 t_dev_hi;
+};
+
/**
* struct mhi_controller - Master MHI controller structure
* @name: Device name of the MHI controller
@@ -323,6 +347,7 @@ struct mhi_controller_config {
* @mhi_event: MHI event ring configurations table
* @mhi_cmd: MHI command ring configurations table
* @mhi_ctxt: MHI device context, shared memory between host and device
+ * @tsc_timesync: MHI TSC timesync
* @pm_mutex: Mutex for suspend/resume operation
* @pm_lock: Lock for protecting MHI power management state
* @timeout_ms: Timeout in ms for state transitions
@@ -401,6 +426,8 @@ struct mhi_controller {
struct mhi_cmd *mhi_cmd;
struct mhi_ctxt *mhi_ctxt;
+ struct mhi_timesync *tsc_timesync;
+
struct mutex pm_mutex;
rwlock_t pm_lock;
u32 timeout_ms;
@@ -795,4 +822,14 @@ bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
*/
int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset);
+/**
+ * mhi_get_remote_tsc_time_sync - get external soc time relative to local soc
+ * time pre and post using MMIO method.
+ * @mhi_dev: Device associated with the channels
+ * @time: mhi_timesync_info to get device time details
+ *
+ * Returns:
+ * 0 for success, error code for failure
+ */
+int mhi_get_remote_tsc_time_sync(struct mhi_device *mhi_dev, struct mhi_timesync_info *time);
#endif /* _MHI_H_ */
--
2.34.1
^ permalink raw reply related
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