summaryrefslogtreecommitdiff
path: root/drivers/net/ipa/ipa_smp2p.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ipa/ipa_smp2p.c')
-rw-r--r--drivers/net/ipa/ipa_smp2p.c93
1 files changed, 55 insertions, 38 deletions
diff --git a/drivers/net/ipa/ipa_smp2p.c b/drivers/net/ipa/ipa_smp2p.c
index 93270e50b6b3..df7639c39d71 100644
--- a/drivers/net/ipa/ipa_smp2p.c
+++ b/drivers/net/ipa/ipa_smp2p.c
@@ -9,13 +9,13 @@
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/panic_notifier.h>
+#include <linux/pm_runtime.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
#include "ipa_smp2p.h"
#include "ipa.h"
#include "ipa_uc.h"
-#include "ipa_clock.h"
/**
* DOC: IPA SMP2P communication with the modem
@@ -23,19 +23,19 @@
* SMP2P is a primitive communication mechanism available between the AP and
* the modem. The IPA driver uses this for two purposes: to enable the modem
* to state that the GSI hardware is ready to use; and to communicate the
- * state of the IPA clock in the event of a crash.
+ * state of IPA power in the event of a crash.
*
* GSI needs to have early initialization completed before it can be used.
* This initialization is done either by Trust Zone or by the modem. In the
* latter case, the modem uses an SMP2P interrupt to tell the AP IPA driver
* when the GSI is ready to use.
*
- * The modem is also able to inquire about the current state of the IPA
- * clock by trigging another SMP2P interrupt to the AP. We communicate
- * whether the clock is enabled using two SMP2P state bits--one to
- * indicate the clock state (on or off), and a second to indicate the
- * clock state bit is valid. The modem will poll the valid bit until it
- * is set, and at that time records whether the AP has the IPA clock enabled.
+ * The modem is also able to inquire about the current state of IPA
+ * power by trigging another SMP2P interrupt to the AP. We communicate
+ * whether power is enabled using two SMP2P state bits--one to indicate
+ * the power state (on or off), and a second to indicate the power state
+ * bit is valid. The modem will poll the valid bit until it is set, and
+ * at that time records whether the AP has IPA power enabled.
*
* Finally, if the AP kernel panics, we update the SMP2P state bits even if
* we never receive an interrupt from the modem requesting this.
@@ -45,14 +45,14 @@
* struct ipa_smp2p - IPA SMP2P information
* @ipa: IPA pointer
* @valid_state: SMEM state indicating enabled state is valid
- * @enabled_state: SMEM state to indicate clock is enabled
+ * @enabled_state: SMEM state to indicate power is enabled
* @valid_bit: Valid bit in 32-bit SMEM state mask
* @enabled_bit: Enabled bit in 32-bit SMEM state mask
* @enabled_bit: Enabled bit in 32-bit SMEM state mask
- * @clock_query_irq: IPA interrupt triggered by modem for clock query
+ * @clock_query_irq: IPA interrupt triggered by modem for power query
* @setup_ready_irq: IPA interrupt triggered by modem to signal GSI ready
- * @clock_on: Whether IPA clock is on
- * @notified: Whether modem has been notified of clock state
+ * @power_on: Whether IPA power is on
+ * @notified: Whether modem has been notified of power state
* @disabled: Whether setup ready interrupt handling is disabled
* @mutex: Mutex protecting ready-interrupt/shutdown interlock
* @panic_notifier: Panic notifier structure
@@ -65,7 +65,7 @@ struct ipa_smp2p {
u32 enabled_bit;
u32 clock_query_irq;
u32 setup_ready_irq;
- bool clock_on;
+ bool power_on;
bool notified;
bool disabled;
struct mutex mutex;
@@ -73,28 +73,30 @@ struct ipa_smp2p {
};
/**
- * ipa_smp2p_notify() - use SMP2P to tell modem about IPA clock state
+ * ipa_smp2p_notify() - use SMP2P to tell modem about IPA power state
* @smp2p: SMP2P information
*
* This is called either when the modem has requested it (by triggering
- * the modem clock query IPA interrupt) or whenever the AP is shutting down
+ * the modem power query IPA interrupt) or whenever the AP is shutting down
* (via a panic notifier). It sets the two SMP2P state bits--one saying
- * whether the IPA clock is running, and the other indicating the first bit
+ * whether the IPA power is on, and the other indicating the first bit
* is valid.
*/
static void ipa_smp2p_notify(struct ipa_smp2p *smp2p)
{
+ struct device *dev;
u32 value;
u32 mask;
if (smp2p->notified)
return;
- smp2p->clock_on = ipa_clock_get_additional(smp2p->ipa);
+ dev = &smp2p->ipa->pdev->dev;
+ smp2p->power_on = pm_runtime_get_if_active(dev, true) > 0;
- /* Signal whether the clock is enabled */
+ /* Signal whether the IPA power is enabled */
mask = BIT(smp2p->enabled_bit);
- value = smp2p->clock_on ? mask : 0;
+ value = smp2p->power_on ? mask : 0;
qcom_smem_state_update_bits(smp2p->enabled_state, mask, value);
/* Now indicate that the enabled flag is valid */
@@ -124,7 +126,7 @@ static int ipa_smp2p_panic_notifier(struct notifier_block *nb,
ipa_smp2p_notify(smp2p);
- if (smp2p->clock_on)
+ if (smp2p->power_on)
ipa_uc_panic_notifier(smp2p->ipa);
return NOTIFY_DONE;
@@ -150,19 +152,31 @@ static void ipa_smp2p_panic_notifier_unregister(struct ipa_smp2p *smp2p)
static irqreturn_t ipa_smp2p_modem_setup_ready_isr(int irq, void *dev_id)
{
struct ipa_smp2p *smp2p = dev_id;
+ struct device *dev;
+ int ret;
mutex_lock(&smp2p->mutex);
- if (!smp2p->disabled) {
- int ret;
+ if (smp2p->disabled)
+ goto out_mutex_unlock;
+ smp2p->disabled = true; /* If any others arrive, ignore them */
- ret = ipa_setup(smp2p->ipa);
- if (ret)
- dev_err(&smp2p->ipa->pdev->dev,
- "error %d from ipa_setup()\n", ret);
- smp2p->disabled = true;
+ /* Power needs to be active for setup */
+ dev = &smp2p->ipa->pdev->dev;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "error %d getting power for setup\n", ret);
+ goto out_power_put;
}
+ /* An error here won't cause driver shutdown, so warn if one occurs */
+ ret = ipa_setup(smp2p->ipa);
+ WARN(ret != 0, "error %d from ipa_setup()\n", ret);
+
+out_power_put:
+ pm_runtime_mark_last_busy(dev);
+ (void)pm_runtime_put_autosuspend(dev);
+out_mutex_unlock:
mutex_unlock(&smp2p->mutex);
return IRQ_HANDLED;
@@ -195,14 +209,17 @@ static void ipa_smp2p_irq_exit(struct ipa_smp2p *smp2p, u32 irq)
free_irq(irq, smp2p);
}
-/* Drop the clock reference if it was taken in ipa_smp2p_notify() */
-static void ipa_smp2p_clock_release(struct ipa *ipa)
+/* Drop the power reference if it was taken in ipa_smp2p_notify() */
+static void ipa_smp2p_power_release(struct ipa *ipa)
{
- if (!ipa->smp2p->clock_on)
+ struct device *dev = &ipa->pdev->dev;
+
+ if (!ipa->smp2p->power_on)
return;
- ipa_clock_put(ipa);
- ipa->smp2p->clock_on = false;
+ pm_runtime_mark_last_busy(dev);
+ (void)pm_runtime_put_autosuspend(dev);
+ ipa->smp2p->power_on = false;
}
/* Initialize the IPA SMP2P subsystem */
@@ -236,7 +253,7 @@ int ipa_smp2p_init(struct ipa *ipa, bool modem_init)
smp2p->ipa = ipa;
- /* These fields are needed by the clock query interrupt
+ /* These fields are needed by the power query interrupt
* handler, so initialize them now.
*/
mutex_init(&smp2p->mutex);
@@ -289,8 +306,8 @@ void ipa_smp2p_exit(struct ipa *ipa)
ipa_smp2p_irq_exit(smp2p, smp2p->setup_ready_irq);
ipa_smp2p_panic_notifier_unregister(smp2p);
ipa_smp2p_irq_exit(smp2p, smp2p->clock_query_irq);
- /* We won't get notified any more; drop clock reference (if any) */
- ipa_smp2p_clock_release(ipa);
+ /* We won't get notified any more; drop power reference (if any) */
+ ipa_smp2p_power_release(ipa);
ipa->smp2p = NULL;
mutex_destroy(&smp2p->mutex);
kfree(smp2p);
@@ -319,13 +336,13 @@ void ipa_smp2p_notify_reset(struct ipa *ipa)
if (!smp2p->notified)
return;
- ipa_smp2p_clock_release(ipa);
+ ipa_smp2p_power_release(ipa);
- /* Reset the clock enabled valid flag */
+ /* Reset the power enabled valid flag */
mask = BIT(smp2p->valid_bit);
qcom_smem_state_update_bits(smp2p->valid_state, mask, 0);
- /* Mark the clock disabled for good measure... */
+ /* Mark the power disabled for good measure... */
mask = BIT(smp2p->enabled_bit);
qcom_smem_state_update_bits(smp2p->enabled_state, mask, 0);