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

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

Merge "coresight: add sw usb mode support for tmc"

parents 0aa4af23 e91870c9
Loading
Loading
Loading
Loading
+183 −5
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * Description: CoreSight Trace Memory Controller driver
 */
@@ -10,11 +10,15 @@
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/usb/usb_qdss.h>

#include "coresight-byte-cntr.h"
#include "coresight-priv.h"
#include "coresight-tmc.h"

#define USB_BLK_SIZE 65536
#define USB_BUF_NUM 255

static struct tmc_drvdata *tmcdrvdata;

static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos,
@@ -39,10 +43,14 @@ static irqreturn_t etr_handler(int irq, void *data)
{
	struct byte_cntr *byte_cntr_data = data;

	if (tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_USB
	    && byte_cntr_data->sw_usb) {
		atomic_inc(&byte_cntr_data->irq_cnt);
		wake_up(&byte_cntr_data->usb_wait_wq);
	} else if (tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
		atomic_inc(&byte_cntr_data->irq_cnt);

		wake_up(&byte_cntr_data->wq);

	}
	return IRQ_HANDLED;
}

@@ -160,6 +168,45 @@ static int tmc_etr_byte_cntr_release(struct inode *in, struct file *fp)
	return 0;
}

int usb_bypass_start(struct byte_cntr *byte_cntr_data)
{
	if (!byte_cntr_data)
		return -ENOMEM;

	mutex_lock(&byte_cntr_data->usb_bypass_lock);

	if (!tmcdrvdata->enable) {
		mutex_unlock(&byte_cntr_data->usb_bypass_lock);
		return -EINVAL;
	}

	atomic_set(&byte_cntr_data->usb_free_buf, USB_BUF_NUM);
	byte_cntr_data->offset = tmcdrvdata->etr_buf->offset;
	/*
	 * IRQ is a '8- byte' counter and to observe interrupt at
	 * 'block_size' bytes of data
	 */
	coresight_csr_set_byte_cntr(byte_cntr_data->csr, USB_BLK_SIZE / 8);

	atomic_set(&byte_cntr_data->irq_cnt, 0);
	mutex_unlock(&byte_cntr_data->usb_bypass_lock);

	return 0;
}

void usb_bypass_stop(struct byte_cntr *byte_cntr_data)
{
	if (!byte_cntr_data)
		return;

	mutex_lock(&byte_cntr_data->usb_bypass_lock);
	wake_up(&byte_cntr_data->usb_wait_wq);
	coresight_csr_set_byte_cntr(byte_cntr_data->csr, 0);
	mutex_unlock(&byte_cntr_data->usb_bypass_lock);

}
EXPORT_SYMBOL(usb_bypass_stop);

static int tmc_etr_byte_cntr_open(struct inode *in, struct file *fp)
{
	struct byte_cntr *byte_cntr_data =
@@ -244,6 +291,131 @@ static int byte_cntr_register_chardev(struct byte_cntr *byte_cntr_data)
	return ret;
}

static void usb_read_work_fn(struct work_struct *work)
{
	int ret, seq = 0;
	struct qdss_request *usb_req = NULL;
	struct etr_buf *etr_buf = tmcdrvdata->etr_buf;
	size_t actual, req_size;
	struct byte_cntr *drvdata =
		container_of(work, struct byte_cntr, read_work);

	while (tmcdrvdata->enable
		&& tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
		if (!atomic_read(&drvdata->irq_cnt)) {
			ret = wait_event_interruptible(drvdata->usb_wait_wq,
				atomic_read(&drvdata->irq_cnt) > 0
				|| !tmcdrvdata->enable || tmcdrvdata->out_mode
				!= TMC_ETR_OUT_MODE_USB);
			if (ret == -ERESTARTSYS || !tmcdrvdata->enable
			|| tmcdrvdata->out_mode != TMC_ETR_OUT_MODE_USB)
				break;
		}

		req_size = USB_BLK_SIZE;
		while (req_size > 0) {
			seq++;
			usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL);
			if (!usb_req)
				return;
			actual = tmc_etr_buf_get_data(etr_buf, drvdata->offset,
					req_size, &usb_req->buf);
			if (actual <= 0) {
				kfree(usb_req);
				usb_req = NULL;
				dev_err(tmcdrvdata->dev, "No data in ETR\n");
				break;
			}
			usb_req->length = actual;
			drvdata->usb_req = usb_req;
			req_size -= actual;
			if ((drvdata->offset + usb_req->length)
					>= tmcdrvdata->size)
				drvdata->offset = 0;
			else
				drvdata->offset += usb_req->length;
			if (atomic_read(&drvdata->usb_free_buf) > 0) {
				ret = usb_qdss_write(tmcdrvdata->usbch,
						drvdata->usb_req);
				if (ret) {
					kfree(usb_req);
					usb_req = NULL;
					drvdata->usb_req = NULL;
					dev_err(tmcdrvdata->dev,
						"Write data failed\n");
					continue;
				}
				atomic_dec(&drvdata->usb_free_buf);

			} else {
				dev_dbg(tmcdrvdata->dev,
				"Drop data, offset = %d, seq = %d, irq = %d\n",
					drvdata->offset, seq,
					atomic_read(&drvdata->irq_cnt));
				kfree(usb_req);
				drvdata->usb_req = NULL;
			}
		}
		if (atomic_read(&drvdata->irq_cnt) > 0)
			atomic_dec(&drvdata->irq_cnt);
	}
	dev_err(tmcdrvdata->dev, "TMC has been stopped.\n");
}

static void usb_write_done(struct byte_cntr *drvdata,
				   struct qdss_request *d_req)
{
	atomic_inc(&drvdata->usb_free_buf);
	if (d_req->status)
		pr_err_ratelimited("USB write failed err:%d\n", d_req->status);
	kfree(d_req);
}

void usb_bypass_notifier(void *priv, unsigned int event,
			struct qdss_request *d_req, struct usb_qdss_ch *ch)
{
	struct byte_cntr *drvdata = priv;

	if (!drvdata)
		return;

	switch (event) {
	case USB_QDSS_CONNECT:
		usb_qdss_alloc_req(ch, USB_BUF_NUM, 0);
		usb_bypass_start(drvdata);
		queue_work(drvdata->usb_wq, &(drvdata->read_work));
		break;

	case USB_QDSS_DISCONNECT:
		usb_bypass_stop(drvdata);
		break;

	case USB_QDSS_DATA_WRITE_DONE:
		usb_write_done(drvdata, d_req);
		break;

	default:
		break;
	}
}
EXPORT_SYMBOL(usb_bypass_notifier);


static int usb_bypass_init(struct byte_cntr *byte_cntr_data)
{
	byte_cntr_data->usb_wq = create_singlethread_workqueue("byte-cntr");
	if (!byte_cntr_data->usb_wq)
		return -ENOMEM;

	byte_cntr_data->offset = 0;
	mutex_init(&byte_cntr_data->usb_bypass_lock);
	init_waitqueue_head(&byte_cntr_data->usb_wait_wq);
	atomic_set(&byte_cntr_data->usb_free_buf, USB_BUF_NUM);
	INIT_WORK(&(byte_cntr_data->read_work), usb_read_work_fn);

	return 0;
}

struct byte_cntr *byte_cntr_init(struct amba_device *adev,
				 struct tmc_drvdata *drvdata)
{
@@ -261,6 +433,12 @@ struct byte_cntr *byte_cntr_init(struct amba_device *adev,
	if (!byte_cntr_data)
		return NULL;

	byte_cntr_data->sw_usb = of_property_read_bool(np, "qcom,sw-usb");
	if (byte_cntr_data->sw_usb) {
		ret = usb_bypass_init(byte_cntr_data);
		if (ret)
			return NULL;
	}
	ret = devm_request_irq(dev, byte_cntr_irq, etr_handler,
			       IRQF_TRIGGER_RISING | IRQF_SHARED,
			       "tmc-etr", byte_cntr_data);
+14 −3
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved.
 */
#ifndef _CORESIGHT_BYTE_CNTR_H
#define _CORESIGHT_BYTE_CNTR_H
@@ -14,16 +14,27 @@ struct byte_cntr {
	struct class		*driver_class;
	bool			enable;
	bool			read_active;
	bool			sw_usb;
	uint32_t		byte_cntr_value;
	uint32_t		block_size;
	int			byte_cntr_irq;
	atomic_t		irq_cnt;
	atomic_t		usb_free_buf;
	wait_queue_head_t	wq;
	wait_queue_head_t	usb_wait_wq;
	struct workqueue_struct *usb_wq;
	struct qdss_request	*usb_req;
	struct work_struct	read_work;
	struct mutex		usb_bypass_lock;
	struct mutex		byte_cntr_lock;
	struct coresight_csr	*csr;
	unsigned long		offset;
};


extern void usb_bypass_notifier(void *priv, unsigned int event,
		struct qdss_request *d_req, struct usb_qdss_ch *ch);
extern void tmc_etr_byte_cntr_start(struct byte_cntr *byte_cntr_data);
extern void tmc_etr_byte_cntr_stop(struct byte_cntr *byte_cntr_data);

extern void usb_bypass_stop(struct byte_cntr *byte_cntr_data);
#endif
+67 −10
Original line number Diff line number Diff line
@@ -343,10 +343,11 @@ void tmc_sg_table_sync_table(struct tmc_sg_table *sg_table)
ssize_t tmc_sg_table_get_data(struct tmc_sg_table *sg_table,
			      u64 offset, size_t len, char **bufpp)
{
	size_t size;
	size_t size, tmp_len;
	int pg_idx = offset >> PAGE_SHIFT;
	int pg_offset = offset & (PAGE_SIZE - 1);
	struct tmc_pages *data_pages = &sg_table->data_pages;
	int i;

	size = tmc_sg_table_buf_size(sg_table);
	if (offset >= size)
@@ -355,7 +356,22 @@ ssize_t tmc_sg_table_get_data(struct tmc_sg_table *sg_table,
	/* Make sure we don't go beyond the end */
	len = (len < (size - offset)) ? len : size - offset;
	/* Respect the page boundaries */
	len = (len < (PAGE_SIZE - pg_offset)) ? len : (PAGE_SIZE - pg_offset);
	if (len > (PAGE_SIZE - pg_offset)) {
		tmp_len = PAGE_SIZE - pg_offset;
		for (i = 0; i < (data_pages->nr_pages - pg_idx); i++) {
			if (i < (data_pages->nr_pages - pg_idx - 1)
			&& (page_address(data_pages->pages[pg_idx + i + 1])
			- page_address(data_pages->pages[pg_idx + i]))
					== PAGE_SIZE) {
				if ((len - tmp_len) < PAGE_SIZE)
					break;
				tmp_len += PAGE_SIZE;
			} else {
				len = tmp_len;
				break;
			}
		}
	}
	if (len > 0)
		*bufpp = page_address(data_pages->pages[pg_idx]) + pg_offset;
	return len;
@@ -1342,6 +1358,26 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
			}
			coresight_cti_map_trigout(drvdata->cti_flush, 3, 0);
			coresight_cti_map_trigin(drvdata->cti_reset, 2, 0);
		} else if (drvdata->byte_cntr->sw_usb) {
			if (!drvdata->etr_buf) {
				free_buf = new_buf =
				tmc_etr_setup_sysfs_buf(drvdata);
				if (IS_ERR(new_buf)) {
					mutex_unlock(&drvdata->mem_lock);
					return -ENOMEM;
				}
			}
			coresight_cti_map_trigout(drvdata->cti_flush, 3, 0);
			coresight_cti_map_trigin(drvdata->cti_reset, 2, 0);

			drvdata->usbch = usb_qdss_open("qdss_mdm",
						drvdata->byte_cntr,
						usb_bypass_notifier);
			if (IS_ERR_OR_NULL(drvdata->usbch)) {
				dev_err(drvdata->dev, "usb_qdss_open failed\n");
				mutex_unlock(&drvdata->mem_lock);
				return -ENODEV;
			}
		} else {
			drvdata->usbch = usb_qdss_open("qdss", drvdata,
							usb_notifier);
@@ -1379,7 +1415,9 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)

	drvdata->mode = CS_MODE_SYSFS;

	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM ||
	    (drvdata->out_mode == TMC_ETR_OUT_MODE_USB
	     && drvdata->byte_cntr->sw_usb))
		tmc_etr_enable_hw(drvdata);

	drvdata->enable = true;
@@ -1435,12 +1473,18 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
	/* Disable the TMC only if it needs to */
	if (drvdata->mode != CS_MODE_DISABLED) {
		if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
			if (!drvdata->byte_cntr->sw_usb) {
				__tmc_etr_disable_to_bam(drvdata);
			spin_unlock_irqrestore(&drvdata->spinlock, flags);
				spin_unlock_irqrestore(&drvdata->spinlock,
								flags);
				tmc_etr_bam_disable(drvdata);
				usb_qdss_close(drvdata->usbch);
				drvdata->mode = CS_MODE_DISABLED;
				goto out;
			} else {
				usb_qdss_close(drvdata->usbch);
				tmc_etr_disable_hw(drvdata);
			}
		} else {
			tmc_etr_disable_hw(drvdata);
		}
@@ -1449,6 +1493,19 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)

	spin_unlock_irqrestore(&drvdata->spinlock, flags);

	if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB
		&& drvdata->byte_cntr->sw_usb) {
		usb_bypass_stop(drvdata->byte_cntr);
		flush_workqueue(drvdata->byte_cntr->usb_wq);
		coresight_cti_unmap_trigin(drvdata->cti_reset, 2, 0);
		coresight_cti_unmap_trigout(drvdata->cti_flush, 3, 0);
		/* Free memory outside the spinlock if need be */
		if (drvdata->etr_buf) {
			tmc_etr_free_sysfs_buf(drvdata->etr_buf);
			drvdata->etr_buf = NULL;
		}
	}

	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
		tmc_etr_byte_cntr_stop(drvdata->byte_cntr);
		coresight_cti_unmap_trigin(drvdata->cti_reset, 2, 0);