Commit fda4e19d authored by David S. Miller's avatar David S. Miller

Merge branch 'iupa-last-things-before-pm-conversion'

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 8db102a6 8dc181f2
......@@ -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 */
};
......@@ -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)
{
......@@ -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);
}
......@@ -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,
......
......@@ -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()
......
......@@ -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;
......@@ -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);
......@@ -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);
}
......
......@@ -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"
......@@ -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 */
......@@ -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:
......@@ -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;
......@@ -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)
......@@ -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;
......@@ -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
*
......@@ -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)
......@@ -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;
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment