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

Commit 4be4575b authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "jtagv8: prevent trace flush hang during power collapse"

parents f5f5fe1e 1d735525
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