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

Commit 1d735525 authored by Aparna Das's avatar Aparna Das
Browse files

jtagv8: prevent trace flush hang during power collapse



Add support to prevent trace flush hang during power collapse
by ensuring trace data is flushed and funnel port is disabled
before CPU is powered down.

Change-Id: I78907c40bcb9e237b6fa4e319013d478751f55da
Signed-off-by: default avatarAparna Das <adas@codeaurora.org>
parent 286b0d28
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -177,6 +177,12 @@ Optional properties:
		  cti-trigout-pctrl. Used for cti component
- pinctrl-<n>: list of pinctrl phandles for the different pinctrl states. Refer
	       to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt".
-qcom,funnel-save-restore : boolean, indicating funnel port needs to be disabled
			    for the ETM whose CPU is being powered down. The port
			    state is restored when CPU is powered up. Used for
			    funnel component.
-qcom,tmc-flush-powerdown : boolean, indicating trace data needs to be flushed before
			    powering down CPU. Used for TMC component.

Examples:

+84 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, 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
@@ -20,8 +20,12 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <soc/qcom/jtag.h>

#include "coresight-priv.h"

@@ -51,6 +55,7 @@ do { \
#define FUNNEL_HOLDTIME_MASK	(0xF00)
#define FUNNEL_HOLDTIME_SHFT	(0x8)
#define FUNNEL_HOLDTIME		(0x7 << FUNNEL_HOLDTIME_SHFT)
#define FUNNEL_NR_PORTS		8

struct funnel_drvdata {
	void __iomem		*base;
@@ -58,6 +63,11 @@ struct funnel_drvdata {
	struct coresight_device	*csdev;
	struct clk		*clk;
	uint32_t		priority;
	spinlock_t		spinlock;
	DECLARE_BITMAP(inport, FUNNEL_NR_PORTS);
	bool			notify;
	struct notifier_block	jtag_save_blk;
	struct notifier_block	jtag_restore_blk;
};

static void __funnel_enable(struct funnel_drvdata *drvdata, int port)
@@ -86,12 +96,32 @@ static int funnel_enable(struct coresight_device *csdev, int inport,
	if (ret)
		return ret;

	spin_lock(&drvdata->spinlock);
	__funnel_enable(drvdata, inport);
	__set_bit(inport, drvdata->inport);
	spin_unlock(&drvdata->spinlock);

	dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
	return 0;
}

static int funnel_enable_cpu_port(struct notifier_block *this,
				  unsigned long event, void *ptr)
{
	struct funnel_drvdata *drvdata = container_of(this,
						      struct funnel_drvdata,
						      jtag_restore_blk);
	int cpu = raw_smp_processor_id();

	spin_lock(&drvdata->spinlock);
	if (!test_bit(cpu, drvdata->inport))
		goto out;
	__funnel_enable(drvdata, cpu);
out:
	spin_unlock(&drvdata->spinlock);
	return NOTIFY_OK;
}

static void __funnel_disable(struct funnel_drvdata *drvdata, int inport)
{
	uint32_t functl;
@@ -110,13 +140,33 @@ static void funnel_disable(struct coresight_device *csdev, int inport,
{
	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	spin_lock(&drvdata->spinlock);
	__funnel_disable(drvdata, inport);
	__clear_bit(inport, drvdata->inport);
	spin_unlock(&drvdata->spinlock);

	clk_disable_unprepare(drvdata->clk);

	dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
}

static int funnel_disable_cpu_port(struct notifier_block *this,
				   unsigned long event, void *ptr)
{
	struct funnel_drvdata *drvdata = container_of(this,
						      struct funnel_drvdata,
						      jtag_save_blk);
	int cpu = raw_smp_processor_id();

	spin_lock(&drvdata->spinlock);
	if (!test_bit(cpu, drvdata->inport))
		goto out;
	__funnel_disable(drvdata, cpu);
out:
	spin_unlock(&drvdata->spinlock);
	return NOTIFY_OK;
}

static const struct coresight_ops_link funnel_link_ops = {
	.enable		= funnel_enable,
	.disable	= funnel_disable,
@@ -206,6 +256,12 @@ static int funnel_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	spin_lock_init(&drvdata->spinlock);

	if (pdev->dev.of_node)
		drvdata->notify = of_property_read_bool(pdev->dev.of_node,
						"qcom,funnel-save-restore");

	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
	if (!desc)
		return -ENOMEM;
@@ -220,6 +276,29 @@ static int funnel_probe(struct platform_device *pdev)
	if (IS_ERR(drvdata->csdev))
		return PTR_ERR(drvdata->csdev);

	if (drvdata->notify) {
		drvdata->jtag_save_blk.notifier_call = funnel_disable_cpu_port;
		ret = msm_jtag_save_register(&drvdata->jtag_save_blk);
		if (ret) {
			dev_err(dev,
				"Jtag save notifier register failed:%d\n",
				ret);
			drvdata->notify = false;
			goto out;
		}

		drvdata->jtag_restore_blk.notifier_call =
							funnel_enable_cpu_port;
		ret = msm_jtag_restore_register(&drvdata->jtag_restore_blk);
		if (ret) {
			dev_err(dev,
				"Jtag restore notifier register failed:%d\n",
				ret);
			msm_jtag_save_unregister(&drvdata->jtag_save_blk);
			drvdata->notify = false;
		}
	}
out:
	dev_info(dev, "FUNNEL initialized\n");
	return 0;
}
@@ -228,6 +307,10 @@ static int funnel_remove(struct platform_device *pdev)
{
	struct funnel_drvdata *drvdata = platform_get_drvdata(pdev);

	if (drvdata->notify) {
		msm_jtag_restore_unregister(&drvdata->jtag_restore_blk);
		msm_jtag_save_unregister(&drvdata->jtag_save_blk);
	}
	coresight_unregister(drvdata->csdev);
	return 0;
}
+61 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <linux/usb_bam.h>
#include <asm/cacheflush.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/jtag.h>

#include "coresight-priv.h"

@@ -198,6 +199,8 @@ struct tmc_drvdata {
	enum tmc_etr_mem_type	memtype;
	uint32_t		delta_bottom;
	int			sg_blk_num;
	bool			notify;
	struct notifier_block	jtag_save_blk;
};

static void tmc_wait_for_flush(struct tmc_drvdata *drvdata)
@@ -244,6 +247,46 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
	tmc_wait_for_ready(drvdata);
}

static int tmc_flush_on_powerdown(struct notifier_block *this,
				  unsigned long event, void *ptr)
{
	struct tmc_drvdata *drvdata = container_of(this, struct tmc_drvdata,
						   jtag_save_blk);
	int count;
	uint8_t stopbit;
	unsigned long flags;
	uint32_t ffcr;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	/*
	 * Current implementation performs flush operation on all TMC devices
	 * that are enabled irrespective of the current sink.
	 */
	if (!drvdata->enable)
		goto out;
	ffcr = tmc_readl(drvdata, TMC_FFCR);
	stopbit = BVAL(ffcr, 12);
	/* Do not stop trace on flush */
	ffcr = ffcr & ~BIT(12);
	tmc_writel(drvdata, ffcr, TMC_FFCR);
	/* Generate manual flush */
	ffcr = ffcr | BIT(6);
	tmc_writel(drvdata, ffcr, TMC_FFCR);
	/* Ensure flush completes */
	for (count = TIMEOUT_US; BVAL(tmc_readl(drvdata, TMC_FFCR), 6) != 0
				&& count > 0; count--)
		udelay(1);
	if (count == 0)
		pr_warn_ratelimited("timeout flushing TMC, TMC_FFCR: %#x\n",
				    tmc_readl(drvdata, TMC_FFCR));
	/* Restore stop trace on flush bit */
	ffcr = ffcr | (stopbit << 12);
	tmc_writel(drvdata, ffcr, TMC_FFCR);
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	return NOTIFY_DONE;
}

static void __tmc_enable(struct tmc_drvdata *drvdata)
{
	tmc_writel(drvdata, 0x1, TMC_CTL);
@@ -2429,6 +2472,10 @@ static int tmc_probe(struct platform_device *pdev)
			if (IS_ERR(drvdata->cti_reset))
				dev_err(dev, "failed to get reset cti\n");
		}

		drvdata->notify = of_property_read_bool(pdev->dev.of_node,
						"qcom,tmc-flush-powerdown");

	}

	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
@@ -2486,6 +2533,18 @@ static int tmc_probe(struct platform_device *pdev)
	if (ret)
		goto err3;

	if (drvdata->notify) {
		drvdata->jtag_save_blk.notifier_call = tmc_flush_on_powerdown;
		drvdata->jtag_save_blk.priority = 1;
		ret = msm_jtag_save_register(&drvdata->jtag_save_blk);
		if (ret) {
			dev_err(dev,
				"Jtag save notifier register failed:%d\n",
				ret);
			drvdata->notify = false;
		}
	}

	dev_info(dev, "TMC initialized\n");
	return 0;
err3:
@@ -2502,6 +2561,8 @@ static int tmc_remove(struct platform_device *pdev)
{
	struct tmc_drvdata *drvdata = platform_get_drvdata(pdev);

	if (drvdata->notify)
		msm_jtag_save_unregister(&drvdata->jtag_save_blk);
	tmc_etr_byte_cntr_exit(drvdata);
	misc_deregister(&drvdata->miscdev);
	coresight_unregister(drvdata->csdev);
+34 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/notifier.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/jtag.h>

@@ -227,6 +229,33 @@ static struct etm_ctx etm;

static struct clk *clock[NR_CPUS];

ATOMIC_NOTIFIER_HEAD(etm_save_notifier_list);
ATOMIC_NOTIFIER_HEAD(etm_restore_notifier_list);

int msm_jtag_save_register(struct notifier_block *nb)
{
	return atomic_notifier_chain_register(&etm_save_notifier_list, nb);
}
EXPORT_SYMBOL(msm_jtag_save_register);

int msm_jtag_save_unregister(struct notifier_block *nb)
{
	return atomic_notifier_chain_unregister(&etm_save_notifier_list, nb);
}
EXPORT_SYMBOL(msm_jtag_save_unregister);

int msm_jtag_restore_register(struct notifier_block *nb)
{
	return atomic_notifier_chain_register(&etm_restore_notifier_list, nb);
}
EXPORT_SYMBOL(msm_jtag_restore_register);

int msm_jtag_restore_unregister(struct notifier_block *nb)
{
	return atomic_notifier_chain_unregister(&etm_restore_notifier_list, nb);
}
EXPORT_SYMBOL(msm_jtag_restore_unregister);

static void etm_os_lock(struct etm_cpu_ctx *etmdata)
{
	if (etm.os_lock_present) {
@@ -340,6 +369,9 @@ static inline void etm_save_state(struct etm_cpu_ctx *etmdata)
			udelay(1);
		if (count == 0)
			pr_err_ratelimited("timeout waiting for idle state\n");

		atomic_notifier_call_chain(&etm_save_notifier_list, 0, NULL);

		break;
	default:
		pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch,
@@ -358,6 +390,8 @@ static inline void etm_restore_state(struct etm_cpu_ctx *etmdata)

	switch (etm.arch) {
	case ETM_ARCH_V4:
		atomic_notifier_call_chain(&etm_restore_notifier_list, 0, NULL);

		/* check OS lock is locked */
		if (BVAL(etm_readl(etmdata, TRCOSLSR), 1) != 1) {
			pr_err_ratelimited("OS lock is unlocked\n");
+23 −0
Original line number Diff line number Diff line
@@ -27,5 +27,28 @@ static inline void msm_jtag_mm_save_state(void) {}
static inline void msm_jtag_mm_restore_state(void){}
static inline bool msm_jtag_fuse_apps_access_disabled(void) { return false; }
#endif
#ifdef CONFIG_MSM_JTAGV8
extern int msm_jtag_save_register(struct notifier_block *nb);
extern int msm_jtag_save_unregister(struct notifier_block *nb);
extern int msm_jtag_restore_register(struct notifier_block *nb);
extern int msm_jtag_restore_unregister(struct notifier_block *nb);
#else
static inline int msm_jtag_save_register(struct notifier_block *nb)
{
	return 0;
}
static inline int msm_jtag_save_unregister(struct notifier_block *nb)
{
	return 0;
}
static inline int msm_jtag_restore_register(struct notifier_block *nb)
{
	return 0;
}
static inline int msm_jtag_restore_unregister(struct notifier_block *nb)
{
	return 0;
}
#endif

#endif