coresight: moving to new "hwtracing" directory

Keeping drivers related to HW tracing on ARM, i.e coresight,
under "drivers/coresight" doesn't make sense when other
architectures start rolling out technologies of the same
nature.

As such creating a new "drivers/hwtracing" directory where all
drivers of the same kind can reside, reducing namespace
pollution under "drivers/".

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Mathieu Poirier
2015-03-30 14:13:41 -06:00
committed by Greg Kroah-Hartman
parent a2d6e18493
commit 01081f5ab9
17 changed files with 4 additions and 4 deletions

View File

@@ -0,0 +1,61 @@
#
# Coresight configuration
#
menuconfig CORESIGHT
bool "CoreSight Tracing Support"
select ARM_AMBA
help
This framework provides a kernel interface for the CoreSight debug
and trace drivers to register themselves with. It's intended to build
a topological view of the CoreSight components based on a DT
specification and configure the right serie of components when a
trace source gets enabled.
if CORESIGHT
config CORESIGHT_LINKS_AND_SINKS
bool "CoreSight Link and Sink drivers"
help
This enables support for CoreSight link and sink drivers that are
responsible for transporting and collecting the trace data
respectively. Link and sinks are dynamically aggregated with a trace
entity at run time to form a complete trace path.
config CORESIGHT_LINK_AND_SINK_TMC
bool "Coresight generic TMC driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Memory Controller driver.
Depending on its configuration the device can act as a link (embedded
trace router - ETR) or sink (embedded trace FIFO). The driver
complies with the generic implementation of the component without
special enhancement or added features.
config CORESIGHT_SINK_TPIU
bool "Coresight generic TPIU driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Port Interface Unit driver,
responsible for bridging the gap between the on-chip coresight
components and a trace for bridging the gap between the on-chip
coresight components and a trace port collection engine, typically
connected to an external host for use case capturing more traces than
the on-board coresight memory can handle.
config CORESIGHT_SINK_ETBV10
bool "Coresight ETBv1.0 driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Embedded Trace Buffer version 1.0 driver
that complies with the generic implementation of the component without
special enhancement or added features.
config CORESIGHT_SOURCE_ETM3X
bool "CoreSight Embedded Trace Macrocell 3.x driver"
depends on !ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for processor ETM3.x and PTM1.x modules,
which allows tracing the instructions that a processor is executing
This is primarily useful for instruction level tracing. Depending
the ETM version data tracing may also be available.
endif

View File

@@ -0,0 +1,11 @@
#
# Makefile for CoreSight drivers.
#
obj-$(CONFIG_CORESIGHT) += coresight.o
obj-$(CONFIG_OF) += of_coresight.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.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 \
coresight-replicator.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o

View File

@@ -0,0 +1,527 @@
/* Copyright (c) 2011-2012, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/seq_file.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
#include "coresight-priv.h"
#define ETB_RAM_DEPTH_REG 0x004
#define ETB_STATUS_REG 0x00c
#define ETB_RAM_READ_DATA_REG 0x010
#define ETB_RAM_READ_POINTER 0x014
#define ETB_RAM_WRITE_POINTER 0x018
#define ETB_TRG 0x01c
#define ETB_CTL_REG 0x020
#define ETB_RWD_REG 0x024
#define ETB_FFSR 0x300
#define ETB_FFCR 0x304
#define ETB_ITMISCOP0 0xee0
#define ETB_ITTRFLINACK 0xee4
#define ETB_ITTRFLIN 0xee8
#define ETB_ITATBDATA0 0xeeC
#define ETB_ITATBCTR2 0xef0
#define ETB_ITATBCTR1 0xef4
#define ETB_ITATBCTR0 0xef8
/* register description */
/* STS - 0x00C */
#define ETB_STATUS_RAM_FULL BIT(0)
/* CTL - 0x020 */
#define ETB_CTL_CAPT_EN BIT(0)
/* FFCR - 0x304 */
#define ETB_FFCR_EN_FTC BIT(0)
#define ETB_FFCR_FON_MAN BIT(6)
#define ETB_FFCR_STOP_FI BIT(12)
#define ETB_FFCR_STOP_TRIGGER BIT(13)
#define ETB_FFCR_BIT 6
#define ETB_FFSR_BIT 1
#define ETB_FRAME_SIZE_WORDS 4
/**
* struct etb_drvdata - specifics associated to an ETB component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @miscdev: specifics to handle "/dev/xyz.etb" entry.
* @clk: the clock this component is associated to.
* @spinlock: only one at a time pls.
* @in_use: synchronise user space access to etb buffer.
* @buf: area of memory where ETB buffer content gets sent.
* @buffer_depth: size of @buf.
* @enable: this ETB is being used.
* @trigger_cntr: amount of words to store after a trigger.
*/
struct etb_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
struct clk *clk;
spinlock_t spinlock;
atomic_t in_use;
u8 *buf;
u32 buffer_depth;
bool enable;
u32 trigger_cntr;
};
static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata)
{
int ret;
u32 depth = 0;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
/* RO registers don't need locking */
depth = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG);
clk_disable_unprepare(drvdata->clk);
return depth;
}
static void etb_enable_hw(struct etb_drvdata *drvdata)
{
int i;
u32 depth;
CS_UNLOCK(drvdata->base);
depth = drvdata->buffer_depth;
/* reset write RAM pointer address */
writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
/* clear entire RAM buffer */
for (i = 0; i < depth; i++)
writel_relaxed(0x0, drvdata->base + ETB_RWD_REG);
/* reset write RAM pointer address */
writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
/* reset read RAM pointer address */
writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + ETB_TRG);
writel_relaxed(ETB_FFCR_EN_FTC | ETB_FFCR_STOP_TRIGGER,
drvdata->base + ETB_FFCR);
/* ETB trace capture enable */
writel_relaxed(ETB_CTL_CAPT_EN, drvdata->base + ETB_CTL_REG);
CS_LOCK(drvdata->base);
}
static int etb_enable(struct coresight_device *csdev)
{
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
unsigned long flags;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
spin_lock_irqsave(&drvdata->spinlock, flags);
etb_enable_hw(drvdata);
drvdata->enable = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "ETB enabled\n");
return 0;
}
static void etb_disable_hw(struct etb_drvdata *drvdata)
{
u32 ffcr;
CS_UNLOCK(drvdata->base);
ffcr = readl_relaxed(drvdata->base + ETB_FFCR);
/* stop formatter when a stop has completed */
ffcr |= ETB_FFCR_STOP_FI;
writel_relaxed(ffcr, drvdata->base + ETB_FFCR);
/* manually generate a flush of the system */
ffcr |= ETB_FFCR_FON_MAN;
writel_relaxed(ffcr, drvdata->base + ETB_FFCR);
if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n",
ETB_FFCR);
}
/* disable trace capture */
writel_relaxed(0x0, drvdata->base + ETB_CTL_REG);
if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n",
ETB_FFCR);
}
CS_LOCK(drvdata->base);
}
static void etb_dump_hw(struct etb_drvdata *drvdata)
{
int i;
u8 *buf_ptr;
u32 read_data, depth;
u32 read_ptr, write_ptr;
u32 frame_off, frame_endoff;
CS_UNLOCK(drvdata->base);
read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
frame_off = write_ptr % ETB_FRAME_SIZE_WORDS;
frame_endoff = ETB_FRAME_SIZE_WORDS - frame_off;
if (frame_off) {
dev_err(drvdata->dev,
"write_ptr: %lu not aligned to formatter frame size\n",
(unsigned long)write_ptr);
dev_err(drvdata->dev, "frameoff: %lu, frame_endoff: %lu\n",
(unsigned long)frame_off, (unsigned long)frame_endoff);
write_ptr += frame_endoff;
}
if ((readl_relaxed(drvdata->base + ETB_STATUS_REG)
& ETB_STATUS_RAM_FULL) == 0)
writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
else
writel_relaxed(write_ptr, drvdata->base + ETB_RAM_READ_POINTER);
depth = drvdata->buffer_depth;
buf_ptr = drvdata->buf;
for (i = 0; i < depth; i++) {
read_data = readl_relaxed(drvdata->base +
ETB_RAM_READ_DATA_REG);
*buf_ptr++ = read_data >> 0;
*buf_ptr++ = read_data >> 8;
*buf_ptr++ = read_data >> 16;
*buf_ptr++ = read_data >> 24;
}
if (frame_off) {
buf_ptr -= (frame_endoff * 4);
for (i = 0; i < frame_endoff; i++) {
*buf_ptr++ = 0x0;
*buf_ptr++ = 0x0;
*buf_ptr++ = 0x0;
*buf_ptr++ = 0x0;
}
}
writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER);
CS_LOCK(drvdata->base);
}
static void etb_disable(struct coresight_device *csdev)
{
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
etb_disable_hw(drvdata);
etb_dump_hw(drvdata);
drvdata->enable = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "ETB disabled\n");
}
static const struct coresight_ops_sink etb_sink_ops = {
.enable = etb_enable,
.disable = etb_disable,
};
static const struct coresight_ops etb_cs_ops = {
.sink_ops = &etb_sink_ops,
};
static void etb_dump(struct etb_drvdata *drvdata)
{
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->enable) {
etb_disable_hw(drvdata);
etb_dump_hw(drvdata);
etb_enable_hw(drvdata);
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "ETB dumped\n");
}
static int etb_open(struct inode *inode, struct file *file)
{
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
if (atomic_cmpxchg(&drvdata->in_use, 0, 1))
return -EBUSY;
dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
return 0;
}
static ssize_t etb_read(struct file *file, char __user *data,
size_t len, loff_t *ppos)
{
u32 depth;
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
etb_dump(drvdata);
depth = drvdata->buffer_depth;
if (*ppos + len > depth * 4)
len = depth * 4 - *ppos;
if (copy_to_user(data, drvdata->buf + *ppos, len)) {
dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__);
return -EFAULT;
}
*ppos += len;
dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
__func__, len, (int)(depth * 4 - *ppos));
return len;
}
static int etb_release(struct inode *inode, struct file *file)
{
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
atomic_set(&drvdata->in_use, 0);
dev_dbg(drvdata->dev, "%s: released\n", __func__);
return 0;
}
static const struct file_operations etb_fops = {
.owner = THIS_MODULE,
.open = etb_open,
.read = etb_read,
.release = etb_release,
.llseek = no_llseek,
};
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
unsigned long flags;
u32 etb_rdr, etb_sr, etb_rrp, etb_rwp;
u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr;
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = clk_prepare_enable(drvdata->clk);
if (ret)
goto out;
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
etb_rdr = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG);
etb_sr = readl_relaxed(drvdata->base + ETB_STATUS_REG);
etb_rrp = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
etb_rwp = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
etb_trg = readl_relaxed(drvdata->base + ETB_TRG);
etb_cr = readl_relaxed(drvdata->base + ETB_CTL_REG);
etb_ffsr = readl_relaxed(drvdata->base + ETB_FFSR);
etb_ffcr = readl_relaxed(drvdata->base + ETB_FFCR);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
return sprintf(buf,
"Depth:\t\t0x%x\n"
"Status:\t\t0x%x\n"
"RAM read ptr:\t0x%x\n"
"RAM wrt ptr:\t0x%x\n"
"Trigger cnt:\t0x%x\n"
"Control:\t0x%x\n"
"Flush status:\t0x%x\n"
"Flush ctrl:\t0x%x\n",
etb_rdr, etb_sr, etb_rrp, etb_rwp,
etb_trg, etb_cr, etb_ffsr, etb_ffcr);
out:
return -EINVAL;
}
static DEVICE_ATTR_RO(status);
static ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->trigger_cntr;
return sprintf(buf, "%#lx\n", val);
}
static ssize_t trigger_cntr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
drvdata->trigger_cntr = val;
return size;
}
static DEVICE_ATTR_RW(trigger_cntr);
static struct attribute *coresight_etb_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etb);
static int etb_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
void __iomem *base;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct etb_drvdata *drvdata;
struct resource *res = &adev->res;
struct coresight_desc *desc;
struct device_node *np = adev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
/* validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
drvdata->clk = adev->pclk;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
clk_disable_unprepare(drvdata->clk);
if (drvdata->buffer_depth < 0)
return -EINVAL;
drvdata->buf = devm_kzalloc(dev,
drvdata->buffer_depth * 4, GFP_KERNEL);
if (!drvdata->buf)
return -ENOMEM;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_SINK;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc->ops = &etb_cs_ops;
desc->pdata = pdata;
desc->dev = dev;
desc->groups = coresight_etb_groups;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
drvdata->miscdev.name = pdata->name;
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
drvdata->miscdev.fops = &etb_fops;
ret = misc_register(&drvdata->miscdev);
if (ret)
goto err_misc_register;
dev_info(dev, "ETB initialized\n");
return 0;
err_misc_register:
coresight_unregister(drvdata->csdev);
return ret;
}
static int etb_remove(struct amba_device *adev)
{
struct etb_drvdata *drvdata = amba_get_drvdata(adev);
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct amba_id etb_ids[] = {
{
.id = 0x0003b907,
.mask = 0x0003ffff,
},
{ 0, 0},
};
static struct amba_driver etb_driver = {
.drv = {
.name = "coresight-etb10",
.owner = THIS_MODULE,
},
.probe = etb_probe,
.remove = etb_remove,
.id_table = etb_ids,
};
module_amba_driver(etb_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver");

View File

@@ -0,0 +1,591 @@
/* Copyright (c) 2012, 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bug.h>
#include <asm/hardware/cp14.h>
#include "coresight-etm.h"
int etm_readl_cp14(u32 reg, unsigned int *val)
{
switch (reg) {
case ETMCR:
*val = etm_read(ETMCR);
return 0;
case ETMCCR:
*val = etm_read(ETMCCR);
return 0;
case ETMTRIGGER:
*val = etm_read(ETMTRIGGER);
return 0;
case ETMSR:
*val = etm_read(ETMSR);
return 0;
case ETMSCR:
*val = etm_read(ETMSCR);
return 0;
case ETMTSSCR:
*val = etm_read(ETMTSSCR);
return 0;
case ETMTEEVR:
*val = etm_read(ETMTEEVR);
return 0;
case ETMTECR1:
*val = etm_read(ETMTECR1);
return 0;
case ETMFFLR:
*val = etm_read(ETMFFLR);
return 0;
case ETMACVRn(0):
*val = etm_read(ETMACVR0);
return 0;
case ETMACVRn(1):
*val = etm_read(ETMACVR1);
return 0;
case ETMACVRn(2):
*val = etm_read(ETMACVR2);
return 0;
case ETMACVRn(3):
*val = etm_read(ETMACVR3);
return 0;
case ETMACVRn(4):
*val = etm_read(ETMACVR4);
return 0;
case ETMACVRn(5):
*val = etm_read(ETMACVR5);
return 0;
case ETMACVRn(6):
*val = etm_read(ETMACVR6);
return 0;
case ETMACVRn(7):
*val = etm_read(ETMACVR7);
return 0;
case ETMACVRn(8):
*val = etm_read(ETMACVR8);
return 0;
case ETMACVRn(9):
*val = etm_read(ETMACVR9);
return 0;
case ETMACVRn(10):
*val = etm_read(ETMACVR10);
return 0;
case ETMACVRn(11):
*val = etm_read(ETMACVR11);
return 0;
case ETMACVRn(12):
*val = etm_read(ETMACVR12);
return 0;
case ETMACVRn(13):
*val = etm_read(ETMACVR13);
return 0;
case ETMACVRn(14):
*val = etm_read(ETMACVR14);
return 0;
case ETMACVRn(15):
*val = etm_read(ETMACVR15);
return 0;
case ETMACTRn(0):
*val = etm_read(ETMACTR0);
return 0;
case ETMACTRn(1):
*val = etm_read(ETMACTR1);
return 0;
case ETMACTRn(2):
*val = etm_read(ETMACTR2);
return 0;
case ETMACTRn(3):
*val = etm_read(ETMACTR3);
return 0;
case ETMACTRn(4):
*val = etm_read(ETMACTR4);
return 0;
case ETMACTRn(5):
*val = etm_read(ETMACTR5);
return 0;
case ETMACTRn(6):
*val = etm_read(ETMACTR6);
return 0;
case ETMACTRn(7):
*val = etm_read(ETMACTR7);
return 0;
case ETMACTRn(8):
*val = etm_read(ETMACTR8);
return 0;
case ETMACTRn(9):
*val = etm_read(ETMACTR9);
return 0;
case ETMACTRn(10):
*val = etm_read(ETMACTR10);
return 0;
case ETMACTRn(11):
*val = etm_read(ETMACTR11);
return 0;
case ETMACTRn(12):
*val = etm_read(ETMACTR12);
return 0;
case ETMACTRn(13):
*val = etm_read(ETMACTR13);
return 0;
case ETMACTRn(14):
*val = etm_read(ETMACTR14);
return 0;
case ETMACTRn(15):
*val = etm_read(ETMACTR15);
return 0;
case ETMCNTRLDVRn(0):
*val = etm_read(ETMCNTRLDVR0);
return 0;
case ETMCNTRLDVRn(1):
*val = etm_read(ETMCNTRLDVR1);
return 0;
case ETMCNTRLDVRn(2):
*val = etm_read(ETMCNTRLDVR2);
return 0;
case ETMCNTRLDVRn(3):
*val = etm_read(ETMCNTRLDVR3);
return 0;
case ETMCNTENRn(0):
*val = etm_read(ETMCNTENR0);
return 0;
case ETMCNTENRn(1):
*val = etm_read(ETMCNTENR1);
return 0;
case ETMCNTENRn(2):
*val = etm_read(ETMCNTENR2);
return 0;
case ETMCNTENRn(3):
*val = etm_read(ETMCNTENR3);
return 0;
case ETMCNTRLDEVRn(0):
*val = etm_read(ETMCNTRLDEVR0);
return 0;
case ETMCNTRLDEVRn(1):
*val = etm_read(ETMCNTRLDEVR1);
return 0;
case ETMCNTRLDEVRn(2):
*val = etm_read(ETMCNTRLDEVR2);
return 0;
case ETMCNTRLDEVRn(3):
*val = etm_read(ETMCNTRLDEVR3);
return 0;
case ETMCNTVRn(0):
*val = etm_read(ETMCNTVR0);
return 0;
case ETMCNTVRn(1):
*val = etm_read(ETMCNTVR1);
return 0;
case ETMCNTVRn(2):
*val = etm_read(ETMCNTVR2);
return 0;
case ETMCNTVRn(3):
*val = etm_read(ETMCNTVR3);
return 0;
case ETMSQ12EVR:
*val = etm_read(ETMSQ12EVR);
return 0;
case ETMSQ21EVR:
*val = etm_read(ETMSQ21EVR);
return 0;
case ETMSQ23EVR:
*val = etm_read(ETMSQ23EVR);
return 0;
case ETMSQ31EVR:
*val = etm_read(ETMSQ31EVR);
return 0;
case ETMSQ32EVR:
*val = etm_read(ETMSQ32EVR);
return 0;
case ETMSQ13EVR:
*val = etm_read(ETMSQ13EVR);
return 0;
case ETMSQR:
*val = etm_read(ETMSQR);
return 0;
case ETMEXTOUTEVRn(0):
*val = etm_read(ETMEXTOUTEVR0);
return 0;
case ETMEXTOUTEVRn(1):
*val = etm_read(ETMEXTOUTEVR1);
return 0;
case ETMEXTOUTEVRn(2):
*val = etm_read(ETMEXTOUTEVR2);
return 0;
case ETMEXTOUTEVRn(3):
*val = etm_read(ETMEXTOUTEVR3);
return 0;
case ETMCIDCVRn(0):
*val = etm_read(ETMCIDCVR0);
return 0;
case ETMCIDCVRn(1):
*val = etm_read(ETMCIDCVR1);
return 0;
case ETMCIDCVRn(2):
*val = etm_read(ETMCIDCVR2);
return 0;
case ETMCIDCMR:
*val = etm_read(ETMCIDCMR);
return 0;
case ETMIMPSPEC0:
*val = etm_read(ETMIMPSPEC0);
return 0;
case ETMIMPSPEC1:
*val = etm_read(ETMIMPSPEC1);
return 0;
case ETMIMPSPEC2:
*val = etm_read(ETMIMPSPEC2);
return 0;
case ETMIMPSPEC3:
*val = etm_read(ETMIMPSPEC3);
return 0;
case ETMIMPSPEC4:
*val = etm_read(ETMIMPSPEC4);
return 0;
case ETMIMPSPEC5:
*val = etm_read(ETMIMPSPEC5);
return 0;
case ETMIMPSPEC6:
*val = etm_read(ETMIMPSPEC6);
return 0;
case ETMIMPSPEC7:
*val = etm_read(ETMIMPSPEC7);
return 0;
case ETMSYNCFR:
*val = etm_read(ETMSYNCFR);
return 0;
case ETMIDR:
*val = etm_read(ETMIDR);
return 0;
case ETMCCER:
*val = etm_read(ETMCCER);
return 0;
case ETMEXTINSELR:
*val = etm_read(ETMEXTINSELR);
return 0;
case ETMTESSEICR:
*val = etm_read(ETMTESSEICR);
return 0;
case ETMEIBCR:
*val = etm_read(ETMEIBCR);
return 0;
case ETMTSEVR:
*val = etm_read(ETMTSEVR);
return 0;
case ETMAUXCR:
*val = etm_read(ETMAUXCR);
return 0;
case ETMTRACEIDR:
*val = etm_read(ETMTRACEIDR);
return 0;
case ETMVMIDCVR:
*val = etm_read(ETMVMIDCVR);
return 0;
case ETMOSLSR:
*val = etm_read(ETMOSLSR);
return 0;
case ETMOSSRR:
*val = etm_read(ETMOSSRR);
return 0;
case ETMPDCR:
*val = etm_read(ETMPDCR);
return 0;
case ETMPDSR:
*val = etm_read(ETMPDSR);
return 0;
default:
*val = 0;
return -EINVAL;
}
}
int etm_writel_cp14(u32 reg, u32 val)
{
switch (reg) {
case ETMCR:
etm_write(val, ETMCR);
break;
case ETMTRIGGER:
etm_write(val, ETMTRIGGER);
break;
case ETMSR:
etm_write(val, ETMSR);
break;
case ETMTSSCR:
etm_write(val, ETMTSSCR);
break;
case ETMTEEVR:
etm_write(val, ETMTEEVR);
break;
case ETMTECR1:
etm_write(val, ETMTECR1);
break;
case ETMFFLR:
etm_write(val, ETMFFLR);
break;
case ETMACVRn(0):
etm_write(val, ETMACVR0);
break;
case ETMACVRn(1):
etm_write(val, ETMACVR1);
break;
case ETMACVRn(2):
etm_write(val, ETMACVR2);
break;
case ETMACVRn(3):
etm_write(val, ETMACVR3);
break;
case ETMACVRn(4):
etm_write(val, ETMACVR4);
break;
case ETMACVRn(5):
etm_write(val, ETMACVR5);
break;
case ETMACVRn(6):
etm_write(val, ETMACVR6);
break;
case ETMACVRn(7):
etm_write(val, ETMACVR7);
break;
case ETMACVRn(8):
etm_write(val, ETMACVR8);
break;
case ETMACVRn(9):
etm_write(val, ETMACVR9);
break;
case ETMACVRn(10):
etm_write(val, ETMACVR10);
break;
case ETMACVRn(11):
etm_write(val, ETMACVR11);
break;
case ETMACVRn(12):
etm_write(val, ETMACVR12);
break;
case ETMACVRn(13):
etm_write(val, ETMACVR13);
break;
case ETMACVRn(14):
etm_write(val, ETMACVR14);
break;
case ETMACVRn(15):
etm_write(val, ETMACVR15);
break;
case ETMACTRn(0):
etm_write(val, ETMACTR0);
break;
case ETMACTRn(1):
etm_write(val, ETMACTR1);
break;
case ETMACTRn(2):
etm_write(val, ETMACTR2);
break;
case ETMACTRn(3):
etm_write(val, ETMACTR3);
break;
case ETMACTRn(4):
etm_write(val, ETMACTR4);
break;
case ETMACTRn(5):
etm_write(val, ETMACTR5);
break;
case ETMACTRn(6):
etm_write(val, ETMACTR6);
break;
case ETMACTRn(7):
etm_write(val, ETMACTR7);
break;
case ETMACTRn(8):
etm_write(val, ETMACTR8);
break;
case ETMACTRn(9):
etm_write(val, ETMACTR9);
break;
case ETMACTRn(10):
etm_write(val, ETMACTR10);
break;
case ETMACTRn(11):
etm_write(val, ETMACTR11);
break;
case ETMACTRn(12):
etm_write(val, ETMACTR12);
break;
case ETMACTRn(13):
etm_write(val, ETMACTR13);
break;
case ETMACTRn(14):
etm_write(val, ETMACTR14);
break;
case ETMACTRn(15):
etm_write(val, ETMACTR15);
break;
case ETMCNTRLDVRn(0):
etm_write(val, ETMCNTRLDVR0);
break;
case ETMCNTRLDVRn(1):
etm_write(val, ETMCNTRLDVR1);
break;
case ETMCNTRLDVRn(2):
etm_write(val, ETMCNTRLDVR2);
break;
case ETMCNTRLDVRn(3):
etm_write(val, ETMCNTRLDVR3);
break;
case ETMCNTENRn(0):
etm_write(val, ETMCNTENR0);
break;
case ETMCNTENRn(1):
etm_write(val, ETMCNTENR1);
break;
case ETMCNTENRn(2):
etm_write(val, ETMCNTENR2);
break;
case ETMCNTENRn(3):
etm_write(val, ETMCNTENR3);
break;
case ETMCNTRLDEVRn(0):
etm_write(val, ETMCNTRLDEVR0);
break;
case ETMCNTRLDEVRn(1):
etm_write(val, ETMCNTRLDEVR1);
break;
case ETMCNTRLDEVRn(2):
etm_write(val, ETMCNTRLDEVR2);
break;
case ETMCNTRLDEVRn(3):
etm_write(val, ETMCNTRLDEVR3);
break;
case ETMCNTVRn(0):
etm_write(val, ETMCNTVR0);
break;
case ETMCNTVRn(1):
etm_write(val, ETMCNTVR1);
break;
case ETMCNTVRn(2):
etm_write(val, ETMCNTVR2);
break;
case ETMCNTVRn(3):
etm_write(val, ETMCNTVR3);
break;
case ETMSQ12EVR:
etm_write(val, ETMSQ12EVR);
break;
case ETMSQ21EVR:
etm_write(val, ETMSQ21EVR);
break;
case ETMSQ23EVR:
etm_write(val, ETMSQ23EVR);
break;
case ETMSQ31EVR:
etm_write(val, ETMSQ31EVR);
break;
case ETMSQ32EVR:
etm_write(val, ETMSQ32EVR);
break;
case ETMSQ13EVR:
etm_write(val, ETMSQ13EVR);
break;
case ETMSQR:
etm_write(val, ETMSQR);
break;
case ETMEXTOUTEVRn(0):
etm_write(val, ETMEXTOUTEVR0);
break;
case ETMEXTOUTEVRn(1):
etm_write(val, ETMEXTOUTEVR1);
break;
case ETMEXTOUTEVRn(2):
etm_write(val, ETMEXTOUTEVR2);
break;
case ETMEXTOUTEVRn(3):
etm_write(val, ETMEXTOUTEVR3);
break;
case ETMCIDCVRn(0):
etm_write(val, ETMCIDCVR0);
break;
case ETMCIDCVRn(1):
etm_write(val, ETMCIDCVR1);
break;
case ETMCIDCVRn(2):
etm_write(val, ETMCIDCVR2);
break;
case ETMCIDCMR:
etm_write(val, ETMCIDCMR);
break;
case ETMIMPSPEC0:
etm_write(val, ETMIMPSPEC0);
break;
case ETMIMPSPEC1:
etm_write(val, ETMIMPSPEC1);
break;
case ETMIMPSPEC2:
etm_write(val, ETMIMPSPEC2);
break;
case ETMIMPSPEC3:
etm_write(val, ETMIMPSPEC3);
break;
case ETMIMPSPEC4:
etm_write(val, ETMIMPSPEC4);
break;
case ETMIMPSPEC5:
etm_write(val, ETMIMPSPEC5);
break;
case ETMIMPSPEC6:
etm_write(val, ETMIMPSPEC6);
break;
case ETMIMPSPEC7:
etm_write(val, ETMIMPSPEC7);
break;
case ETMSYNCFR:
etm_write(val, ETMSYNCFR);
break;
case ETMEXTINSELR:
etm_write(val, ETMEXTINSELR);
break;
case ETMTESSEICR:
etm_write(val, ETMTESSEICR);
break;
case ETMEIBCR:
etm_write(val, ETMEIBCR);
break;
case ETMTSEVR:
etm_write(val, ETMTSEVR);
break;
case ETMAUXCR:
etm_write(val, ETMAUXCR);
break;
case ETMTRACEIDR:
etm_write(val, ETMTRACEIDR);
break;
case ETMVMIDCVR:
etm_write(val, ETMVMIDCVR);
break;
case ETMOSLAR:
etm_write(val, ETMOSLAR);
break;
case ETMOSSRR:
etm_write(val, ETMOSSRR);
break;
case ETMPDCR:
etm_write(val, ETMPDCR);
break;
case ETMPDSR:
etm_write(val, ETMPDSR);
break;
default:
return -EINVAL;
}
return 0;
}

View File

@@ -0,0 +1,251 @@
/* Copyright (c) 2014-2015, 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.
*/
#ifndef _CORESIGHT_CORESIGHT_ETM_H
#define _CORESIGHT_CORESIGHT_ETM_H
#include <linux/spinlock.h>
#include "coresight-priv.h"
/*
* Device registers:
* 0x000 - 0x2FC: Trace registers
* 0x300 - 0x314: Management registers
* 0x318 - 0xEFC: Trace registers
*
* Coresight registers
* 0xF00 - 0xF9C: Management registers
* 0xFA0 - 0xFA4: Management registers in PFTv1.0
* Trace registers in PFTv1.1
* 0xFA8 - 0xFFC: Management registers
*/
/* Trace registers (0x000-0x2FC) */
#define ETMCR 0x000
#define ETMCCR 0x004
#define ETMTRIGGER 0x008
#define ETMSR 0x010
#define ETMSCR 0x014
#define ETMTSSCR 0x018
#define ETMTECR2 0x01c
#define ETMTEEVR 0x020
#define ETMTECR1 0x024
#define ETMFFLR 0x02c
#define ETMACVRn(n) (0x040 + (n * 4))
#define ETMACTRn(n) (0x080 + (n * 4))
#define ETMCNTRLDVRn(n) (0x140 + (n * 4))
#define ETMCNTENRn(n) (0x150 + (n * 4))
#define ETMCNTRLDEVRn(n) (0x160 + (n * 4))
#define ETMCNTVRn(n) (0x170 + (n * 4))
#define ETMSQ12EVR 0x180
#define ETMSQ21EVR 0x184
#define ETMSQ23EVR 0x188
#define ETMSQ31EVR 0x18c
#define ETMSQ32EVR 0x190
#define ETMSQ13EVR 0x194
#define ETMSQR 0x19c
#define ETMEXTOUTEVRn(n) (0x1a0 + (n * 4))
#define ETMCIDCVRn(n) (0x1b0 + (n * 4))
#define ETMCIDCMR 0x1bc
#define ETMIMPSPEC0 0x1c0
#define ETMIMPSPEC1 0x1c4
#define ETMIMPSPEC2 0x1c8
#define ETMIMPSPEC3 0x1cc
#define ETMIMPSPEC4 0x1d0
#define ETMIMPSPEC5 0x1d4
#define ETMIMPSPEC6 0x1d8
#define ETMIMPSPEC7 0x1dc
#define ETMSYNCFR 0x1e0
#define ETMIDR 0x1e4
#define ETMCCER 0x1e8
#define ETMEXTINSELR 0x1ec
#define ETMTESSEICR 0x1f0
#define ETMEIBCR 0x1f4
#define ETMTSEVR 0x1f8
#define ETMAUXCR 0x1fc
#define ETMTRACEIDR 0x200
#define ETMVMIDCVR 0x240
/* Management registers (0x300-0x314) */
#define ETMOSLAR 0x300
#define ETMOSLSR 0x304
#define ETMOSSRR 0x308
#define ETMPDCR 0x310
#define ETMPDSR 0x314
#define ETM_MAX_ADDR_CMP 16
#define ETM_MAX_CNTR 4
#define ETM_MAX_CTXID_CMP 3
/* Register definition */
/* ETMCR - 0x00 */
#define ETMCR_PWD_DWN BIT(0)
#define ETMCR_STALL_MODE BIT(7)
#define ETMCR_ETM_PRG BIT(10)
#define ETMCR_ETM_EN BIT(11)
#define ETMCR_CYC_ACC BIT(12)
#define ETMCR_CTXID_SIZE (BIT(14)|BIT(15))
#define ETMCR_TIMESTAMP_EN BIT(28)
/* ETMCCR - 0x04 */
#define ETMCCR_FIFOFULL BIT(23)
/* ETMPDCR - 0x310 */
#define ETMPDCR_PWD_UP BIT(3)
/* ETMTECR1 - 0x024 */
#define ETMTECR1_ADDR_COMP_1 BIT(0)
#define ETMTECR1_INC_EXC BIT(24)
#define ETMTECR1_START_STOP BIT(25)
/* ETMCCER - 0x1E8 */
#define ETMCCER_TIMESTAMP BIT(22)
#define ETM_MODE_EXCLUDE BIT(0)
#define ETM_MODE_CYCACC BIT(1)
#define ETM_MODE_STALL BIT(2)
#define ETM_MODE_TIMESTAMP BIT(3)
#define ETM_MODE_CTXID BIT(4)
#define ETM_MODE_ALL 0x1f
#define ETM_SQR_MASK 0x3
#define ETM_TRACEID_MASK 0x3f
#define ETM_EVENT_MASK 0x1ffff
#define ETM_SYNC_MASK 0xfff
#define ETM_ALL_MASK 0xffffffff
#define ETMSR_PROG_BIT 1
#define ETM_SEQ_STATE_MAX_VAL (0x2)
#define PORT_SIZE_MASK (GENMASK(21, 21) | GENMASK(6, 4))
#define ETM_HARD_WIRE_RES_A /* Hard wired, always true */ \
((0x0f << 0) | \
/* Resource index A */ \
(0x06 << 4))
#define ETM_ADD_COMP_0 /* Single addr comparator 1 */ \
((0x00 << 7) | \
/* Resource index B */ \
(0x00 << 11))
#define ETM_EVENT_NOT_A BIT(14) /* NOT(A) */
#define ETM_DEFAULT_EVENT_VAL (ETM_HARD_WIRE_RES_A | \
ETM_ADD_COMP_0 | \
ETM_EVENT_NOT_A)
/**
* struct etm_drvdata - specifics associated to an ETM component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @clk: the clock this component is associated to.
* @spinlock: only one at a time pls.
* @cpu: the cpu this component is affined to.
* @port_size: port size as reported by ETMCR bit 4-6 and 21.
* @arch: ETM/PTM version number.
* @use_cpu14: true if management registers need to be accessed via CP14.
* @enable: is this ETM/PTM currently tracing.
* @sticky_enable: true if ETM base configuration has been done.
* @boot_enable:true if we should start tracing at boot time.
* @os_unlock: true if access to management registers is allowed.
* @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
* @nr_cntr: Number of counters as found in ETMCCR bit 13-15.
* @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19.
* @nr_ext_out: Number of external output as found in ETMCCR bit 20-22.
* @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
* @etmccr: value of register ETMCCR.
* @etmccer: value of register ETMCCER.
* @traceid: value of the current ID for this component.
* @mode: controls various modes supported by this ETM/PTM.
* @ctrl: used in conjunction with @mode.
* @trigger_event: setting for register ETMTRIGGER.
* @startstop_ctrl: setting for register ETMTSSCR.
* @enable_event: setting for register ETMTEEVR.
* @enable_ctrl1: setting for register ETMTECR1.
* @fifofull_level: setting for register ETMFFLR.
* @addr_idx: index for the address comparator selection.
* @addr_val: value for address comparator register.
* @addr_acctype: access type for address comparator register.
* @addr_type: current status of the comparator register.
* @cntr_idx: index for the counter register selection.
* @cntr_rld_val: reload value of a counter register.
* @cntr_event: control for counter enable register.
* @cntr_rld_event: value for counter reload event register.
* @cntr_val: counter value register.
* @seq_12_event: event causing the transition from 1 to 2.
* @seq_21_event: event causing the transition from 2 to 1.
* @seq_23_event: event causing the transition from 2 to 3.
* @seq_31_event: event causing the transition from 3 to 1.
* @seq_32_event: event causing the transition from 3 to 2.
* @seq_13_event: event causing the transition from 1 to 3.
* @seq_curr_state: current value of the sequencer register.
* @ctxid_idx: index for the context ID registers.
* @ctxid_val: value for the context ID to trigger on.
* @ctxid_mask: mask applicable to all the context IDs.
* @sync_freq: Synchronisation frequency.
* @timestamp_event: Defines an event that requests the insertion
of a timestamp into the trace stream.
*/
struct etm_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
spinlock_t spinlock;
int cpu;
int port_size;
u8 arch;
bool use_cp14;
bool enable;
bool sticky_enable;
bool boot_enable;
bool os_unlock;
u8 nr_addr_cmp;
u8 nr_cntr;
u8 nr_ext_inp;
u8 nr_ext_out;
u8 nr_ctxid_cmp;
u32 etmccr;
u32 etmccer;
u32 traceid;
u32 mode;
u32 ctrl;
u32 trigger_event;
u32 startstop_ctrl;
u32 enable_event;
u32 enable_ctrl1;
u32 fifofull_level;
u8 addr_idx;
u32 addr_val[ETM_MAX_ADDR_CMP];
u32 addr_acctype[ETM_MAX_ADDR_CMP];
u32 addr_type[ETM_MAX_ADDR_CMP];
u8 cntr_idx;
u32 cntr_rld_val[ETM_MAX_CNTR];
u32 cntr_event[ETM_MAX_CNTR];
u32 cntr_rld_event[ETM_MAX_CNTR];
u32 cntr_val[ETM_MAX_CNTR];
u32 seq_12_event;
u32 seq_21_event;
u32 seq_23_event;
u32 seq_31_event;
u32 seq_32_event;
u32 seq_13_event;
u32 seq_curr_state;
u8 ctxid_idx;
u32 ctxid_val[ETM_MAX_CTXID_CMP];
u32 ctxid_mask;
u32 sync_freq;
u32 timestamp_event;
};
enum etm_addr_type {
ETM_ADDR_TYPE_NONE,
ETM_ADDR_TYPE_SINGLE,
ETM_ADDR_TYPE_RANGE,
ETM_ADDR_TYPE_START,
ETM_ADDR_TYPE_STOP,
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,258 @@
/* Copyright (c) 2011-2012, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
#include "coresight-priv.h"
#define FUNNEL_FUNCTL 0x000
#define FUNNEL_PRICTL 0x004
#define FUNNEL_HOLDTIME_MASK 0xf00
#define FUNNEL_HOLDTIME_SHFT 0x8
#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
/**
* struct funnel_drvdata - specifics associated to a funnel component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @clk: the clock this component is associated to.
* @priority: port selection order.
*/
struct funnel_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
unsigned long priority;
};
static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
{
u32 functl;
CS_UNLOCK(drvdata->base);
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
functl &= ~FUNNEL_HOLDTIME_MASK;
functl |= FUNNEL_HOLDTIME;
functl |= (1 << port);
writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
CS_LOCK(drvdata->base);
}
static int funnel_enable(struct coresight_device *csdev, int inport,
int outport)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
funnel_enable_hw(drvdata, inport);
dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
return 0;
}
static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
{
u32 functl;
CS_UNLOCK(drvdata->base);
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
functl &= ~(1 << inport);
writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
CS_LOCK(drvdata->base);
}
static void funnel_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
funnel_disable_hw(drvdata, inport);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
}
static const struct coresight_ops_link funnel_link_ops = {
.enable = funnel_enable,
.disable = funnel_disable,
};
static const struct coresight_ops funnel_cs_ops = {
.link_ops = &funnel_link_ops,
};
static ssize_t priority_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->priority;
return sprintf(buf, "%#lx\n", val);
}
static ssize_t priority_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
drvdata->priority = val;
return size;
}
static DEVICE_ATTR_RW(priority);
static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
{
u32 functl;
CS_UNLOCK(drvdata->base);
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
CS_LOCK(drvdata->base);
return functl;
}
static ssize_t funnel_ctrl_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
u32 val;
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
val = get_funnel_ctrl_hw(drvdata);
clk_disable_unprepare(drvdata->clk);
return sprintf(buf, "%#x\n", val);
}
static DEVICE_ATTR_RO(funnel_ctrl);
static struct attribute *coresight_funnel_attrs[] = {
&dev_attr_funnel_ctrl.attr,
&dev_attr_priority.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_funnel);
static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
{
void __iomem *base;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct funnel_drvdata *drvdata;
struct resource *res = &adev->res;
struct coresight_desc *desc;
struct device_node *np = adev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
drvdata->clk = adev->pclk;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_LINK;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
desc->ops = &funnel_cs_ops;
desc->pdata = pdata;
desc->dev = dev;
desc->groups = coresight_funnel_groups;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "FUNNEL initialized\n");
return 0;
}
static int funnel_remove(struct amba_device *adev)
{
struct funnel_drvdata *drvdata = amba_get_drvdata(adev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct amba_id funnel_ids[] = {
{
.id = 0x0003b908,
.mask = 0x0003ffff,
},
{ 0, 0},
};
static struct amba_driver funnel_driver = {
.drv = {
.name = "coresight-funnel",
.owner = THIS_MODULE,
},
.probe = funnel_probe,
.remove = funnel_remove,
.id_table = funnel_ids,
};
module_amba_driver(funnel_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Funnel driver");

View File

@@ -0,0 +1,63 @@
/* Copyright (c) 2011-2012, 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.
*/
#ifndef _CORESIGHT_PRIV_H
#define _CORESIGHT_PRIV_H
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/coresight.h>
/*
* Coresight management registers (0xf00-0xfcc)
* 0xfa0 - 0xfa4: Management registers in PFTv1.0
* Trace registers in PFTv1.1
*/
#define CORESIGHT_ITCTRL 0xf00
#define CORESIGHT_CLAIMSET 0xfa0
#define CORESIGHT_CLAIMCLR 0xfa4
#define CORESIGHT_LAR 0xfb0
#define CORESIGHT_LSR 0xfb4
#define CORESIGHT_AUTHSTATUS 0xfb8
#define CORESIGHT_DEVID 0xfc8
#define CORESIGHT_DEVTYPE 0xfcc
#define TIMEOUT_US 100
#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb)
static inline void CS_LOCK(void __iomem *addr)
{
do {
/* Wait for things to settle */
mb();
writel_relaxed(0x0, addr + CORESIGHT_LAR);
} while (0);
}
static inline void CS_UNLOCK(void __iomem *addr)
{
do {
writel_relaxed(CORESIGHT_UNLOCK, addr + CORESIGHT_LAR);
/* Make sure everyone has seen this */
mb();
} while (0);
}
#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
extern int etm_readl_cp14(u32 off, unsigned int *val);
extern int etm_writel_cp14(u32 off, u32 val);
#else
static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#endif
#endif

View File

@@ -0,0 +1,137 @@
/* Copyright (c) 2011-2012, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
/**
* struct replicator_drvdata - specifics associated to a replicator component
* @dev: the device entity associated with this component
* @csdev: component vitals needed by the framework
*/
struct replicator_drvdata {
struct device *dev;
struct coresight_device *csdev;
};
static int replicator_enable(struct coresight_device *csdev, int inport,
int outport)
{
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
dev_info(drvdata->dev, "REPLICATOR enabled\n");
return 0;
}
static void replicator_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
dev_info(drvdata->dev, "REPLICATOR disabled\n");
}
static const struct coresight_ops_link replicator_link_ops = {
.enable = replicator_enable,
.disable = replicator_disable,
};
static const struct coresight_ops replicator_cs_ops = {
.link_ops = &replicator_link_ops,
};
static int replicator_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata = NULL;
struct replicator_drvdata *drvdata;
struct coresight_desc *desc;
struct device_node *np = pdev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
pdev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_LINK;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
desc->ops = &replicator_cs_ops;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "REPLICATOR initialized\n");
return 0;
}
static int replicator_remove(struct platform_device *pdev)
{
struct replicator_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static const struct of_device_id replicator_match[] = {
{.compatible = "arm,coresight-replicator"},
{}
};
static struct platform_driver replicator_driver = {
.probe = replicator_probe,
.remove = replicator_remove,
.driver = {
.name = "coresight-replicator",
.of_match_table = replicator_match,
},
};
static int __init replicator_init(void)
{
return platform_driver_register(&replicator_driver);
}
module_init(replicator_init);
static void __exit replicator_exit(void)
{
platform_driver_unregister(&replicator_driver);
}
module_exit(replicator_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Replicator driver");

View File

@@ -0,0 +1,822 @@
/* Copyright (c) 2012, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
#include "coresight-priv.h"
#define TMC_RSZ 0x004
#define TMC_STS 0x00c
#define TMC_RRD 0x010
#define TMC_RRP 0x014
#define TMC_RWP 0x018
#define TMC_TRG 0x01c
#define TMC_CTL 0x020
#define TMC_RWD 0x024
#define TMC_MODE 0x028
#define TMC_LBUFLEVEL 0x02c
#define TMC_CBUFLEVEL 0x030
#define TMC_BUFWM 0x034
#define TMC_RRPHI 0x038
#define TMC_RWPHI 0x03c
#define TMC_AXICTL 0x110
#define TMC_DBALO 0x118
#define TMC_DBAHI 0x11c
#define TMC_FFSR 0x300
#define TMC_FFCR 0x304
#define TMC_PSCR 0x308
#define TMC_ITMISCOP0 0xee0
#define TMC_ITTRFLIN 0xee8
#define TMC_ITATBDATA0 0xeec
#define TMC_ITATBCTR2 0xef0
#define TMC_ITATBCTR1 0xef4
#define TMC_ITATBCTR0 0xef8
/* register description */
/* TMC_CTL - 0x020 */
#define TMC_CTL_CAPT_EN BIT(0)
/* TMC_STS - 0x00C */
#define TMC_STS_TRIGGERED BIT(1)
/* TMC_AXICTL - 0x110 */
#define TMC_AXICTL_PROT_CTL_B0 BIT(0)
#define TMC_AXICTL_PROT_CTL_B1 BIT(1)
#define TMC_AXICTL_SCT_GAT_MODE BIT(7)
#define TMC_AXICTL_WR_BURST_LEN 0xF00
/* TMC_FFCR - 0x304 */
#define TMC_FFCR_EN_FMT BIT(0)
#define TMC_FFCR_EN_TI BIT(1)
#define TMC_FFCR_FON_FLIN BIT(4)
#define TMC_FFCR_FON_TRIG_EVT BIT(5)
#define TMC_FFCR_FLUSHMAN BIT(6)
#define TMC_FFCR_TRIGON_TRIGIN BIT(8)
#define TMC_FFCR_STOP_ON_FLUSH BIT(12)
#define TMC_STS_TRIGGERED_BIT 2
#define TMC_FFCR_FLUSHMAN_BIT 6
enum tmc_config_type {
TMC_CONFIG_TYPE_ETB,
TMC_CONFIG_TYPE_ETR,
TMC_CONFIG_TYPE_ETF,
};
enum tmc_mode {
TMC_MODE_CIRCULAR_BUFFER,
TMC_MODE_SOFTWARE_FIFO,
TMC_MODE_HARDWARE_FIFO,
};
enum tmc_mem_intf_width {
TMC_MEM_INTF_WIDTH_32BITS = 0x2,
TMC_MEM_INTF_WIDTH_64BITS = 0x3,
TMC_MEM_INTF_WIDTH_128BITS = 0x4,
TMC_MEM_INTF_WIDTH_256BITS = 0x5,
};
/**
* struct tmc_drvdata - specifics associated to an TMC component
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
* @clk: the clock this component is associated to.
* @spinlock: only one at a time pls.
* @read_count: manages preparation of buffer for reading.
* @buf: area of memory where trace data get sent.
* @paddr: DMA start location in RAM.
* @vaddr: virtual representation of @paddr.
* @size: @buf size.
* @enable: this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type.
* @trigger_cntr: amount of words to store after a trigger.
*/
struct tmc_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
struct clk *clk;
spinlock_t spinlock;
int read_count;
bool reading;
char *buf;
dma_addr_t paddr;
void __iomem *vaddr;
u32 size;
bool enable;
enum tmc_config_type config_type;
u32 trigger_cntr;
};
static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
{
/* Ensure formatter, unformatter and hardware fifo are empty */
if (coresight_timeout(drvdata->base,
TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n",
TMC_STS);
}
}
static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
{
u32 ffcr;
ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
ffcr |= TMC_FFCR_STOP_ON_FLUSH;
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
ffcr |= TMC_FFCR_FLUSHMAN;
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
/* Ensure flush completes */
if (coresight_timeout(drvdata->base,
TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n",
TMC_FFCR);
}
tmc_wait_for_ready(drvdata);
}
static void tmc_enable_hw(struct tmc_drvdata *drvdata)
{
writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
}
static void tmc_disable_hw(struct tmc_drvdata *drvdata)
{
writel_relaxed(0x0, drvdata->base + TMC_CTL);
}
static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
{
/* Zero out the memory to help with debug */
memset(drvdata->buf, 0, drvdata->size);
CS_UNLOCK(drvdata->base);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
u32 axictl;
/* Zero out the memory to help with debug */
memset(drvdata->vaddr, 0, drvdata->size);
CS_UNLOCK(drvdata->base);
writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
axictl |= TMC_AXICTL_WR_BURST_LEN;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl = (axictl &
~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
TMC_AXICTL_PROT_CTL_B1;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
drvdata->base + TMC_FFCR);
writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
{
int ret;
unsigned long flags;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
return -EBUSY;
}
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_enable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_enable_hw(drvdata);
} else {
if (mode == TMC_MODE_CIRCULAR_BUFFER)
tmc_etb_enable_hw(drvdata);
else
tmc_etf_enable_hw(drvdata);
}
drvdata->enable = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC enabled\n");
return 0;
}
static int tmc_enable_sink(struct coresight_device *csdev)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
}
static int tmc_enable_link(struct coresight_device *csdev, int inport,
int outport)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
{
enum tmc_mem_intf_width memwidth;
u8 memwords;
char *bufp;
u32 read_data;
int i;
memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
memwords = 1;
else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
memwords = 2;
else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
memwords = 4;
else
memwords = 8;
bufp = drvdata->buf;
while (1) {
for (i = 0; i < memwords; i++) {
read_data = readl_relaxed(drvdata->base + TMC_RRD);
if (read_data == 0xFFFFFFFF)
return;
memcpy(bufp, &read_data, 4);
bufp += 4;
}
}
}
static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_etb_dump_hw(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
{
u32 rwp, val;
rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS);
/* How much memory do we still have */
if (val & BIT(0))
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
else
drvdata->buf = drvdata->vaddr;
}
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_etr_dump_hw(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
tmc_flush_and_stop(drvdata);
tmc_disable_hw(drvdata);
CS_LOCK(drvdata->base);
}
static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
{
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_disable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_disable_hw(drvdata);
} else {
if (mode == TMC_MODE_CIRCULAR_BUFFER)
tmc_etb_disable_hw(drvdata);
else
tmc_etf_disable_hw(drvdata);
}
out:
drvdata->enable = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "TMC disabled\n");
}
static void tmc_disable_sink(struct coresight_device *csdev)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
}
static void tmc_disable_link(struct coresight_device *csdev, int inport,
int outport)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
static const struct coresight_ops_sink tmc_sink_ops = {
.enable = tmc_enable_sink,
.disable = tmc_disable_sink,
};
static const struct coresight_ops_link tmc_link_ops = {
.enable = tmc_enable_link,
.disable = tmc_disable_link,
};
static const struct coresight_ops tmc_etb_cs_ops = {
.sink_ops = &tmc_sink_ops,
};
static const struct coresight_ops tmc_etr_cs_ops = {
.sink_ops = &tmc_sink_ops,
};
static const struct coresight_ops tmc_etf_cs_ops = {
.sink_ops = &tmc_sink_ops,
.link_ops = &tmc_link_ops,
};
static int tmc_read_prepare(struct tmc_drvdata *drvdata)
{
int ret;
unsigned long flags;
enum tmc_mode mode;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->enable)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_disable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_disable_hw(drvdata);
} else {
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode == TMC_MODE_CIRCULAR_BUFFER) {
tmc_etb_disable_hw(drvdata);
} else {
ret = -ENODEV;
goto err;
}
}
out:
drvdata->reading = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC read start\n");
return 0;
err:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
{
unsigned long flags;
enum tmc_mode mode;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->enable)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
tmc_etb_enable_hw(drvdata);
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
tmc_etr_enable_hw(drvdata);
} else {
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode == TMC_MODE_CIRCULAR_BUFFER)
tmc_etb_enable_hw(drvdata);
}
out:
drvdata->reading = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC read end\n");
}
static int tmc_open(struct inode *inode, struct file *file)
{
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
int ret = 0;
if (drvdata->read_count++)
goto out;
ret = tmc_read_prepare(drvdata);
if (ret)
return ret;
out:
nonseekable_open(inode, file);
dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
return 0;
}
static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
loff_t *ppos)
{
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
char *bufp = drvdata->buf + *ppos;
if (*ppos + len > drvdata->size)
len = drvdata->size - *ppos;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (bufp == (char *)(drvdata->vaddr + drvdata->size))
bufp = drvdata->vaddr;
else if (bufp > (char *)(drvdata->vaddr + drvdata->size))
bufp -= drvdata->size;
if ((bufp + len) > (char *)(drvdata->vaddr + drvdata->size))
len = (char *)(drvdata->vaddr + drvdata->size) - bufp;
}
if (copy_to_user(data, bufp, len)) {
dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__);
return -EFAULT;
}
*ppos += len;
dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
__func__, len, (int)(drvdata->size - *ppos));
return len;
}
static int tmc_release(struct inode *inode, struct file *file)
{
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
if (--drvdata->read_count) {
if (drvdata->read_count < 0) {
dev_err(drvdata->dev, "mismatched close\n");
drvdata->read_count = 0;
}
goto out;
}
tmc_read_unprepare(drvdata);
out:
dev_dbg(drvdata->dev, "%s: released\n", __func__);
return 0;
}
static const struct file_operations tmc_fops = {
.owner = THIS_MODULE,
.open = tmc_open,
.read = tmc_read,
.release = tmc_release,
.llseek = no_llseek,
};
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
unsigned long flags;
u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg;
u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr;
u32 devid;
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = clk_prepare_enable(drvdata->clk);
if (ret)
goto out;
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ);
tmc_sts = readl_relaxed(drvdata->base + TMC_STS);
tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP);
tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP);
tmc_trg = readl_relaxed(drvdata->base + TMC_TRG);
tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL);
tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR);
tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
tmc_mode = readl_relaxed(drvdata->base + TMC_MODE);
tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR);
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
return sprintf(buf,
"Depth:\t\t0x%x\n"
"Status:\t\t0x%x\n"
"RAM read ptr:\t0x%x\n"
"RAM wrt ptr:\t0x%x\n"
"Trigger cnt:\t0x%x\n"
"Control:\t0x%x\n"
"Flush status:\t0x%x\n"
"Flush ctrl:\t0x%x\n"
"Mode:\t\t0x%x\n"
"PSRC:\t\t0x%x\n"
"DEVID:\t\t0x%x\n",
tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg,
tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid);
out:
return -EINVAL;
}
static DEVICE_ATTR_RO(status);
static ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->trigger_cntr;
return sprintf(buf, "%#lx\n", val);
}
static ssize_t trigger_cntr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
drvdata->trigger_cntr = val;
return size;
}
static DEVICE_ATTR_RW(trigger_cntr);
static struct attribute *coresight_etb_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etb);
static struct attribute *coresight_etr_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etr);
static struct attribute *coresight_etf_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etf);
static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct tmc_drvdata *drvdata;
struct resource *res = &adev->res;
struct coresight_desc *desc;
struct device_node *np = adev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
drvdata->clk = adev->pclk;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config_type = BMVAL(devid, 6, 7);
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (np)
ret = of_property_read_u32(np,
"arm,buffer-size",
&drvdata->size);
if (ret)
drvdata->size = SZ_1M;
} else {
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
}
clk_disable_unprepare(drvdata->clk);
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
&drvdata->paddr, GFP_KERNEL);
if (!drvdata->vaddr)
return -ENOMEM;
memset(drvdata->vaddr, 0, drvdata->size);
drvdata->buf = drvdata->vaddr;
} else {
drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
if (!drvdata->buf)
return -ENOMEM;
}
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc) {
ret = -ENOMEM;
goto err_devm_kzalloc;
}
desc->pdata = pdata;
desc->dev = dev;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
desc->type = CORESIGHT_DEV_TYPE_SINK;
desc->ops = &tmc_etb_cs_ops;
desc->groups = coresight_etb_groups;
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
desc->type = CORESIGHT_DEV_TYPE_SINK;
desc->ops = &tmc_etr_cs_ops;
desc->groups = coresight_etr_groups;
} else {
desc->type = CORESIGHT_DEV_TYPE_LINKSINK;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
desc->ops = &tmc_etf_cs_ops;
desc->groups = coresight_etf_groups;
}
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto err_devm_kzalloc;
}
drvdata->miscdev.name = pdata->name;
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
drvdata->miscdev.fops = &tmc_fops;
ret = misc_register(&drvdata->miscdev);
if (ret)
goto err_misc_register;
dev_info(dev, "TMC initialized\n");
return 0;
err_misc_register:
coresight_unregister(drvdata->csdev);
err_devm_kzalloc:
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
dma_free_coherent(dev, drvdata->size,
&drvdata->paddr, GFP_KERNEL);
return ret;
}
static int tmc_remove(struct amba_device *adev)
{
struct tmc_drvdata *drvdata = amba_get_drvdata(adev);
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
dma_free_coherent(drvdata->dev, drvdata->size,
&drvdata->paddr, GFP_KERNEL);
return 0;
}
static struct amba_id tmc_ids[] = {
{
.id = 0x0003b961,
.mask = 0x0003ffff,
},
{ 0, 0},
};
static struct amba_driver tmc_driver = {
.drv = {
.name = "coresight-tmc",
.owner = THIS_MODULE,
},
.probe = tmc_probe,
.remove = tmc_remove,
.id_table = tmc_ids,
};
module_amba_driver(tmc_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver");

View File

@@ -0,0 +1,207 @@
/* Copyright (c) 2011-2012, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
#include "coresight-priv.h"
#define TPIU_SUPP_PORTSZ 0x000
#define TPIU_CURR_PORTSZ 0x004
#define TPIU_SUPP_TRIGMODES 0x100
#define TPIU_TRIG_CNTRVAL 0x104
#define TPIU_TRIG_MULT 0x108
#define TPIU_SUPP_TESTPATM 0x200
#define TPIU_CURR_TESTPATM 0x204
#define TPIU_TEST_PATREPCNTR 0x208
#define TPIU_FFSR 0x300
#define TPIU_FFCR 0x304
#define TPIU_FSYNC_CNTR 0x308
#define TPIU_EXTCTL_INPORT 0x400
#define TPIU_EXTCTL_OUTPORT 0x404
#define TPIU_ITTRFLINACK 0xee4
#define TPIU_ITTRFLIN 0xee8
#define TPIU_ITATBDATA0 0xeec
#define TPIU_ITATBCTR2 0xef0
#define TPIU_ITATBCTR1 0xef4
#define TPIU_ITATBCTR0 0xef8
/** register definition **/
/* FFCR - 0x304 */
#define FFCR_FON_MAN BIT(6)
/**
* @base: memory mapped base address for this component.
* @dev: the device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @clk: the clock this component is associated to.
*/
struct tpiu_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
};
static void tpiu_enable_hw(struct tpiu_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* TODO: fill this up */
CS_LOCK(drvdata->base);
}
static int tpiu_enable(struct coresight_device *csdev)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
tpiu_enable_hw(drvdata);
dev_info(drvdata->dev, "TPIU enabled\n");
return 0;
}
static void tpiu_disable_hw(struct tpiu_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Clear formatter controle reg. */
writel_relaxed(0x0, drvdata->base + TPIU_FFCR);
/* Generate manual flush */
writel_relaxed(FFCR_FON_MAN, drvdata->base + TPIU_FFCR);
CS_LOCK(drvdata->base);
}
static void tpiu_disable(struct coresight_device *csdev)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
tpiu_disable_hw(drvdata);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "TPIU disabled\n");
}
static const struct coresight_ops_sink tpiu_sink_ops = {
.enable = tpiu_enable,
.disable = tpiu_disable,
};
static const struct coresight_ops tpiu_cs_ops = {
.sink_ops = &tpiu_sink_ops,
};
static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
void __iomem *base;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct tpiu_drvdata *drvdata;
struct resource *res = &adev->res;
struct coresight_desc *desc;
struct device_node *np = adev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
drvdata->clk = adev->pclk;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
/* Disable tpiu to support older devices */
tpiu_disable_hw(drvdata);
clk_disable_unprepare(drvdata->clk);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_SINK;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
desc->ops = &tpiu_cs_ops;
desc->pdata = pdata;
desc->dev = dev;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "TPIU initialized\n");
return 0;
}
static int tpiu_remove(struct amba_device *adev)
{
struct tpiu_drvdata *drvdata = amba_get_drvdata(adev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct amba_id tpiu_ids[] = {
{
.id = 0x0003b912,
.mask = 0x0003ffff,
},
{ 0, 0},
};
static struct amba_driver tpiu_driver = {
.drv = {
.name = "coresight-tpiu",
.owner = THIS_MODULE,
},
.probe = tpiu_probe,
.remove = tpiu_remove,
.id_table = tpiu_ids,
};
module_amba_driver(tpiu_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver");

View File

@@ -0,0 +1,720 @@
/* Copyright (c) 2012, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
#include "coresight-priv.h"
static DEFINE_MUTEX(coresight_mutex);
static int coresight_id_match(struct device *dev, void *data)
{
int trace_id, i_trace_id;
struct coresight_device *csdev, *i_csdev;
csdev = data;
i_csdev = to_coresight_device(dev);
/*
* No need to care about oneself and components that are not
* sources or not enabled
*/
if (i_csdev == csdev || !i_csdev->enable ||
i_csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
return 0;
/* Get the source ID for both compoment */
trace_id = source_ops(csdev)->trace_id(csdev);
i_trace_id = source_ops(i_csdev)->trace_id(i_csdev);
/* All you need is one */
if (trace_id == i_trace_id)
return 1;
return 0;
}
static int coresight_source_is_unique(struct coresight_device *csdev)
{
int trace_id = source_ops(csdev)->trace_id(csdev);
/* this shouldn't happen */
if (trace_id < 0)
return 0;
return !bus_for_each_dev(&coresight_bustype, NULL,
csdev, coresight_id_match);
}
static int coresight_find_link_inport(struct coresight_device *csdev)
{
int i;
struct coresight_device *parent;
struct coresight_connection *conn;
parent = container_of(csdev->path_link.next,
struct coresight_device, path_link);
for (i = 0; i < parent->nr_outport; i++) {
conn = &parent->conns[i];
if (conn->child_dev == csdev)
return conn->child_port;
}
dev_err(&csdev->dev, "couldn't find inport, parent: %s, child: %s\n",
dev_name(&parent->dev), dev_name(&csdev->dev));
return 0;
}
static int coresight_find_link_outport(struct coresight_device *csdev)
{
int i;
struct coresight_device *child;
struct coresight_connection *conn;
child = container_of(csdev->path_link.prev,
struct coresight_device, path_link);
for (i = 0; i < csdev->nr_outport; i++) {
conn = &csdev->conns[i];
if (conn->child_dev == child)
return conn->outport;
}
dev_err(&csdev->dev, "couldn't find outport, parent: %s, child: %s\n",
dev_name(&csdev->dev), dev_name(&child->dev));
return 0;
}
static int coresight_enable_sink(struct coresight_device *csdev)
{
int ret;
if (!csdev->enable) {
if (sink_ops(csdev)->enable) {
ret = sink_ops(csdev)->enable(csdev);
if (ret)
return ret;
}
csdev->enable = true;
}
atomic_inc(csdev->refcnt);
return 0;
}
static void coresight_disable_sink(struct coresight_device *csdev)
{
if (atomic_dec_return(csdev->refcnt) == 0) {
if (sink_ops(csdev)->disable) {
sink_ops(csdev)->disable(csdev);
csdev->enable = false;
}
}
}
static int coresight_enable_link(struct coresight_device *csdev)
{
int ret;
int link_subtype;
int refport, inport, outport;
inport = coresight_find_link_inport(csdev);
outport = coresight_find_link_outport(csdev);
link_subtype = csdev->subtype.link_subtype;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
refport = inport;
else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
refport = outport;
else
refport = 0;
if (atomic_inc_return(&csdev->refcnt[refport]) == 1) {
if (link_ops(csdev)->enable) {
ret = link_ops(csdev)->enable(csdev, inport, outport);
if (ret)
return ret;
}
}
csdev->enable = true;
return 0;
}
static void coresight_disable_link(struct coresight_device *csdev)
{
int i, nr_conns;
int link_subtype;
int refport, inport, outport;
inport = coresight_find_link_inport(csdev);
outport = coresight_find_link_outport(csdev);
link_subtype = csdev->subtype.link_subtype;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
refport = inport;
nr_conns = csdev->nr_inport;
} else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
refport = outport;
nr_conns = csdev->nr_outport;
} else {
refport = 0;
nr_conns = 1;
}
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
link_ops(csdev)->disable(csdev, inport, outport);
}
for (i = 0; i < nr_conns; i++)
if (atomic_read(&csdev->refcnt[i]) != 0)
return;
csdev->enable = false;
}
static int coresight_enable_source(struct coresight_device *csdev)
{
int ret;
if (!coresight_source_is_unique(csdev)) {
dev_warn(&csdev->dev, "traceID %d not unique\n",
source_ops(csdev)->trace_id(csdev));
return -EINVAL;
}
if (!csdev->enable) {
if (source_ops(csdev)->enable) {
ret = source_ops(csdev)->enable(csdev);
if (ret)
return ret;
}
csdev->enable = true;
}
atomic_inc(csdev->refcnt);
return 0;
}
static void coresight_disable_source(struct coresight_device *csdev)
{
if (atomic_dec_return(csdev->refcnt) == 0) {
if (source_ops(csdev)->disable) {
source_ops(csdev)->disable(csdev);
csdev->enable = false;
}
}
}
static int coresight_enable_path(struct list_head *path)
{
int ret = 0;
struct coresight_device *cd;
list_for_each_entry(cd, path, path_link) {
if (cd == list_first_entry(path, struct coresight_device,
path_link)) {
ret = coresight_enable_sink(cd);
} else if (list_is_last(&cd->path_link, path)) {
/*
* Don't enable the source just yet - this needs to
* happen at the very end when all links and sink
* along the path have been configured properly.
*/
;
} else {
ret = coresight_enable_link(cd);
}
if (ret)
goto err;
}
return 0;
err:
list_for_each_entry_continue_reverse(cd, path, path_link) {
if (cd == list_first_entry(path, struct coresight_device,
path_link)) {
coresight_disable_sink(cd);
} else if (list_is_last(&cd->path_link, path)) {
;
} else {
coresight_disable_link(cd);
}
}
return ret;
}
static int coresight_disable_path(struct list_head *path)
{
struct coresight_device *cd;
list_for_each_entry_reverse(cd, path, path_link) {
if (cd == list_first_entry(path, struct coresight_device,
path_link)) {
coresight_disable_sink(cd);
} else if (list_is_last(&cd->path_link, path)) {
/*
* The source has already been stopped, no need
* to do it again here.
*/
;
} else {
coresight_disable_link(cd);
}
}
return 0;
}
static int coresight_build_paths(struct coresight_device *csdev,
struct list_head *path,
bool enable)
{
int i, ret = -EINVAL;
struct coresight_connection *conn;
list_add(&csdev->path_link, path);
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->activated) {
if (enable)
ret = coresight_enable_path(path);
else
ret = coresight_disable_path(path);
} else {
for (i = 0; i < csdev->nr_outport; i++) {
conn = &csdev->conns[i];
if (coresight_build_paths(conn->child_dev,
path, enable) == 0)
ret = 0;
}
}
if (list_first_entry(path, struct coresight_device, path_link) != csdev)
dev_err(&csdev->dev, "wrong device in %s\n", __func__);
list_del(&csdev->path_link);
return ret;
}
int coresight_enable(struct coresight_device *csdev)
{
int ret = 0;
LIST_HEAD(path);
mutex_lock(&coresight_mutex);
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
ret = -EINVAL;
dev_err(&csdev->dev, "wrong device type in %s\n", __func__);
goto out;
}
if (csdev->enable)
goto out;
if (coresight_build_paths(csdev, &path, true)) {
dev_err(&csdev->dev, "building path(s) failed\n");
goto out;
}
if (coresight_enable_source(csdev))
dev_err(&csdev->dev, "source enable failed\n");
out:
mutex_unlock(&coresight_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(coresight_enable);
void coresight_disable(struct coresight_device *csdev)
{
LIST_HEAD(path);
mutex_lock(&coresight_mutex);
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", __func__);
goto out;
}
if (!csdev->enable)
goto out;
coresight_disable_source(csdev);
if (coresight_build_paths(csdev, &path, false))
dev_err(&csdev->dev, "releasing path(s) failed\n");
out:
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_disable);
static ssize_t enable_sink_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->activated);
}
static ssize_t enable_sink_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val)
csdev->activated = true;
else
csdev->activated = false;
return size;
}
static DEVICE_ATTR_RW(enable_sink);
static ssize_t enable_source_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->enable);
}
static ssize_t enable_source_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret = 0;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val) {
ret = coresight_enable(csdev);
if (ret)
return ret;
} else {
coresight_disable(csdev);
}
return size;
}
static DEVICE_ATTR_RW(enable_source);
static struct attribute *coresight_sink_attrs[] = {
&dev_attr_enable_sink.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_sink);
static struct attribute *coresight_source_attrs[] = {
&dev_attr_enable_source.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_source);
static struct device_type coresight_dev_type[] = {
{
.name = "none",
},
{
.name = "sink",
.groups = coresight_sink_groups,
},
{
.name = "link",
},
{
.name = "linksink",
.groups = coresight_sink_groups,
},
{
.name = "source",
.groups = coresight_source_groups,
},
};
static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);
kfree(csdev);
}
static int coresight_orphan_match(struct device *dev, void *data)
{
int i;
bool still_orphan = false;
struct coresight_device *csdev, *i_csdev;
struct coresight_connection *conn;
csdev = data;
i_csdev = to_coresight_device(dev);
/* No need to check oneself */
if (csdev == i_csdev)
return 0;
/* Move on to another component if no connection is orphan */
if (!i_csdev->orphan)
return 0;
/*
* Circle throuch all the connection of that component. If we find
* an orphan connection whose name matches @csdev, link it.
*/
for (i = 0; i < i_csdev->nr_outport; i++) {
conn = &i_csdev->conns[i];
/* We have found at least one orphan connection */
if (conn->child_dev == NULL) {
/* Does it match this newly added device? */
if (!strcmp(dev_name(&csdev->dev), conn->child_name)) {
conn->child_dev = csdev;
} else {
/* This component still has an orphan */
still_orphan = true;
}
}
}
i_csdev->orphan = still_orphan;
/*
* Returning '0' ensures that all known component on the
* bus will be checked.
*/
return 0;
}
static void coresight_fixup_orphan_conns(struct coresight_device *csdev)
{
/*
* No need to check for a return value as orphan connection(s)
* are hooked-up with each newly added component.
*/
bus_for_each_dev(&coresight_bustype, NULL,
csdev, coresight_orphan_match);
}
static int coresight_name_match(struct device *dev, void *data)
{
char *to_match;
struct coresight_device *i_csdev;
to_match = data;
i_csdev = to_coresight_device(dev);
if (!strcmp(to_match, dev_name(&i_csdev->dev)))
return 1;
return 0;
}
static void coresight_fixup_device_conns(struct coresight_device *csdev)
{
int i;
struct device *dev = NULL;
struct coresight_connection *conn;
for (i = 0; i < csdev->nr_outport; i++) {
conn = &csdev->conns[i];
dev = bus_find_device(&coresight_bustype, NULL,
(void *)conn->child_name,
coresight_name_match);
if (dev) {
conn->child_dev = to_coresight_device(dev);
} else {
csdev->orphan = true;
conn->child_dev = NULL;
}
}
}
/**
* coresight_timeout - loop until a bit has changed to a specific state.
* @addr: base address of the area of interest.
* @offset: address of a register, starting from @addr.
* @position: the position of the bit of interest.
* @value: the value the bit should have.
*
* Return: 0 as soon as the bit has taken the desired state or -EAGAIN if
* TIMEOUT_US has elapsed, which ever happens first.
*/
int coresight_timeout(void __iomem *addr, u32 offset, int position, int value)
{
int i;
u32 val;
for (i = TIMEOUT_US; i > 0; i--) {
val = __raw_readl(addr + offset);
/* waiting on the bit to go from 0 to 1 */
if (value) {
if (val & BIT(position))
return 0;
/* waiting on the bit to go from 1 to 0 */
} else {
if (!(val & BIT(position)))
return 0;
}
/*
* Delay is arbitrary - the specification doesn't say how long
* we are expected to wait. Extra check required to make sure
* we don't wait needlessly on the last iteration.
*/
if (i - 1)
udelay(1);
}
return -EAGAIN;
}
struct bus_type coresight_bustype = {
.name = "coresight",
};
static int __init coresight_init(void)
{
return bus_register(&coresight_bustype);
}
postcore_initcall(coresight_init);
struct coresight_device *coresight_register(struct coresight_desc *desc)
{
int i;
int ret;
int link_subtype;
int nr_refcnts = 1;
atomic_t *refcnts = NULL;
struct coresight_device *csdev;
struct coresight_connection *conns;
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
if (!csdev) {
ret = -ENOMEM;
goto err_kzalloc_csdev;
}
if (desc->type == CORESIGHT_DEV_TYPE_LINK ||
desc->type == CORESIGHT_DEV_TYPE_LINKSINK) {
link_subtype = desc->subtype.link_subtype;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
nr_refcnts = desc->pdata->nr_inport;
else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
nr_refcnts = desc->pdata->nr_outport;
}
refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL);
if (!refcnts) {
ret = -ENOMEM;
goto err_kzalloc_refcnts;
}
csdev->refcnt = refcnts;
csdev->nr_inport = desc->pdata->nr_inport;
csdev->nr_outport = desc->pdata->nr_outport;
conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL);
if (!conns) {
ret = -ENOMEM;
goto err_kzalloc_conns;
}
for (i = 0; i < csdev->nr_outport; i++) {
conns[i].outport = desc->pdata->outports[i];
conns[i].child_name = desc->pdata->child_names[i];
conns[i].child_port = desc->pdata->child_ports[i];
}
csdev->conns = conns;
csdev->type = desc->type;
csdev->subtype = desc->subtype;
csdev->ops = desc->ops;
csdev->orphan = false;
csdev->dev.type = &coresight_dev_type[desc->type];
csdev->dev.groups = desc->groups;
csdev->dev.parent = desc->dev;
csdev->dev.release = coresight_device_release;
csdev->dev.bus = &coresight_bustype;
dev_set_name(&csdev->dev, "%s", desc->pdata->name);
ret = device_register(&csdev->dev);
if (ret)
goto err_device_register;
mutex_lock(&coresight_mutex);
coresight_fixup_device_conns(csdev);
coresight_fixup_orphan_conns(csdev);
mutex_unlock(&coresight_mutex);
return csdev;
err_device_register:
kfree(conns);
err_kzalloc_conns:
kfree(refcnts);
err_kzalloc_refcnts:
kfree(csdev);
err_kzalloc_csdev:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(coresight_register);
void coresight_unregister(struct coresight_device *csdev)
{
mutex_lock(&coresight_mutex);
kfree(csdev->conns);
device_unregister(&csdev->dev);
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_unregister);
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,200 @@
/* Copyright (c) 2012, 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/module.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
#include <linux/coresight.h>
#include <linux/cpumask.h>
#include <asm/smp_plat.h>
static int of_dev_node_match(struct device *dev, void *data)
{
return dev->of_node == data;
}
static struct device *
of_coresight_get_endpoint_device(struct device_node *endpoint)
{
struct device *dev = NULL;
/*
* If we have a non-configuable replicator, it will be found on the
* platform bus.
*/
dev = bus_find_device(&platform_bus_type, NULL,
endpoint, of_dev_node_match);
if (dev)
return dev;
/*
* We have a configurable component - circle through the AMBA bus
* looking for the device that matches the endpoint node.
*/
return bus_find_device(&amba_bustype, NULL,
endpoint, of_dev_node_match);
}
static struct device_node *of_get_coresight_endpoint(
const struct device_node *parent, struct device_node *prev)
{
struct device_node *node = of_graph_get_next_endpoint(parent, prev);
of_node_put(prev);
return node;
}
static void of_coresight_get_ports(struct device_node *node,
int *nr_inport, int *nr_outport)
{
struct device_node *ep = NULL;
int in = 0, out = 0;
do {
ep = of_get_coresight_endpoint(node, ep);
if (!ep)
break;
if (of_property_read_bool(ep, "slave-mode"))
in++;
else
out++;
} while (ep);
*nr_inport = in;
*nr_outport = out;
}
static int of_coresight_alloc_memory(struct device *dev,
struct coresight_platform_data *pdata)
{
/* List of output port on this component */
pdata->outports = devm_kzalloc(dev, pdata->nr_outport *
sizeof(*pdata->outports),
GFP_KERNEL);
if (!pdata->outports)
return -ENOMEM;
/* Children connected to this component via @outports */
pdata->child_names = devm_kzalloc(dev, pdata->nr_outport *
sizeof(*pdata->child_names),
GFP_KERNEL);
if (!pdata->child_names)
return -ENOMEM;
/* Port number on the child this component is connected to */
pdata->child_ports = devm_kzalloc(dev, pdata->nr_outport *
sizeof(*pdata->child_ports),
GFP_KERNEL);
if (!pdata->child_ports)
return -ENOMEM;
return 0;
}
struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node)
{
int i = 0, ret = 0, cpu;
struct coresight_platform_data *pdata;
struct of_endpoint endpoint, rendpoint;
struct device *rdev;
struct device_node *dn;
struct device_node *ep = NULL;
struct device_node *rparent = NULL;
struct device_node *rport = NULL;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
/* Use device name as sysfs handle */
pdata->name = dev_name(dev);
/* Get the number of input and output port for this component */
of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport);
if (pdata->nr_outport) {
ret = of_coresight_alloc_memory(dev, pdata);
if (ret)
return ERR_PTR(ret);
/* Iterate through each port to discover topology */
do {
/* Get a handle on a port */
ep = of_get_coresight_endpoint(node, ep);
if (!ep)
break;
/*
* No need to deal with input ports, processing for as
* processing for output ports will deal with them.
*/
if (of_find_property(ep, "slave-mode", NULL))
continue;
/* Get a handle on the local endpoint */
ret = of_graph_parse_endpoint(ep, &endpoint);
if (ret)
continue;
/* The local out port number */
pdata->outports[i] = endpoint.id;
/*
* Get a handle on the remote port and parent
* attached to it.
*/
rparent = of_graph_get_remote_port_parent(ep);
rport = of_graph_get_remote_port(ep);
if (!rparent || !rport)
continue;
if (of_graph_parse_endpoint(rport, &rendpoint))
continue;
rdev = of_coresight_get_endpoint_device(rparent);
if (!rdev)
continue;
pdata->child_names[i] = dev_name(rdev);
pdata->child_ports[i] = rendpoint.id;
i++;
} while (ep);
}
/* Affinity defaults to CPU0 */
pdata->cpu = 0;
dn = of_parse_phandle(node, "cpu", 0);
for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) {
if (dn == of_get_cpu_node(cpu, NULL)) {
pdata->cpu = cpu;
break;
}
}
return pdata;
}
EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);