Commit Graph

6 Commits

Author SHA1 Message Date
test0r 73191b7bc1 bes2600: drop sdio_rx_work relay, IRQ→bh-direct (no-relay architecture)
Patch C v3 — match cw1200 mainline architecture
(drivers/net/wireless/st/cw1200/).  Eliminates the
sdio_rx_work workqueue relay that introduced a thread-safety
race on hw_priv->hw_bufs_used in v1 (PR #3 closed) and that
v2's atomic_t prep was a workaround for (PR #10 superseded by
v3 plan PR #11).

Architectural changes:

  - bes2600_gpio_irq_handler: now calls self->irq_handler()
    directly instead of queue_work(self->sdio_wq, &self->rx_work).
    Bumps bh_rx atomic + wakes bh_wq.
  - bes2600_bh_rx_helper (BES_SDIO_RX_MULTIPLE_ENABLE branch):
    now calls priv->sbus_ops->bus_rx_batch() to do the SDIO read
    inline.  No pipe_read, no skb_dequeue.
  - bes2600_sdio_read_rx_batch (new): the SDIO read sequence
    extracted from sdio_rx_work, registered as
    sbus_ops->bus_rx_batch.  Runs in bh thread context.
  - bes2600_sdio_extract_packets: calls
    bes2600_bh_handle_rx_skb() directly per parsed SKB.  No
    skb_queue_tail, no rx_queue.
  - bes2600_bh_handle_rx_skb (new in bh.c): the per-SKB
    bookkeeping that bh_rx_helper used to do post-pipe_read
    (seq# check, exception, confirm-condition, wsm_handle_rx).
    Wakes bh thread for tx-burst via atomic_inc(&priv->bh_tx)
    instead of bes2600_bh_wakeup() — we ARE the bh thread.
  - Post-tx queue_work(rx_work) site: replaced with
    self->irq_handler() to wake bh for piggyback RX check.

Deleted infrastructure:

  - struct sbus_priv: rx_queue, rx_queue_lock, rx_work fields
  - bes2600_sdio_pipe_read: function deleted (unused)
  - sdio_rx_work: function deleted (unused)
  - sbus_ops->pipe_read assignment: removed for SDIO bus
  - skb_queue_head_init(&self->rx_queue), spin_lock_init(...),
    INIT_WORK(rx_work): probe-time setup removed
  - cancel_work_sync(rx_work) + drain loop in empty_work: removed
  - flush_work(rx_work) in drain helper: replaced with msleep(2)
  - work_pending(rx_work) check in suspend predicate: removed

Concurrency invariant restored:

  - hw_priv->hw_bufs_used: single-writer (bh thread only)
    by construction.  No atomic_t needed.
  - hw_priv->hw_bufs_used_vif[]: ditto.
  - hw_priv->wsm_tx_pending[]: ditto.
  - All other shared state: unchanged or already protected.

Phase 7 partial verification (rep 1, 2026-05-07):

  - Module loads clean, srcversion 371C6606B73AF19299228CA
  - Link associates, no WARN/BUG/oops
  - sdio_rx_work dispatches: 0 (function deleted)
  - bes2600_bh_work redispatches: 0 (single long-lived
    invariant preserved)
  - Chip handled stress traffic without wedge

Phase 7 full N=3 stress ramp deferred to follow-up rep series
(rep 2 had a TCP-level nc race; not a bes2600 issue but
invalidated rep 2's throughput number).
2026-05-20 20:17:58 +02:00
test0r d48f2ae73c bes2600: handle multi-function SDIO cards in mmc_hw_reset bus_reset
c5.2 (recover-wedged-firmware-via-mmc-hw-reset) wraps mmc_hw_reset()
and treats any non-zero return as a recovery failure. On
single-function SDIO cards mmc_hw_reset returns 0 after doing the
remove + rescan inline. On multi-function cards (BES2600 has WLAN
func 1 + BT companion func 2) the kernel's mmc_sdio_hw_reset() does
NOT do the rescan: it tears the card down and returns 1 to signal
"caller must trigger rescan".

Field observation on PineTab2 (linux-pinetab2 6.19.10-danctnix1):
when a real LMAC wedge fired bes2600_chrdev_wifi_force_close ->
bes2600_chrdev_do_bus_reset, mmc_hw_reset returned 1, c5.2's wrapper
treated that as "bus_reset failed: 1", logged the error, and gave
up. The card was already removed (mmc2: card 0001 removed) but
nothing scheduled a rescan; wifi (and the BT companion which shares
the same SDIO host) stayed silent until the user rebooted four
minutes later.

Fix:

  - Capture the mmc_host pointer before calling mmc_hw_reset (the
    card pointer is invalid after the remove).
  - On positive return (multi-function path), log informationally
    and call mmc_detect_change(host, 0) to schedule a rescan.
    Return 0 so callers see the recovery as successful.
  - Negative return is still treated as failure as before.

The mmc_detect_change side effect is asynchronous; the chrdev's
wait_event_timeout(probe_done_wq, !sbus_priv) still observes the
remove half synchronously, and the rescan + re-probe runs out of
the host detect work afterwards.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-05-20 20:17:58 +02:00
test0r 51d46a2e25 bes2600: short-circuit wake handshake when chip is confirmed ACTIVE
The previous patch ("bes2600: gate PM indication completion on pending
request and track chip state") added enum bes2600_chip_pm_state and the
chip_pm_state field tracking what the host has *seen the firmware
confirm*. This patch makes the wake side use it.

Without this, every bes2600_pwr_device_exit_lp_mode() unconditionally
runs gpio_wake() + sbus_active() + wsm_set_operational_mode(active),
even when the chip is already in confirmed-ACTIVE state and the wake
sequence has nothing to do. The visible failure mode on PineTab2:

  bes2600_pwr_enter_lp_mode, wait pm ind timeout
  repeat set gpio_wake_flag, sub_sys:0
  bes2600_sdio_active failed, subsys:0
  bes2600_pwr_device_exit_lp_mode, active mcu fail

cycling every ~9 s, ~22 cycles in 10 minutes. Three pieces:

  1. enter_lp_mode timed out (firmware indication lost). With c6.1,
     chip_pm_state is now UNKNOWN.
  2. lock_device fires exit_lp_mode.
  3. gpio_wake hits "bit already set" because device_enter_lp_mode
     was skipped when the indication timed out, so gpio_sleep was
     never called - the bit reflects driver intent, not chip state.
     gpio_wake silently no-ops (no GPIO edge), bit stays set.
  4. sbus_active spends 200 x 2 ms looking for MCU_WAKEUP_READY that
     never comes (firmware was never told to wake), then fails.
  5. Driver continues to wsm_set_operational_mode against the wedged
     bus, compounding the failure.

This patch's three moves:

  * bes2600_pwr_device_exit_lp_mode() reads chip_pm_state at entry.
    On BES2600_CHIP_PM_ACTIVE, log at devel level and return without
    touching gpio_wake / sbus_active / WSM. The chip is in the state
    we want; the handshake exists only to drive a transition.

  * On BES2600_CHIP_PM_LP or BES2600_CHIP_PM_UNKNOWN, run the wake
    handshake as before, but on sbus_active() failure: set
    chip_pm_state = UNKNOWN, log once at err level, and bail out.
    Do NOT call wsm_set_operational_mode over a wedged bus - it
    would just emit a second error and leave the chip in an even
    less defined state.

  * bes2600_gpio_wakeup_mcu() / bes2600_gpio_allow_mcu_sleep():
    demote "repeat set/clear gpio_wake_flag" from bes_err to
    bes_devel. Multi-subsystem wake-hold (e.g. WIFI + BT both want
    MCU awake) is the steady-state case, and the symmetric clear
    while bit-already-clear is racy bookkeeping rather than a
    hardware error. The wake-side log line also now correctly
    updates the bit so the per-subsystem reference count stays
    accurate, fixing a pre-existing minor leak where an existing
    holder's repeat-call wouldn't bump the bit (which never matters
    today since BIT(flag) is 1, but matters if the structure ever
    grows to per-flag refcounts).

Net effect on the cycle:

  * If chip is genuinely ACTIVE (chip_pm_state == ACTIVE), wake skips
    cleanly. Storm goes silent.
  * If chip is genuinely LP, behaviour is unchanged.
  * If chip is UNKNOWN (post-timeout state), one wake attempt is
    made; on failure, state stays UNKNOWN and we don't emit a
    second cascade error per attempt. Repeated UNKNOWN with failed
    wake will eventually be picked up by the LMAC active-monitor
    and escalated to mmc_hw_reset (c5.2).

No new locks, no new state. Only consumption of the chip_pm_state
field added in the prerequisite patch.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-05-20 20:17:58 +02:00
test0r e0f664cbc9 bes2600: recover wedged firmware via mmc_hw_reset on link break
When the LMAC active monitor detects 'link break between lmac and host'
(the hw_buf_used==pending watchdog in bes2600_bh_lmac_active_monitor),
bes2600_chrdev_wifi_force_close(hw_priv, true) is invoked to tear the
device down and prepare for a fresh probe. On the wifi_force_close_work
side this calls bes2600_chrdev_do_system_close() which dispatches
sbus_ops->power_switch(0).

On PineTab2 (RK3566 + BES2600WM over SDIO) this recovery path is a
no-op:

  * bes2600_sdio_power_down() writes a SYSTEM_CLOSE host-int message,
    clears MMC_CAP_NONREMOVABLE, and schedules sdio_scan_work, which is
    the literal one-line stub bes_warn("...this function does
    nothing\n").
  * bes2600_sdio_on() (the eventual power_switch(1) counterpart)
    toggles pdata->powerup, which is NULL on PineTab2 because the
    wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 device
    tree node (see arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi:
    'The reset pin is claimed by sdio_mmcseq, It is better to move it
    to U-Boot so the OS can use it.').

Net result: the chip is never reset. The function drivers are not
removed (the SDIO core has no signal that the card is gone), the
firmware stays wedged, and a subsequent rmmod bes2600 leaves the SDIO
function in a half-torn-down state. modprobe bes2600 then fails with
'probe with driver bes2600_wlan failed with error -123' (-ENOMEDIUM)
on both functions (:1 wifi, :2 BT-companion) until a full system
reboot.

Observed on PineTab2 (linux-pinetab2 6.19.10-danctnix1-1) after ~150
minutes of background-scan rejects (wsm_generic_confirm 0x0007,
[SCAN] Scan failed (-22)) accumulating until the LMAC stopped
acknowledging TX buffers (hw_buf_used:24 pending:24). Reproducible
under sustained scan pressure.

Add a sbus operation bus_reset() that the recovery path can call when
power_switch() has no effective chip-reset signal of its own. Provide
an SDIO implementation that calls mmc_hw_reset(self->func->card),
which on a multi-function SDIO card (PineTab2 binds func 1 for WLAN
and func 2 for the BT-companion path) takes the remove-and-rescan
path: mmc_sdio_hw_reset() marks the card removed and schedules
mmc_rescan, which tears down the bound function drivers and re-detects
the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
With a single function probed it instead invokes mmc_power_cycle()
directly, which on PineTab2 toggles the wifi-reset GPIO via
sdio_pwrseq.

Add bes2600_chrdev_do_bus_reset() as the chrdev-side helper. It
invokes the bus op and then waits on probe_done_wq for the SDIO
remove() callback to clear sbus_priv, mirroring the wait pattern
already used by bes2600_chrdev_do_system_close() so that a subsequent
bes2600_switch_wifi(true) sees a clean state and can wait on the
fresh probe.

Wire it into bes2600_chrdev_wifi_force_close_work(): when halt_dev is
set (the hard-exception path used by both
bes2600_bh_lmac_active_monitor and bes2600_bh_mcu_active_monitor) and
the underlying bus implements bus_reset, take the new recovery path;
otherwise fall back to the legacy power_switch(0) sequence so this
patch is a no-op on USB or any other future bus that does not provide
bus_reset.

mmc_hw_reset() is exported by the MMC core and is the canonical
recovery primitive; calling it without holding the SDIO host claim is
correct because the multi-func remove-and-rescan path acquires the
host claim via the mmc workqueue, and the single-func mmc_power_cycle
path does not require the host claim.

No DT change is required: this works against the existing PineTab2
DTS, where the wifi-reset GPIO and the optional sdio_pwrkey GPIO (on
v2.0 boards) are both already configured as MMC pwrseq resets.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-05-20 20:16:59 +02:00
test0r e0d752aae9 sync bes2600/ to v7.0-danctnix1 baseline (rebasing reference) 2026-05-19 09:04:33 +02:00
Julian ba20341e70 Upload
Source: https://github.com/cringeops/bes2600
Source: https://github.com/cringeops/bes2600/pull/14
Source: https://github.com/cringeops/bes2600/pull/17
Source: https://github.com/cringeops/bes2600/pull/20
2025-09-17 16:35:45 +02:00