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

Merge branch 'ipa-autosuspend'

Alex Elder says:

====================
net: ipa: enable automatic suspend

At long last, the first patch in this series enables automatic
suspend managed by the power management core.  The remaining two
just rename things to be "power" oriented rather than "clock"
oriented.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4af14dba 2775cbc5
obj-$(CONFIG_QCOM_IPA) += ipa.o
ipa-y := ipa_main.o ipa_clock.o ipa_reg.o ipa_mem.o \
ipa-y := ipa_main.o ipa_power.o ipa_reg.o ipa_mem.o \
ipa_table.o ipa_interrupt.o gsi.o gsi_trans.o \
ipa_gsi.o ipa_smp2p.o ipa_uc.o \
ipa_endpoint.o ipa_cmd.o ipa_modem.o \
......
......@@ -23,7 +23,7 @@ struct icc_path;
struct net_device;
struct platform_device;
struct ipa_clock;
struct ipa_power;
struct ipa_smp2p;
struct ipa_interrupt;
......@@ -36,11 +36,11 @@ struct ipa_interrupt;
* @nb: Notifier block used for remoteproc SSR
* @notifier: Remoteproc SSR notifier
* @smp2p: SMP2P information
* @clock: IPA clocking information
* @power: IPA power information
* @table_addr: DMA address of filter/route table content
* @table_virt: Virtual address of filter/route table content
* @interrupt: IPA Interrupt information
* @uc_clocked: true if clock is active by proxy for microcontroller
* @uc_powered: true if power is active by proxy for microcontroller
* @uc_loaded: true after microcontroller has reported it's ready
* @reg_addr: DMA address used for IPA register access
* @reg_virt: Virtual address used for IPA register access
......@@ -78,13 +78,13 @@ struct ipa {
struct notifier_block nb;
void *notifier;
struct ipa_smp2p *smp2p;
struct ipa_clock *clock;
struct ipa_power *power;
dma_addr_t table_addr;
__le64 *table_virt;
struct ipa_interrupt *interrupt;
bool uc_clocked;
bool uc_powered;
bool uc_loaded;
dma_addr_t reg_addr;
......@@ -134,11 +134,11 @@ struct ipa {
*
* Activities performed at the init stage can be done without requiring
* any access to IPA hardware. Activities performed at the config stage
* require the IPA clock to be running, because they involve access
* to IPA registers. The setup stage is performed only after the GSI
* hardware is ready (more on this below). The setup stage allows
* the AP to perform more complex initialization by issuing "immediate
* commands" using a special interface to the IPA.
* require IPA power, because they involve access to IPA registers.
* The setup stage is performed only after the GSI hardware is ready
* (more on this below). The setup stage allows the AP to perform
* more complex initialization by issuing "immediate commands" using
* a special interface to the IPA.
*
* This function, @ipa_setup(), starts the setup stage.
*
......
......@@ -513,7 +513,7 @@ static const struct ipa_interconnect_data ipa_interconnect_data[] = {
};
/* Clock and interconnect configuration data for an SoC having IPA v3.1 */
static const struct ipa_clock_data ipa_clock_data = {
static const struct ipa_power_data ipa_power_data = {
.core_clock_rate = 16 * 1000 * 1000, /* Hz */
.interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect_data = ipa_interconnect_data,
......@@ -529,5 +529,5 @@ const struct ipa_data ipa_data_v3_1 = {
.endpoint_data = ipa_gsi_endpoint_data,
.resource_data = &ipa_resource_data,
.mem_data = &ipa_mem_data,
.clock_data = &ipa_clock_data,
.power_data = &ipa_power_data,
};
......@@ -394,7 +394,7 @@ static const struct ipa_interconnect_data ipa_interconnect_data[] = {
};
/* Clock and interconnect configuration data for an SoC having IPA v3.5.1 */
static const struct ipa_clock_data ipa_clock_data = {
static const struct ipa_power_data ipa_power_data = {
.core_clock_rate = 75 * 1000 * 1000, /* Hz */
.interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect_data = ipa_interconnect_data,
......@@ -414,5 +414,5 @@ const struct ipa_data ipa_data_v3_5_1 = {
.endpoint_data = ipa_gsi_endpoint_data,
.resource_data = &ipa_resource_data,
.mem_data = &ipa_mem_data,
.clock_data = &ipa_clock_data,
.power_data = &ipa_power_data,
};
......@@ -382,7 +382,7 @@ static const struct ipa_interconnect_data ipa_interconnect_data[] = {
};
/* Clock and interconnect configuration data for an SoC having IPA v4.11 */
static const struct ipa_clock_data ipa_clock_data = {
static const struct ipa_power_data ipa_power_data = {
.core_clock_rate = 60 * 1000 * 1000, /* Hz */
.interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect_data = ipa_interconnect_data,
......@@ -397,5 +397,5 @@ const struct ipa_data ipa_data_v4_11 = {
.endpoint_data = ipa_gsi_endpoint_data,
.resource_data = &ipa_resource_data,
.mem_data = &ipa_mem_data,
.clock_data = &ipa_clock_data,
.power_data = &ipa_power_data,
};
......@@ -360,7 +360,7 @@ static const struct ipa_interconnect_data ipa_interconnect_data[] = {
};
/* Clock and interconnect configuration data for an SoC having IPA v4.2 */
static const struct ipa_clock_data ipa_clock_data = {
static const struct ipa_power_data ipa_power_data = {
.core_clock_rate = 100 * 1000 * 1000, /* Hz */
.interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect_data = ipa_interconnect_data,
......@@ -376,5 +376,5 @@ const struct ipa_data ipa_data_v4_2 = {
.endpoint_data = ipa_gsi_endpoint_data,
.resource_data = &ipa_resource_data,
.mem_data = &ipa_mem_data,
.clock_data = &ipa_clock_data,
.power_data = &ipa_power_data,
};
......@@ -443,7 +443,7 @@ static const struct ipa_interconnect_data ipa_interconnect_data[] = {
};
/* Clock and interconnect configuration data for an SoC having IPA v4.5 */
static const struct ipa_clock_data ipa_clock_data = {
static const struct ipa_power_data ipa_power_data = {
.core_clock_rate = 150 * 1000 * 1000, /* Hz (150? 60?) */
.interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect_data = ipa_interconnect_data,
......@@ -458,5 +458,5 @@ const struct ipa_data ipa_data_v4_5 = {
.endpoint_data = ipa_gsi_endpoint_data,
.resource_data = &ipa_resource_data,
.mem_data = &ipa_mem_data,
.clock_data = &ipa_clock_data,
.power_data = &ipa_power_data,
};
......@@ -432,7 +432,7 @@ static const struct ipa_interconnect_data ipa_interconnect_data[] = {
};
/* Clock and interconnect configuration data for an SoC having IPA v4.9 */
static const struct ipa_clock_data ipa_clock_data = {
static const struct ipa_power_data ipa_power_data = {
.core_clock_rate = 60 * 1000 * 1000, /* Hz */
.interconnect_count = ARRAY_SIZE(ipa_interconnect_data),
.interconnect_data = ipa_interconnect_data,
......@@ -447,5 +447,5 @@ const struct ipa_data ipa_data_v4_9 = {
.endpoint_data = ipa_gsi_endpoint_data,
.resource_data = &ipa_resource_data,
.mem_data = &ipa_mem_data,
.clock_data = &ipa_clock_data,
.power_data = &ipa_power_data,
};
......@@ -19,7 +19,7 @@
* IPA and GSI resources to use for a given platform. This data is supplied
* via the Device Tree match table, associated with a particular compatible
* string. The data defines information about how resources, endpoints and
* channels, memory, clocking and so on are allocated and used for the
* channels, memory, power and so on are allocated and used for the
* platform.
*
* Resources are data structures used internally by the IPA hardware. The
......@@ -265,12 +265,12 @@ struct ipa_interconnect_data {
};
/**
* struct ipa_clock_data - description of IPA clock and interconnect rates
* struct ipa_power_data - description of IPA power configuration data
* @core_clock_rate: Core clock rate (Hz)
* @interconnect_count: Number of entries in the interconnect_data array
* @interconnect_data: IPA interconnect configuration data
*/
struct ipa_clock_data {
struct ipa_power_data {
u32 core_clock_rate;
u32 interconnect_count; /* # entries in interconnect_data[] */
const struct ipa_interconnect_data *interconnect_data;
......@@ -286,7 +286,7 @@ struct ipa_clock_data {
* @endpoint_data: IPA endpoint/GSI channel data
* @resource_data: IPA resource configuration data
* @mem_data: IPA memory region data
* @clock_data: IPA clock and interconnect data
* @power_data: IPA power data
*/
struct ipa_data {
enum ipa_version version;
......@@ -297,7 +297,7 @@ struct ipa_data {
const struct ipa_gsi_endpoint_data *endpoint_data;
const struct ipa_resource_data *resource_data;
const struct ipa_mem_data *mem_data;
const struct ipa_clock_data *clock_data;
const struct ipa_power_data *power_data;
};
extern const struct ipa_data ipa_data_v3_1;
......
......@@ -21,7 +21,7 @@
#include "ipa_modem.h"
#include "ipa_table.h"
#include "ipa_gsi.h"
#include "ipa_clock.h"
#include "ipa_power.h"
#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
......@@ -810,7 +810,7 @@ static u32 hol_block_timer_val(struct ipa *ipa, u32 microseconds)
return hol_block_timer_qtime_val(ipa, microseconds);
/* Use 64 bit arithmetic to avoid overflow... */
rate = ipa_clock_rate(ipa);
rate = ipa_core_clock_rate(ipa);
ticks = DIV_ROUND_CLOSEST(microseconds * rate, 128 * USEC_PER_SEC);
/* ...but we still need to fit into a 32-bit register */
WARN_ON(ticks > U32_MAX);
......
......@@ -116,7 +116,8 @@ static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
iowrite32(pending, ipa->reg_virt + offset);
}
out_power_put:
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return IRQ_HANDLED;
}
......
......@@ -20,7 +20,7 @@
#include <linux/soc/qcom/mdt_loader.h>
#include "ipa.h"
#include "ipa_clock.h"
#include "ipa_power.h"
#include "ipa_data.h"
#include "ipa_endpoint.h"
#include "ipa_resource.h"
......@@ -326,8 +326,8 @@ static void ipa_idle_indication_cfg(struct ipa *ipa,
* @ipa: IPA pointer
*
* Configures when the IPA signals it is idle to the global clock
* controller, which can respond by scalling down the clock to
* save power.
* controller, which can respond by scaling down the clock to save
* power.
*/
static void ipa_hardware_dcd_config(struct ipa *ipa)
{
......@@ -417,7 +417,7 @@ static void ipa_hardware_deconfig(struct ipa *ipa)
* @ipa: IPA pointer
* @data: IPA configuration data
*
* Perform initialization requiring IPA clock to be enabled.
* Perform initialization requiring IPA power to be enabled.
*/
static int ipa_config(struct ipa *ipa, const struct ipa_data *data)
{
......@@ -647,7 +647,7 @@ static bool ipa_version_valid(enum ipa_version version)
* in several stages:
* - The "init" stage involves activities that can be initialized without
* access to the IPA hardware.
* - The "config" stage requires the IPA clock to be active so IPA registers
* - The "config" stage requires IPA power to be active so IPA registers
* can be accessed, but does not require the use of IPA immediate commands.
* - The "setup" stage uses IPA immediate commands, and so requires the GSI
* layer to be initialized.
......@@ -663,14 +663,14 @@ static int ipa_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct ipa_data *data;
struct ipa_clock *clock;
struct ipa_power *power;
bool modem_init;
struct ipa *ipa;
int ret;
ipa_validate_build();
/* Get configuration data early; needed for clock initialization */
/* Get configuration data early; needed for power initialization */
data = of_device_get_match_data(dev);
if (!data) {
dev_err(dev, "matched hardware not supported\n");
......@@ -691,20 +691,20 @@ static int ipa_probe(struct platform_device *pdev)
/* The clock and interconnects might not be ready when we're
* probed, so might return -EPROBE_DEFER.
*/
clock = ipa_clock_init(dev, data->clock_data);
if (IS_ERR(clock))
return PTR_ERR(clock);
power = ipa_power_init(dev, data->power_data);
if (IS_ERR(power))
return PTR_ERR(power);
/* No more EPROBE_DEFER. Allocate and initialize the IPA structure */
ipa = kzalloc(sizeof(*ipa), GFP_KERNEL);
if (!ipa) {
ret = -ENOMEM;
goto err_clock_exit;
goto err_power_exit;
}
ipa->pdev = pdev;
dev_set_drvdata(dev, ipa);
ipa->clock = clock;
ipa->power = power;
ipa->version = data->version;
init_completion(&ipa->completion);
......@@ -737,7 +737,7 @@ static int ipa_probe(struct platform_device *pdev)
if (ret)
goto err_table_exit;
/* The clock needs to be active for config and setup */
/* Power needs to be active for config and setup */
ret = pm_runtime_get_sync(dev);
if (WARN_ON(ret < 0))
goto err_power_put;
......@@ -766,14 +766,15 @@ static int ipa_probe(struct platform_device *pdev)
if (ret)
goto err_deconfig;
done:
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return 0;
err_deconfig:
ipa_deconfig(ipa);
err_power_put:
(void)pm_runtime_put(dev);
pm_runtime_put_noidle(dev);
ipa_modem_exit(ipa);
err_table_exit:
ipa_table_exit(ipa);
......@@ -787,8 +788,8 @@ static int ipa_probe(struct platform_device *pdev)
ipa_reg_exit(ipa);
err_kfree_ipa:
kfree(ipa);
err_clock_exit:
ipa_clock_exit(clock);
err_power_exit:
ipa_power_exit(power);
return ret;
}
......@@ -796,10 +797,11 @@ static int ipa_probe(struct platform_device *pdev)
static int ipa_remove(struct platform_device *pdev)
{
struct ipa *ipa = dev_get_drvdata(&pdev->dev);
struct ipa_clock *clock = ipa->clock;
struct ipa_power *power = ipa->power;
struct device *dev = &pdev->dev;
int ret;
ret = pm_runtime_get_sync(&pdev->dev);
ret = pm_runtime_get_sync(dev);
if (WARN_ON(ret < 0))
goto out_power_put;
......@@ -818,8 +820,7 @@ static int ipa_remove(struct platform_device *pdev)
ipa_deconfig(ipa);
out_power_put:
(void)pm_runtime_put(&pdev->dev);
pm_runtime_put_noidle(dev);
ipa_modem_exit(ipa);
ipa_table_exit(ipa);
ipa_endpoint_exit(ipa);
......@@ -827,7 +828,7 @@ static int ipa_remove(struct platform_device *pdev)
ipa_mem_exit(ipa);
ipa_reg_exit(ipa);
kfree(ipa);
ipa_clock_exit(clock);
ipa_power_exit(power);
return 0;
}
......
......@@ -21,7 +21,7 @@
#include "ipa_smp2p.h"
#include "ipa_qmi.h"
#include "ipa_uc.h"
#include "ipa_clock.h"
#include "ipa_power.h"
#define IPA_NETDEV_NAME "rmnet_ipa%d"
#define IPA_NETDEV_TAILROOM 0 /* for padding by mux layer */
......@@ -67,14 +67,15 @@ static int ipa_open(struct net_device *netdev)
netif_start_queue(netdev);
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return 0;
err_disable_tx:
ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
err_power_put:
(void)pm_runtime_put(dev);
pm_runtime_put_noidle(dev);
return ret;
}
......@@ -97,7 +98,8 @@ static int ipa_stop(struct net_device *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_power_put:
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return 0;
}
......@@ -145,7 +147,7 @@ ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
*/
ipa_power_modem_queue_stop(ipa);
(void)pm_runtime_put(dev);
pm_runtime_put_noidle(dev);
return NETDEV_TX_BUSY;
}
......@@ -154,7 +156,8 @@ ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
ret = ipa_endpoint_skb_tx(endpoint, skb);
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
if (ret) {
if (ret != -E2BIG)
......@@ -398,7 +401,8 @@ static void ipa_modem_crashed(struct ipa *ipa)
dev_err(dev, "error %d zeroing modem memory regions\n", ret);
out_power_put:
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
}
static int ipa_modem_notify(struct notifier_block *nb, unsigned long action,
......@@ -411,7 +415,7 @@ static int ipa_modem_notify(struct notifier_block *nb, unsigned long action,
switch (action) {
case QCOM_SSR_BEFORE_POWERUP:
dev_info(dev, "received modem starting event\n");
ipa_uc_clock(ipa);
ipa_uc_power(ipa);
ipa_smp2p_notify_reset(ipa);
break;
......
......@@ -3,24 +3,24 @@
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2018-2020 Linaro Ltd.
*/
#ifndef _IPA_CLOCK_H_
#define _IPA_CLOCK_H_
#ifndef _IPA_POWER_H_
#define _IPA_POWER_H_
struct device;
struct ipa;
struct ipa_clock_data;
struct ipa_power_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_core_clock_rate() - Return the current IPA core clock rate
* @ipa: IPA structure
*
* Return: The current clock rate (in Hz), or 0.
*/
u32 ipa_clock_rate(struct ipa *ipa);
u32 ipa_core_clock_rate(struct ipa *ipa);
/**
* ipa_power_modem_queue_stop() - Possibly stop the modem netdev TX queue
......@@ -55,19 +55,19 @@ int ipa_power_setup(struct ipa *ipa);
void ipa_power_teardown(struct ipa *ipa);
/**
* ipa_clock_init() - Initialize IPA clocking
* ipa_power_init() - Initialize IPA power management
* @dev: IPA device
* @data: Clock configuration data
*
* Return: A pointer to an ipa_clock structure, or a pointer-coded error
* Return: A pointer to an ipa_power structure, or a pointer-coded error
*/
struct ipa_clock *ipa_clock_init(struct device *dev,
const struct ipa_clock_data *data);
struct ipa_power *ipa_power_init(struct device *dev,
const struct ipa_power_data *data);
/**
* ipa_clock_exit() - Inverse of ipa_clock_init()
* @clock: IPA clock pointer
* ipa_power_exit() - Inverse of ipa_power_init()
* @power: IPA power pointer
*/
void ipa_clock_exit(struct ipa_clock *clock);
void ipa_power_exit(struct ipa_power *power);
#endif /* _IPA_CLOCK_H_ */
#endif /* _IPA_POWER_H_ */
......@@ -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,13 +73,13 @@ 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)
......@@ -92,11 +92,11 @@ static void ipa_smp2p_notify(struct ipa_smp2p *smp2p)
return;
dev = &smp2p->ipa->pdev->dev;
smp2p->clock_on = pm_runtime_get_if_active(dev, true) > 0;
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 */
......@@ -126,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;
......@@ -174,7 +174,8 @@ static irqreturn_t ipa_smp2p_modem_setup_ready_isr(int irq, void *dev_id)
WARN(ret != 0, "error %d from ipa_setup()\n", ret);
out_power_put:
(void)pm_runtime_put(dev);
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
out_mutex_unlock:
mutex_unlock(&smp2p->mutex);
......@@ -208,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;
(void)pm_runtime_put(&ipa->pdev->dev);
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 */
......@@ -249,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);
......@@ -302,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);
......@@ -332,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);
......
......@@ -39,7 +39,7 @@ void ipa_smp2p_disable(struct ipa *ipa);
* ipa_smp2p_notify_reset() - Reset modem notification state
* @ipa: IPA pointer
*
* If the modem crashes it queries the IPA clock state. In cleaning
* If the modem crashes it queries the IPA power state. In cleaning
* up after such a crash this is used to reset some state maintained
* for managing this notification.
*/
......
......@@ -147,15 +147,16 @@ static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id)
* should only receive responses from the microcontroller when it has
* sent it a request message.
*
* We can drop the clock reference taken in ipa_uc_clock() once we
* We can drop the power reference taken in ipa_uc_power() once we
* know the microcontroller has finished its initialization.
*/
switch (shared->response) {
case IPA_UC_RESPONSE_INIT_COMPLETED:
if (ipa->uc_clocked) {
if (ipa->uc_powered) {
ipa->uc_loaded = true;
(void)pm_runtime_put(dev);
ipa->uc_clocked = false;
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
ipa->uc_powered = false;
} else {
dev_warn(dev, "unexpected init_completed response\n");
}
......@@ -170,7 +171,7 @@ static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id)
/* Configure the IPA microcontroller subsystem */
void ipa_uc_config(struct ipa *ipa)
{
ipa->uc_clocked = false;
ipa->uc_powered = false;
ipa->uc_loaded = false;
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_0, ipa_uc_event_handler);
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_1, ipa_uc_response_hdlr);
......@@ -179,14 +180,19 @@ void ipa_uc_config(struct ipa *ipa)
/* Inverse of ipa_uc_config() */
void ipa_uc_deconfig(struct ipa *ipa)
{
struct device *dev = &ipa->pdev->dev;
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_1);
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_0);
if (ipa->uc_clocked)
(void)pm_runtime_put(&ipa->pdev->dev);
if (!ipa->uc_powered)
return;
pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
}
/* Take a proxy clock reference for the microcontroller */
void ipa_uc_clock(struct ipa *ipa)
/* Take a proxy power reference for the microcontroller */
void ipa_uc_power(struct ipa *ipa)
{
static bool already;
struct device *dev;
......@@ -203,7 +209,7 @@ void ipa_uc_clock(struct ipa *ipa)
pm_runtime_put_noidle(dev);
dev_err(dev, "error %d getting proxy power\n", ret);
} else {
ipa->uc_clocked = true;
ipa->uc_powered = true;
}
}
......
......@@ -21,18 +21,18 @@ void ipa_uc_config(struct ipa *ipa);
void ipa_uc_deconfig(struct ipa *ipa);
/**
* ipa_uc_clock() - Take a proxy clock reference for the microcontroller
* ipa_uc_power() - Take a proxy power reference for the microcontroller
* @ipa: IPA pointer
*
* The first time the modem boots, it loads firmware for and starts the
* IPA-resident microcontroller. The microcontroller signals that it
* has completed its initialization by sending an INIT_COMPLETED response
* message to the AP. The AP must ensure the IPA core clock is operating
* until it receives this message, and to do so we take a "proxy" clock
* message to the AP. The AP must ensure the IPA is powered until
* it receives this message, and to do so we take a "proxy" clock
* reference on its behalf here. Once we receive the INIT_COMPLETED
* message (in ipa_uc_response_hdlr()) we drop this clock reference.
* message (in ipa_uc_response_hdlr()) we drop this power reference.
*/
void ipa_uc_clock(struct ipa *ipa);
void ipa_uc_power(struct ipa *ipa);
/**
* ipa_uc_panic_notifier()
......
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