mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Lachlan Hodges <lachlan.hodges@morsemicro.com>
To: johannes@sipsolutions.net,
	Lachlan Hodges <lachlan.hodges@morsemicro.com>,
	Dan Callaghan <dan.callaghan@morsemicro.com>,
	Arien Judge <arien.judge@morsemicro.com>
Cc: ayman.grais@morsemicro.com, linux-wireless@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH wireless-next 18/35] wifi: mm81x: add ps.c
Date: Fri, 27 Feb 2026 15:10:28 +1100	[thread overview]
Message-ID: <20260227041108.66508-19-lachlan.hodges@morsemicro.com> (raw)
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>

(Patches split per file for review, see cover letter for more
information)

Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
 drivers/net/wireless/morsemicro/mm81x/ps.c | 239 +++++++++++++++++++++
 1 file changed, 239 insertions(+)
 create mode 100644 drivers/net/wireless/morsemicro/mm81x/ps.c

diff --git a/drivers/net/wireless/morsemicro/mm81x/ps.c b/drivers/net/wireless/morsemicro/mm81x/ps.c
new file mode 100644
index 000000000000..432165697acf
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/ps.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include "hif.h"
+#include "debug.h"
+#include "skbq.h"
+#include "mac.h"
+#include "bus.h"
+#include "ps.h"
+
+#define MM81X_WAKEUP_DELAY_MS 10
+
+static bool mm81x_ps_is_busy_pin_asserted(struct mm81x *mm)
+{
+	bool active_high =
+		!(mm->firmware_flags & MM81X_FW_FLAGS_BUSY_ACTIVE_LOW);
+
+	if (!mm->ps.gpios_supported)
+		return false;
+
+	return (gpiod_get_value_cansleep(mm->ps.busy_gpio) == active_high);
+}
+
+static void mm81x_ps_set_wake_gpio(struct mm81x *mm, bool raise)
+{
+	if (!mm->ps.gpios_supported)
+		return;
+
+	gpiod_set_value_cansleep(mm->ps.wake_gpio, raise);
+
+	mm81x_dbg(mm, MM81X_DBG_ANY, "%s wake up pin",
+		  (raise) ? "set" : "cleared");
+}
+
+static void mm81x_ps_wait_after_wake_pin_raise(struct mm81x *mm)
+{
+	unsigned long timeout;
+
+	if (!mm->ps.gpios_supported)
+		return;
+
+	if (mm81x_ps_is_busy_pin_asserted(mm))
+		return;
+
+	timeout = msecs_to_jiffies(MM81X_WAKEUP_DELAY_MS);
+	wait_for_completion_timeout(mm->ps.awake, timeout);
+}
+
+static void mm81x_ps_wakeup(struct mm81x_ps *mps)
+{
+	DECLARE_COMPLETION_ONSTACK(awake);
+	struct mm81x *mm = container_of(mps, struct mm81x, ps);
+
+	if (!mps->enable || !mps->suspended)
+		return;
+
+	WRITE_ONCE(mps->awake, &awake);
+	mm81x_ps_set_wake_gpio(mm, true);
+	mm81x_ps_wait_after_wake_pin_raise(mm);
+	WRITE_ONCE(mps->awake, NULL);
+
+	mm81x_set_bus_enable(mm, true);
+	mps->suspended = false;
+}
+
+static void mm81x_ps_sleep(struct mm81x_ps *mps)
+{
+	struct mm81x *mm = container_of(mps, struct mm81x, ps);
+
+	if (!mps->enable || mps->suspended)
+		return;
+
+	mps->suspended = true;
+	mm81x_set_bus_enable(mm, false);
+	mm81x_ps_set_wake_gpio(mm, false);
+}
+
+static irqreturn_t mm81x_ps_irq_handle(int irq, void *arg)
+{
+	struct mm81x_ps *mps = (struct mm81x_ps *)arg;
+	struct mm81x *mm = container_of(mps, struct mm81x, ps);
+	struct completion *awake = READ_ONCE(mps->awake);
+
+	if (awake)
+		complete(awake);
+	else
+		queue_work(mm->chip_wq, &mps->async_wake_work);
+
+	return IRQ_HANDLED;
+}
+
+static void mm81x_ps_async_wake_work(struct work_struct *work)
+{
+	struct mm81x_ps *mps =
+		container_of(work, struct mm81x_ps, async_wake_work);
+	struct mm81x *mm = container_of(mps, struct mm81x, ps);
+
+	if (mm81x_ps_is_busy_pin_asserted(mm)) {
+		mutex_lock(&mps->lock);
+		mm81x_ps_wakeup(mps);
+		mutex_unlock(&mps->lock);
+	}
+}
+
+static void mm81x_ps_evaluate(struct mm81x_ps *mps)
+{
+	struct mm81x *mm = container_of(mps, struct mm81x, ps);
+	bool needs_wake = false;
+	unsigned long expire;
+	unsigned long flags_on_entry =
+		(mm->hif.event_flags &
+		 ~BIT(MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND));
+
+	if (!mps->enable)
+		return;
+
+	needs_wake = (mps->wakers > 0);
+	needs_wake |= (flags_on_entry > 0);
+	needs_wake |= (mm81x_hif_get_tx_buffered_count(mm) > 0);
+
+	if (needs_wake) {
+		mm81x_ps_wakeup(mps);
+		return;
+	}
+
+	if (!mm81x_ps_is_busy_pin_asserted(mm)) {
+		mm81x_ps_sleep(mps);
+		return;
+	}
+
+	expire = msecs_to_jiffies(DEFAULT_BUS_TIMEOUT_MS);
+	cancel_delayed_work(&mps->delayed_eval_work);
+	queue_delayed_work(mm->chip_wq, &mps->delayed_eval_work, expire);
+}
+
+static void mm81x_ps_evaluate_work(struct work_struct *work)
+{
+	struct mm81x_ps *mps =
+		container_of(work, struct mm81x_ps, delayed_eval_work.work);
+
+	if (mps->enable) {
+		mutex_lock(&mps->lock);
+		mm81x_ps_evaluate(mps);
+		mutex_unlock(&mps->lock);
+	}
+}
+
+static int mm81x_ps_irq_init(struct mm81x *mm)
+{
+	int irq;
+	struct mm81x_ps *mps = &mm->ps;
+
+	if (!mm->ps.gpios_supported)
+		return 0;
+
+	irq = gpiod_to_irq(mm->ps.busy_gpio);
+	if (irq < 0)
+		return irq;
+
+	return request_irq(irq, (irq_handler_t)mm81x_ps_irq_handle,
+			   (mm->firmware_flags &
+			    MM81X_FW_FLAGS_BUSY_ACTIVE_LOW) ?
+				   IRQF_TRIGGER_FALLING :
+				   IRQF_TRIGGER_RISING,
+			   "mm81x_notify", mps);
+}
+
+static void mm81x_ps_irq_deinit(struct mm81x *mm)
+{
+	struct mm81x_ps *mps = &mm->ps;
+
+	if (mm->ps.gpios_supported)
+		free_irq(gpiod_to_irq(mm->ps.busy_gpio), mps);
+}
+
+void mm81x_ps_enable(struct mm81x *mm)
+{
+	struct mm81x_ps *mps = &mm->ps;
+
+	if (mps->enable) {
+		mutex_lock(&mps->lock);
+		if (mps->wakers == 0) {
+			WARN_ON_ONCE(1);
+		} else {
+			mps->wakers--;
+			mm81x_ps_evaluate(mps);
+		}
+		mutex_unlock(&mps->lock);
+	}
+}
+
+void mm81x_ps_disable(struct mm81x *mm)
+{
+	struct mm81x_ps *mps = &mm->ps;
+
+	if (mps->enable) {
+		mutex_lock(&mps->lock);
+		mps->wakers++;
+		mm81x_ps_evaluate(mps);
+		mutex_unlock(&mps->lock);
+	}
+}
+
+int mm81x_ps_init(struct mm81x *mm)
+{
+	struct mm81x_ps *mps = &mm->ps;
+
+	mps->enable = !(mm->bus_type == MM81X_BUS_TYPE_SDIO &&
+			!mm->ps.gpios_supported);
+	mps->suspended = true;
+	mps->wakers = 1; /* we default to being on */
+	mutex_init(&mps->lock);
+
+	INIT_WORK(&mps->async_wake_work, mm81x_ps_async_wake_work);
+	INIT_DELAYED_WORK(&mps->delayed_eval_work, mm81x_ps_evaluate_work);
+
+	return mm81x_ps_irq_init(mm);
+}
+
+void mm81x_ps_finish(struct mm81x *mm)
+{
+	struct mm81x_ps *mps = &mm->ps;
+
+	if (mps->enable) {
+		mps->enable = false;
+		mm81x_ps_irq_deinit(mm);
+		cancel_work_sync(&mps->async_wake_work);
+		cancel_delayed_work_sync(&mps->delayed_eval_work);
+	}
+}
-- 
2.43.0


  parent reply	other threads:[~2026-02-27  4:13 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20260227041108.66508-1-lachlan.hodges@morsemicro.com>
2026-02-27  4:10 ` [PATCH wireless-next 01/35] wifi: mm81x: add bus.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 02/35] wifi: mm81x: add command.c Lachlan Hodges
2026-03-06  8:38   ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 03/35] wifi: mm81x: add command_defs.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 04/35] wifi: mm81x: add command.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 05/35] wifi: mm81x: add core.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 06/35] wifi: mm81x: add core.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 07/35] wifi: mm81x: add debug.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 08/35] wifi: mm81x: add debug.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 09/35] wifi: mm81x: add fw.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 10/35] wifi: mm81x: add fw.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 11/35] wifi: mm81x: add hif.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 12/35] wifi: mm81x: add hw.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 13/35] wifi: mm81x: add hw.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 14/35] wifi: mm81x: add mac.c Lachlan Hodges
2026-03-06  9:04   ` Johannes Berg
2026-03-09  4:43     ` Lachlan Hodges
2026-03-09  7:08       ` Johannes Berg
2026-03-09  9:23         ` Lachlan Hodges
2026-03-09  9:37           ` Johannes Berg
2026-03-20  6:39             ` Lachlan Hodges
2026-03-20  7:18               ` Johannes Berg
2026-03-20 10:06                 ` Arien Judge
2026-02-27  4:10 ` [PATCH wireless-next 15/35] wifi: mm81x: add mac.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 16/35] wifi: mm81x: add mmrc.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 17/35] wifi: mm81x: add mmrc.h Lachlan Hodges
2026-03-06  9:07   ` Johannes Berg
2026-02-27  4:10 ` Lachlan Hodges [this message]
2026-03-06  9:07   ` [PATCH wireless-next 18/35] wifi: mm81x: add ps.c Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 19/35] wifi: mm81x: add ps.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 20/35] wifi: mm81x: add rate_code.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 21/35] wifi: mm81x: add rc.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 22/35] wifi: mm81x: add rc.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 23/35] wifi: mm81x: add sdio.c Lachlan Hodges
2026-02-27 11:10   ` Krzysztof Kozlowski
2026-03-02  6:30     ` Lachlan Hodges
2026-03-06  8:20       ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 24/35] wifi: mm81x: add skbq.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 25/35] wifi: mm81x: add skbq.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 26/35] wifi: mm81x: add usb.c Lachlan Hodges
2026-03-06  9:11   ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 27/35] wifi: mm81x: add yaps.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 28/35] wifi: mm81x: add yaps.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 29/35] wifi: mm81x: add yaps_hw.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 30/35] wifi: mm81x: add yaps_hw.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 31/35] dt-bindings: vendor-prefixes: add Morse Micro Lachlan Hodges
2026-02-27 10:50   ` Krzysztof Kozlowski
2026-02-27  4:10 ` [PATCH wireless-next 32/35] dt-bindings: net: wireless: morsemicro: add mm81x family Lachlan Hodges
2026-02-27 10:59   ` Krzysztof Kozlowski
2026-02-27  4:10 ` [PATCH wireless-next 33/35] mmc: sdio: add Morse Micro vendor ids Lachlan Hodges
2026-02-27 10:49   ` Krzysztof Kozlowski
2026-03-04 16:45   ` Ulf Hansson
2026-02-27  4:10 ` [PATCH wireless-next 34/35] wifi: mm81x: add Kconfig and Makefile Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 35/35] wifi: mm81x: add MAINTAINERS entry Lachlan Hodges

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260227041108.66508-19-lachlan.hodges@morsemicro.com \
    --to=lachlan.hodges@morsemicro.com \
    --cc=arien.judge@morsemicro.com \
    --cc=ayman.grais@morsemicro.com \
    --cc=dan.callaghan@morsemicro.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®