From 98d83a7ebbf26b99b2a40b02bf6eb90fdc3275b3 Mon Sep 17 00:00:00 2001 From: Tingwei Zhang Date: Tue, 29 Oct 2019 11:07:08 +0800 Subject: [PATCH] coresight: Add snapshot of Coresight byte counter driver Add snapshot for coresight byte counter driver from msm-4.19 commit d42a5b113e84 ("coresight: tmc: migrate byte counter to new tmc framework"). Make necessary change to adopt coresight framework change. Change-Id: I85a2f894a0ace3118624b5280b9bbd192ebcfff9 Signed-off-by: Tingwei Zhang --- drivers/hwtracing/coresight/Makefile | 3 +- .../hwtracing/coresight/coresight-byte-cntr.c | 291 ++++++++++++++++++ .../hwtracing/coresight/coresight-byte-cntr.h | 29 ++ 3 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/coresight/coresight-byte-cntr.c create mode 100644 drivers/hwtracing/coresight/coresight-byte-cntr.h diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 00b64f9b2297..964db14b284d 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o coresight-platform.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ - coresight-tmc-etr.o + coresight-tmc-etr.o \ + coresight-byte-cntr.o obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c new file mode 100644 index 000000000000..518362d9a76e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Memory Controller driver + */ +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-byte-cntr.h" +#include "coresight-priv.h" +#include "coresight-tmc.h" + +static struct tmc_drvdata *tmcdrvdata; + +static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos, + size_t bytes, size_t *len, char **bufp) +{ + struct etr_buf *etr_buf = tmcdrvdata->etr_buf; + size_t actual; + + if (*len >= bytes) + *len = bytes; + else if (((uint32_t)*ppos % bytes) + *len > bytes) + *len = bytes - ((uint32_t)*ppos % bytes); + + actual = tmc_etr_buf_get_data(etr_buf, *ppos, *len, bufp); + *len = actual; + if (actual == bytes || (actual + (uint32_t)*ppos) % bytes == 0) + atomic_dec(&byte_cntr_data->irq_cnt); +} + + +static irqreturn_t etr_handler(int irq, void *data) +{ + struct byte_cntr *byte_cntr_data = data; + + atomic_inc(&byte_cntr_data->irq_cnt); + + wake_up(&byte_cntr_data->wq); + + return IRQ_HANDLED; +} + +static void tmc_etr_flush_bytes(loff_t *ppos, size_t bytes, size_t *len) +{ + uint32_t rwp = 0; + dma_addr_t paddr = tmcdrvdata->etr_buf->hwaddr; + + rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP); + + if (rwp >= (paddr + *ppos)) { + if (bytes > (rwp - paddr - *ppos)) + *len = rwp - paddr - *ppos; + } +} + +static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data, + size_t len, loff_t *ppos) +{ + struct byte_cntr *byte_cntr_data = fp->private_data; + char *bufp; + + if (!data) + return -EINVAL; + + mutex_lock(&byte_cntr_data->byte_cntr_lock); + if (!byte_cntr_data->read_active) + goto err0; + + if (byte_cntr_data->enable) { + if (!atomic_read(&byte_cntr_data->irq_cnt)) { + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + if (wait_event_interruptible(byte_cntr_data->wq, + atomic_read(&byte_cntr_data->irq_cnt) > 0)) + return -ERESTARTSYS; + mutex_lock(&byte_cntr_data->byte_cntr_lock); + if (!byte_cntr_data->read_active) + goto err0; + } + + tmc_etr_read_bytes(byte_cntr_data, ppos, + byte_cntr_data->block_size, &len, &bufp); + + } else { + if (!atomic_read(&byte_cntr_data->irq_cnt)) { + tmc_etr_flush_bytes(ppos, byte_cntr_data->block_size, + &len); + if (!len) + goto err0; + } else { + tmc_etr_read_bytes(byte_cntr_data, ppos, + byte_cntr_data->block_size, + &len, &bufp); + } + } + + if (copy_to_user(data, bufp, len)) { + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + dev_dbg(&tmcdrvdata->csdev->dev, + "%s: copy_to_user failed\n", __func__); + return -EFAULT; + } + + if (*ppos + len >= tmcdrvdata->size) + *ppos = 0; + else + *ppos += len; +err0: + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + + return len; +} + +void tmc_etr_byte_cntr_start(struct byte_cntr *byte_cntr_data) +{ + if (!byte_cntr_data) + return; + + mutex_lock(&byte_cntr_data->byte_cntr_lock); + + if (byte_cntr_data->block_size == 0) { + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + return; + } + + atomic_set(&byte_cntr_data->irq_cnt, 0); + byte_cntr_data->enable = true; + mutex_unlock(&byte_cntr_data->byte_cntr_lock); +} +EXPORT_SYMBOL(tmc_etr_byte_cntr_start); + +void tmc_etr_byte_cntr_stop(struct byte_cntr *byte_cntr_data) +{ + if (!byte_cntr_data) + return; + + mutex_lock(&byte_cntr_data->byte_cntr_lock); + byte_cntr_data->enable = false; + coresight_csr_set_byte_cntr(byte_cntr_data->csr, 0); + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + +} +EXPORT_SYMBOL(tmc_etr_byte_cntr_stop); + + +static int tmc_etr_byte_cntr_release(struct inode *in, struct file *fp) +{ + struct byte_cntr *byte_cntr_data = fp->private_data; + + mutex_lock(&byte_cntr_data->byte_cntr_lock); + byte_cntr_data->read_active = false; + + coresight_csr_set_byte_cntr(byte_cntr_data->csr, 0); + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + + return 0; +} + +static int tmc_etr_byte_cntr_open(struct inode *in, struct file *fp) +{ + struct byte_cntr *byte_cntr_data = + container_of(in->i_cdev, struct byte_cntr, dev); + + mutex_lock(&byte_cntr_data->byte_cntr_lock); + + if (!tmcdrvdata->enable || !byte_cntr_data->block_size) { + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + return -EINVAL; + } + + /* 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, + (byte_cntr_data->block_size) / 8); + + fp->private_data = byte_cntr_data; + nonseekable_open(in, fp); + byte_cntr_data->enable = true; + byte_cntr_data->read_active = true; + mutex_unlock(&byte_cntr_data->byte_cntr_lock); + + return 0; +} + +static const struct file_operations byte_cntr_fops = { + .owner = THIS_MODULE, + .open = tmc_etr_byte_cntr_open, + .read = tmc_etr_byte_cntr_read, + .release = tmc_etr_byte_cntr_release, + .llseek = no_llseek, +}; + +static int byte_cntr_register_chardev(struct byte_cntr *byte_cntr_data) +{ + int ret; + unsigned int baseminor = 0; + unsigned int count = 1; + struct device *device; + dev_t dev; + + ret = alloc_chrdev_region(&dev, baseminor, count, "byte-cntr"); + if (ret < 0) { + pr_err("alloc_chrdev_region failed %d\n", ret); + return ret; + } + cdev_init(&byte_cntr_data->dev, &byte_cntr_fops); + + byte_cntr_data->dev.owner = THIS_MODULE; + byte_cntr_data->dev.ops = &byte_cntr_fops; + + ret = cdev_add(&byte_cntr_data->dev, dev, 1); + if (ret) + goto exit_unreg_chrdev_region; + + byte_cntr_data->driver_class = class_create(THIS_MODULE, + "coresight-tmc-etr-stream"); + if (IS_ERR(byte_cntr_data->driver_class)) { + ret = -ENOMEM; + pr_err("class_create failed %d\n", ret); + goto exit_unreg_chrdev_region; + } + + device = device_create(byte_cntr_data->driver_class, NULL, + byte_cntr_data->dev.dev, byte_cntr_data, + "byte-cntr"); + + if (IS_ERR(device)) { + pr_err("class_device_create failed %d\n", ret); + ret = -ENOMEM; + goto exit_destroy_class; + } + + return 0; + +exit_destroy_class: + class_destroy(byte_cntr_data->driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(byte_cntr_data->dev.dev, 1); + return ret; +} + +struct byte_cntr *byte_cntr_init(struct amba_device *adev, + struct tmc_drvdata *drvdata) +{ + struct device *dev = &adev->dev; + struct device_node *np = adev->dev.of_node; + int byte_cntr_irq; + int ret; + struct byte_cntr *byte_cntr_data; + + byte_cntr_irq = of_irq_get_byname(np, "byte-cntr-irq"); + if (byte_cntr_irq < 0) + return NULL; + + byte_cntr_data = devm_kzalloc(dev, sizeof(*byte_cntr_data), GFP_KERNEL); + if (!byte_cntr_data) + return NULL; + + ret = devm_request_irq(dev, byte_cntr_irq, etr_handler, + IRQF_TRIGGER_RISING | IRQF_SHARED, + "tmc-etr", byte_cntr_data); + if (ret) { + devm_kfree(dev, byte_cntr_data); + dev_err(dev, "Byte_cntr interrupt registration failed\n"); + return NULL; + } + + ret = byte_cntr_register_chardev(byte_cntr_data); + if (ret) { + devm_free_irq(dev, byte_cntr_irq, byte_cntr_data); + devm_kfree(dev, byte_cntr_data); + dev_err(dev, "Byte_cntr char dev registration failed\n"); + return NULL; + } + + tmcdrvdata = drvdata; + byte_cntr_data->byte_cntr_irq = byte_cntr_irq; + byte_cntr_data->csr = drvdata->csr; + atomic_set(&byte_cntr_data->irq_cnt, 0); + init_waitqueue_head(&byte_cntr_data->wq); + mutex_init(&byte_cntr_data->byte_cntr_lock); + + return byte_cntr_data; +} +EXPORT_SYMBOL(byte_cntr_init); diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.h b/drivers/hwtracing/coresight/coresight-byte-cntr.h new file mode 100644 index 000000000000..6dd931fd01f6 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved. + */ +#ifndef _CORESIGHT_BYTE_CNTR_H +#define _CORESIGHT_BYTE_CNTR_H +#include +#include +#include +#include + +struct byte_cntr { + struct cdev dev; + struct class *driver_class; + bool enable; + bool read_active; + uint32_t byte_cntr_value; + uint32_t block_size; + int byte_cntr_irq; + atomic_t irq_cnt; + wait_queue_head_t wq; + struct mutex byte_cntr_lock; + struct coresight_csr *csr; +}; + +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); + +#endif