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

Commit 409ef172 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "iommu: arm-smmu: add capturebus support for smmuv500"

parents c3d355d1 38edc278
Loading
Loading
Loading
Loading
+63 −0
Original line number Original line Diff line number Diff line
@@ -223,3 +223,66 @@ void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr,
		arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base,
		arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base,
						CLK_TESTBUS, READ, 0));
						CLK_TESTBUS, READ, 0));
}
}

void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val)
{
	u64 tcr_cntl_val = readq_relaxed(tbu_base + TNX_TCR_CNTL);

	/* Don't override OT_CAPTURE configuration*/
	if (!(tcr_cntl_val & TNX_TCR_CNTL_TBU_OT_CAPTURE_EN))
		writeq_relaxed(val, tbu_base + TNX_TCR_CNTL);
	else
		pr_err_ratelimited("OT capture enbl, skip TCR CNTL write\n");
}

u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base)
{
	return readq_relaxed(tbu_base + TNX_TCR_CNTL);
}

void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel,
					u64 mask, u64 match)
{
	writeq_relaxed(mask, tbu_base + ARM_SMMU_CAPTURE1_MASK(sel));
	writeq_relaxed(match, tbu_base + ARM_SMMU_CAPTURE1_MATCH(sel));
}

void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, u64 *mask,
					u64 *match)
{
	int i;

	for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
		mask[i] = readq_relaxed(tbu_base +
				ARM_SMMU_CAPTURE1_MASK(i+1));
		match[i] = readq_relaxed(tbu_base +
				ARM_SMMU_CAPTURE1_MATCH(i+1));
	}
}

void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base,
		u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT])
{
	int  i, j;
	u64 valid;

	valid = readl_relaxed(tbu_base + TNX_TCR_CNTL_2);

	for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
		if (valid & BIT(i))
			for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j)
				snapshot[i][j] = readq_relaxed(tbu_base +
					ARM_SMMU_CAPTURE_SNAPSHOT(i, j));
		else
			for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j)
				snapshot[i][j] = 0xdededede;
	}
}

void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base)
{
	u64 val = 0;

	val |= INTR_CLR | RESET_VALID;
	writeq_relaxed(val, tbu_base + TNX_TCR_CNTL);
}
+56 −1
Original line number Original line Diff line number Diff line
@@ -38,6 +38,28 @@
extern int tbu_testbus_sel;
extern int tbu_testbus_sel;
extern int tcu_testbus_sel;
extern int tcu_testbus_sel;


#define TNX_TCR_CNTL			0x130
#define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN	BIT(18)
#define TNX_TCR_CNTL_ALWAYS_CAPTURE	BIT(15)
#define TNX_TCR_CNTL_MATCH_MASK_UPD	BIT(7)
#define TNX_TCR_CNTL_MATCH_MASK_VALID	BIT(6)

#define CAPTURE1_SNAPSHOT_1		0x138

#define TNX_TCR_CNTL_2			0x178
#define TNX_TCR_CNTL_2_CAP1_VALID	BIT(0)

#define ARM_SMMU_CAPTURE1_MASK(i)	(0x100 + (0x8 * (i-1)))
#define ARM_SMMU_CAPTURE1_MATCH(i)	(0x118 + (0x8 * (i-1)))
#define ARM_SMMU_CAPTURE_SNAPSHOT(i, j)	(CAPTURE1_SNAPSHOT_1 + \
					 (0x10 * (i)) + ((j) * 0x8))

#define NO_OF_MASK_AND_MATCH		0x3
#define NO_OF_CAPTURE_POINTS		0x4
#define REGS_PER_CAPTURE_POINT		0x2
#define INTR_CLR			BIT(0)
#define RESET_VALID			BIT(7)

enum tcu_testbus {
enum tcu_testbus {
	PTW_AND_CACHE_TESTBUS,
	PTW_AND_CACHE_TESTBUS,
	CLK_TESTBUS,
	CLK_TESTBUS,
@@ -66,7 +88,15 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base,
			int tbu_testbus_sel);
			int tbu_testbus_sel);
void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr,
void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr,
			void __iomem *tcu_base, int tcu_testbus_sel);
			void __iomem *tcu_base, int tcu_testbus_sel);

void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val);
u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base);
void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel,
					u64 mask, u64 match);
void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base,
					u64 *mask, u64 *match);
void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base,
		u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]);
void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base);
#else
#else
static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
				bool write, u32 val)
				bool write, u32 val)
@@ -95,5 +125,30 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev,
			int tcu_testbus_sel)
			int tcu_testbus_sel)
{
{
}
}
static inline void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base,
						   u64 val)
{
}
static inline u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base)
{
	return 0;
}

static inline void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64
						     sel, u64 mask, u64 match)
{
}
static inline void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base,
					u64 *mask, u64 *match)
{
}
static inline void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base,
		u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT])
{
}
static inline void arm_smmu_debug_clear_intr_and_validbits(void __iomem
							   *tbu_base)
{
}
#endif
#endif
+356 −10
Original line number Original line Diff line number Diff line
@@ -4,6 +4,7 @@
 */
 */


#include <linux/bitfield.h>
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/of.h>
@@ -378,16 +379,6 @@ static const struct arm_smmu_impl qcom_smmu_impl = {


#define TBU_DBG_TIMEOUT_US		100
#define TBU_DBG_TIMEOUT_US		100


#define TNX_TCR_CNTL			0x130
#define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN	BIT(18)
#define TNX_TCR_CNTL_ALWAYS_CAPTURE	BIT(15)
#define TNX_TCR_CNTL_MATCH_MASK_UPD	BIT(7)
#define TNX_TCR_CNTL_MATCH_MASK_VALID	BIT(6)

#define CAPTURE1_SNAPSHOT_1		0x138

#define TNX_TCR_CNTL_2			0x178
#define TNX_TCR_CNTL_2_CAP1_VALID	BIT(0)


struct actlr_setting {
struct actlr_setting {
	struct arm_smmu_smr smr;
	struct arm_smmu_smr smr;
@@ -426,6 +417,10 @@ static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
static DEFINE_MUTEX(capture_reg_lock);
static DEFINE_MUTEX(capture_reg_lock);
static DEFINE_SPINLOCK(testbus_lock);
static DEFINE_SPINLOCK(testbus_lock);


#ifdef CONFIG_IOMMU_DEBUGFS
static struct dentry *debugfs_capturebus_dir;
#endif

#ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS
#ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS
static struct dentry *debugfs_testbus_dir;
static struct dentry *debugfs_testbus_dir;


@@ -829,6 +824,325 @@ static void qsmmuv500_log_outstanding_transactions(struct work_struct *work)
	BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
	BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
}
}


static ssize_t arm_smmu_debug_capturebus_snapshot_read(struct file *file,
		char __user *ubuf, size_t count, loff_t *offset)
{
	struct qsmmuv500_tbu_device *tbu = file->private_data;
	struct arm_smmu_device *smmu = tbu->smmu;
	void __iomem *tbu_base = tbu->base;
	u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT];
	char buf[400];
	ssize_t retval;
	size_t buflen;
	int buf_len = sizeof(buf);
	int i, j;

	if (*offset)
		return 0;

	memset(buf, 0, buf_len);

	if (arm_smmu_power_on(smmu->pwr))
		return -EINVAL;

	if (arm_smmu_power_on(tbu->pwr)) {
		arm_smmu_power_off(smmu, smmu->pwr);
		return -EINVAL;
	}

	if (!mutex_trylock(&capture_reg_lock)) {
		dev_warn_ratelimited(smmu->dev,
			"capture bus regs in use, not dumping it.\n");
		return -EBUSY;
	}

	arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot);

	mutex_unlock(&capture_reg_lock);
	arm_smmu_power_off(tbu->smmu, tbu->pwr);
	arm_smmu_power_off(smmu, smmu->pwr);

	for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
		for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) {
			scnprintf(buf + strlen(buf), buf_len - strlen(buf),
				 "Capture_%d_Snapshot_%d : 0x%0llx\n",
				  i+1, j+1, snapshot[i][j]);
		}
	}

	buflen = min(count, strlen(buf));
	if (copy_to_user(ubuf, buf, buflen)) {
		dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
		retval = -EFAULT;
	} else {
		*offset = 1;
		retval = buflen;
	}

	return retval;
}

static const struct file_operations arm_smmu_debug_capturebus_snapshot_fops = {
	.open	= simple_open,
	.read	= arm_smmu_debug_capturebus_snapshot_read,
};

static ssize_t arm_smmu_debug_capturebus_config_write(struct file *file,
		const char __user *ubuf, size_t count, loff_t *offset)
{
	struct qsmmuv500_tbu_device *tbu = file->private_data;
	struct arm_smmu_device *smmu = tbu->smmu;
	void __iomem *tbu_base = tbu->base;
	char *comma1, *comma2;
	char buf[100];
	u64 sel, mask, match, val;

	if (count >= sizeof(buf)) {
		dev_err_ratelimited(smmu->dev, "Input too large\n");
		goto invalid_format;
	}

	memset(buf, 0, sizeof(buf));

	if (copy_from_user(buf, ubuf, count)) {
		dev_err_ratelimited(smmu->dev, "Couldn't copy from user\n");
		return -EFAULT;
	}

	comma1 = strnchr(buf, count, ',');
	if (!comma1)
		goto invalid_format;

	*comma1  = '\0';

	if (kstrtou64(buf, 0, &sel))
		goto invalid_format;

	if (sel > 4) {
		goto invalid_format;
	} else if (sel == 4) {
		if (kstrtou64(comma1 + 1, 0, &val))
			goto invalid_format;
		goto program_capturebus;
	}

	comma2 = strnchr(comma1 + 1, count, ',');
	if (!comma2)
		goto invalid_format;

	/* split up the words */
	*comma2 = '\0';

	if (kstrtou64(comma1 + 1, 0, &mask))
		goto invalid_format;

	if (kstrtou64(comma2 + 1, 0, &match))
		goto invalid_format;

program_capturebus:
	if (arm_smmu_power_on(smmu->pwr))
		return -EINVAL;

	if (arm_smmu_power_on(tbu->pwr)) {
		arm_smmu_power_off(smmu, smmu->pwr);
		return -EINVAL;
	}

	if (!mutex_trylock(&capture_reg_lock)) {
		dev_warn_ratelimited(smmu->dev,
			"capture bus regs in use, not configuring it.\n");
		return -EBUSY;
	}

	if (sel == 4)
		arm_smmu_debug_set_tnx_tcr_cntl(tbu_base, val);
	else
		arm_smmu_debug_set_mask_and_match(tbu_base, sel, mask, match);

	mutex_unlock(&capture_reg_lock);
	arm_smmu_power_off(tbu->smmu, tbu->pwr);
	arm_smmu_power_off(smmu, smmu->pwr);

	return count;

invalid_format:
	dev_err_ratelimited(smmu->dev, "Invalid format\n");
	dev_err_ratelimited(smmu->dev,
			    "Expected:<1/2/3,Mask,Match> <4,TNX_TCR_CNTL>\n");
	return -EINVAL;
}

static ssize_t arm_smmu_debug_capturebus_config_read(struct file *file,
		char __user *ubuf, size_t count, loff_t *offset)
{
	struct qsmmuv500_tbu_device *tbu = file->private_data;
	struct arm_smmu_device *smmu = tbu->smmu;
	void __iomem *tbu_base = tbu->base;
	u64 val;
	u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH];
	char buf[400];
	ssize_t retval;
	size_t buflen;
	int buf_len = sizeof(buf);
	int i;

	if (*offset)
		return 0;

	memset(buf, 0, buf_len);

	if (arm_smmu_power_on(smmu->pwr))
		return -EINVAL;

	if (arm_smmu_power_on(tbu->pwr)) {
		arm_smmu_power_off(smmu, smmu->pwr);
		return -EINVAL;
	}

	if (!mutex_trylock(&capture_reg_lock)) {
		dev_warn_ratelimited(smmu->dev,
			"capture bus regs in use, not configuring it.\n");
		return -EBUSY;
	}

	arm_smmu_debug_get_mask_and_match(tbu_base,
					mask, match);
	val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base);

	mutex_unlock(&capture_reg_lock);
	arm_smmu_power_off(tbu->smmu, tbu->pwr);
	arm_smmu_power_off(smmu, smmu->pwr);

	for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
		scnprintf(buf + strlen(buf), buf_len - strlen(buf),
				"Mask_%d : 0x%0llx\t", i+1, mask[i]);
		scnprintf(buf + strlen(buf), buf_len - strlen(buf),
				"Match_%d : 0x%0llx\n", i+1, match[i]);
	}
	scnprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0lx\n", val);

	buflen = min(count, strlen(buf));
	if (copy_to_user(ubuf, buf, buflen)) {
		dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
		retval = -EFAULT;
	} else {
		*offset = 1;
		retval = buflen;
	}

	return retval;
}

static const struct file_operations arm_smmu_debug_capturebus_config_fops = {
	.open	= simple_open,
	.write	= arm_smmu_debug_capturebus_config_write,
	.read	= arm_smmu_debug_capturebus_config_read,
};

#ifdef CONFIG_IOMMU_DEBUGFS
static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu)
{
	struct dentry *capturebus_dir;

	if (!iommu_debugfs_dir)
		return 0;

	if (!debugfs_capturebus_dir) {
		debugfs_capturebus_dir = debugfs_create_dir(
					 "capturebus", iommu_debugfs_dir);
		if (IS_ERR(debugfs_capturebus_dir)) {
			dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus debugfs directory\n");
			return PTR_ERR(debugfs_capturebus_dir);
		}
	}

	capturebus_dir = debugfs_create_dir(dev_name(tbu->dev),
				debugfs_capturebus_dir);
	if (IS_ERR(capturebus_dir)) {
		dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s debugfs directory\n",
				dev_name(tbu->dev));
		goto err;
	}

	if (IS_ERR(debugfs_create_file("config", 0400, capturebus_dir, tbu,
			&arm_smmu_debug_capturebus_config_fops))) {
		dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/config debugfs file\n",
				dev_name(tbu->dev));
		goto err_rmdir;
	}

	if (IS_ERR(debugfs_create_file("snapshot", 0400, capturebus_dir, tbu,
			&arm_smmu_debug_capturebus_snapshot_fops))) {
		dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/snapshot debugfs file\n",
				dev_name(tbu->dev));
		goto err_rmdir;
	}
	return 0;
err_rmdir:
	debugfs_remove_recursive(capturebus_dir);
err:
	return -ENODEV;
}
#else
static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu)
{
	return 0;
}
#endif

static irqreturn_t arm_smmu_debug_capture_bus_match(int irq, void *dev)
{
	struct qsmmuv500_tbu_device *tbu = dev;
	struct arm_smmu_device *smmu = tbu->smmu;
	void __iomem *tbu_base = tbu->base;
	u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH];
	u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT];
	int i, j;
	u64 val;

	if (arm_smmu_power_on(smmu->pwr))
		return IRQ_NONE;

	if (arm_smmu_power_on(tbu->pwr)) {
		arm_smmu_power_off(smmu, smmu->pwr);
		return IRQ_NONE;
	}

	if (!mutex_trylock(&capture_reg_lock)) {
		dev_warn_ratelimited(smmu->dev,
			"capture bus regs in use, not dumping it.\n");
		return IRQ_NONE;
	}

	val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base);
	arm_smmu_debug_get_mask_and_match(tbu_base, mask, match);
	arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot);
	arm_smmu_debug_clear_intr_and_validbits(tbu_base);

	mutex_unlock(&capture_reg_lock);
	arm_smmu_power_off(tbu->smmu, tbu->pwr);
	arm_smmu_power_off(smmu, smmu->pwr);

	dev_info(tbu->dev, "TNX_TCR_CNTL : 0x%0llx\n", val);

	for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
		dev_info(tbu->dev,
				"Mask_%d : 0x%0llx\n", i+1, mask[i]);
		dev_info(tbu->dev,
				"Match_%d : 0x%0llx\n", i+1, match[i]);
	}

	for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
		for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) {
			dev_info(tbu->dev,
					"Capture_%d_Snapshot_%d : 0x%0llx\n",
					i+1, j+1, snapshot[i][j]);
		}
	}

	return IRQ_HANDLED;
}

static void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu)
static void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu)
{
{
	u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress;
	u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress;
@@ -1385,8 +1699,11 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain,


static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
{
{
	struct resource *res;
	struct qsmmuv500_tbu_device *tbu;
	struct qsmmuv500_tbu_device *tbu;
	struct qsmmuv500_archdata *data = cookie;
	struct qsmmuv500_archdata *data = cookie;
	struct platform_device *pdev = to_platform_device(dev);
	int i, err, num_irqs = 0;


	if (!dev->driver) {
	if (!dev->driver) {
		dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n");
		dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n");
@@ -1399,7 +1716,36 @@ static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
	tbu->smmu = &data->smmu;
	tbu->smmu = &data->smmu;
	list_add(&tbu->list, &data->tbus);
	list_add(&tbu->list, &data->tbus);


	while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs)))
		num_irqs++;

	tbu->irqs = devm_kzalloc(dev, sizeof(*tbu->irqs) * num_irqs,
				  GFP_KERNEL);
	if (!tbu->irqs)
		return -ENOMEM;

	for (i = 0; i < num_irqs; ++i) {
		int irq = platform_get_irq(pdev, i);

		if (irq < 0) {
			dev_err(dev, "failed to get irq index %d\n", i);
			return -ENODEV;
		}
		tbu->irqs[i] = irq;

		err = devm_request_threaded_irq(tbu->dev, tbu->irqs[i],
					NULL, arm_smmu_debug_capture_bus_match,
					IRQF_ONESHOT | IRQF_SHARED,
					"capture bus", tbu);
		if (err) {
			dev_err(dev, "failed to request capture bus irq%d (%u)\n",
				i, tbu->irqs[i]);
			return err;
		}
	}

	qsmmuv500_tbu_testbus_init(tbu);
	qsmmuv500_tbu_testbus_init(tbu);
	qsmmuv500_capturebus_init(tbu);
	return 0;
	return 0;
}
}


+1 −0
Original line number Original line Diff line number Diff line
@@ -418,6 +418,7 @@ struct qsmmuv500_tbu_device {
	/* Protects halt count */
	/* Protects halt count */
	spinlock_t			halt_lock;
	spinlock_t			halt_lock;
	u32				halt_count;
	u32				halt_count;
	unsigned int			*irqs;
};
};


struct arm_smmu_master_cfg {
struct arm_smmu_master_cfg {