Commit 5e43df14 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-ipa-wake-up-system-on-RX-available'



Alex Elder says:

====================
net: ipa: wake up system on RX available

This series arranges for the IPA driver to wake up a suspended
system if the IPA hardware has a packet to deliver to the AP.

Version 2 replaced the first patch from version 1 with three
patches, in response to David Miller's feedback.  And based on
Bjorn Andersson's feedback on version 2, this version reworks
the tracking of IPA clock references.  As a result, we no
longer need a flag to determine whether a "don't' suspend" clock
reference is held (though an bit in a bitmask is still used for
a different purpose).

In summary:
    - A refcount_t is used to track IPA clock references where an
      atomic_t was previously used.  (This may go away soon as well,
      with upcoming work to implement runtime PM.)
    - We no longer track whether a special reference has been taken
      to avoid suspending IPA.
    - A bit in a bitmask is used to ensure we only trigger a system
      resume once per system suspend.
And from the original series:
    - Suspending endpoints only occurs when suspending the driver,
      not when dropping the last clock reference.  Resuming
      endpoints is also disconnected from starting the clock.
    - The IPA SUSPEND interrupt is now a wakeup interrupt.  If it
      fires, it schedules a system resume operation.
    - The GSI interrupt is no longer a wakeup interrupt.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ba4ee3c0 54f7e443
Loading
Loading
Loading
Loading
+4 −13
Original line number Diff line number Diff line
@@ -1987,31 +1987,26 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev, bool prefetch,
	}
	gsi->irq = irq;

	ret = enable_irq_wake(gsi->irq);
	if (ret)
		dev_warn(dev, "error %d enabling gsi wake irq\n", ret);
	gsi->irq_wake_enabled = !ret;

	/* Get GSI memory range and map it */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gsi");
	if (!res) {
		dev_err(dev, "DT error getting \"gsi\" memory property\n");
		ret = -ENODEV;
		goto err_disable_irq_wake;
		goto err_free_irq;
	}

	size = resource_size(res);
	if (res->start > U32_MAX || size > U32_MAX - res->start) {
		dev_err(dev, "DT memory resource \"gsi\" out of range\n");
		ret = -EINVAL;
		goto err_disable_irq_wake;
		goto err_free_irq;
	}

	gsi->virt = ioremap(res->start, size);
	if (!gsi->virt) {
		dev_err(dev, "unable to remap \"gsi\" memory\n");
		ret = -ENOMEM;
		goto err_disable_irq_wake;
		goto err_free_irq;
	}

	ret = gsi_channel_init(gsi, prefetch, count, data, modem_alloc);
@@ -2025,9 +2020,7 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev, bool prefetch,

err_iounmap:
	iounmap(gsi->virt);
err_disable_irq_wake:
	if (gsi->irq_wake_enabled)
		(void)disable_irq_wake(gsi->irq);
err_free_irq:
	free_irq(gsi->irq, gsi);

	return ret;
@@ -2038,8 +2031,6 @@ void gsi_exit(struct gsi *gsi)
{
	mutex_destroy(&gsi->mutex);
	gsi_channel_exit(gsi);
	if (gsi->irq_wake_enabled)
		(void)disable_irq_wake(gsi->irq);
	free_irq(gsi->irq, gsi);
	iounmap(gsi->virt);
}
+0 −1
Original line number Diff line number Diff line
@@ -150,7 +150,6 @@ struct gsi {
	struct net_device dummy_dev;	/* needed for NAPI */
	void __iomem *virt;
	u32 irq;
	bool irq_wake_enabled;
	u32 channel_count;
	u32 evt_ring_count;
	struct gsi_channel channel[GSI_CHANNEL_COUNT_MAX];
+12 −4
Original line number Diff line number Diff line
@@ -27,15 +27,25 @@ struct ipa_clock;
struct ipa_smp2p;
struct ipa_interrupt;

/**
 * enum ipa_flag - IPA state flags
 * @IPA_FLAG_RESUMED:	Whether resume from suspend has been signaled
 * @IPA_FLAG_COUNT:	Number of defined IPA flags
 */
enum ipa_flag {
	IPA_FLAG_RESUMED,
	IPA_FLAG_COUNT,		/* Last; not a flag */
};

/**
 * struct ipa - IPA information
 * @gsi:		Embedded GSI structure
 * @flags:		Boolean state flags
 * @version:		IPA hardware version
 * @pdev:		Platform device
 * @modem_rproc:	Remoteproc handle for modem subsystem
 * @smp2p:		SMP2P information
 * @clock:		IPA clocking information
 * @suspend_ref:	Whether clock reference preventing suspend taken
 * @table_addr:		DMA address of filter/route table content
 * @table_virt:		Virtual address of filter/route table content
 * @interrupt:		IPA Interrupt information
@@ -70,6 +80,7 @@ struct ipa_interrupt;
 */
struct ipa {
	struct gsi gsi;
	DECLARE_BITMAP(flags, IPA_FLAG_COUNT);
	enum ipa_version version;
	struct platform_device *pdev;
	struct rproc *modem_rproc;
@@ -77,7 +88,6 @@ struct ipa {
	void *notifier;
	struct ipa_smp2p *smp2p;
	struct ipa_clock *clock;
	atomic_t suspend_ref;

	dma_addr_t table_addr;
	__le64 *table_virt;
@@ -104,8 +114,6 @@ struct ipa {
	void *zero_virt;
	size_t zero_size;

	struct wakeup_source *wakeup_source;

	/* Bit masks indicating endpoint state */
	u32 available;		/* supported by hardware */
	u32 filter_map;
+11 −17
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
 * Copyright (C) 2018-2020 Linaro Ltd.
 */

#include <linux/atomic.h>
#include <linux/refcount.h>
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/device.h>
@@ -51,7 +51,7 @@
 * @config_path:	Configuration space interconnect
 */
struct ipa_clock {
	atomic_t count;
	refcount_t count;
	struct mutex mutex; /* protects clock enable/disable */
	struct clk *core;
	struct icc_path *memory_path;
@@ -195,14 +195,13 @@ static void ipa_clock_disable(struct ipa *ipa)
 */
bool ipa_clock_get_additional(struct ipa *ipa)
{
	return !!atomic_inc_not_zero(&ipa->clock->count);
	return refcount_inc_not_zero(&ipa->clock->count);
}

/* Get an IPA clock reference.  If the reference count is non-zero, it is
 * incremented and return is immediate.  Otherwise it is checked again
 * under protection of the mutex, and if appropriate the clock (and
 * interconnects) are enabled suspended endpoints (if any) are resumed
 * before returning.
 * under protection of the mutex, and if appropriate the IPA clock
 * is enabled.
 *
 * Incrementing the reference count is intentionally deferred until
 * after the clock is running and endpoints are resumed.
@@ -229,28 +228,23 @@ void ipa_clock_get(struct ipa *ipa)
		goto out_mutex_unlock;
	}

	ipa_endpoint_resume(ipa);

	atomic_inc(&clock->count);
	refcount_set(&clock->count, 1);

out_mutex_unlock:
	mutex_unlock(&clock->mutex);
}

/* Attempt to remove an IPA clock reference.  If this represents the last
 * reference, suspend endpoints and disable the clock (and interconnects)
 * under protection of a mutex.
/* Attempt to remove an IPA clock reference.  If this represents the
 * last reference, disable the IPA clock under protection of the mutex.
 */
void ipa_clock_put(struct ipa *ipa)
{
	struct ipa_clock *clock = ipa->clock;

	/* If this is not the last reference there's nothing more to do */
	if (!atomic_dec_and_mutex_lock(&clock->count, &clock->mutex))
	if (!refcount_dec_and_mutex_lock(&clock->count, &clock->mutex))
		return;

	ipa_endpoint_suspend(ipa);

	ipa_clock_disable(ipa);

	mutex_unlock(&clock->mutex);
@@ -294,7 +288,7 @@ struct ipa_clock *ipa_clock_init(struct device *dev)
		goto err_kfree;

	mutex_init(&clock->mutex);
	atomic_set(&clock->count, 0);
	refcount_set(&clock->count, 0);

	return clock;

@@ -311,7 +305,7 @@ void ipa_clock_exit(struct ipa_clock *clock)
{
	struct clk *clk = clock->core;

	WARN_ON(atomic_read(&clock->count) != 0);
	WARN_ON(refcount_read(&clock->count) != 0);
	mutex_destroy(&clock->mutex);
	ipa_interconnect_exit(clock);
	kfree(clock);
+14 −0
Original line number Diff line number Diff line
@@ -237,8 +237,16 @@ struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
		goto err_kfree;
	}

	ret = enable_irq_wake(irq);
	if (ret) {
		dev_err(dev, "error %d enabling wakeup for \"ipa\" IRQ\n", ret);
		goto err_free_irq;
	}

	return interrupt;

err_free_irq:
	free_irq(interrupt->irq, interrupt);
err_kfree:
	kfree(interrupt);

@@ -248,6 +256,12 @@ struct ipa_interrupt *ipa_interrupt_setup(struct ipa *ipa)
/* Tear down the IPA interrupt framework */
void ipa_interrupt_teardown(struct ipa_interrupt *interrupt)
{
	struct device *dev = &interrupt->ipa->pdev->dev;
	int ret;

	ret = disable_irq_wake(interrupt->irq);
	if (ret)
		dev_err(dev, "error %d disabling \"ipa\" IRQ wakeup\n", ret);
	free_irq(interrupt->irq, interrupt);
	kfree(interrupt);
}
Loading