aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ipa/ipa_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ipa/ipa_main.c')
-rw-r--r--drivers/net/ipa/ipa_main.c222
1 files changed, 74 insertions, 148 deletions
diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
index 9810c61a0320..cdfa98a76e1f 100644
--- a/drivers/net/ipa/ipa_main.c
+++ b/drivers/net/ipa/ipa_main.c
@@ -15,11 +15,12 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
#include <linux/qcom_scm.h>
#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"
@@ -80,29 +81,6 @@
#define IPA_XO_CLOCK_DIVIDER 192 /* 1 is subtracted where used */
/**
- * ipa_suspend_handler() - Handle the suspend IPA interrupt
- * @ipa: IPA pointer
- * @irq_id: IPA interrupt type (unused)
- *
- * If an RX endpoint is in suspend state, 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 resume everything.
- */
-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_FLAG_RESUMED, ipa->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);
-}
-
-/**
* ipa_setup() - Set up IPA hardware
* @ipa: IPA pointer
*
@@ -124,19 +102,9 @@ int ipa_setup(struct ipa *ipa)
if (ret)
return ret;
- ipa->interrupt = ipa_interrupt_setup(ipa);
- if (IS_ERR(ipa->interrupt)) {
- ret = PTR_ERR(ipa->interrupt);
- goto err_gsi_teardown;
- }
- ipa_interrupt_add(ipa->interrupt, IPA_IRQ_TX_SUSPEND,
- ipa_suspend_handler);
-
- ipa_uc_setup(ipa);
-
- ret = device_init_wakeup(dev, true);
+ ret = ipa_power_setup(ipa);
if (ret)
- goto err_uc_teardown;
+ goto err_gsi_teardown;
ipa_endpoint_setup(ipa);
@@ -167,7 +135,7 @@ int ipa_setup(struct ipa *ipa)
ipa_endpoint_default_route_set(ipa, exception_endpoint->endpoint_id);
/* We're all set. Now prepare for communication with the modem */
- ret = ipa_modem_setup(ipa);
+ ret = ipa_qmi_setup(ipa);
if (ret)
goto err_default_route_clear;
@@ -184,11 +152,7 @@ err_command_disable:
ipa_endpoint_disable_one(command_endpoint);
err_endpoint_teardown:
ipa_endpoint_teardown(ipa);
- (void)device_init_wakeup(dev, false);
-err_uc_teardown:
- ipa_uc_teardown(ipa);
- ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);
- ipa_interrupt_teardown(ipa->interrupt);
+ ipa_power_teardown(ipa);
err_gsi_teardown:
gsi_teardown(&ipa->gsi);
@@ -204,17 +168,17 @@ static void ipa_teardown(struct ipa *ipa)
struct ipa_endpoint *exception_endpoint;
struct ipa_endpoint *command_endpoint;
- ipa_modem_teardown(ipa);
+ /* We're going to tear everything down, as if setup never completed */
+ ipa->setup_complete = false;
+
+ ipa_qmi_teardown(ipa);
ipa_endpoint_default_route_clear(ipa);
exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];
ipa_endpoint_disable_one(exception_endpoint);
command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX];
ipa_endpoint_disable_one(command_endpoint);
ipa_endpoint_teardown(ipa);
- (void)device_init_wakeup(&ipa->pdev->dev, false);
- ipa_uc_teardown(ipa);
- ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_TX_SUSPEND);
- ipa_interrupt_teardown(ipa->interrupt);
+ ipa_power_teardown(ipa);
gsi_teardown(&ipa->gsi);
}
@@ -253,9 +217,6 @@ ipa_hardware_config_qsb(struct ipa *ipa, const struct ipa_data *data)
const struct ipa_qsb_data *data1;
u32 val;
- /* assert(data->qsb_count > 0); */
- /* assert(data->qsb_count < 3); */
-
/* QMB 0 represents DDR; QMB 1 (if present) represents PCIe */
data0 = &data->qsb_data[IPA_QSB_MASTER_DDR];
if (data->qsb_count > 1)
@@ -289,12 +250,11 @@ ipa_hardware_config_qsb(struct ipa *ipa, const struct ipa_data *data)
/* Compute the value to use in the COUNTER_CFG register AGGR_GRANULARITY
* field to represent the given number of microseconds. The value is one
* less than the number of timer ticks in the requested period. 0 is not
- * a valid granularity value.
+ * a valid granularity value (so for example @usec must be at least 16 for
+ * a TIMER_FREQUENCY of 32000).
*/
-static u32 ipa_aggr_granularity_val(u32 usec)
+static __always_inline u32 ipa_aggr_granularity_val(u32 usec)
{
- /* assert(usec != 0); */
-
return DIV_ROUND_CLOSEST(usec * TIMER_FREQUENCY, USEC_PER_SEC) - 1;
}
@@ -366,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)
{
@@ -457,48 +417,54 @@ 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)
{
int ret;
- /* Get a clock reference to allow initialization. This reference
- * is held after initialization completes, and won't get dropped
- * unless/until a system suspend request arrives.
- */
- ipa_clock_get(ipa);
-
ipa_hardware_config(ipa, data);
- ret = ipa_endpoint_config(ipa);
+ ret = ipa_mem_config(ipa);
if (ret)
goto err_hardware_deconfig;
- ret = ipa_mem_config(ipa);
+ ipa->interrupt = ipa_interrupt_config(ipa);
+ if (IS_ERR(ipa->interrupt)) {
+ ret = PTR_ERR(ipa->interrupt);
+ ipa->interrupt = NULL;
+ goto err_mem_deconfig;
+ }
+
+ ipa_uc_config(ipa);
+
+ ret = ipa_endpoint_config(ipa);
if (ret)
- goto err_endpoint_deconfig;
+ goto err_uc_deconfig;
ipa_table_config(ipa); /* No deconfig required */
/* Assign resource limitation to each group; no deconfig required */
ret = ipa_resource_config(ipa, data->resource_data);
if (ret)
- goto err_mem_deconfig;
+ goto err_endpoint_deconfig;
ret = ipa_modem_config(ipa);
if (ret)
- goto err_mem_deconfig;
+ goto err_endpoint_deconfig;
return 0;
-err_mem_deconfig:
- ipa_mem_deconfig(ipa);
err_endpoint_deconfig:
ipa_endpoint_deconfig(ipa);
+err_uc_deconfig:
+ ipa_uc_deconfig(ipa);
+ ipa_interrupt_deconfig(ipa->interrupt);
+ ipa->interrupt = NULL;
+err_mem_deconfig:
+ ipa_mem_deconfig(ipa);
err_hardware_deconfig:
ipa_hardware_deconfig(ipa);
- ipa_clock_put(ipa);
return ret;
}
@@ -510,10 +476,12 @@ err_hardware_deconfig:
static void ipa_deconfig(struct ipa *ipa)
{
ipa_modem_deconfig(ipa);
- ipa_mem_deconfig(ipa);
ipa_endpoint_deconfig(ipa);
+ ipa_uc_deconfig(ipa);
+ ipa_interrupt_deconfig(ipa->interrupt);
+ ipa->interrupt = NULL;
+ ipa_mem_deconfig(ipa);
ipa_hardware_deconfig(ipa);
- ipa_clock_put(ipa);
}
static int ipa_firmware_load(struct device *dev)
@@ -612,7 +580,6 @@ MODULE_DEVICE_TABLE(of, ipa_match);
* */
static void ipa_validate_build(void)
{
-#ifdef IPA_VALIDATE
/* At one time we assumed a 64-bit build, allowing some do_div()
* calls to be replaced by simple division or modulo operations.
* We currently only perform divide and modulo operations on u32,
@@ -646,7 +613,6 @@ static void ipa_validate_build(void)
BUILD_BUG_ON(!ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY));
BUILD_BUG_ON(ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY) >
field_max(AGGR_GRANULARITY_FMASK));
-#endif /* IPA_VALIDATE */
}
static bool ipa_version_valid(enum ipa_version version)
@@ -681,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.
@@ -697,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");
@@ -725,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);
@@ -771,18 +737,23 @@ static int ipa_probe(struct platform_device *pdev)
if (ret)
goto err_table_exit;
+ /* Power needs to be active for config and setup */
+ ret = pm_runtime_get_sync(dev);
+ if (WARN_ON(ret < 0))
+ goto err_power_put;
+
ret = ipa_config(ipa, data);
if (ret)
- goto err_modem_exit;
+ goto err_power_put;
dev_info(dev, "IPA driver initialized");
/* If the modem is doing early initialization, it will trigger a
- * call to ipa_setup() call when it has finished. In that case
- * we're done here.
+ * call to ipa_setup() when it has finished. In that case we're
+ * done here.
*/
if (modem_init)
- return 0;
+ goto done;
/* Otherwise we need to load the firmware and have Trust Zone validate
* and install it. If that succeeds we can proceed with setup.
@@ -794,12 +765,16 @@ static int ipa_probe(struct platform_device *pdev)
ret = ipa_setup(ipa);
if (ret)
goto err_deconfig;
+done:
+ pm_runtime_mark_last_busy(dev);
+ (void)pm_runtime_put_autosuspend(dev);
return 0;
err_deconfig:
ipa_deconfig(ipa);
-err_modem_exit:
+err_power_put:
+ pm_runtime_put_noidle(dev);
ipa_modem_exit(ipa);
err_table_exit:
ipa_table_exit(ipa);
@@ -813,8 +788,8 @@ err_reg_exit:
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;
}
@@ -822,9 +797,14 @@ err_clock_exit:
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(dev);
+ if (WARN_ON(ret < 0))
+ goto out_power_put;
+
if (ipa->setup_complete) {
ret = ipa_modem_stop(ipa);
/* If starting or stopping is in progress, try once more */
@@ -839,6 +819,8 @@ static int ipa_remove(struct platform_device *pdev)
}
ipa_deconfig(ipa);
+out_power_put:
+ pm_runtime_put_noidle(dev);
ipa_modem_exit(ipa);
ipa_table_exit(ipa);
ipa_endpoint_exit(ipa);
@@ -846,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;
}
@@ -860,62 +842,6 @@ static void ipa_shutdown(struct platform_device *pdev)
dev_err(&pdev->dev, "shutdown: remove returned %d\n", ret);
}
-/**
- * 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);
-
- /* When a suspended RX endpoint has a packet ready to receive, we
- * get an IPA SUSPEND interrupt. We trigger a system resume in
- * that case, but only on the first such interrupt since suspend.
- */
- __clear_bit(IPA_FLAG_RESUMED, ipa->flags);
-
- ipa_endpoint_suspend(ipa);
-
- 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);
-
- ipa_endpoint_resume(ipa);
-
- return 0;
-}
-
-static const struct dev_pm_ops ipa_pm_ops = {
- .suspend = ipa_suspend,
- .resume = ipa_resume,
-};
-
static const struct attribute_group *ipa_attribute_groups[] = {
&ipa_attribute_group,
&ipa_feature_attribute_group,