Skip to content

Commit

Permalink
Merge branch 'iupa-last-things-before-pm-conversion'
Browse files Browse the repository at this point in the history
Alex Elder says:

====================
net: ipa: last things before PM conversion

This series contains a few remaining changes needed before fully
switching over to using runtime power management rather than the
previous "IPA clock" mechanism.

The first patch moves the calls to enable and disable the IPA
interrupt as a system wakeup interrupt into "ipa_clock.c" with the
rest of the power-related code.

The second adds a flag to make it possible to distinguish runtime
suspend from system suspend.

The third and fourth patches arrange for the ->start_xmit path to
resume hardware if necessary, to ensure it is powered.  If power is
not active, the TX queue is stopped, and arrangements are made for
the queue to be restarted once hardware power is active again.

The fifth patch keeps the TX queue active during suspend.  This
isn't necessary for system suspend but it's important for runtime
suspend.

And the last patch makes it so we don't hold the hardware active
while the modem network device is open.
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Aug 14, 2021
2 parents 8db102a + 8dc181f commit fda4e19
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 20 deletions.
49 changes: 41 additions & 8 deletions drivers/net/ipa/ipa_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ struct ipa_interconnect {
/**
* enum ipa_power_flag - IPA power flags
* @IPA_POWER_FLAG_RESUMED: Whether resume from suspend has been signaled
* @IPA_POWER_FLAG_SYSTEM: Hardware is system (not runtime) suspended
* @IPA_POWER_FLAG_COUNT: Number of defined power flags
*/
enum ipa_power_flag {
IPA_POWER_FLAG_RESUMED,
IPA_POWER_FLAG_SYSTEM,
IPA_POWER_FLAG_COUNT, /* Last; not a flag */
};

Expand Down Expand Up @@ -281,6 +283,27 @@ int ipa_clock_put(struct ipa *ipa)
return pm_runtime_put(&ipa->pdev->dev);
}

static int ipa_suspend(struct device *dev)
{
struct ipa *ipa = dev_get_drvdata(dev);

__set_bit(IPA_POWER_FLAG_SYSTEM, ipa->clock->flags);

return pm_runtime_force_suspend(dev);
}

static int ipa_resume(struct device *dev)
{
struct ipa *ipa = dev_get_drvdata(dev);
int ret;

ret = pm_runtime_force_resume(dev);

__clear_bit(IPA_POWER_FLAG_SYSTEM, ipa->clock->flags);

return ret;
}

/* Return the current IPA core clock rate */
u32 ipa_clock_rate(struct ipa *ipa)
{
Expand All @@ -299,25 +322,35 @@ u32 ipa_clock_rate(struct ipa *ipa)
*/
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.
/* To handle an IPA interrupt we will have resumed the hardware
* just to handle the interrupt, so we're done. If we are in a
* system suspend, trigger a system resume.
*/
if (!test_and_set_bit(IPA_POWER_FLAG_RESUMED, ipa->clock->flags))
pm_wakeup_dev_event(&ipa->pdev->dev, 0, true);
if (!__test_and_set_bit(IPA_POWER_FLAG_RESUMED, ipa->clock->flags))
if (test_bit(IPA_POWER_FLAG_SYSTEM, 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)
int ipa_power_setup(struct ipa *ipa)
{
int ret;

ipa_interrupt_add(ipa->interrupt, IPA_IRQ_TX_SUSPEND,
ipa_suspend_handler);

ret = device_init_wakeup(&ipa->pdev->dev, true);
if (ret)
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);

return ret;
}

void ipa_power_teardown(struct ipa *ipa)
{
(void)device_init_wakeup(&ipa->pdev->dev, false);
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);
}

Expand Down Expand Up @@ -381,8 +414,8 @@ void ipa_clock_exit(struct ipa_clock *clock)
}

const struct dev_pm_ops ipa_pm_ops = {
.suspend = pm_runtime_force_suspend,
.resume = pm_runtime_force_resume,
.suspend = ipa_suspend,
.resume = ipa_resume,
.runtime_suspend = ipa_runtime_suspend,
.runtime_resume = ipa_runtime_resume,
.runtime_idle = ipa_runtime_idle,
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/ipa/ipa_clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ u32 ipa_clock_rate(struct ipa *ipa);
/**
* ipa_power_setup() - Set up IPA power management
* @ipa: IPA pointer
*
* Return: 0 if successful, or a negative error code
*/
void ipa_power_setup(struct ipa *ipa);
int ipa_power_setup(struct ipa *ipa);

/**
* ipa_power_teardown() - Inverse of ipa_power_setup()
Expand Down
6 changes: 1 addition & 5 deletions drivers/net/ipa/ipa_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ int ipa_setup(struct ipa *ipa)
if (ret)
return ret;

ipa_power_setup(ipa);

ret = device_init_wakeup(dev, true);
ret = ipa_power_setup(ipa);
if (ret)
goto err_gsi_teardown;

Expand Down Expand Up @@ -154,7 +152,6 @@ int ipa_setup(struct ipa *ipa)
err_endpoint_teardown:
ipa_endpoint_teardown(ipa);
ipa_power_teardown(ipa);
(void)device_init_wakeup(dev, false);
err_gsi_teardown:
gsi_teardown(&ipa->gsi);

Expand All @@ -181,7 +178,6 @@ static void ipa_teardown(struct ipa *ipa)
ipa_endpoint_disable_one(command_endpoint);
ipa_endpoint_teardown(ipa);
ipa_power_teardown(ipa);
(void)device_init_wakeup(&ipa->pdev->dev, false);
gsi_teardown(&ipa->gsi);
}

Expand Down
72 changes: 66 additions & 6 deletions drivers/net/ipa/ipa_modem.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_rmnet.h>
#include <linux/pm_runtime.h>
#include <linux/remoteproc/qcom_rproc.h>

#include "ipa.h"
Expand All @@ -33,9 +34,14 @@ enum ipa_modem_state {
IPA_MODEM_STATE_STOPPING,
};

/** struct ipa_priv - IPA network device private data */
/**
* struct ipa_priv - IPA network device private data
* @ipa: IPA pointer
* @work: Work structure used to wake the modem netdev TX queue
*/
struct ipa_priv {
struct ipa *ipa;
struct work_struct work;
};

/** ipa_open() - Opens the modem network interface */
Expand All @@ -59,6 +65,8 @@ static int ipa_open(struct net_device *netdev)

netif_start_queue(netdev);

(void)ipa_clock_put(ipa);

return 0;

err_disable_tx:
Expand All @@ -74,12 +82,17 @@ static int ipa_stop(struct net_device *netdev)
{
struct ipa_priv *priv = netdev_priv(netdev);
struct ipa *ipa = priv->ipa;
int ret;

ret = ipa_clock_get(ipa);
if (WARN_ON(ret < 0))
goto out_clock_put;

netif_stop_queue(netdev);

ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);
ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);

out_clock_put:
(void)ipa_clock_put(ipa);

return 0;
Expand All @@ -93,13 +106,15 @@ static int ipa_stop(struct net_device *netdev)
* NETDEV_TX_OK: Success
* NETDEV_TX_BUSY: Error while transmitting the skb. Try again later
*/
static int ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
static netdev_tx_t
ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct net_device_stats *stats = &netdev->stats;
struct ipa_priv *priv = netdev_priv(netdev);
struct ipa_endpoint *endpoint;
struct ipa *ipa = priv->ipa;
u32 skb_len = skb->len;
struct device *dev;
int ret;

if (!skb_len)
Expand All @@ -109,7 +124,31 @@ static int ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
if (endpoint->data->qmap && skb->protocol != htons(ETH_P_MAP))
goto err_drop_skb;

/* The hardware must be powered for us to transmit */
dev = &ipa->pdev->dev;
ret = pm_runtime_get(dev);
if (ret < 1) {
/* If a resume won't happen, just drop the packet */
if (ret < 0 && ret != -EINPROGRESS) {
pm_runtime_put_noidle(dev);
goto err_drop_skb;
}

/* No power (yet). Stop the network stack from transmitting
* until we're resumed; ipa_modem_resume() arranges for the
* TX queue to be started again.
*/
netif_stop_queue(netdev);

(void)pm_runtime_put(dev);

return NETDEV_TX_BUSY;
}

ret = ipa_endpoint_skb_tx(endpoint, skb);

(void)pm_runtime_put(dev);

if (ret) {
if (ret != -E2BIG)
return NETDEV_TX_BUSY;
Expand Down Expand Up @@ -183,12 +222,28 @@ void ipa_modem_suspend(struct net_device *netdev)
if (!(netdev->flags & IFF_UP))
return;

netif_stop_queue(netdev);

ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);
ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
}

/**
* ipa_modem_wake_queue_work() - enable modem netdev queue
* @work: Work structure
*
* Re-enable transmit on the modem network device. This is called
* in (power management) work queue context, scheduled when resuming
* the modem. We can't enable the queue directly in ipa_modem_resume()
* because transmits restart the instant the queue is awakened; but the
* device power state won't be ACTIVE until *after* ipa_modem_resume()
* returns.
*/
static void ipa_modem_wake_queue_work(struct work_struct *work)
{
struct ipa_priv *priv = container_of(work, struct ipa_priv, work);

netif_wake_queue(priv->ipa->modem_netdev);
}

/** ipa_modem_resume() - resume callback for runtime_pm
* @dev: pointer to device
*
Expand All @@ -205,7 +260,8 @@ void ipa_modem_resume(struct net_device *netdev)
ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);

netif_wake_queue(netdev);
/* Arrange for the TX queue to be restarted */
(void)queue_pm_work(&priv->work);
}

int ipa_modem_start(struct ipa *ipa)
Expand Down Expand Up @@ -233,6 +289,7 @@ int ipa_modem_start(struct ipa *ipa)
SET_NETDEV_DEV(netdev, &ipa->pdev->dev);
priv = netdev_priv(netdev);
priv->ipa = ipa;
INIT_WORK(&priv->work, ipa_modem_wake_queue_work);
ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = netdev;
ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = netdev;
ipa->modem_netdev = netdev;
Expand Down Expand Up @@ -277,6 +334,9 @@ int ipa_modem_stop(struct ipa *ipa)

/* Clean up the netdev and endpoints if it was started */
if (netdev) {
struct ipa_priv *priv = netdev_priv(netdev);

cancel_work_sync(&priv->work);
/* If it was opened, stop it first */
if (netdev->flags & IFF_UP)
(void)ipa_stop(netdev);
Expand Down

0 comments on commit fda4e19

Please sign in to comment.