Skip to content

Commit

Permalink
Merge branch 'ipa-runtime-pm'
Browse files Browse the repository at this point in the history
Alex Elder says:

====================
net: ipa: more work toward runtime PM

The first two patches in this series are basically bug fixes, but in
practice I don't think we've seen the problems they might cause.

The third patch moves clock and interconnect related error messages
around a bit, reporting better information and doing so in the
functions where they are enabled or disabled (rather than those
functions' callers).

The last three patches move power-related code into "ipa_clock.c",
as a step toward generalizing the purpose of that source file.
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Aug 5, 2021
2 parents 1f52247 + afb08b7 commit 8394548
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 128 deletions.
12 changes: 0 additions & 12 deletions drivers/net/ipa/ipa.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,9 @@ 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
* @completion: Used to signal pipeline clear transfer complete
Expand Down Expand Up @@ -83,7 +72,6 @@ enum ipa_flag {
*/
struct ipa {
struct gsi gsi;
DECLARE_BITMAP(flags, IPA_FLAG_COUNT);
enum ipa_version version;
struct platform_device *pdev;
struct completion completion;
Expand Down
147 changes: 130 additions & 17 deletions drivers/net/ipa/ipa_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/pm.h>
#include <linux/bitops.h>

#include "ipa.h"
#include "ipa_clock.h"
#include "ipa_endpoint.h"
#include "ipa_modem.h"
#include "ipa_data.h"

Expand Down Expand Up @@ -42,18 +45,30 @@ struct ipa_interconnect {
u32 peak_bandwidth;
};

/**
* enum ipa_power_flag - IPA power flags
* @IPA_POWER_FLAG_RESUMED: Whether resume from suspend has been signaled
* @IPA_POWER_FLAG_COUNT: Number of defined power flags
*/
enum ipa_power_flag {
IPA_POWER_FLAG_RESUMED,
IPA_POWER_FLAG_COUNT, /* Last; not a flag */
};

/**
* struct ipa_clock - IPA clocking information
* @count: Clocking reference count
* @mutex: Protects clock enable/disable
* @core: IPA core clock
* @flags: Boolean state flags
* @interconnect_count: Number of elements in interconnect[]
* @interconnect: Interconnect array
*/
struct ipa_clock {
refcount_t count;
struct mutex mutex; /* protects clock enable/disable */
struct clk *core;
DECLARE_BITMAP(flags, IPA_POWER_FLAG_COUNT);
u32 interconnect_count;
struct ipa_interconnect *interconnect;
};
Expand Down Expand Up @@ -144,8 +159,12 @@ static int ipa_interconnect_enable(struct ipa *ipa)
ret = icc_set_bw(interconnect->path,
interconnect->average_bandwidth,
interconnect->peak_bandwidth);
if (ret)
if (ret) {
dev_err(&ipa->pdev->dev,
"error %d enabling %s interconnect\n",
ret, icc_get_name(interconnect->path));
goto out_unwind;
}
interconnect++;
}

Expand All @@ -159,10 +178,11 @@ static int ipa_interconnect_enable(struct ipa *ipa)
}

/* To disable an interconnect, we just its bandwidth to 0 */
static void ipa_interconnect_disable(struct ipa *ipa)
static int ipa_interconnect_disable(struct ipa *ipa)
{
struct ipa_interconnect *interconnect;
struct ipa_clock *clock = ipa->clock;
struct device *dev = &ipa->pdev->dev;
int result = 0;
u32 count;
int ret;
Expand All @@ -172,13 +192,16 @@ static void ipa_interconnect_disable(struct ipa *ipa)
while (count--) {
interconnect--;
ret = icc_set_bw(interconnect->path, 0, 0);
if (ret && !result)
result = ret;
if (ret) {
dev_err(dev, "error %d disabling %s interconnect\n",
ret, icc_get_name(interconnect->path));
/* Try to disable all; record only the first error */
if (!result)
result = ret;
}
}

if (result)
dev_err(&ipa->pdev->dev,
"error %d disabling IPA interconnects\n", ret);
return result;
}

/* Turn on IPA clocks, including interconnects */
Expand All @@ -191,8 +214,10 @@ static int ipa_clock_enable(struct ipa *ipa)
return ret;

ret = clk_prepare_enable(ipa->clock->core);
if (ret)
ipa_interconnect_disable(ipa);
if (ret) {
dev_err(&ipa->pdev->dev, "error %d enabling core clock\n", ret);
(void)ipa_interconnect_disable(ipa);
}

return ret;
}
Expand All @@ -201,7 +226,7 @@ static int ipa_clock_enable(struct ipa *ipa)
static void ipa_clock_disable(struct ipa *ipa)
{
clk_disable_unprepare(ipa->clock->core);
ipa_interconnect_disable(ipa);
(void)ipa_interconnect_disable(ipa);
}

/* Get an IPA clock reference, but only if the reference count is
Expand Down Expand Up @@ -238,13 +263,8 @@ void ipa_clock_get(struct ipa *ipa)
goto out_mutex_unlock;

ret = ipa_clock_enable(ipa);
if (ret) {
dev_err(&ipa->pdev->dev, "error %d enabling IPA clock\n", ret);
goto out_mutex_unlock;
}

refcount_set(&clock->count, 1);

if (!ret)
refcount_set(&clock->count, 1);
out_mutex_unlock:
mutex_unlock(&clock->mutex);
}
Expand All @@ -271,6 +291,40 @@ u32 ipa_clock_rate(struct ipa *ipa)
return ipa->clock ? (u32)clk_get_rate(ipa->clock->core) : 0;
}

/**
* ipa_suspend_handler() - Handle the suspend IPA interrupt
* @ipa: IPA pointer
* @irq_id: IPA interrupt type (unused)
*
* If an RX endpoint is suspended, and the IPA has a packet destined for
* that endpoint, the IPA generates a SUSPEND interrupt to inform the AP
* that it should resume the endpoint. If we get one of these interrupts
* we just wake up the system.
*/
static void ipa_suspend_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
{
/* Just report the event, and let system resume handle the rest.
* More than one endpoint could signal this; if so, ignore
* all but the first.
*/
if (!test_and_set_bit(IPA_POWER_FLAG_RESUMED, ipa->clock->flags))
pm_wakeup_dev_event(&ipa->pdev->dev, 0, true);

/* Acknowledge/clear the suspend interrupt on all endpoints */
ipa_interrupt_suspend_clear_all(ipa->interrupt);
}

void ipa_power_setup(struct ipa *ipa)
{
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_TX_SUSPEND,
ipa_suspend_handler);
}

void ipa_power_teardown(struct ipa *ipa)
{
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);
}

/* Initialize IPA clocking */
struct ipa_clock *
ipa_clock_init(struct device *dev, const struct ipa_clock_data *data)
Expand Down Expand Up @@ -329,3 +383,62 @@ void ipa_clock_exit(struct ipa_clock *clock)
kfree(clock);
clk_put(clk);
}

/**
* ipa_suspend() - Power management system suspend callback
* @dev: IPA device structure
*
* Return: Always returns zero
*
* Called by the PM framework when a system suspend operation is invoked.
* Suspends endpoints and releases the clock reference held to keep
* the IPA clock running until this point.
*/
static int ipa_suspend(struct device *dev)
{
struct ipa *ipa = dev_get_drvdata(dev);

/* Endpoints aren't usable until setup is complete */
if (ipa->setup_complete) {
__clear_bit(IPA_POWER_FLAG_RESUMED, ipa->clock->flags);
ipa_endpoint_suspend(ipa);
gsi_suspend(&ipa->gsi);
}

ipa_clock_put(ipa);

return 0;
}

/**
* ipa_resume() - Power management system resume callback
* @dev: IPA device structure
*
* Return: Always returns 0
*
* Called by the PM framework when a system resume operation is invoked.
* Takes an IPA clock reference to keep the clock running until suspend,
* and resumes endpoints.
*/
static int ipa_resume(struct device *dev)
{
struct ipa *ipa = dev_get_drvdata(dev);

/* This clock reference will keep the IPA out of suspend
* until we get a power management suspend request.
*/
ipa_clock_get(ipa);

/* Endpoints aren't usable until setup is complete */
if (ipa->setup_complete) {
gsi_resume(&ipa->gsi);
ipa_endpoint_resume(ipa);
}

return 0;
}

const struct dev_pm_ops ipa_pm_ops = {
.suspend = ipa_suspend,
.resume = ipa_resume,
};
15 changes: 15 additions & 0 deletions drivers/net/ipa/ipa_clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ struct device;
struct ipa;
struct ipa_clock_data;

/* IPA device power management function block */
extern const struct dev_pm_ops ipa_pm_ops;

/**
* ipa_clock_rate() - Return the current IPA core clock rate
* @ipa: IPA structure
Expand All @@ -19,6 +22,18 @@ struct ipa_clock_data;
*/
u32 ipa_clock_rate(struct ipa *ipa);

/**
* ipa_power_setup() - Set up IPA power management
* @ipa: IPA pointer
*/
void ipa_power_setup(struct ipa *ipa);

/**
* ipa_power_teardown() - Inverse of ipa_power_setup()
* @ipa: IPA pointer
*/
void ipa_power_teardown(struct ipa *ipa);

/**
* ipa_clock_init() - Initialize IPA clocking
* @dev: IPA device
Expand Down
Loading

0 comments on commit 8394548

Please sign in to comment.