123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /* Copyright (c) 2017-2018, 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/interrupt.h>
- #include <linux/dma-mapping.h>
- #include <linux/fs.h>
- #include <linux/of_irq.h>
- #include <linux/moduleparam.h>
- #include <linux/delay.h>
- #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)
- {
- if (*bufp >= (char *)(tmcdrvdata->vaddr + tmcdrvdata->size))
- *bufp = tmcdrvdata->vaddr;
- if (*len >= bytes)
- *len = bytes;
- else if (((uint32_t)*ppos % bytes) + *len > bytes)
- *len = bytes - ((uint32_t)*ppos % bytes);
- if ((*bufp + *len) > (char *)(tmcdrvdata->vaddr +
- tmcdrvdata->size))
- *len = (char *)(tmcdrvdata->vaddr + tmcdrvdata->size) -
- *bufp;
- if (*len == bytes || (*len + (uint32_t)*ppos) % bytes == 0)
- atomic_dec(&byte_cntr_data->irq_cnt);
- }
- static void tmc_etr_sg_read_pos(loff_t *ppos,
- size_t bytes, bool noirq, size_t *len,
- char **bufpp)
- {
- uint32_t rwp, i = 0;
- uint32_t blk_num, sg_tbl_num, blk_num_loc, read_off;
- uint32_t *virt_pte, *virt_st_tbl;
- void *virt_blk;
- phys_addr_t phys_pte;
- int total_ents = DIV_ROUND_UP(tmcdrvdata->size, PAGE_SIZE);
- int ents_per_pg = PAGE_SIZE/sizeof(uint32_t);
- if (*len == 0)
- return;
- blk_num = *ppos / PAGE_SIZE;
- read_off = *ppos % PAGE_SIZE;
- virt_st_tbl = (uint32_t *)tmcdrvdata->vaddr;
- /* Compute table index and block entry index within that table */
- if (blk_num && (blk_num == (total_ents - 1)) &&
- !(blk_num % (ents_per_pg - 1))) {
- sg_tbl_num = blk_num / ents_per_pg;
- blk_num_loc = ents_per_pg - 1;
- } else {
- sg_tbl_num = blk_num / (ents_per_pg - 1);
- blk_num_loc = blk_num % (ents_per_pg - 1);
- }
- for (i = 0; i < sg_tbl_num; i++) {
- virt_pte = virt_st_tbl + (ents_per_pg - 1);
- phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
- virt_st_tbl = (uint32_t *)phys_to_virt(phys_pte);
- }
- virt_pte = virt_st_tbl + blk_num_loc;
- phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
- virt_blk = phys_to_virt(phys_pte);
- *bufpp = (char *)(virt_blk + read_off);
- if (noirq) {
- rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP);
- tmc_etr_sg_rwp_pos(tmcdrvdata, rwp);
- if (tmcdrvdata->sg_blk_num == blk_num &&
- rwp >= (phys_pte + read_off))
- *len = rwp - phys_pte - read_off;
- else if (tmcdrvdata->sg_blk_num > blk_num)
- *len = PAGE_SIZE - read_off;
- else
- *len = bytes;
- } else {
- if (*len > (PAGE_SIZE - read_off))
- *len = PAGE_SIZE - read_off;
- if (*len >= (bytes - ((uint32_t)*ppos % bytes)))
- *len = bytes - ((uint32_t)*ppos % bytes);
- if (*len == bytes || (*len + (uint32_t)*ppos) % bytes == 0)
- atomic_dec(&tmcdrvdata->byte_cntr->irq_cnt);
- }
- /*
- * Invalidate cache range before reading. This will make sure that CPU
- * reads latest contents from DDR
- */
- dmac_inv_range((void *)(*bufpp), (void *)(*bufpp) + *len);
- }
- 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;
- rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP);
- if (rwp >= (tmcdrvdata->paddr + *ppos)) {
- if (bytes > (rwp - tmcdrvdata->paddr - *ppos))
- *len = rwp - tmcdrvdata->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;
- bufp = (char *)(tmcdrvdata->buf + *ppos);
- 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;
- }
- if (tmcdrvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG)
- tmc_etr_read_bytes(byte_cntr_data, ppos,
- byte_cntr_data->block_size, &len,
- &bufp);
- else
- tmc_etr_sg_read_pos(ppos, byte_cntr_data->block_size, 0,
- &len, &bufp);
- } else {
- if (!atomic_read(&byte_cntr_data->irq_cnt)) {
- if (tmcdrvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG)
- tmc_etr_flush_bytes(ppos,
- byte_cntr_data->block_size,
- &len);
- else
- tmc_etr_sg_read_pos(ppos,
- byte_cntr_data->block_size,
- 1,
- &len, &bufp);
- if (!len)
- goto err0;
- } else {
- if (tmcdrvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG)
- tmc_etr_read_bytes(byte_cntr_data, ppos,
- byte_cntr_data->block_size,
- &len, &bufp);
- else
- tmc_etr_sg_read_pos(ppos,
- byte_cntr_data->block_size,
- 1,
- &len, &bufp);
- }
- }
- if (copy_to_user(data, bufp, len)) {
- mutex_unlock(&byte_cntr_data->byte_cntr_lock);
- dev_dbg(tmcdrvdata->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);
|