Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2204388f authored by Skylar Chang's avatar Skylar Chang
Browse files

msm: ipa3: ensure IPA registers are dumped by modem



This change adds a smp2p (shared memory) handshake with
modem. This handshake is used to synchronize IPA clocks
vote with modem during apps or modem crash. Using this
handshake modem can dump IPA registers during crash even
though it didn't vote for IPA clocks as long as apps holds
the clocks vote for it.

CRs-Fixed: 933200
Change-Id: Iae7db0b3a8fa74a1599eaeb6fa40d34c1592fc15
Acked-by: default avatarAdy Abraham <adya@qti.qualcomm.com>
Signed-off-by: default avatarSkylar Chang <chiaweic@codeaurora.org>
parent 82fbed04
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -97,6 +97,17 @@ Optional properties:
- clock-names: This property shall contain the clock input names used
    by driver in same order as the clocks property.This should be "iface_clk"

IPA SMP2P sub nodes

-compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from
					      ipa driver to modem.

-compatible: "qcom,smp2pgpio-map-ipa-1-in" - represents the in gpio to
					     ipa driver from modem.

-gpios: Binding to the gpio defined in XXX-smp2p.dtsi


Example:

qcom,ipa@fd4c0000 {
@@ -147,4 +158,15 @@ qcom,ipa@fd4c0000 {
		qcom,descriptor-fifo-offset = <0xd00>;
		qcom,descriptor-fifo-size = <0x300>;
	};

	/* smp2p gpio information */
	qcom,smp2pgpio_map_ipa_1_out {
		compatible = "qcom,smp2pgpio-map-ipa-1-out";
		gpios = <&smp2pgpio_ipa_1_out 0 0>;
	};

	qcom,smp2pgpio_map_ipa_1_in {
		compatible = "qcom,smp2pgpio-map-ipa-1-in";
		gpios = <&smp2pgpio_ipa_1_in 0 0>;
	};
};
+28 −28
Original line number Diff line number Diff line
@@ -2648,6 +2648,8 @@ static struct of_device_id ipa_plat_drv_match[] = {
	{ .compatible = "qcom,ipa-smmu-ap-cb", },
	{ .compatible = "qcom,ipa-smmu-wlan-cb", },
	{ .compatible = "qcom,ipa-smmu-uc-cb", },
	{ .compatible = "qcom,smp2pgpio-map-ipa-1-in", },
	{ .compatible = "qcom,smp2pgpio-map-ipa-1-out", },
	{}
};

@@ -2655,21 +2657,29 @@ static int ipa_generic_plat_drv_probe(struct platform_device *pdev_p)
{
	int result;

	pr_debug("ipa: IPA driver probing started\n");
	/*
	* IPA probe function can be called for multiple times as the same probe
	* function handles multiple compatibilities
	*/
	pr_debug("ipa: IPA driver probing started for %s\n",
		pdev_p->dev.of_node->name);

	if (!ipa_api_ctrl) {
		ipa_api_ctrl = kzalloc(sizeof(*ipa_api_ctrl), GFP_KERNEL);
		if (!ipa_api_ctrl)
			return -ENOMEM;

		/* Get IPA HW Version */
	result = of_property_read_u32(pdev_p->dev.of_node, "qcom,ipa-hw-ver",
		&ipa_api_hw_type);
		result = of_property_read_u32(pdev_p->dev.of_node,
			"qcom,ipa-hw-ver", &ipa_api_hw_type);
		if ((result) || (ipa_api_hw_type == 0)) {
			pr_err("ipa: get resource failed for ipa-hw-ver!\n");
		result = -ENODEV;
		goto fail;
			kfree(ipa_api_ctrl);
			ipa_api_ctrl = 0;
			return -ENODEV;
		}
		pr_debug("ipa: ipa_api_hw_type = %d", ipa_api_hw_type);
	}

	/* call probe based on IPA HW version */
	switch (ipa_api_hw_type) {
@@ -2679,30 +2689,20 @@ static int ipa_generic_plat_drv_probe(struct platform_device *pdev_p)
	case IPA_HW_v2_6L:
		result = ipa_plat_drv_probe(pdev_p, ipa_api_ctrl,
			ipa_plat_drv_match);
		if (result) {
			pr_err("ipa: ipa_plat_drv_probe failed\n");
			goto fail;
		}
		break;
	case IPA_HW_v3_0:
	case IPA_HW_v3_1:
		result = ipa3_plat_drv_probe(pdev_p, ipa_api_ctrl,
			ipa_plat_drv_match);
		if (result) {
			pr_err("ipa: ipa3_plat_drv_probe failed\n");
			goto fail;
		}
		break;
	default:
		pr_err("ipa: unsupported version %d\n", ipa_api_hw_type);
		result = -EPERM;
		goto fail;
		return -EPERM;
	}

	return 0;
fail:
	kfree(ipa_api_ctrl);
	ipa_api_ctrl = 0;
	if (result && result != -EPROBE_DEFER)
		pr_err("ipa: ipa_plat_drv_probe failed\n");

	return result;
}

+145 −10
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/rbtree.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/msm-bus.h>
@@ -41,6 +42,10 @@
#define CREATE_TRACE_POINTS
#include "ipa_trace.h"

#define IPA_GPIO_IN_QUERY_CLK_IDX 0
#define IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX 0
#define IPA_GPIO_OUT_CLK_VOTE_IDX 1

#define IPA_SUMMING_THRESHOLD (0x10)
#define IPA_PIPE_MEM_START_OFST (0x0)
#define IPA_PIPE_MEM_SIZE (0x0)
@@ -3527,6 +3532,62 @@ static void ipa3_destroy_flt_tbl_idrs(void)
	}
}

static void ipa3_freeze_clock_vote_and_notify_modem(void)
{
	int res;
	u32 ipa_clk_state;
	struct ipa3_active_client_logging_info log_info;

	if (ipa3_ctx->smp2p_info.res_sent)
		return;

	IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "FREEZE_VOTE");
	res = ipa3_inc_client_enable_clks_no_block(&log_info);
	if (res)
		ipa_clk_state = 0;
	else
		ipa_clk_state = 1;

	if (ipa3_ctx->smp2p_info.out_base_id) {
		gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
			IPA_GPIO_OUT_CLK_VOTE_IDX, ipa_clk_state);
		gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
			IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX, 1);
		ipa3_ctx->smp2p_info.res_sent = true;
	} else {
		IPAERR("smp2p out gpio not assigned\n");
	}

	IPADBG("IPA clocks are %s\n", ipa_clk_state ? "ON" : "OFF");
}

static int ipa3_panic_notifier(struct notifier_block *this,
	unsigned long event, void *ptr)
{
	int res;

	ipa3_freeze_clock_vote_and_notify_modem();

	IPADBG("Calling uC panic handler\n");
	res = ipa3_uc_panic_notifier(this, event, ptr);
	if (res)
		IPAERR("uC panic handler failed %d\n" , res);

	return NOTIFY_DONE;
}

static struct notifier_block ipa3_panic_blk = {
	.notifier_call = ipa3_panic_notifier,
	/* IPA panic handler needs to run before modem shuts down */
	.priority = INT_MAX,
};

static void ipa3_register_panic_hdlr(void)
{
	atomic_notifier_chain_register(&panic_notifier_list,
		&ipa3_panic_blk);
}

static void ipa3_trigger_ipa_ready_cbs(void)
{
	struct ipa3_ready_cb_info *info;
@@ -4691,6 +4752,66 @@ static int ipa_smmu_ap_cb_probe(struct device *dev)
	return result;
}

static irqreturn_t ipa3_smp2p_modem_clk_query_isr(int irq, void *ctxt)
{
	ipa3_freeze_clock_vote_and_notify_modem();

	return IRQ_HANDLED;
}

static int ipa3_smp2p_probe(struct device *dev)
{
	struct device_node *node = dev->of_node;
	int res;

	IPADBG("node->name=%s\n", node->name);
	if (strcmp("qcom,smp2pgpio_map_ipa_1_out", node->name) == 0) {
		res = of_get_gpio(node, 0);
		if (res < 0) {
			IPADBG("of_get_gpio returned %d\n", res);
			return res;
		}

		ipa3_ctx->smp2p_info.out_base_id = res;
		IPADBG("smp2p out_base_id=%d\n",
			ipa3_ctx->smp2p_info.out_base_id);
	} else if (strcmp("qcom,smp2pgpio_map_ipa_1_in", node->name) == 0) {
		int irq;

		res = of_get_gpio(node, 0);
		if (res < 0) {
			IPADBG("of_get_gpio returned %d\n", res);
			return res;
		}

		ipa3_ctx->smp2p_info.in_base_id = res;
		IPADBG("smp2p in_base_id=%d\n",
			ipa3_ctx->smp2p_info.in_base_id);

		/* register for modem clk query */
		irq = gpio_to_irq(ipa3_ctx->smp2p_info.in_base_id +
			IPA_GPIO_IN_QUERY_CLK_IDX);
		if (irq < 0) {
			IPAERR("gpio_to_irq failed %d\n", irq);
			return -ENODEV;
		}
		IPADBG("smp2p irq#=%d\n", irq);
		res = request_irq(irq,
			(irq_handler_t)ipa3_smp2p_modem_clk_query_isr,
			IRQF_TRIGGER_RISING, "ipa_smp2p_clk_vote", dev);
		if (res) {
			IPAERR("fail to register smp2p irq=%d\n", irq);
			return -ENODEV;
		}
		res = enable_irq_wake(ipa3_ctx->smp2p_info.in_base_id +
			IPA_GPIO_IN_QUERY_CLK_IDX);
		if (res)
			IPAERR("failed to enable irq wake\n");
	}

	return 0;
}

int ipa3_plat_drv_probe(struct platform_device *pdev_p,
	struct ipa_api_controller *api_ctrl, struct of_device_id *pdrv_match)
{
@@ -4698,6 +4819,7 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
	struct device *dev = &pdev_p->dev;

	IPADBG("IPA driver probing started\n");
	IPADBG("dev->of_node->name = %s\n", dev->of_node->name);

	if (of_device_is_compatible(dev->of_node, "qcom,ipa-smmu-ap-cb"))
		return ipa_smmu_ap_cb_probe(dev);
@@ -4708,6 +4830,14 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
	if (of_device_is_compatible(dev->of_node, "qcom,ipa-smmu-uc-cb"))
		return ipa_smmu_uc_cb_probe(dev);

	if (of_device_is_compatible(dev->of_node,
	    "qcom,smp2pgpio-map-ipa-1-in"))
		return ipa3_smp2p_probe(dev);

	if (of_device_is_compatible(dev->of_node,
	    "qcom,smp2pgpio-map-ipa-1-out"))
		return ipa3_smp2p_probe(dev);

	master_dev = dev;
	if (!ipa3_pdev)
		ipa3_pdev = pdev_p;
@@ -4724,10 +4854,15 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
		return result;
	}

	if (of_property_read_bool(pdev_p->dev.of_node, "qcom,arm-smmu")) {
		arm_smmu = true;
	result = of_platform_populate(pdev_p->dev.of_node,
		pdrv_match, NULL, &pdev_p->dev);
	if (result) {
		IPAERR("failed to populate platform\n");
		return result;
	}

	if (of_property_read_bool(pdev_p->dev.of_node, "qcom,arm-smmu")) {
		arm_smmu = true;
	} else if (of_property_read_bool(pdev_p->dev.of_node,
				"qcom,msm-smmu")) {
		IPAERR("Legacy IOMMU not supported\n");
@@ -4739,6 +4874,7 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
			IPAERR("DMA set mask failed\n");
			return -EOPNOTSUPP;
		}
	}

	if (!ipa3_bus_scale_table)
		ipa3_bus_scale_table = msm_bus_cl_get_pdata(pdev_p);
@@ -4749,7 +4885,6 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
		IPAERR("ipa3_init failed\n");
		return result;
	}
	}

	return result;
}
+9 −1
Original line number Diff line number Diff line
@@ -1391,6 +1391,12 @@ struct ipa3_hash_tuple {
	bool meta_data;
};

struct ipa3_smp2p_info {
	u32 out_base_id;
	u32 in_base_id;
	bool res_sent;
};

/**
 * struct ipa3_ready_cb_info - A list of all the registrations
 *  for an indication of IPA driver readiness
@@ -1608,6 +1614,7 @@ struct ipa3_context {
	bool ipa_initialization_complete;
	struct list_head ipa_ready_cb_list;
	struct completion init_completion_obj;
	struct ipa3_smp2p_info smp2p_info;
};

/**
@@ -2384,7 +2391,6 @@ int ipa3_uc_loaded_check(void);
void ipa3_uc_load_notify(void);
int ipa3_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status,
		    bool polling_mode, unsigned long timeout_jiffies);
void ipa3_register_panic_hdlr(void);
void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
			      struct ipa3_uc_hdlrs *hdlrs);
int ipa3_create_nat_device(void);
@@ -2446,6 +2452,8 @@ int ipa3_calc_extra_wrd_bytes(const struct ipa_ipfltri_rule_eq *attrib);
const char *ipa3_rm_resource_str(enum ipa_rm_resource_name resource_name);
int ipa3_restore_suspend_handler(void);
int ipa3_inject_dma_task_for_gsi(void);
int ipa3_uc_panic_notifier(struct notifier_block *this,
	unsigned long event, void *ptr);
void ipa3_inc_acquire_wakelock(void);
void ipa3_dec_release_wakelock(void);
int ipa3_load_fws(const struct firmware *firmware);
+2 −12
Original line number Diff line number Diff line
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -397,7 +397,7 @@ static void ipa3_uc_event_handler(enum ipa_irq_type interrupt,

}

static int ipa3_uc_panic_notifier(struct notifier_block *this,
int ipa3_uc_panic_notifier(struct notifier_block *this,
		unsigned long event, void *ptr)
{
	int result = 0;
@@ -436,16 +436,6 @@ fail:
	return NOTIFY_DONE;
}

static struct notifier_block ipa3_uc_panic_blk = {
	.notifier_call  = ipa3_uc_panic_notifier,
};

void ipa3_register_panic_hdlr(void)
{
	atomic_notifier_chain_register(&panic_notifier_list,
			&ipa3_uc_panic_blk);
}

static void ipa3_uc_response_hdlr(enum ipa_irq_type interrupt,
				void *private_data,
				void *interrupt_data)