* Re: [PATCH net] nfc: nci: fix out-of-bounds write in nci_target_auto_activated()
From: Simon Horman @ 2026-06-25 12:57 UTC (permalink / raw)
To: Sam P
Cc: david, davem, edumazet, kuba, pabeni, oe-linux-nfc, netdev,
linux-kernel, stable
In-Reply-To: <443e2ee1-e9c1-45ca-be57-0c67966ec7d9@bynar.io>
On Wed, Jun 24, 2026 at 12:33:21AM +0200, Sam P wrote:
> On 23/06/2026 19:21, Simon Horman wrote:
> > > diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
> > > index c96512bb86531..566ca839fa488 100644
> > > --- a/net/nfc/nci/ntf.c
> > > +++ b/net/nfc/nci/ntf.c
> > > @@ -603,6 +603,12 @@ static void nci_target_auto_activated(struct nci_dev *ndev,
> > > struct nfc_target *target;
> > > int rc;
> > >
> > > + /* This is a new target, check if we've enough room */
> > > + if (ndev->n_targets == NCI_MAX_DISCOVERED_TARGETS) {
> > > + pr_debug("not enough room, ignoring new target...\n");
> > > + return;
> >
> > [Severity: High]
> > Does this early return cause the state machine to stall?
> >
> > Looking at nci_rf_intf_activated_ntf_packet(), the state is transitioned
> > to NCI_POLL_ACTIVE right before this function is called:
> >
> > net/nfc/nci/ntf.c:nci_rf_intf_activated_ntf_packet() {
> > ...
> > atomic_set(&ndev->state, NCI_POLL_ACTIVE);
> > if (err == NCI_STATUS_OK)
> > nci_target_auto_activated(ndev, &ntf);
> > ...
> > }
> >
> > Because of the early return here, nfc_targets_found() is skipped. Does this
> > leave the state wedged in NCI_POLL_ACTIVE, permanently blocking future
> > attempts to stop polling since nci_stop_poll() requires the state to be
> > NCI_DISCOVERY?
> >
> > > + }
> > > +
> > > target = &ndev->targets[ndev->n_targets];
> >
> > [ ... ]
>
> Thanks for sharing the review.
>
> I don't believe the early return can stall the state machine, as
> nci_target_auto_activated() does not touch ndev->state.
>
> nci_rf_intf_activated_ntf_packet() sets ndev->state = NCI_POLL_ACTIVE
> before it calls nci_target_auto_activated(), so the transition has
> already happened by the time the new check runs. Neither
> nci_target_auto_activated() nor nfc_targets_found() modifies ndev->state;
> nfc_targets_found() only clears the nfc_dev dev->polling flag under
> device_lock, which is independent of the NCI state machine that
> nci_stop_poll() tests. So ndev->state is NCI_POLL_ACTIVE after an
> auto-activation whether the target is appended or the guard returns
> early; the state behaviour is the same with or without this patch.
>
> The check is the same n_targets == NCI_MAX_DISCOVERED_TARGETS guard the
> sibling nci_add_new_target() (the RF_DISCOVER_NTF path) has used since
> the Fixes: commit; it only drops the out-of-spec 11th+ target, which a
> conformant controller shouldn't produce. The device still returns to
> NCI_DISCOVERY/NCI_IDLE through the normal RF_DEACTIVATE path.
Thanks, I agree with your analysis.
> > [Severity: Critical]
> > This isn't a bug introduced by this patch, but can the ntf union copy
> > uninitialized stack memory into persistent device structures?
> >
> > In net/nfc/nci/ntf.c:nci_rf_discover_ntf_packet(), the local ntf struct
> > is declared on the stack:
> >
> > struct nci_rf_discover_ntf ntf;
> >
> > If the incoming packet has ntf.rf_tech_specific_params_len == 0, the
> > extraction routines are bypassed, leaving the rf_tech_specific_params union
> > uninitialized. The struct is then passed to nci_add_new_target():
> >
> > if (add_target == true)
> > nci_add_new_target(ndev, &ntf);
> >
> > This eventually calls nci_add_new_protocol(), which blindly reads from this
> > union:
> >
> > nfca_poll = (struct rf_tech_specific_params_nfca_poll *)params;
> > target->sens_res = nfca_poll->sens_res;
> > target->sel_res = nfca_poll->sel_res;
> > target->nfcid1_len = nfca_poll->nfcid1_len;
> >
> > Could this uninitialized stack garbage be copied into ndev->targets, and
> > later broadcasted to userspace via Netlink in nfc_targets_found()?
>
> I managed to repro this issue locally via KMSAN, I can queue up a patch
> for this that I can submit separately, ntf just needs to be zero-initd.
Thanks, I agree this can be handled separately.
> > [Severity: Critical]
> > This isn't a bug introduced by this patch, but does the general bytes length
> > calculation underflow?
> >
> > In net/nfc/nci/ntf.c:nci_store_general_bytes_nfc_dep():
> >
> > ndev->remote_gb_len = min_t(__u8,
> > (ntf->activation_params.listen_nfc_dep.atr_req_len -
> > NFC_ATR_REQ_GT_OFFSET), NFC_ATR_REQ_GB_MAXSIZE);
> >
> > memcpy(ndev->remote_gb,
> > (ntf->activation_params.listen_nfc_dep.atr_req +
> > NFC_ATR_REQ_GT_OFFSET), ndev->remote_gb_len);
> >
> > The length is computed by subtracting NFC_ATR_REQ_GT_OFFSET (14) from
> > atr_req_len. If atr_req_len is less than 14, this subtraction will
> > underflow.
> >
> > Because of the min_t cast, this large underflowed value is bounded to
> > NFC_ATR_REQ_GB_MAXSIZE (48). The memcpy() then copies 48 bytes starting
> > from atr_req + 14 into ndev->remote_gb.
> >
> > Since the source buffer is part of the ntf union allocated on the stack
> > in nci_rf_intf_activated_ntf_packet(), could this leak up to 48 bytes of
> > uninitialized kernel stack memory to userspace?
>
> This one is already being fixed on the list:
>
> [PATCH net v4 1/5] nfc: nci: fix u8 underflow in
> nci_store_general_bytes_nfc_dep (Lekë Hapçiu)
> https://lore.kernel.org/all/20260424180151.3808557-2-snowwlake@icloud.com/
Ack.
> > [Severity: High]
> > This isn't a bug introduced by this patch, but does the lack of skb->len
> > validation in NCI response handlers allow out-of-bounds reads?
> >
> > In nci_core_init_rsp_packet_v1() in net/nfc/nci/rsp.c, fields are read
> > and arrays are copied from the payload:
> >
> > ndev->num_supported_rf_interfaces =
> > rsp_1->num_supported_rf_interfaces;
> >
> > memcpy(ndev->supported_rf_interfaces, rsp_1->supported_rf_interfaces,
> > ndev->num_supported_rf_interfaces);
> >
> > Similarly in nci_core_init_rsp_packet_v2():
> >
> > while (rf_interface_idx < ndev->num_supported_rf_interfaces) {
> > ndev->supported_rf_interfaces[rf_interface_idx++] =
> > *supported_rf_interface++;
> > }
> >
> > Since the NCI control header is stripped via skb_pull(), leaving the
> > payload length in skb->len, what happens if an NFCC sends a short response
> > packet (e.g. plen = 1)? Could this cause the handlers to read adjacent
> > kernel slab memory?
>
> I've since submitted a fix for this issue:
>
> [PATCH net] nfc: nci: fix uninit-value in
> nci_core_init_rsp_packet()
> https://lore.kernel.org/netdev/20260623222402.175798-1-sam@bynar.io/
Ack.
> > [Severity: High]
> > This isn't a bug introduced by this patch, but is there a missing boundary
> > validation when extracting activation parameters?
> >
> > In net/nfc/nci/ntf.c:nci_extract_activation_params_iso_dep():
> >
> > nfca_poll->rats_res_len = min_t(__u8, *data++, NFC_ATS_MAXSIZE);
> > if (nfca_poll->rats_res_len > 0) {
> > memcpy(nfca_poll->rats_res, data, nfca_poll->rats_res_len);
> > }
> >
> > And in nci_extract_activation_params_nfc_dep():
> >
> > poll->atr_res_len = min_t(__u8, *data++, NFC_ATR_RES_MAXSIZE - 2);
> > if (poll->atr_res_len > 0)
> > memcpy(poll->atr_res, data, poll->atr_res_len);
> >
> > These inner lengths are bounded by a maximum size like NFC_ATS_MAXSIZE, but
> > they are not validated against the remaining bytes in the SKB (skb->len).
> >
> > If a malformed packet provides a large inner length but a short payload,
> > could this memcpy() read out of bounds into the device structure,
> > potentially leaking adjacent slab memory to userspace later?
>
> This one is already being fixed on the list too:
>
> [PATCH v3] nfc: nci: add data_len bound checks to activation
> parameter extractors (Bryam Vargas)
> https://lore.kernel.org/all/20260612-b4-disp-6d52d8b0-v3-1-e26221f8826d@proton.me/
Ack.
> > [Severity: High]
> > This isn't a bug introduced by this patch, but is there a potential
> > priority inversion or timeout deadlock involving the device lock?
> >
> > Functions like nfc_stop_poll() in net/nfc/core.c acquire the device lock:
> >
> > device_lock(&dev->dev);
> > ...
> > if (dev->ops->stop_poll)
> > dev->ops->stop_poll(dev);
> >
> > This eventually calls nci_request(), which synchronously waits for a
> > completion signaled by the nci_rx_work thread. However, if an NTF packet
> > is received first, nci_rx_work processes it and invokes
> > nfc_targets_found(), which also attempts to acquire the device lock:
> >
> > device_lock(&dev->dev);
> >
> > Since the calling thread already holds the device lock, nci_rx_work blocks
> > indefinitely. Because the RX worker is blocked, it cannot process the
> > pending RSP, causing nci_request() to time out and fail. Could this
> > deadlock the RX thread?
>
> No patch for this one, although I'm not sure how accurate it is.
With the above in mind, this now looks good to me.
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [BUG] TCP connection deadlock under simultaneous bidirectional ICSK_ACK_NOMEM (OOM)
From: xietangxin @ 2026-06-25 13:22 UTC (permalink / raw)
To: Menglong Dong
Cc: edumazet, davem, kuba, pabeni, jmaloy, menglong8.dong, kuniyu,
horms, willemb, netdev, linux-kernel, linux-stable
In-Reply-To: <g7DvITFWSiW9AoI49uyghw@linux.dev>
On 6/8/2026 7:55 PM, Menglong Dong wrote:
> On 2026/6/4 16:22 xietangxin <xietangxin@yeah.net> write:
>> Hi all,
>>
>> We have observed a TCP connection deadlock on stable 6.6 under heavy stress testing.
>>
>> 1.Both Peer A and Peer B enter the ICSK_ACK_NOMEM branch in tcp_select_window().
>> After commit 8c670bdfa58e ("tcp: correct handling of extreme memory squeeze"),
>> Both peers freeze their rcv_nxt and set rcv_wnd = 0.
>>
>> 2.Prior to freezing, both sides had already sent out flight data.
>> Since both sides are dropping incoming data packets due to OOM, rcv_nxt stops advancing,
>> but the peer's seq of subsequent packets continues to grow.
>>
>> 3.When Peer A receives Peer B's Zero Window ACK,
>> the packet's seq is far ahead of Peer A's frozen rcv_nxt.
>> Both peers drop each other's packet, also no Zero Window Probes are triggered
>> because snd_wnd is never updated to 0.
>>
>
> Hi,
>
> The problem you addressed is already fixed in this commit:
> 0e24d17bd966 ("tcp: implement RFC 7323 window retraction receiver requirements"),
> which hasn't been picked to the 6.6 branch.
>
> That patch doesn't have the Fix tag, so I'm not sure if it will be picked
> to the 6.6 branch. Just CC the linux-stable :)
>
> Thanks!
> Menglong Dong
>
>>
>> Simplified Packet Trace:
>>
>> Assume Peer A's rcv_nxt = 1000, and Peer B's rcv_nxt = 5000 initially.
>>
>> Time Dir Type Seq Ack Win Len Status
>> ------------------------------------------------------------------------
>> T1: B -> A [PSH, ACK] 1000 5000 3000 100 (A hits OOM, rcv_nxt=1000)
>> T2: B -> A [ACK] 1100 5000 3000 200 (Dropped due to A's OOM)
>> T3: B -> A [PSH, ACK] 1300 5000 3000 200 (Dropped due to A's OOM)
>>
>> T4: A -> B [PSH, ACK] 5000 1000 3000 100 (B hits OOM, rcv_nxt=5000)
>> T5: A -> B [ACK] 5100 1000 3000 200 (Dropped due to B's OOM)
>> T6: A -> B [PSH, ACK] 5300 1000 3000 200 (Dropped due to B's OOM)
>>
>> -- Both sides are now in OOM. B's Seq is 1500; A's Seq is 5500 --
>>
>> T7: B -> A [ZeroWin] 1500 5000 0 0 (Dropped: Seq 1500 != 1000)
>> T8: A -> B [ZeroWin] 5500 1000 0 0 (Dropped: Seq 5500 != 5000)
>> T9: A -> B [WinUpdate] 5500 1000 20 0 (Dropped: Seq 5500 != 5000)
>>
>> Should we relax the sequence check in tcp_sequence() for zero window ACK?
>>
>> Any feedback or guidance would be greatly appreciated.
>>
>> --
>> Best regards,
>> Tangxin Xie
>>
>>
>>
>
>
>
Hi,
We observed a throughput regression (dropping from ~1GB/s to 100MB/s)
in our test environment after commit 0e24d17bd966
("tcp: implement RFC 7323 window retraction receiver requirements").
When the rcv_buf reaches the pressure triggers tcp_clamp_window().
then rcv_ssthresh is strictly capped to 2 * advmss.
Subsequently, even after the user completely consumes the data and releases
a massive amount of free_space, tcp_select_window() is still heavily
suppressed by the clamped rcv_ssthresh. As a result, the receiver advertises
an extremely small window (Win=23) to the peer.
The sender cannot transmit any new data segments, until the sender's RTO timer
expires and triggers a slow-start recovery. This 200ms silence window slashes
our bandwidth by 90%.
No. Time Source Destination Info
-----------------------------------------------------------------------------------------------
1045 08:16:06.8005 192.168.1.9 192.168.1.10 [TCP ZeroWindow] 57334 -> 6666 [PSH, ACK] Win=0
1052 08:16:06.8013 192.168.1.9 192.168.1.10 [TCP Window Update] 57334 -> 6666 [ACK] Win=23
1055 08:16:06.8036 192.168.1.10 192.168.1.9 6666 -> 57334 [ACK] Seq=2999704568 Ack=2416286095
=========================== 200ms SILENCE (RTO WAITING) ===================================
1088 08:16:07.0056 192.168.1.10 192.168.1.9 [TCP Retransmission] 6666 -> 57334 Len=1448
1090 08:16:07.0060 192.168.1.10 192.168.1.9 [TCP Retransmission] Len=2896
--
Best regards,
Tangxin Xie
^ permalink raw reply
* [PATCH nf-next] netfilter: remove redundant null check before kvfree()
From: Subasri S @ 2026-06-25 13:31 UTC (permalink / raw)
To: Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: netfilter-devel, coreteam, netdev, linux-kernel, Subasri S
kvfree() internally performs NULL check on the pointer
handed to it and takes no action if it indeed is NULL.
Hence there is no need for a pre-check of the memory
pointer before handing it to kvfree().
Issue reported by ifnullfree.cocci Coccinelle semantic
patch script.
Signed-off-by: Subasri S <subasris1210@gmail.com>
---
net/netfilter/nft_set_rbtree.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index 018bbb6df4ce..efc25e788a1c 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -544,8 +544,7 @@ static int nft_array_intervals_alloc(struct nft_array *array, u32 max_intervals)
if (!intervals)
return -ENOMEM;
- if (array->intervals)
- kvfree(array->intervals);
+ kvfree(array->intervals);
array->intervals = intervals;
array->max_intervals = max_intervals;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net] mlxsw: spectrum_acl_erp: Fix const qualifier of delta_clear()
From: Petr Machata @ 2026-06-25 13:27 UTC (permalink / raw)
To: Evgenii Burenchev
Cc: stable, Greg Kroah-Hartman, idosch, petrm, andrew+netdev, davem,
edumazet, kuba, pabeni, jiri, netdev, linux-kernel, lvc-project
In-Reply-To: <20260625114831.17386-1-evg28bur@yandex.ru>
Evgenii Burenchev <evg28bur@yandex.ru> writes:
> mlxsw_sp_acl_erp_delta_clear() takes 'const char *enc_key' but modifies
> the memory it points to. This is a logical error in the function
> declaration.
>
> The only caller passes a non-const buffer (aentry->ht_key.enc_key), so
> the const qualifier is misleading and unnecessary.
>
> Remove const from the enc_key parameter to match the actual usage.
>
> Found by Linux Verification Center (linuxtesting.org) with SVACE.
>
> Fixes: c22291f7cf45 ("mlxsw: spectrum: acl: Implement delta for ERP")
> Signed-off-by: Evgenii Burenchev <evg28bur@yandex.ru>
Dunno how much of a net material this is, there's no bug to be fixed,
it's a source code cleanliness improvement. But the patch is correct.
Reviewed-by: Petr Machata <petrm@nvidia.com>
^ permalink raw reply
* [PATCH v1 1/1] ocp: Add I2C control support for Adva TimeCard
From: Sagi Maimon @ 2026-06-25 13:38 UTC (permalink / raw)
To: jonathan.lemon, vadim.fedorenko, richardcochran, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: linux-kernel, netdev, Sagi Maimon
- Load i2c-dev module to expose /dev/i2c-N character devices
- Add sysfs-based I2C bus control for Adva TimeCard model
Signed-off-by: Sagi Maimon <maimon.sagi@gmail.com>
---
drivers/ptp/ptp_ocp.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 35e911f1ad78..1b4ccb4feca5 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -4224,6 +4224,34 @@ static const struct ocp_attr_group art_timecard_groups[] = {
{ },
};
+static ssize_t
+i2c_bus_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+
+ if (!bp->pps_select)
+ return -ENODEV;
+ return sysfs_emit(buf, "0x%08x\n",
+ ioread32(&bp->pps_select->__pad1));
+}
+
+static ssize_t
+i2c_bus_ctrl_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+ u32 val;
+
+ if (!bp->pps_select)
+ return -ENODEV;
+ if (kstrtou32(buf, 0, &val))
+ return -EINVAL;
+ iowrite32(val, &bp->pps_select->__pad1);
+ return count;
+}
+
+static DEVICE_ATTR_RW(i2c_bus_ctrl);
+
static struct attribute *adva_timecard_attrs[] = {
&dev_attr_serialnum.attr,
&dev_attr_gnss_sync.attr,
@@ -4272,6 +4300,7 @@ static struct attribute *adva_timecard_x1_attrs[] = {
&dev_attr_ts_window_adjust.attr,
&dev_attr_utc_tai_offset.attr,
&dev_attr_tod_correction.attr,
+ &dev_attr_i2c_bus_ctrl.attr,
NULL,
};
@@ -5235,6 +5264,7 @@ ptp_ocp_init(void)
const char *what;
int err;
+ request_module("i2c-dev");
ptp_ocp_debugfs_init();
what = "timecard class";
--
2.47.0
^ permalink raw reply related
* Re: [PATCH net-next] selftests/xsk: preserve UMEM view in bidi test
From: Jason Xing @ 2026-06-25 13:38 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: netdev, bpf, magnus.karlsson, stfomichev, kuba, pabeni, horms,
tushar.vyavahare
In-Reply-To: <20260623091008.1046547-1-maciej.fijalkowski@intel.com>
On Tue, Jun 23, 2026 at 5:10 PM Maciej Fijalkowski
<maciej.fijalkowski@intel.com> wrote:
>
> The UMEM state refactor made __send_pkts() use xsk->umem for Tx
> address generation. At the same time, the shared-UMEM Tx setup copies the
> Rx UMEM state into a Tx-local state object and resets base_addr and
> next_buffer before configuring the Tx socket.
>
> Passing that Tx-local object to xsk_configure() makes xsk->umem point to
> the zero-based Tx allocator state. This breaks the BIDIRECTIONAL test once
> the roles are switched: the same socket is then used for Rx validation, but
> received descriptors from the other logical UMEM half are checked against
> base_addr == 0. With the new UMEM bounds check, a valid address such as
> base_addr + XDP_PACKET_HEADROOM is rejected as being outside the UMEM
> window.
>
> Keep xsk->umem as the shared/Rx UMEM view used for socket configuration
> and Rx validation. Use the ifobject-local UMEM copy only for Tx descriptor
> address generation, preserving the BIDIRECTIONAL test's intent of using
> the proper logical UMEM half after the direction switch.
>
> Fixes: b17631032769 ("selftests/xsk: Move UMEM state from ifobject to xsk_socket_info")
> Signed-off-by: Maciej Fijalkowski maciej.fijalkowski@intel.com
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Thanks!
^ permalink raw reply
* Re: [PATCH v2 net-next] selftests/xsk: Preserve UMEM view in BIDIRECTIONAL test
From: Jason Xing @ 2026-06-25 13:40 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: netdev, bpf, magnus.karlsson, stfomichev, kuba, pabeni, horms,
tushar.vyavahare
In-Reply-To: <20260625115215.1101928-1-maciej.fijalkowski@intel.com>
On Thu, Jun 25, 2026 at 7:52 PM Maciej Fijalkowski
<maciej.fijalkowski@intel.com> wrote:
>
> The UMEM state refactor made __send_pkts() use xsk->umem for Tx
> address generation. At the same time, the shared-UMEM Tx setup copies the
> Rx UMEM state into a Tx-local state object and resets base_addr and
> next_buffer before configuring the Tx socket.
>
> Passing that Tx-local object to xsk_configure() makes xsk->umem point to
> the zero-based Tx allocator state. This breaks the BIDIRECTIONAL test once
> the roles are switched: the same socket is then used for Rx validation, but
> received descriptors from the other logical UMEM half are checked against
> base_addr == 0. With the new UMEM bounds check, a valid address such as
> base_addr + XDP_PACKET_HEADROOM is rejected as being outside the UMEM
> window.
>
> Keep xsk->umem as the shared/Rx UMEM view used for socket configuration
> and Rx validation. Use the ifobject-local UMEM copy only for Tx descriptor
> address generation, preserving the BIDIRECTIONAL test's intent of using
> the proper logical UMEM half after the direction switch.
>
> Fixes: b17631032769 ("selftests/xsk: Move UMEM state from ifobject to xsk_socket_info")
> Reviewed-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
> Tested-by: Tushar Vyavahare <tushar.vyavahare@intel.com>
> Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Oh, you've already pushed a v2 patch, so again:
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Thanks!
^ permalink raw reply
* Re: [PATCH] vhost/net: fix clear_user start address in VHOST_GET_FEATURES_ARRAY
From: Eugenio Perez Martin @ 2026-06-25 13:48 UTC (permalink / raw)
To: rom.wang
Cc: Michael S . Tsirkin, Jason Wang, Paolo Abeni, kvm, virtualization,
netdev, linux-kernel, Yufeng Wang
In-Reply-To: <20260526080336.61296-1-r4o5m6e8o@163.com>
On Tue, May 26, 2026 at 10:04 AM rom.wang <r4o5m6e8o@163.com> wrote:
>
> From: Yufeng Wang <wangyufeng@kylinos.cn>
>
> The clear_user() call in VHOST_GET_FEATURES_ARRAY incorrectly starts
> at argp, which is the beginning of the features array, overwriting the
> data just written by copy_to_user(). It should start after the copied
> elements at argp + copied * sizeof(u64) to only zero the trailing
> unused space.
>
> Fixes: 333c515d1896 ("vhost-net: allow configuring extended features")
> Signed-off-by: Yufeng Wang <wangyufeng@kylinos.cn>
> ---
> drivers/vhost/net.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index db341c922673..70c578acf840 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -1777,7 +1777,8 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
> return -EFAULT;
>
> /* Zero the trailing space provided by user-space, if any */
> - if (clear_user(argp, size_mul(count - copied, sizeof(u64))))
> + if (clear_user(argp + copied * sizeof(u64),
> + size_mul(count - copied, sizeof(u64))))
The fix looks good to me, but why not use size_mul() macro for copied
* sizeof(u64) multiplication?
> return -EFAULT;
> return 0;
> case VHOST_SET_FEATURES_ARRAY:
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH] vhost/net: fix clear_user start address in VHOST_GET_FEATURES_ARRAY
From: Eugenio Perez Martin @ 2026-06-25 13:56 UTC (permalink / raw)
To: rom.wang
Cc: Michael S . Tsirkin, Jason Wang, Paolo Abeni, kvm, virtualization,
netdev, linux-kernel, Yufeng Wang
In-Reply-To: <CAJaqyWcFm0A5ucL5TLP8+T8JNOiZyaL-_mb747_fKhH9Qm83ig@mail.gmail.com>
On Thu, Jun 25, 2026 at 3:48 PM Eugenio Perez Martin
<eperezma@redhat.com> wrote:
>
> On Tue, May 26, 2026 at 10:04 AM rom.wang <r4o5m6e8o@163.com> wrote:
> >
> > From: Yufeng Wang <wangyufeng@kylinos.cn>
> >
> > The clear_user() call in VHOST_GET_FEATURES_ARRAY incorrectly starts
> > at argp, which is the beginning of the features array, overwriting the
> > data just written by copy_to_user(). It should start after the copied
> > elements at argp + copied * sizeof(u64) to only zero the trailing
> > unused space.
> >
> > Fixes: 333c515d1896 ("vhost-net: allow configuring extended features")
> > Signed-off-by: Yufeng Wang <wangyufeng@kylinos.cn>
> > ---
> > drivers/vhost/net.c | 3 ++-
> > 1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> > index db341c922673..70c578acf840 100644
> > --- a/drivers/vhost/net.c
> > +++ b/drivers/vhost/net.c
> > @@ -1777,7 +1777,8 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
> > return -EFAULT;
> >
> > /* Zero the trailing space provided by user-space, if any */
> > - if (clear_user(argp, size_mul(count - copied, sizeof(u64))))
> > + if (clear_user(argp + copied * sizeof(u64),
> > + size_mul(count - copied, sizeof(u64))))
>
> The fix looks good to me, but why not use size_mul() macro for copied
> * sizeof(u64) multiplication?
>
Also, could you add a new switch to tools/virtio/vhost_net_test.c to
use the VHOST_GET_FEATURES_ARRAY and VHOST_SET_FEATURES_ARRAY instead
of VHOST_GET_FEATURES and VHOST_SET_FEATURES?
> > return -EFAULT;
> > return 0;
> > case VHOST_SET_FEATURES_ARRAY:
> > --
> > 2.34.1
> >
> >
^ permalink raw reply
* Re: [PATCH] net: pch_gbe: return errors from MIIM accesses
From: Andrew Lunn @ 2026-06-25 14:06 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Andrew Lunn, davem, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
In-Reply-To: <20260625030503.93588-1-pengpeng@iscas.ac.cn>
On Thu, Jun 25, 2026 at 11:05:03AM +0800, Pengpeng Hou wrote:
> pch_gbe_mac_ctrl_miim() polls for the MIIM controller to become ready,
> but returns zero on the initial ready timeout and ignores the completion
> timeout after issuing the operation. MDIO and PHY helpers can then report
> success with zero or stale data.
>
> Make the MIIM helper return an errno and pass read data through an output
> parameter. Propagate the error through the MDIO read path, the probe-time
> PHY discovery path, and the internal PHY register helpers that already
> return an error status.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Do you have the hardware? This is a complex change to make without
being able to test it.
You need to think about the risks.
On the one side, MII pretty much never fails. It is a big shift
register, which just gets clocked out. So the errors you are trying to
report probably never happen.
On the other side, you are making a big change to the code. How likely
is it, you made an error, and have broken this code?
I personally would never make a change this size unless i could test
it.
Please also take a look at drivers/net/mii.c. Notice that all calls to
mii->mdio_read() assume success. There is no error checking.
Andrew
^ permalink raw reply
* [PATCH 0/6] Add support for IPQ5018 Bluetooth
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
Hello,
This patch series introduces Bluetooth support for IPQ5018.
Bluetooth firmware on the IPQ5018 platform runs on the M0 co-processor.
The firmware is loaded by the host into a dedicated reserved memory
carveout and authenticated by TrustZone. A Secure Channel Manager (SCM)
call safely brings the peripheral core out of reset.
A shared memory ring buffer topology handles runtime data frame
transport between the host APSS and the M0 core. An outgoing APCS IPC
bit and an incoming GIC interrupt handle host/guest signaling.
This series has been tested and verified on various IPQ5018 router
boards utilizing firmware extracted from GPL distributions, using both
mdt and mbn file formats.
[ 14.781511] Bluetooth: hci0: QCA Product ID :0x00000016
[ 14.781583] Bluetooth: hci0: QCA SOC Version :0x20180100
[ 14.785926] Bluetooth: hci0: QCA ROM Version :0x00000100
[ 14.791546] Bluetooth: hci0: QCA Patch Version:0x00003ded
[ 14.796698] Bluetooth: hci0: QCA controller version 0x01000100
[ 14.802217] Bluetooth: hci0: QCA Downloading qca/mpnv10.bin
[ 16.393850] Bluetooth: hci0: QCA Build Info: BTFW.MAPLE.1.0.0-00102-MPL_ROM_PATCHZ-1
Best regards,
George Moussalem
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
George Moussalem (6):
dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
remoteproc: qcom: Add M0 BTSS secure PIL driver
Bluetooth: btqca: Add IPQ5018 support
dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
.../bindings/net/bluetooth/qcom,ipq5018-bt.yaml | 63 ++
.../bindings/remoteproc/qcom,m0-btss-pil.yaml | 72 ++
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 34 +-
drivers/bluetooth/Kconfig | 11 +
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btqca.c | 16 +
drivers/bluetooth/btqca.h | 3 +
drivers/bluetooth/btqcomipc.c | 939 +++++++++++++++++++++
drivers/remoteproc/Kconfig | 12 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++
11 files changed, 1412 insertions(+), 1 deletion(-)
---
base-commit: 4d1ab324fcb7d20df5a071edb0304461846fdc12
change-id: 20260625-ipq5018-bluetooth-06ff66c9d753
prerequisite-message-id: <20260612-block-as-nvmem-v5-0-95e0b30fff90@oss.qualcomm.com>
prerequisite-patch-id: 6ce8686c1683f468d86b4502f5ec9d19c392a382
prerequisite-patch-id: e362f7fcbacff716b7ef720e6780786a7d88c013
prerequisite-patch-id: 9168930e40551e842c8171d5433a6f39ad4b78a4
prerequisite-patch-id: 64fecfbd1e085d7d2ab0ae23295ca34ec8e14c5e
prerequisite-patch-id: 566804aaa690ee9aa285d0fd75fd16d94fbadebf
prerequisite-patch-id: dc18bec338f54b3051f4523f9d1d3c0566a20ccd
prerequisite-patch-id: b6b3eb46429936ab49423d295433daf47981db0f
prerequisite-patch-id: 75caa99e3bbcdf41b6462b9f5f703bea1d4a65fa
prerequisite-patch-id: 35e9968f482f78ca233eb0306d9c5fdbff093175
Best regards,
--
George Moussalem <george.moussalem@outlook.com>
^ permalink raw reply
* [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Document the M0 Bluetooth Subsystem remote processor core found in the
Qualcomm IPQ5018 SoC. Firmware loaded is authenticated via TrustZone.
The firmware running on the M0 core provides bluetooth functionality.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
.../bindings/remoteproc/qcom,m0-btss-pil.yaml | 72 ++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
new file mode 100644
index 000000000000..397bb6815d71
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/qcom,m0-btss-pil.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm M0 BTSS Peripheral Image Loader
+
+maintainers:
+ - George Moussalem <george.moussalem@outlook.com>
+
+description:
+ Qualcomm M0 BTSS Peripheral Secure Image Loader loads firmware and powers up
+ the M0 BTSS remote processor core on the Qualcomm IPQ5018 SoC.
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq5018-btss-pil
+
+ firmware-name:
+ maxItems: 1
+ description: Firmware name for the M0 Bluetooth Subsystem core
+
+ clocks:
+ items:
+ - description: M0 BTSS low power oscillator clock
+
+ clock-names:
+ items:
+ - const: btss_lpo_clk
+
+ memory-region:
+ items:
+ - description: M0 BTSS reserved memory carveout
+
+ resets:
+ items:
+ - description: M0 BTSS reset
+
+ reset-names:
+ items:
+ - const: btss_reset
+
+required:
+ - compatible
+ - firmware-name
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - memory-region
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+
+ remoteproc {
+ compatible = "qcom,ipq5018-btss-pil";
+
+ firmware-name = "qca/bt_fw_patch.mbn";
+
+ clocks = <&gcc GCC_BTSS_LPO_CLK>;
+ clock-names = "btss_lpo_clk";
+ resets = <&gcc GCC_BTSS_BCR>;
+ reset-names = "btss_reset";
+
+ memory-region = <&btss_region>;
+ };
--
2.53.0
^ permalink raw reply related
* [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add support to bring up the M0 core of the bluetooth subsystem found in
the IPQ5018 SoC.
The signed firmware loaded is authenticated by TrustZone. If successful,
the M0 core boots the firmware and the peripheral is taken out of reset
using a Secure Channel Manager call to TrustZone.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/remoteproc/Kconfig | 12 ++
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
3 files changed, 274 insertions(+)
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index c521c744e7db..6b52f78f1427 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -163,6 +163,18 @@ config PRU_REMOTEPROC
processors on various TI SoCs. It's safe to say N here if you're
not interested in the PRU or if you are unsure.
+config QCOM_M0_BTSS_PIL
+ tristate "Qualcomm M0 BTSS Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ select QCOM_MDT_LOADER
+ select QCOM_RPROC_COMMON
+ select QCOM_SCM
+ help
+ Say y here to support the Secure Peripheral Imager Loader for the
+ Qualcomm Bluetooth Subsystem running on the M0 remote processor found
+ in the IPQ5018 SoC. The M0 core is started and stopped using a
+ Secure Channel Manager call to TrustZone.
+
config QCOM_PIL_INFO
tristate
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 1c7598b8475d..df80faf8d0df 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o
obj-$(CONFIG_MESON_MX_AO_ARC_REMOTEPROC)+= meson_mx_ao_arc.o
obj-$(CONFIG_PRU_REMOTEPROC) += pru_rproc.o
+obj-$(CONFIG_QCOM_M0_BTSS_PIL) += qcom_m0_btss_pil.o
obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil_info.o
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o
diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
new file mode 100644
index 000000000000..7168e270e4d4
--- /dev/null
+++ b/drivers/remoteproc/qcom_m0_btss_pil.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "qcom_common.h"
+
+#define BTSS_PAS_ID 0xc
+
+struct m0_btss {
+ struct device *dev;
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ void __iomem *mem_region;
+ size_t mem_size;
+ struct reset_control *btss_reset;
+};
+
+static int m0_btss_start(struct rproc *rproc)
+{
+ int ret;
+
+ if (!qcom_scm_pas_supported(BTSS_PAS_ID)) {
+ dev_err(rproc->dev.parent,
+ "PAS is not available for peripheral: 0x%x\n",
+ BTSS_PAS_ID);
+ return -ENODEV;
+ }
+
+ ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
+ if (ret) {
+ dev_err(rproc->dev.parent, "Failed to start rproc: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int m0_btss_stop(struct rproc *rproc)
+{
+ int ret;
+
+ if (rproc->state == RPROC_RUNNING || rproc->state == RPROC_CRASHED) {
+ ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
+ if (ret) {
+ dev_err(rproc->dev.parent, "Failed to stop rproc: %d\n",
+ ret);
+ return ret;
+ }
+
+ dev_info(rproc->dev.parent, "Successfully stopped rproc\n");
+ }
+
+ return 0;
+}
+
+static int m0_btss_load(struct rproc *rproc, const struct firmware *fw)
+{
+ struct m0_btss *desc = rproc->priv;
+ const struct elf32_phdr *phdrs;
+ const struct firmware *seg_fw;
+ const struct elf32_phdr *phdr;
+ const struct elf32_hdr *ehdr;
+ void __iomem *metadata;
+ size_t metadata_size;
+ int i, ret;
+
+ ehdr = (const struct elf32_hdr *)fw->data;
+ phdrs = (const struct elf32_phdr *)(ehdr + 1);
+
+ ret = request_firmware(&fw, rproc->firmware, rproc->dev.parent);
+ if (ret) {
+ dev_err(rproc->dev.parent, "Failed to request firmware: %d\n",
+ ret);
+ return ret;
+ }
+
+ metadata = qcom_mdt_read_metadata(fw, &metadata_size, rproc->firmware,
+ rproc->dev.parent);
+ if (IS_ERR(metadata)) {
+ ret = PTR_ERR(metadata);
+ dev_err(rproc->dev.parent,
+ "Failed to read firmware metadata: %d\n", ret);
+ goto release_fw;
+ }
+
+ ret = qcom_scm_pas_init_image(BTSS_PAS_ID, metadata,
+ metadata_size, NULL);
+ if (ret) {
+ dev_err(rproc->dev.parent, "PAS init image failed: %d\n", ret);
+ goto free_metadata;
+ }
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ char *seg_name __free(kfree) = kstrdup(rproc->firmware,
+ GFP_KERNEL);
+ if (!seg_name)
+ return -ENOMEM;
+
+ phdr = &phdrs[i];
+
+ /* Only process valid loadable data segments */
+ if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
+ continue;
+
+ if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {
+ dev_err(rproc->dev.parent,
+ "Segment data exceeds the reserved memory area!\n");
+ goto free_metadata;
+ }
+
+ /* Check if firmware is split across multiple segment files */
+ if (phdr->p_offset > fw->size ||
+ phdr->p_offset + phdr->p_filesz > fw->size) {
+ sprintf(seg_name + strlen(seg_name) - 3, "b%02d", i);
+ ret = request_firmware(&seg_fw, seg_name,
+ rproc->dev.parent);
+ if (ret) {
+ dev_err(rproc->dev.parent,
+ "Could not find split segment binary: %s\n",
+ seg_name);
+ goto free_metadata;
+ }
+
+ /*
+ * Use the virtual instead of the physical address as
+ * the offset
+ */
+ memcpy_toio(desc->mem_region + phdr->p_vaddr,
+ seg_fw->data, phdr->p_filesz);
+
+ release_firmware(seg_fw);
+ } else {
+ memcpy_toio(desc->mem_region + phdr->p_vaddr,
+ fw->data + phdr->p_offset, phdr->p_filesz);
+ }
+ }
+
+ return 0;
+
+free_metadata:
+ kfree(metadata);
+release_fw:
+ release_firmware(fw);
+ return ret;
+}
+
+static const struct rproc_ops m0_btss_ops = {
+ .start = m0_btss_start,
+ .stop = m0_btss_stop,
+ .load = m0_btss_load,
+ .get_boot_addr = rproc_elf_get_boot_addr,
+};
+
+static int m0_btss_alloc_memory_region(struct m0_btss *desc)
+{
+ struct device *dev = desc->dev;
+ struct resource res;
+ int ret;
+
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (ret) {
+ dev_err(dev, "unable to acquire memory-region resource\n");
+ return ret;
+ }
+
+ desc->mem_phys = res.start;
+ desc->mem_reloc = res.start;
+ desc->mem_size = resource_size(&res);
+ desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+ if (!desc->mem_region) {
+ dev_err(dev, "unable to map memory region: %pR\n", &res);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int m0_btss_pil_probe(struct platform_device *pdev)
+{
+ // struct reset_control *btss_reset;
+ struct device *dev = &pdev->dev;
+ const char *fw_name = NULL;
+ struct m0_btss *desc;
+ struct clk *lpo_clk;
+ struct rproc *rproc;
+ int ret;
+
+ ret = of_property_read_string(dev->of_node, "firmware-name",
+ &fw_name);
+ if (ret < 0)
+ return ret;
+
+ rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
+ fw_name, sizeof(*desc));
+ if (!rproc) {
+ dev_err(dev, "failed to allocate rproc\n");
+ return -ENOMEM;
+ }
+
+ desc = rproc->priv;
+ desc->dev = dev;
+
+ ret = m0_btss_alloc_memory_region(desc);
+ if (ret)
+ return ret;
+
+ lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
+ if (IS_ERR(lpo_clk))
+ return dev_err_probe(dev, PTR_ERR(lpo_clk),
+ "Failed to get lpo clock\n");
+
+ desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
+ if (IS_ERR_OR_NULL(desc->btss_reset))
+ return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
+ "unable to acquire btss_reset\n");
+
+ ret = reset_control_deassert(desc->btss_reset);
+ if (ret)
+ return dev_err_probe(rproc->dev.parent, ret,
+ "Failed to deassert reset\n");
+
+ rproc->auto_boot = false;
+ ret = devm_rproc_add(dev, rproc);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, rproc);
+
+ return 0;
+}
+
+static const struct of_device_id m0_btss_of_match[] = {
+ { .compatible = "qcom,ipq5018-btss-pil" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, m0_btss_of_match);
+
+static struct platform_driver m0_btss_pil_driver = {
+ .probe = m0_btss_pil_probe,
+ .driver = {
+ .name = "qcom-m0-btss-pil",
+ .of_match_table = m0_btss_of_match,
+ },
+};
+
+module_platform_driver(m0_btss_pil_driver);
+
+MODULE_DESCRIPTION("Qualcomm M0 Bluetooth Subsystem Peripheral Image Loader");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add the IPQ5018 SoC type and support for loading its firmware.
The firmware tested has been taken from GPL sources of various router
boards. Firmware files needed are:
- qca/bt_fw_patch.mbn
- qca/mpnv10.bin
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/bluetooth/btqca.c | 16 ++++++++++++++++
drivers/bluetooth/btqca.h | 3 +++
2 files changed, 19 insertions(+)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 04ebe290bc78..e136e91976cf 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -380,6 +380,9 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
break;
case TLV_TYPE_NVM:
+ if (soc_type == QCA_IPQ5018)
+ break;
+
if (fw_size < sizeof(struct tlv_type_hdr))
return -EINVAL;
@@ -794,6 +797,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
else
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
+ if (soc_type == QCA_IPQ5018)
+ goto download_nvm;
+
if (soc_type == QCA_WCN6750)
qca_send_patch_config_cmd(hdev);
@@ -881,6 +887,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
qca_read_fw_board_id(hdev, &boardid);
+download_nvm:
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
if (firmware_name) {
@@ -939,6 +946,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
"hmtnv", soc_type, ver, rom_ver, boardid);
break;
+ case QCA_IPQ5018:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/mpnv%02x.bin", rom_ver);
+ break;
default:
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
@@ -958,6 +969,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}
+ if (soc_type == QCA_IPQ5018)
+ msleep(NVM_READY_DELAY_MS);
+
switch (soc_type) {
case QCA_QCA2066:
case QCA_QCA6390:
@@ -965,6 +979,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_IPQ5018:
err = qca_disable_soc_logging(hdev);
if (err < 0)
return err;
@@ -1001,6 +1016,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_IPQ5018:
/* get fw build info */
err = qca_read_fw_build_info(hdev);
if (err < 0)
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 8f3c1b1c77b3..343cd62d1137 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -54,6 +54,8 @@
#define QCA_HSP_GF_SOC_ID 0x1200
#define QCA_HSP_GF_SOC_MASK 0x0000ff00
+#define NVM_READY_DELAY_MS 1500
+
enum qca_baudrate {
QCA_BAUDRATE_115200 = 0,
QCA_BAUDRATE_57600,
@@ -158,6 +160,7 @@ enum qca_btsoc_type {
QCA_WCN6750,
QCA_WCN6855,
QCA_WCN7850,
+ QCA_IPQ5018,
};
#if IS_ENABLED(CONFIG_BT_QCA)
--
2.53.0
^ permalink raw reply related
* [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Document the Qualcomm IPQ5018 Bluetooth controller.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
.../bindings/net/bluetooth/qcom,ipq5018-bt.yaml | 63 ++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
new file mode 100644
index 000000000000..afd33f851858
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ5018 Bluetooth
+
+maintainers:
+ - George Moussalem <george.moussalem@outlook.com>
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq5018-bt
+
+ interrupts:
+ items:
+ - description:
+ Interrupt line from the M0 Bluetooth Subsystem to the host processor
+ to notify it of events such as re
+
+ qcom,ipc:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: phandle to a syscon node representing the APCS registers
+ - description: u32 representing offset to the register within the syscon
+ - description: u32 representing the ipc bit within the register
+ description: |
+ These entries specify the outgoing IPC bit used for signaling the remote
+ M0 BTSS core of a host event or for sending an ACK if the remote processor
+ expects it.
+
+ qcom,rproc:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the remote processor node representing the M0 BTSS core.
+
+required:
+ - compatible
+ - interrupts
+ - qcom,ipc
+ - qcom,rproc
+
+allOf:
+ - $ref: bluetooth-controller.yaml#
+ - $ref: qcom,bluetooth-common.yaml
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ bluetooth: bluetooth {
+ compatible = "qcom,ipq5018-bt";
+
+ qcom,ipc = <&apcs_glb 8 23>;
+ interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,rproc = <&m0_btss>;
+ };
--
2.53.0
^ permalink raw reply related
* [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add nodes for the M0 remoteproc, reserved memory carveout, and Bluetooth
to bring up the M0 core and enable the Bluetooth Subsystem.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 34 +++++++++++++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 6f8004a22a1f..4fdf20c87b0a 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -17,6 +17,17 @@ / {
#address-cells = <2>;
#size-cells = <2>;
+ bluetooth: bluetooth {
+ compatible = "qcom,ipq5018-bt";
+
+ qcom,ipc = <&apcs_glb 8 23>;
+ interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,rproc = <&m0_btss>;
+
+ status = "disabled";
+ };
+
clocks {
gephy_rx_clk: gephy-rx-clk {
compatible = "fixed-clock";
@@ -131,11 +142,31 @@ psci {
method = "smc";
};
+ m0_btss: remoteproc {
+ compatible = "qcom,ipq5018-btss-pil";
+
+ firmware-name = "qca/bt_fw_patch.mbn";
+
+ clocks = <&gcc GCC_BTSS_LPO_CLK>;
+ clock-names = "btss_lpo_clk";
+ resets = <&gcc GCC_BTSS_BCR>;
+ reset-names = "btss_reset";
+
+ memory-region = <&btss_region>;
+
+ status = "disabled";
+ };
+
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
+ btss_region: bluetooth@7000000 {
+ reg = <0x0 0x07000000 0x0 0x58000>;
+ no-map;
+ };
+
bootloader@4a800000 {
reg = <0x0 0x4a800000 0x0 0x200000>;
no-map;
@@ -647,7 +678,8 @@ watchdog: watchdog@b017000 {
apcs_glb: mailbox@b111000 {
compatible = "qcom,ipq5018-apcs-apps-global",
- "qcom,ipq6018-apcs-apps-global";
+ "qcom,ipq6018-apcs-apps-global",
+ "syscon";
reg = <0x0b111000 0x1000>;
#clock-cells = <1>;
clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;
--
2.53.0
^ permalink raw reply related
* [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add driver support for the Qualcomm IPQ5018 bluetooth chip.
The firmware runs on the M0 co-processor.
The host and the M0 core use a shared memory carveout for transport
using ring buffers. This driver implements the transport layer between
the HCI core and the Bluetooth subsystem running on the M0 core.
Notifications of host and M0 core events are triggered by an IPC
register BIT and an interrupt line respectfully.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/bluetooth/Kconfig | 11 +
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btqcomipc.c | 939 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 951 insertions(+)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..6b8bed6a6ffd 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -413,6 +413,17 @@ config BT_MTKUART
Say Y here to compile support for MediaTek Bluetooth UART devices
into the kernel or say M to compile it as module (btmtkuart).
+config BT_QCOMIPC
+ tristate "Qualcomm IPQ5018 IPC based HCI support"
+ select BT_QCA
+ help
+ Qualcomm IPQ5018 IPC based HCI driver.
+ This driver is used to bridge HCI data onto shared memory between
+ the host and the M0 BTSS core.
+
+ Say Y here to compile support for HCI over Qualcomm IPC into the
+ kernel or say M to compile as a module.
+
config BT_QCOMSMD
tristate "Qualcomm SMD based HCI support"
depends on RPMSG || (COMPILE_TEST && RPMSG=n)
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index e6b1c1180d1d..05f19047bed0 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_MTKSDIO) += btmtksdio.o
obj-$(CONFIG_BT_MTKUART) += btmtkuart.o
+obj-$(CONFIG_BT_QCOMIPC) += btqcomipc.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
diff --git a/drivers/bluetooth/btqcomipc.c b/drivers/bluetooth/btqcomipc.c
new file mode 100644
index 000000000000..662a75b6c4a9
--- /dev/null
+++ b/drivers/bluetooth/btqcomipc.c
@@ -0,0 +1,939 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/remoteproc.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btqca.h"
+
+/** Message header format.
+ *
+ * ----------------------------------------------------------------
+ * BitPos | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 - 0 |
+ * ---------------------------------------------------------------
+ * Field | long_msg |ACK | RFU | pkt_type | cmd |
+ * ----------------------------------------------------------------
+ *
+ * - long_msg : If set, indicates that the payload is larger than the
+ * IPC_MSG_PLD_SZ. The payload instead contains a pointer to the
+ * long message buffer in the shared BTSS memory space.
+ *
+ * - ACK : Set if sending ACK if required by sending acknowledegement
+ * to sender i.e. send an ack IPC interrupt if set.
+ *
+ * - RFU : Reserved for future use.
+ *
+ * - pkt_type : IPC Packet Type
+ *
+ * - cmd : Contains unique command ID
+ */
+
+#define IPC_MSG_HDR_SZ 4
+#define IPC_MSG_PLD_SZ 40
+#define IPC_TOTAL_MSG_SZ (IPC_MSG_HDR_SZ + IPC_MSG_PLD_SZ)
+
+/* Message Header */
+#define IPC_HDR_LONG_MSG BIT(15)
+#define IPC_HDR_REQ_ACK BIT(14)
+#define IPC_HDR_PKT_TYPE_MASK GENMASK(9, 8)
+#define IPC_HDR_PKT_TYPE_CUST 0
+#define IPC_HDR_PKT_TYPE_HCI 1
+#define IPC_HDR_PKT_TYPE_AUDIO 2
+#define IPC_HDR_PKT_TYPE_RFU 3
+#define IPC_HDR_CMD_MASK GENMASK(7, 0)
+
+#define IPC_CMD_STOP 1
+#define IPC_CMD_SWITCH_TO_UART 2
+#define IPC_CMD_PREPARE_DUMP 3
+#define IPC_CMD_COLLECT_DUMP 4
+#define IPC_CMD_START 5
+
+#define IPC_TX_QSIZE 32
+
+#define TO_APPS_ADDR(a) (desc->mem_region + (int)(uintptr_t)a)
+#define TO_BT_ADDR(a) (a - desc->mem_region)
+#define IPC_LBUF_SZ(w, x, y, z) (((TO_BT_ADDR((void *)w) + w->x) - w->y) / w->z)
+
+#define GET_NO_OF_BLOCKS(a, b) ((a + b - 1) / b)
+
+#define GET_RX_INDEX_FROM_BUF(x, y) ((x - desc->rx_ctxt->lring_buf) / y)
+
+#define GET_TX_INDEX_FROM_BUF(x, y) ((x - desc->tx_ctxt->lring_buf) / y)
+
+#define IS_RX_MEM_NON_CONTIGIOUS(buf, len, sz) \
+ ((buf + len) > (desc->rx_ctxt->lring_buf + \
+ (sz * desc->rx_ctxt->lmsg_buf_cnt)))
+
+#define POWER_CONTROL_DELAY_MS 50
+
+#define BTSS_PAS_ID 0xc
+
+struct long_msg_info {
+ __le16 smsg_free_cnt;
+ __le16 lmsg_free_cnt;
+ u8 ridx;
+ u8 widx;
+} __packed;
+
+struct ipc_aux_ptr {
+ __le32 len;
+ __le32 buf;
+} __packed;
+
+struct ring_buffer {
+ __le16 msg_hdr;
+ __le16 len;
+ union {
+ u8 smsg_data[IPC_MSG_PLD_SZ];
+ __le32 lmsg_data;
+ } payload;
+} __packed;
+
+struct ring_buffer_info {
+ __le32 rbuf;
+ u8 ring_buf_cnt;
+ u8 ridx;
+ u8 widx;
+ u8 tidx;
+ __le32 next;
+} __packed;
+
+struct context_info {
+ __le16 total_size;
+ u8 lmsg_buf_cnt;
+ u8 smsg_buf_cnt;
+ struct ring_buffer_info sring_buf_info;
+ __le32 sring_buf;
+ __le32 lring_buf;
+ __le32 reserved;
+} __packed;
+
+struct qcom_btss {
+ struct device *dev;
+ struct rproc *rproc;
+ struct hci_dev *hdev;
+
+ struct regmap *regmap;
+ u32 offset;
+ u32 bit;
+ int irq;
+
+ void *mem_region;
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ size_t mem_size;
+
+ struct sk_buff_head tx_q;
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ wait_queue_head_t wait_q;
+ spinlock_t lock;
+
+ struct context_info *tx_ctxt;
+ struct context_info *rx_ctxt;
+ struct long_msg_info lmsg_ctxt;
+
+ bool running;
+};
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb);
+
+static void *btss_alloc_lmsg(struct qcom_btss *desc, u32 len,
+ struct ipc_aux_ptr *aux_ptr, bool *is_lbuf_full)
+{
+ struct device *dev = desc->dev;
+ u8 idx, blks, blks_consumed;
+ void *ret_ptr;
+ u32 lsz;
+
+ if (desc->tx_ctxt->lring_buf == 0) {
+ dev_err(dev, "no long message buffer initialized\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf, lmsg_buf_cnt);
+ blks = GET_NO_OF_BLOCKS(len, lsz);
+
+ if (!desc->lmsg_ctxt.lmsg_free_cnt ||
+ (blks > desc->lmsg_ctxt.lmsg_free_cnt))
+ return ERR_PTR(-EAGAIN);
+
+ idx = desc->lmsg_ctxt.widx;
+
+ if ((desc->lmsg_ctxt.widx + blks) > desc->tx_ctxt->lmsg_buf_cnt) {
+ blks_consumed = desc->tx_ctxt->lmsg_buf_cnt - idx;
+ aux_ptr->len = len - (blks_consumed * lsz);
+ aux_ptr->buf = desc->tx_ctxt->lring_buf;
+ }
+
+ desc->lmsg_ctxt.widx = (desc->lmsg_ctxt.widx + blks) %
+ desc->tx_ctxt->lmsg_buf_cnt;
+
+ desc->lmsg_ctxt.lmsg_free_cnt -= blks;
+
+ if (desc->lmsg_ctxt.lmsg_free_cnt <=
+ ((desc->tx_ctxt->lmsg_buf_cnt * 20) / 100))
+ *is_lbuf_full = true;
+
+ ret_ptr = TO_APPS_ADDR(desc->tx_ctxt->lring_buf) + (idx * lsz);
+
+ return ret_ptr;
+}
+
+static struct ring_buffer_info *btss_get_tx_rbuf(struct qcom_btss *desc,
+ bool *is_sbuf_full)
+{
+ u8 idx;
+ struct ring_buffer_info *rinfo;
+
+ for (rinfo = &(desc->tx_ctxt->sring_buf_info); rinfo != NULL;
+ rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+ idx = (rinfo->widx + 1) % (desc->tx_ctxt->smsg_buf_cnt);
+
+ if (idx != rinfo->tidx) {
+ desc->lmsg_ctxt.smsg_free_cnt--;
+
+ if (desc->lmsg_ctxt.smsg_free_cnt <=
+ ((desc->tx_ctxt->smsg_buf_cnt * 20) / 100))
+ *is_sbuf_full = true;
+
+ return rinfo;
+ }
+ }
+
+ return ERR_PTR(-EAGAIN);
+}
+
+static int btss_send(struct qcom_btss *desc, u16 msg_hdr,
+ struct sk_buff *skb)
+{
+ struct hci_dev *hdev = desc->hdev;
+ struct ring_buffer_info *rinfo;
+ struct ipc_aux_ptr aux_ptr;
+ struct ring_buffer *rbuf;
+ bool is_lbuf_full = false;
+ bool is_sbuf_full = false;
+ u16 hdr = msg_hdr;
+ void *ptr_buf;
+ u32 len;
+
+ /* Account for HCI packet type as it's not included in the skb payload */
+ len = skb->len + 1;
+ memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+ if (len > IPC_MSG_PLD_SZ) {
+ hdr |= IPC_HDR_LONG_MSG;
+
+ ptr_buf = btss_alloc_lmsg(desc, len,
+ &aux_ptr, &is_lbuf_full);
+ if (IS_ERR(ptr_buf)) {
+ bt_dev_err(hdev, "long msg buf full");
+ hdev->stat.err_tx++;
+ return PTR_ERR(ptr_buf);
+ }
+ }
+
+ rinfo = btss_get_tx_rbuf(desc, &is_sbuf_full);
+ if (IS_ERR(rinfo)) {
+ bt_dev_err(hdev, "short msg buf full");
+ hdev->stat.err_tx++;
+ return PTR_ERR(rinfo);
+ }
+
+ rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[rinfo->widx];
+
+ if (len > IPC_MSG_PLD_SZ)
+ rbuf->payload.lmsg_data = cpu_to_le32(TO_BT_ADDR(ptr_buf));
+ else
+ ptr_buf = rbuf->payload.smsg_data;
+
+ /* if it's a short message, the aux len and buf are NULL */
+ memcpy_toio(ptr_buf, &hci_skb_pkt_type(skb), 1);
+ memcpy_toio((u8 *)ptr_buf + 1, skb->data, skb->len - aux_ptr.len);
+ if (aux_ptr.buf) {
+ memcpy_toio(TO_APPS_ADDR(aux_ptr.buf),
+ (skb->data + (skb->len - aux_ptr.len)), aux_ptr.len);
+ }
+
+ if (is_sbuf_full || is_lbuf_full)
+ hdr |= IPC_HDR_REQ_ACK;
+
+ rbuf->msg_hdr = cpu_to_le16(hdr);
+ rbuf->len = cpu_to_le16(len);
+
+ rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+ regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+ return 0;
+}
+
+static void btss_process_tx_queue(struct qcom_btss *desc)
+{
+ struct sk_buff *skb;
+ u16 hdr;
+ int ret;
+
+ while ((skb = skb_dequeue(&desc->tx_q))) {
+ hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+
+ ret = btss_send(desc, hdr, skb);
+ if (ret) {
+ bt_dev_err(desc->hdev, "Failed to send message");
+ skb_queue_head(&desc->tx_q, skb);
+ break;
+ }
+
+ btqcomipc_update_stats(desc->hdev, skb);
+ kfree_skb(skb);
+ }
+}
+
+static void btss_free_lmsg(struct qcom_btss *desc, u32 lmsg, u16 len)
+{
+ u8 idx;
+ u8 blks;
+ u32 lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf,
+ lmsg_buf_cnt);
+
+ idx = GET_TX_INDEX_FROM_BUF(lmsg, lsz);
+
+ if (idx != desc->lmsg_ctxt.ridx)
+ return;
+
+ blks = GET_NO_OF_BLOCKS(len, lsz);
+
+ desc->lmsg_ctxt.ridx = (desc->lmsg_ctxt.ridx + blks) %
+ desc->tx_ctxt->lmsg_buf_cnt;
+
+ desc->lmsg_ctxt.lmsg_free_cnt += blks;
+}
+
+static int btss_send_ctrl(struct qcom_btss *desc, u16 msg_hdr)
+{
+ struct ring_buffer_info *rinfo;
+ struct ring_buffer *rbuf;
+
+ rinfo = btss_get_tx_rbuf(desc, NULL);
+ if (IS_ERR(rinfo))
+ return PTR_ERR(rinfo);
+
+ rbuf = &((struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf))[rinfo->widx];
+ rbuf->msg_hdr = cpu_to_le16(msg_hdr);
+ rbuf->len = 0;
+
+ rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+ regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+ return 0;
+}
+
+static void btss_recv_cust_frame(struct qcom_btss *desc, u8 cmd)
+{
+ u16 msg_hdr = 0;
+ int ret;
+
+ msg_hdr |= cmd;
+
+ switch (cmd) {
+ case IPC_CMD_STOP:
+ bt_dev_info(desc->hdev, "BTSS stopped, gracefully stopping APSS IPC");
+ break;
+ case IPC_CMD_START:
+ desc->tx_ctxt = (struct context_info *)((void *)desc->rx_ctxt +
+ le16_to_cpu(desc->rx_ctxt->total_size));
+ desc->lmsg_ctxt.widx = 0;
+ desc->lmsg_ctxt.ridx = 0;
+ desc->lmsg_ctxt.smsg_free_cnt = desc->tx_ctxt->smsg_buf_cnt;
+ desc->lmsg_ctxt.lmsg_free_cnt = desc->tx_ctxt->lmsg_buf_cnt;
+ WRITE_ONCE(desc->running, true);
+ wake_up(&desc->wait_q);
+
+ bt_dev_info(desc->hdev, "BTSS started, initializing APSS BT IPC");
+ return;
+ default:
+ bt_dev_err(desc->hdev, "Unsupported CMD ID: %u", cmd);
+ return;
+ }
+
+ if (unlikely(!READ_ONCE(desc->running))) {
+ bt_dev_err(desc->hdev, "BTSS not initialized, no message sent");
+ return;
+ }
+
+ WRITE_ONCE(desc->running, false);
+
+ ret = btss_send_ctrl(desc, msg_hdr);
+ if (ret)
+ bt_dev_err(desc->hdev, "Failed to send control message");
+}
+
+static inline int btss_recv_hci_frame(struct qcom_btss *desc, const u8 *data, size_t len)
+{
+ unsigned char pkt_type;
+ struct sk_buff *skb;
+ size_t pkt_len;
+
+ if (len < 1)
+ return -EPROTO;
+
+ pkt_type = data[0];
+
+ switch (pkt_type) {
+ case HCI_EVENT_PKT:
+ {
+ if (len < HCI_EVENT_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *)(data + 1);
+
+ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+ break;
+ }
+ case HCI_COMMAND_PKT: {
+ if (len < HCI_COMMAND_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_command_hdr *hdr = (struct hci_command_hdr *)(data + 1);
+
+ pkt_len = HCI_COMMAND_HDR_SIZE + le16_to_cpu(hdr->plen);
+ break;
+ }
+ case HCI_ACLDATA_PKT:
+ {
+ if (len < HCI_ACL_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *)(data + 1);
+
+ pkt_len = HCI_ACL_HDR_SIZE + le16_to_cpu(hdr->dlen);
+ break;
+ }
+ case HCI_SCODATA_PKT:
+ {
+ if (len < HCI_SCO_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *)(data + 1);
+
+ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+ break;
+ }
+ default:
+ return -EPROTO;
+ }
+
+ if (pkt_len > len)
+ return -EINVAL;
+
+ skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+ if (!skb) {
+ desc->hdev->stat.err_rx++;
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *)desc->hdev;
+ hci_skb_pkt_type(skb) = pkt_type;
+ skb_put_data(skb, data + 1, pkt_len);
+
+ hci_recv_frame(desc->hdev, skb);
+ desc->hdev->stat.byte_rx += pkt_len;
+
+ return 0;
+}
+
+static inline int btss_process_rx(struct qcom_btss *desc,
+ struct ring_buffer_info *rinfo,
+ bool *ack, u8 *rx_count)
+{
+ u8 ridx, lbuf_idx, blks_consumed, pkt_type, cmd;
+ struct ipc_aux_ptr aux_ptr;
+ struct ring_buffer *rbuf;
+ uint8_t *rxbuf = NULL;
+ unsigned char *buf;
+ u32 lsz;
+ int ret;
+
+ ridx = rinfo->ridx;
+
+ while (ridx != rinfo->widx) {
+ memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+ rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[ridx];
+
+ if (rbuf->msg_hdr & IPC_HDR_LONG_MSG) {
+ rxbuf = TO_APPS_ADDR(rbuf->payload.lmsg_data);
+ lsz = IPC_LBUF_SZ(desc->rx_ctxt, total_size, lring_buf,
+ lmsg_buf_cnt);
+
+ if (IS_RX_MEM_NON_CONTIGIOUS(rbuf->payload.lmsg_data,
+ rbuf->len, lsz)) {
+ lbuf_idx = GET_RX_INDEX_FROM_BUF(
+ rbuf->payload.lmsg_data, lsz);
+
+ blks_consumed = desc->rx_ctxt->lmsg_buf_cnt -
+ lbuf_idx;
+ aux_ptr.len = rbuf->len - (blks_consumed * lsz);
+ aux_ptr.buf = desc->rx_ctxt->lring_buf;
+ }
+ } else {
+ rxbuf = rbuf->payload.smsg_data;
+ }
+
+ *ack = (rbuf->msg_hdr & IPC_HDR_REQ_ACK);
+
+ pkt_type = FIELD_GET(IPC_HDR_PKT_TYPE_MASK, rbuf->msg_hdr);
+
+ switch (pkt_type) {
+ case IPC_HDR_PKT_TYPE_HCI:
+ buf = kmalloc(rbuf->len, GFP_ATOMIC);
+ if (!buf) {
+ rinfo->ridx = ridx;
+ return -ENOMEM;
+ }
+
+ memcpy_fromio(buf, rxbuf, rbuf->len - aux_ptr.len);
+
+ if (aux_ptr.buf)
+ memcpy_fromio(buf + (rbuf->len - aux_ptr.len),
+ TO_APPS_ADDR(aux_ptr.buf), aux_ptr.len);
+
+ ret = btss_recv_hci_frame(desc, buf, rbuf->len);
+ if (ret)
+ bt_dev_err(desc->hdev, "Failed to process HCI frame: %d", ret);
+ kfree(buf);
+ break;
+ case IPC_HDR_PKT_TYPE_CUST:
+ cmd = FIELD_GET(IPC_HDR_CMD_MASK, rbuf->msg_hdr);
+ btss_recv_cust_frame(desc, cmd);
+ break;
+ default:
+ break;
+ }
+
+ ridx = (ridx + 1) % rinfo->ring_buf_cnt;
+
+ if (rx_count)
+ (*rx_count)++;
+
+ rinfo->ridx = ridx;
+ }
+
+ return 0;
+}
+
+static void btss_process_ack(struct qcom_btss *desc)
+{
+ struct ring_buffer_info *rinfo;
+ struct ring_buffer *rbuf;
+ u8 tidx;
+
+ for (rinfo = &desc->tx_ctxt->sring_buf_info; rinfo != NULL;
+ rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+ tidx = rinfo->tidx;
+ rbuf = (struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf);
+
+ while (tidx != rinfo->ridx) {
+ if (rbuf[tidx].msg_hdr & IPC_HDR_LONG_MSG) {
+ btss_free_lmsg(desc,
+ rbuf[tidx].payload.lmsg_data,
+ rbuf[tidx].len);
+ }
+
+ tidx = (tidx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+ desc->lmsg_ctxt.smsg_free_cnt++;
+ }
+
+ rinfo->tidx = tidx;
+
+ btss_process_tx_queue(desc);
+ }
+}
+
+static void btss_worker(struct work_struct *work)
+{
+ struct qcom_btss *desc = container_of(work, struct qcom_btss, work);
+ struct ring_buffer_info *rinfo;
+ bool ack = false;
+ u32 offset;
+ int ret;
+
+ if (desc->rproc->state != RPROC_RUNNING)
+ return;
+
+ spin_lock(&desc->lock);
+
+ if (unlikely(!READ_ONCE(desc->running))) {
+ // FW sets offset of RX context info at start of memory region upon boot
+ offset = readl(desc->mem_region);
+ dev_dbg(desc->dev, "offset after M0 boot: 0x%08x\n", offset);
+ desc->rx_ctxt = (struct context_info *)(desc->mem_region + offset);
+ } else {
+ btss_process_ack(desc);
+ }
+
+ for (rinfo = &(desc->rx_ctxt->sring_buf_info);
+ rinfo != NULL;
+ rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+ ret = btss_process_rx(desc, rinfo, &ack,
+ &desc->rx_ctxt->smsg_buf_cnt);
+ if (ret) {
+ bt_dev_err(desc->hdev, "Failed to process peer msgs: %d", ret);
+ goto spin_unlock;
+ }
+ }
+
+ if (ack)
+ regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+spin_unlock:
+ spin_unlock(&desc->lock);
+}
+
+static irqreturn_t btss_irq_handler(int irq, void *data)
+{
+ struct qcom_btss *desc = data;
+
+ queue_work(desc->wq, &desc->work);
+
+ return IRQ_HANDLED;
+}
+
+static int btss_init(struct qcom_btss *desc)
+{
+ struct device *dev = desc->dev;
+ int ret;
+
+ init_waitqueue_head(&desc->wait_q);
+ spin_lock_init(&desc->lock);
+ skb_queue_head_init(&desc->tx_q);
+
+ desc->wq = create_singlethread_workqueue("btss_wq");
+ if (!desc->wq) {
+ dev_err(dev, "Failed to initialize workqueue\n");
+ return -EAGAIN;
+ }
+
+ INIT_WORK(&desc->work, btss_worker);
+
+ ret = devm_request_threaded_irq(dev, desc->irq, NULL, btss_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "btss_irq", desc);
+
+ if (ret)
+ dev_err(dev, "error registering irq[%d] ret = %d\n",
+ desc->irq, ret);
+
+ return ret;
+}
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ u8 pkt_type = hci_skb_pkt_type(skb);
+
+ hdev->stat.byte_tx += skb->len;
+ switch (pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ default:
+ break;
+ }
+}
+
+static int btqcomipc_open(struct hci_dev *hdev)
+{
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+ int ret;
+
+ ret = btss_init(desc);
+ if (ret) {
+ bt_dev_err(hdev, "Failed initializing BTSS: %d", ret);
+ return ret;
+ }
+
+ /* wait 2 seconds for filesystem to become available */
+ msleep(2000);
+
+ /* Boot M0 firmware */
+ ret = rproc_boot(desc->rproc);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to boot M0 processor: %d", ret);
+ return ret;
+ }
+
+ msleep(POWER_CONTROL_DELAY_MS);
+ ret = wait_event_timeout(desc->wait_q, READ_ONCE(desc->running),
+ msecs_to_jiffies(1000));
+
+ if (!ret) {
+ bt_dev_err(hdev, "Timeout waiting for BTSS start");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int btqcomipc_close(struct hci_dev *hdev)
+{
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+ rproc_shutdown(desc->rproc);
+ msleep(POWER_CONTROL_DELAY_MS);
+
+ return 0;
+}
+
+static int btqcomipc_shutdown(struct hci_dev *hdev)
+{
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+ WRITE_ONCE(desc->running, false);
+ if (desc->wq != NULL) {
+ disable_irq(desc->irq);
+ flush_workqueue(desc->wq);
+ devm_free_irq(desc->dev, desc->irq, desc);
+ skb_queue_purge(&desc->tx_q);
+ destroy_workqueue(desc->wq);
+ desc->wq = NULL;
+ }
+
+ return 0;
+}
+
+static int btqcomipc_setup(struct hci_dev *hdev)
+{
+ struct qca_btsoc_version ver;
+ int ret;
+
+ /*
+ * Enable controller to do both LE scan and BR/EDR inquiry
+ * simultaneously.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+
+ /*
+ * Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute
+ * setup for every hci up.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+ ret = qca_read_soc_version(hdev, &ver, QCA_IPQ5018);
+ if (ret)
+ return -EINVAL;
+
+ ret = qca_uart_setup(hdev, 0, QCA_IPQ5018, ver, NULL);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to setup UART: %d\n", ret);
+ return ret;
+ }
+
+ bt_dev_info(hdev, "QCA Build Info: %s", hdev->fw_info);
+
+ /* Obtain and set BD address from NVMEM cell */
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+ hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+
+ return 0;
+}
+
+static int btqcomipc_send(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ u16 hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+ int ret;
+
+ if (unlikely(!READ_ONCE(desc->running))) {
+ bt_dev_err(hdev, "BTSS not initialized, failed to send message");
+ ret = -ENODEV;
+ goto free_skb;
+ }
+
+ ret = btss_send(desc, hdr, skb);
+ if (ret) {
+ if (ret == -EAGAIN) {
+ if (skb_queue_len(&desc->tx_q) >= IPC_TX_QSIZE) {
+ bt_dev_err(hdev, "TX queue full, dropping message");
+ hdev->stat.err_tx++;
+ ret = -ENOBUFS;
+ } else {
+ skb_queue_tail(&desc->tx_q, skb);
+ return 0;
+ }
+ } else {
+ bt_dev_err(hdev, "Failed to send message: %d", ret);
+ hdev->stat.err_tx++;
+ }
+ }
+
+ btqcomipc_update_stats(desc->hdev, skb);
+
+free_skb:
+ kfree_skb(skb);
+
+ return ret;
+}
+
+static int btqcomipc_flush(struct hci_dev *hdev)
+{
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+ skb_queue_purge(&desc->tx_q);
+ return 0;
+}
+
+static int btqcomipc_alloc_memory_region(struct qcom_btss *desc)
+{
+ struct device *dev = desc->dev;
+ struct resource res;
+ int ret;
+
+ /* lookup reserved-memory region of the remoteproc node */
+ ret = of_reserved_mem_region_to_resource(desc->rproc->dev.parent->of_node, 0, &res);
+ if (ret) {
+ dev_err(dev, "unable to acquire memory-region resource\n");
+ return ret;
+ }
+
+ desc->mem_phys = res.start;
+ desc->mem_reloc = res.start;
+ desc->mem_size = resource_size(&res);
+ desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+ if (!desc->mem_region) {
+ dev_err(dev, "unable to map memory region: %pR\n", &res);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void btqcomipc_rproc_put(void *data)
+{
+ struct rproc *rproc = data;
+
+ rproc_put(rproc);
+}
+
+static int btqcomipc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qcom_btss *desc;
+ phandle rproc_phandle;
+ struct hci_dev *hdev;
+ unsigned int args[2];
+ int ret;
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->dev = dev;
+
+ if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle))
+ return dev_err_probe(dev, -ENOENT, "Failed to get remoteproc handle\n");
+
+ desc->rproc = rproc_get_by_phandle(rproc_phandle);
+ if (!desc->rproc)
+ return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get remoteproc\n");
+
+ devm_add_action_or_reset(dev, btqcomipc_rproc_put, desc->rproc);
+
+ ret = btqcomipc_alloc_memory_region(desc);
+ if (ret)
+ return ret;
+
+ desc->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "qcom,ipc", 2, args);
+ if (IS_ERR(desc->regmap))
+ return PTR_ERR(desc->regmap);
+
+ desc->offset = args[0];
+ desc->bit = args[1];
+
+ desc->irq = platform_get_irq(pdev, 0);
+ if (desc->irq < 0)
+ return dev_err_probe(dev, desc->irq, "Failed to acquire IRQ\n");
+
+ hdev = hci_alloc_dev();
+ if (!hdev)
+ return -ENOMEM;
+
+ hci_set_drvdata(hdev, desc);
+ desc->hdev = hdev;
+ SET_HCIDEV_DEV(hdev, &pdev->dev);
+ hdev->bus = HCI_IPC;
+
+ hdev->open = btqcomipc_open;
+ hdev->close = btqcomipc_close;
+ hdev->shutdown = btqcomipc_shutdown;
+ hdev->setup = btqcomipc_setup;
+ hdev->send = btqcomipc_send;
+ hdev->flush = btqcomipc_flush;
+ hdev->set_bdaddr = qca_set_bdaddr;
+
+ ret = hci_register_dev(hdev);
+ if (ret < 0) {
+ hci_free_dev(hdev);
+ return dev_err_probe(dev, -EBUSY, "Failed to register hdev\n");
+ }
+
+ platform_set_drvdata(pdev, desc);
+
+ return 0;
+}
+
+static void btqcomipc_remove(struct platform_device *pdev)
+{
+ struct qcom_btss *desc = platform_get_drvdata(pdev);
+
+ if (!desc || !desc->hdev)
+ return;
+
+ hci_unregister_dev(desc->hdev);
+ hci_free_dev(desc->hdev);
+}
+
+static const struct of_device_id btqcomipc_of_match[] = {
+ { .compatible = "qcom,ipq5018-bt" },
+ { /* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, btqcomipc_of_match);
+
+static struct platform_driver btqcomipc_driver = {
+ .probe = btqcomipc_probe,
+ .remove = btqcomipc_remove,
+ .driver = {
+ .name = "btqcomipc",
+ .of_match_table = btqcomipc_of_match,
+ },
+};
+
+module_platform_driver(btqcomipc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Bluetooth IPC Driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net] sctp: fix SCTP_RESET_STREAMS stream list length limit
From: Xin Long @ 2026-06-25 14:12 UTC (permalink / raw)
To: Yousef Alhouseen
Cc: Marcelo Ricardo Leitner, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-sctp, netdev,
linux-kernel
In-Reply-To: <20260624122213.4052-1-alhouseenyousef@gmail.com>
On Wed, Jun 24, 2026 at 8:22 AM Yousef Alhouseen
<alhouseenyousef@gmail.com> wrote:
>
> SCTP_RESET_STREAMS carries a flexible array of u16 stream IDs, but the
> optlen clamps treat USHRT_MAX as a byte count and then multiply
> sizeof(__u16) by the fixed header size.
>
> That caps the copied and validated option buffer at about 64 KiB, which
> rejects valid requests containing more than about half of the u16 stream
> ID range.
>
> Use struct_size_t() for the maximum struct sctp_reset_streams layout
> instead, so the bound matches the flexible array described by
> srs_number_streams.
>
> Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
Fixes: 5960cefab9df ("sctp: add a ceiling to optlen in some sockopts")
Acked-by: Xin Long <lucien.xin@gmail.com>
^ permalink raw reply
* Re: [RFC PATCH net-next v8 03/12] net: phylink: add phylink_release_pcs() to externally release a PCS
From: Maxime Chevallier @ 2026-06-25 14:13 UTC (permalink / raw)
To: Christian Marangi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Simon Horman, Jonathan Corbet, Shuah Khan,
Lorenzo Bianconi, Heiner Kallweit, Russell King, Saravana Kannan,
Philipp Zabel, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
Justin Stitt, netdev, devicetree, linux-kernel, linux-doc,
linux-arm-kernel, linux-mediatek, llvm
In-Reply-To: <20260618125752.1223-4-ansuelsmth@gmail.com>
Hello Christian,
On 6/18/26 14:57, Christian Marangi wrote:
> Add phylink_release_pcs() to externally release a PCS from a phylink
> instance. This can be used to handle case when a single PCS needs to be
> removed and the phylink instance needs to be refreshed.
>
> On calling phylink_release_pcs(), the PCS will be removed from the
> phylink internal PCS list and the phylink supported_interfaces value is
> reparsed with the remaining PCS interfaces.
>
> Also a phylink resolve is triggered to handle the PCS removal.
>
> The flag force_major_config is set to make phylink resolve reconfigure
> the interface (even if it didn't change).
> This is needed to handle the special case when the current PCS used
> by phylink is removed and a major_config is needed to propagae the
> configuration change. With this option enabled we also force mac_config
> even if the PHY link is not up for the in-band case.
>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> ---
> drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++
> include/linux/phylink.h | 2 ++
> 2 files changed, 58 insertions(+)
>
> diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> index c38bcd43b8c8..064d6f5a06da 100644
> --- a/drivers/net/phy/phylink.c
> +++ b/drivers/net/phy/phylink.c
> @@ -158,6 +158,8 @@ static const phy_interface_t phylink_sfp_interface_preference[] = {
> static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
>
> static void phylink_run_resolve(struct phylink *pl);
> +static void phylink_link_down(struct phylink *pl);
> +static void phylink_pcs_disable(struct phylink_pcs *pcs);
>
> /**
> * phylink_set_port_modes() - set the port type modes in the ethtool mask
> @@ -918,6 +920,60 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
> }
> }
>
> +/**
> + * phylink_release_pcs - Removes a PCS from the phylink PCS available list
> + * @pcs: a pointer to the phylink_pcs struct to be released
> + *
> + * This function release a PCS from the phylink PCS available list if
> + * actually in use. It also refreshes the supported interfaces of the
> + * phylink instance by copying the supported interfaces from the phylink
> + * conf and merging the supported interfaces of the remaining available PCS
> + * in the list and trigger a resolve.
> + */
> +void phylink_release_pcs(struct phylink_pcs *pcs)
> +{
> + struct phylink *pl;
> +
> + ASSERT_RTNL();
> +
> + pl = pcs->phylink;
> + if (!pl)
> + return;
> +
> + mutex_lock(&pl->state_mutex);
> +
> + list_del(&pcs->list);
> + pcs->phylink = NULL;
> +
> + /*
> + * Check if we are removing the PCS currently
> + * in use by phylink. If this is the case, tear down
> + * the link, force phylink resolve to reconfigure the
> + * interface mode, disable the current PCS and set the
> + * phylink PCS to NULL.
> + */
> + if (pl->pcs == pcs) {
> + phylink_link_down(pl);
> + phylink_pcs_disable(pl->pcs);
> +
> + pl->force_major_config = true;
> + pl->pcs = NULL;
> + }
> +
> + mutex_unlock(&pl->state_mutex);
> +
> + /* Refresh supported interfaces */
> + phy_interface_copy(pl->supported_interfaces,
> + pl->config->supported_interfaces);
> + list_for_each_entry(pcs, &pl->pcs_list, list)
> + phy_interface_or(pl->supported_interfaces,
> + pl->supported_interfaces,
> + pcs->supported_interfaces);
I've given more thought to that 'supported_interfaces' thing. This
patchset redefines the meaning of
pl->config->supported_interfaces
Currently, it's filled by the MAC driver and means "Every interface
we can support, including the ones provided by PCSs that we can use
with this MAC".
It now becomes "Every interface we support without needing a PCS", at
least the way I understand that.
It's not an error in your code, but I think this is worth documenting
somewhere as this changes one the things that's already fairly
error-prone in new drivers.
I don't know to what extent people use that, be we have a porting guide
that explains how to use phylink in a MAC driver, maybe an update in there
would be nice as well :
https://docs.kernel.org/networking/sfp-phylink.html#rough-guide-to-converting-a-network-driver-to-sfp-phylink
Maxime
^ permalink raw reply
* Re: [PATCH net 4/7] xsk: reclaim offending invalid desc in generic multi-buffer Tx
From: Jason Xing @ 2026-06-25 14:16 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: netdev, bpf, magnus.karlsson, stfomichev, kuba, pabeni, horms,
bjorn
In-Reply-To: <20260623133240.1048434-5-maciej.fijalkowski@intel.com>
On Tue, Jun 23, 2026 at 9:33 PM Maciej Fijalkowski
<maciej.fijalkowski@intel.com> wrote:
>
> After an invalid descriptor is found in __xsk_generic_xmit(),
> xskq_cons_peek_desc() returns false and the loop body is not entered.
> Jason's drain fixes reclaim descriptors already attached to xs->skb and
> later continuation descriptors handled through drain_cont, but the
> offending descriptor that made peek fail is only released from the Tx
> ring.
>
> This loses one completion for each invalid multi-buffer packet in the
> generic path. Userspace then waits forever for a descriptor that has
> already been consumed by the kernel.
>
> If the failed descriptor belongs to an already-started or already-draining
> multi-buffer packet, publish its address to the completion ring before
> releasing it. Standalone invalid descriptors keep the existing behavior.
>
> Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
> Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> ---
> net/xdp/xsk.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index c489fadc3608..43791647cf18 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -1125,8 +1125,22 @@ static int __xsk_generic_xmit(struct sock *sk)
> }
>
> if (xskq_has_descs(xs->tx)) {
> + bool reclaim_desc = xs->skb || xs->drain_cont;
> +
> + if (reclaim_desc) {
> + err = xsk_cq_reserve_locked(xs->pool);
> + if (err) {
> + err = -EAGAIN;
xs->tx->invalid_descs--;
If the same descriptor is peeked N times due to repeated CQ reserve
failures, invalid_descs will be over-counted by N-1.
Thanks,
Jason
> + goto out;
> + }
> + }
> +
> if (xs->skb)
> xsk_drop_skb(xs->skb);
> +
> + if (reclaim_desc)
> + xsk_cq_submit_addr_single_locked(xs->pool, &desc);
> +
> xskq_cons_release(xs->tx);
> xs->drain_cont = xp_mb_desc(&desc);
> }
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: Philipp Zabel @ 2026-06-25 14:18 UTC (permalink / raw)
To: george.moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
Konrad Dybcio, Mathieu Poirier
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <20260625-ipq5018-bluetooth-v1-2-d999be0e04f7@outlook.com>
On Do, 2026-06-25 at 18:10 +0400, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Add support to bring up the M0 core of the bluetooth subsystem found in
> the IPQ5018 SoC.
>
> The signed firmware loaded is authenticated by TrustZone. If successful,
> the M0 core boots the firmware and the peripheral is taken out of reset
> using a Secure Channel Manager call to TrustZone.
>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
> drivers/remoteproc/Kconfig | 12 ++
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
> 3 files changed, 274 insertions(+)
>
[...]
> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
> new file mode 100644
> index 000000000000..7168e270e4d4
> --- /dev/null
> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
> @@ -0,0 +1,261 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/elf.h>
> +#include <linux/firmware/qcom/qcom_scm.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/soc/qcom/mdt_loader.h>
> +
> +#include "qcom_common.h"
> +
> +#define BTSS_PAS_ID 0xc
> +
> +struct m0_btss {
> + struct device *dev;
> + phys_addr_t mem_phys;
> + phys_addr_t mem_reloc;
> + void __iomem *mem_region;
> + size_t mem_size;
> + struct reset_control *btss_reset;
Why is this stored here? It doesn't seem to be used.
[...]
> +static int m0_btss_pil_probe(struct platform_device *pdev)
> +{
> + // struct reset_control *btss_reset;
It looks like this shouldn't be commented out.
> + struct device *dev = &pdev->dev;
> + const char *fw_name = NULL;
> + struct m0_btss *desc;
> + struct clk *lpo_clk;
> + struct rproc *rproc;
> + int ret;
> +
> + ret = of_property_read_string(dev->of_node, "firmware-name",
> + &fw_name);
> + if (ret < 0)
> + return ret;
> +
> + rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
> + fw_name, sizeof(*desc));
> + if (!rproc) {
> + dev_err(dev, "failed to allocate rproc\n");
> + return -ENOMEM;
> + }
> +
> + desc = rproc->priv;
> + desc->dev = dev;
> +
> + ret = m0_btss_alloc_memory_region(desc);
> + if (ret)
> + return ret;
> +
> + lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
> + if (IS_ERR(lpo_clk))
> + return dev_err_probe(dev, PTR_ERR(lpo_clk),
> + "Failed to get lpo clock\n");
> +
> + desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
Please use devm_reset_control_get_exclusive() directly.
> + if (IS_ERR_OR_NULL(desc->btss_reset))
> + return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
> + "unable to acquire btss_reset\n");
> +
> + ret = reset_control_deassert(desc->btss_reset);
> + if (ret)
> + return dev_err_probe(rproc->dev.parent, ret,
> + "Failed to deassert reset\n");
Shouldn't this be asserted on remove? Otherwise after an unbind/bind
cycle probe will enable the clock with reset already deasserted.
That may or may not be problematic.
Consider using devm_reset_control_get_exclusive_deasserted().
regards
Philipp
^ permalink raw reply
* Re: [PATCH v1 1/1] ocp: Add I2C control support for Adva TimeCard
From: Andrew Lunn @ 2026-06-25 14:20 UTC (permalink / raw)
To: Sagi Maimon
Cc: jonathan.lemon, vadim.fedorenko, richardcochran, andrew+netdev,
davem, edumazet, kuba, pabeni, linux-kernel, netdev
In-Reply-To: <20260625133802.11741-1-maimon.sagi@gmail.com>
On Thu, Jun 25, 2026 at 04:38:02PM +0300, Sagi Maimon wrote:
> - Load i2c-dev module to expose /dev/i2c-N character devices
> - Add sysfs-based I2C bus control for Adva TimeCard model
A list like this suggests the patch should actually be two patches.
Also, the commit message should answer the question "Why?". I can see
what you have done by reading the patch. But Why have you done it?
> + request_module("i2c-dev");
linux/drivers$ grep -r i2c-dev | grep request
grep: i2c/i2c-dev.o: binary file matches
No other drive does this. Why is this driver special?
Andrew
---
pw-bot: cr
^ permalink raw reply
* [PATCH net v2] sctp: fix SCTP_RESET_STREAMS stream list length limit
From: Yousef Alhouseen @ 2026-06-25 14:23 UTC (permalink / raw)
To: Marcelo Ricardo Leitner, Xin Long
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, linux-sctp, netdev, linux-kernel, Yousef Alhouseen
SCTP_RESET_STREAMS carries a flexible array of u16 stream IDs, but the
optlen clamps treat USHRT_MAX as a byte count and then multiply
sizeof(__u16) by the fixed header size.
That caps the copied and validated option buffer at about 64 KiB, which
rejects valid requests containing more than about half of the u16 stream
ID range.
Use struct_size_t() for the maximum struct sctp_reset_streams layout
instead, so the bound matches the flexible array described by
srs_number_streams.
Fixes: 5960cefab9df ("sctp: add a ceiling to optlen in some sockopts")
Acked-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
---
Changes in v2:
- Add Fixes and Acked-by tags from Xin Long.
- v1: https://lore.kernel.org/r/20260624122213.4052-1-alhouseenyousef@gmail.com
net/sctp/socket.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 66e12fb0c..b8f13044a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4111,8 +4111,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk,
if (optlen < sizeof(*params))
return -EINVAL;
/* srs_number_streams is u16, so optlen can't be bigger than this. */
- optlen = min_t(unsigned int, optlen, USHRT_MAX +
- sizeof(__u16) * sizeof(*params));
+ optlen = min_t(unsigned int, optlen,
+ struct_size_t(struct sctp_reset_streams, srs_stream_list,
+ USHRT_MAX));
if (params->srs_number_streams * sizeof(__u16) >
optlen - sizeof(*params))
@@ -4598,8 +4599,8 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
if (optlen > 0) {
/* Trim it to the biggest size sctp sockopt may need if necessary */
optlen = min_t(unsigned int, optlen,
- PAGE_ALIGN(USHRT_MAX +
- sizeof(__u16) * sizeof(struct sctp_reset_streams)));
+ PAGE_ALIGN(struct_size_t(struct sctp_reset_streams,
+ srs_stream_list, USHRT_MAX)));
kopt = memdup_sockptr(optval, optlen);
if (IS_ERR(kopt))
return PTR_ERR(kopt);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: George Moussalem @ 2026-06-25 14:24 UTC (permalink / raw)
To: Philipp Zabel, Jens Axboe, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
Konrad Dybcio, Mathieu Poirier
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <439f76c3fcafdfb91cca426fcae17ef776776eab.camel@pengutronix.de>
Thanks, that was quick!
On 6/25/26 18:18, Philipp Zabel wrote:
> On Do, 2026-06-25 at 18:10 +0400, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add support to bring up the M0 core of the bluetooth subsystem found in
>> the IPQ5018 SoC.
>>
>> The signed firmware loaded is authenticated by TrustZone. If successful,
>> the M0 core boots the firmware and the peripheral is taken out of reset
>> using a Secure Channel Manager call to TrustZone.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>> drivers/remoteproc/Kconfig | 12 ++
>> drivers/remoteproc/Makefile | 1 +
>> drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
>> 3 files changed, 274 insertions(+)
>>
> [...]
>> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
>> new file mode 100644
>> index 000000000000..7168e270e4d4
>> --- /dev/null
>> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
>> @@ -0,0 +1,261 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/elf.h>
>> +#include <linux/firmware/qcom/qcom_scm.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/soc/qcom/mdt_loader.h>
>> +
>> +#include "qcom_common.h"
>> +
>> +#define BTSS_PAS_ID 0xc
>> +
>> +struct m0_btss {
>> + struct device *dev;
>> + phys_addr_t mem_phys;
>> + phys_addr_t mem_reloc;
>> + void __iomem *mem_region;
>> + size_t mem_size;
>> + struct reset_control *btss_reset;
>
> Why is this stored here? It doesn't seem to be used.
will remove it and use devm_reset_control_get_exclusive_deasserted as
suggested below.
>
> [...]
>> +static int m0_btss_pil_probe(struct platform_device *pdev)
>> +{
>> + // struct reset_control *btss_reset;
>
> It looks like this shouldn't be commented out.
>
>> + struct device *dev = &pdev->dev;
>> + const char *fw_name = NULL;
>> + struct m0_btss *desc;
>> + struct clk *lpo_clk;
>> + struct rproc *rproc;
>> + int ret;
>> +
>> + ret = of_property_read_string(dev->of_node, "firmware-name",
>> + &fw_name);
>> + if (ret < 0)
>> + return ret;
>> +
>> + rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
>> + fw_name, sizeof(*desc));
>> + if (!rproc) {
>> + dev_err(dev, "failed to allocate rproc\n");
>> + return -ENOMEM;
>> + }
>> +
>> + desc = rproc->priv;
>> + desc->dev = dev;
>> +
>> + ret = m0_btss_alloc_memory_region(desc);
>> + if (ret)
>> + return ret;
>> +
>> + lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
>> + if (IS_ERR(lpo_clk))
>> + return dev_err_probe(dev, PTR_ERR(lpo_clk),
>> + "Failed to get lpo clock\n");
>> +
>> + desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
>
> Please use devm_reset_control_get_exclusive() directly.
>
>> + if (IS_ERR_OR_NULL(desc->btss_reset))
>> + return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
>> + "unable to acquire btss_reset\n");
>> +
>> + ret = reset_control_deassert(desc->btss_reset);
>> + if (ret)
>> + return dev_err_probe(rproc->dev.parent, ret,
>> + "Failed to deassert reset\n");
>
> Shouldn't this be asserted on remove? Otherwise after an unbind/bind
> cycle probe will enable the clock with reset already deasserted.
> That may or may not be problematic.
>
> Consider using devm_reset_control_get_exclusive_deasserted().
>
>
> regards
> Philipp
Regards,
George
^ permalink raw reply
* Re: [PATCH net 1/2] net: dsa: mxl862xx: avoid unaligned 16-bit access in api_wrap
From: Jakub Kicinski @ 2026-06-25 14:31 UTC (permalink / raw)
To: David Laight
Cc: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Paolo Abeni, netdev, linux-kernel
In-Reply-To: <20260625084459.7393409f@pumpkin>
On Thu, 25 Jun 2026 08:44:59 +0100 David Laight wrote:
> > struct mxl862xx_mac_table_clear {
> > u8 type;
> > u8 port_id;
> > } __packed;
>
> Does that one need an aligned(2) ?
Right, I meant that if we need to remember to do that instead of
depending on natural alignment - chances are someone will forget
while adding a new register, and the bug will be back.
> > So I guess the "just don't pack" will have some corner cases, too.
^ 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