M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
menuconfig CORESIGHT
bool "CoreSight Tracing Support"
help
CoreSight components are compliant with the ARM CoreSight
architecture specification and can be connected in various
topologies to suite a particular SoCs tracing needs. These trace
components can generally be classified as sources, links and
sinks. Trace data produced by one or more sources flows through
the intermediate links connecting the source to the currently
selected sink.
This framework provides an interface for the CoreSight debug and
trace drivers to register themselves with. It's intended to build
up a topological view of the CoreSight components and configure
the right series of components on user input via sysfs. It also
provides status information to user space applications through
sysfs interface.
If unsure, say 'N' here to avoid potential power, performance and
memory penalty.
if CORESIGHT
config HAVE_CORESIGHT_SINK
bool
config CORESIGHT_CTI
bool "CoreSight Cross Trigger Interface driver"
help
This driver provides support for Cross Trigger Interface that is
used to input or output i.e. pass cross trigger events from one
hardware component to another. It can also be used to pass
software generated events.
config CORESIGHT_CSR
bool "CoreSight Slave Register driver"
help
This driver provides support for CoreSight Slave Register block
that hosts miscellaneous configuration registers.
config CORESIGHT_TMC
bool "CoreSight Trace Memory Controller driver"
select CORESIGHT_CTI
select CORESIGHT_CSR
select HAVE_CORESIGHT_SINK
help
This driver provides support for Trace Memory Controller which
can be configured as either an ETB (Embedded Trace Buffer),
ETR (Embedded Trace Router) or ETF (Embedded Trace Fifo). It acts
as sink when configured as ETB, ETR or ETF in circular buffer mode
whereas it is a link when configured as ETF in hardware fifo mode.
ETB collects trace data in a circular buffer whereas ETR can be
used to route trace data to memory allocated in RAM. ETF in
circular buffer mode is like an ETB whereas in hardware fifo mode
it is a fifo link.
config CORESIGHT_TPIU
bool "CoreSight Trace Port Interface Unit driver"
select HAVE_CORESIGHT_SINK
help
This driver provides support for Trace Port Interface Unit which
acts as a conduit for offchip trace collection.
config CORESIGHT_ETB
bool "CoreSight Embedded Trace Buffer driver"
select HAVE_CORESIGHT_SINK
help
This driver provides support for the legacy Embedded Trace Buffer
which is a circular buffer.
if HAVE_CORESIGHT_SINK
config CORESIGHT_FUNNEL
bool "CoreSight Funnel driver"
help
This driver provides support for Funnel which is a link that
typically has multiple input ports and a single output port. Input
trace data streams from the input ports are interleaved into a
single output trace data stream coming out of the output port.
config CORESIGHT_REPLICATOR
bool "CoreSight Replicator driver"
help
This driver provides support for Replicator that typically has
a single input port and two output ports. Single trace data
stream on the input port is replicated to produce two identical
trace data output streams coming out of the two output ports.
config CORESIGHT_STM
bool "CoreSight System Trace Macrocell driver"
help
This driver provides support for hardware assisted software
instrumentation based tracing. This is primarily useful for
logging useful software events or data.
config CORESIGHT_STM_DEFAULT_ENABLE
bool "Turn on STM tracing by default"
depends on CORESIGHT_STM
help
Turns on CoreSight STM tracing (hardware assisted software
instrumentation based tracing) by default. Otherwise, tracing is
disabled by default but can be enabled via sysfs.
If unsure, say 'N' here to avoid potential power and performance
penalty.
config CORESIGHT_HWEVENT
bool "CoreSight Hardware Event driver"
depends on CORESIGHT_STM
select CORESIGHT_CSR
help
This driver provides support for monitoring and tracing CoreSight
Hardware Event across STM interface. It configures Coresight
Hardware Event mux control registers to select hardware events
based on user input.
config CORESIGHT_ETM
bool "CoreSight Embedded Trace Macrocell driver"
help
This driver provides support for processor tracing which allows
tracing the instructions that the processor is executing. This is
primarily useful for instruction level tracing.
config CORESIGHT_ETM_DEFAULT_ENABLE
bool "Turn on ETM tracing by default"
depends on CORESIGHT_ETM
help
Turns on CoreSight ETM tracing (processor tracing) by default.
Otherwise, tracing is disabled by default but can be enabled via
sysfs.
If unsure, say 'N' here to avoid potential power and performance
penalty.
config CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE
bool "Turn on PC saving by default"
depends on CORESIGHT_ETM
help
Turns on program counter saving on reset by default. Otherwise,
PC saving is disabled by default but can be enabled via sysfs.
If unsure, say 'N' here to avoid potential power penalty.
endif
config CORESIGHT_EVENT
tristate "CoreSight Event driver"
help
This driver provides support for registering with various events
and performing CoreSight actions like aborting trace on their
occurrence.
endif
+15
View File
@@ -0,0 +1,15 @@
#
# Makefile for CoreSight drivers.
#
obj-$(CONFIG_CORESIGHT) += coresight.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_TMC) += coresight-tmc.o
obj-$(CONFIG_CORESIGHT_TPIU) += coresight-tpiu.o
obj-$(CONFIG_CORESIGHT_ETB) += coresight-etb.o
obj-$(CONFIG_CORESIGHT_FUNNEL) += coresight-funnel.o
obj-$(CONFIG_CORESIGHT_REPLICATOR) += coresight-replicator.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
obj-$(CONFIG_CORESIGHT_ETM) += coresight-etm.o coresight-etm-cp14.o
obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o
+253
View File
@@ -0,0 +1,253 @@
/* Copyright (c) 2012-2013, 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/of.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
#define csr_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define csr_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define CSR_LOCK(drvdata) \
do { \
mb(); \
csr_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define CSR_UNLOCK(drvdata) \
do { \
csr_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#define CSR_SWDBGPWRCTRL (0x000)
#define CSR_SWDBGPWRACK (0x004)
#define CSR_SWSPADREG0 (0x008)
#define CSR_SWSPADREG1 (0x00C)
#define CSR_STMTRANSCTRL (0x010)
#define CSR_STMAWIDCTRL (0x014)
#define CSR_STMCHNOFST0 (0x018)
#define CSR_STMCHNOFST1 (0x01C)
#define CSR_STMEXTHWCTRL0 (0x020)
#define CSR_STMEXTHWCTRL1 (0x024)
#define CSR_STMEXTHWCTRL2 (0x028)
#define CSR_STMEXTHWCTRL3 (0x02C)
#define CSR_USBBAMCTRL (0x030)
#define CSR_USBFLSHCTRL (0x034)
#define CSR_TIMESTAMPCTRL (0x038)
#define CSR_AOTIMEVAL0 (0x03C)
#define CSR_AOTIMEVAL1 (0x040)
#define CSR_QDSSTIMEVAL0 (0x044)
#define CSR_QDSSTIMEVAL1 (0x048)
#define CSR_QDSSTIMELOAD0 (0x04C)
#define CSR_QDSSTIMELOAD1 (0x050)
#define CSR_DAPMSAVAL (0x054)
#define CSR_QDSSCLKVOTE (0x058)
#define CSR_QDSSCLKIPI (0x05C)
#define CSR_QDSSPWRREQIGNORE (0x060)
#define CSR_QDSSSPARE (0x064)
#define CSR_IPCAT (0x068)
#define BLKSIZE_256 0
#define BLKSIZE_512 1
#define BLKSIZE_1024 2
#define BLKSIZE_2048 3
struct csr_drvdata {
void __iomem *base;
phys_addr_t pbase;
struct device *dev;
struct coresight_device *csdev;
uint32_t blksize;
};
static struct csr_drvdata *csrdrvdata;
void msm_qdss_csr_enable_bam_to_usb(void)
{
struct csr_drvdata *drvdata = csrdrvdata;
uint32_t usbbamctrl, usbflshctrl;
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
usbbamctrl = (usbbamctrl & ~0x3) | drvdata->blksize;
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl = (usbflshctrl & ~0x3FFFC) | (0x1000 << 2);
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
usbflshctrl |= 0x2;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
usbbamctrl |= 0x4;
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
CSR_LOCK(drvdata);
}
EXPORT_SYMBOL(msm_qdss_csr_enable_bam_to_usb);
void msm_qdss_csr_disable_bam_to_usb(void)
{
struct csr_drvdata *drvdata = csrdrvdata;
uint32_t usbbamctrl;
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
usbbamctrl &= (~0x4);
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
CSR_LOCK(drvdata);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_bam_to_usb);
void msm_qdss_csr_disable_flush(void)
{
struct csr_drvdata *drvdata = csrdrvdata;
uint32_t usbflshctrl;
CSR_UNLOCK(drvdata);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl &= ~0x2;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_flush);
int coresight_csr_hwctrl_set(phys_addr_t addr, uint32_t val)
{
struct csr_drvdata *drvdata = csrdrvdata;
int ret = 0;
CSR_UNLOCK(drvdata);
if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL0))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL0);
else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL1))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL1);
else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL2))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL2);
else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL3))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL3);
else
ret = -EINVAL;
CSR_LOCK(drvdata);
return ret;
}
EXPORT_SYMBOL(coresight_csr_hwctrl_set);
static int __devinit csr_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct csr_drvdata *drvdata;
struct resource *res;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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;
/* Store the driver data pointer for use in exported functions */
csrdrvdata = drvdata;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base");
if (!res)
return -ENODEV;
drvdata->pbase = res->start;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
if (pdev->dev.of_node) {
ret = of_property_read_u32(pdev->dev.of_node, "qcom,blk-size",
&drvdata->blksize);
if (ret)
drvdata->blksize = BLKSIZE_256;
}
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_NONE;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "CSR initialized\n");
return 0;
}
static int __devexit csr_remove(struct platform_device *pdev)
{
struct csr_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id csr_match[] = {
{.compatible = "qcom,coresight-csr"},
{}
};
static struct platform_driver csr_driver = {
.probe = csr_probe,
.remove = __devexit_p(csr_remove),
.driver = {
.name = "coresight-csr",
.owner = THIS_MODULE,
.of_match_table = csr_match,
},
};
static int __init csr_init(void)
{
return platform_driver_register(&csr_driver);
}
module_init(csr_init);
static void __exit csr_exit(void)
{
platform_driver_unregister(&csr_driver);
}
module_exit(csr_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight CSR driver");
+481
View File
@@ -0,0 +1,481 @@
/* Copyright (c) 2013, 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/mutex.h>
#include <linux/clk.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include <linux/coresight-cti.h>
#include "coresight-priv.h"
#define cti_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define cti_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define CTI_LOCK(drvdata) \
do { \
mb(); \
cti_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define CTI_UNLOCK(drvdata) \
do { \
cti_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#define CTICONTROL (0x000)
#define CTIINTACK (0x010)
#define CTIAPPSET (0x014)
#define CTIAPPCLEAR (0x018)
#define CTIAPPPULSE (0x01C)
#define CTIINEN(n) (0x020 + (n * 4))
#define CTIOUTEN(n) (0x0A0 + (n * 4))
#define CTITRIGINSTATUS (0x130)
#define CTITRIGOUTSTATUS (0x134)
#define CTICHINSTATUS (0x138)
#define CTICHOUTSTATUS (0x13C)
#define CTIGATE (0x140)
#define ASICCTL (0x144)
#define ITCHINACK (0xEDC)
#define ITTRIGINACK (0xEE0)
#define ITCHOUT (0xEE4)
#define ITTRIGOUT (0xEE8)
#define ITCHOUTACK (0xEEC)
#define ITTRIGOUTACK (0xEF0)
#define ITCHIN (0xEF4)
#define ITTRIGIN (0xEF8)
#define CTI_MAX_TRIGGERS (8)
#define CTI_MAX_CHANNELS (4)
#define to_cti_drvdata(c) container_of(c, struct cti_drvdata, cti)
struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
struct mutex mutex;
struct coresight_cti cti;
int refcnt;
};
static LIST_HEAD(cti_list);
static DEFINE_MUTEX(cti_lock);
static int cti_verify_bounds(int trig, int ch)
{
if (trig >= CTI_MAX_TRIGGERS)
return -EINVAL;
if (ch >= CTI_MAX_CHANNELS)
return -EINVAL;
return 0;
}
static int cti_enable(struct cti_drvdata *drvdata)
{
int ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
CTI_UNLOCK(drvdata);
cti_writel(drvdata, 0x1, CTICONTROL);
CTI_LOCK(drvdata);
return 0;
}
static void __cti_map_trigin(struct cti_drvdata *drvdata, int trig, int ch)
{
uint32_t ctien;
CTI_UNLOCK(drvdata);
ctien = cti_readl(drvdata, CTIINEN(trig));
cti_writel(drvdata, (ctien | 0x1 << ch), CTIINEN(trig));
CTI_LOCK(drvdata);
}
int coresight_cti_map_trigin(struct coresight_cti *cti, int trig, int ch)
{
struct cti_drvdata *drvdata;
int ret = 0;
if (IS_ERR_OR_NULL(cti))
return -EINVAL;
ret = cti_verify_bounds(trig, ch);
if (ret)
return ret;
drvdata = to_cti_drvdata(cti);
mutex_lock(&drvdata->mutex);
if (drvdata->refcnt == 0) {
ret = cti_enable(drvdata);
if (ret)
goto err;
}
drvdata->refcnt++;
__cti_map_trigin(drvdata, trig, ch);
err:
mutex_unlock(&drvdata->mutex);
return ret;
}
EXPORT_SYMBOL(coresight_cti_map_trigin);
static void __cti_map_trigout(struct cti_drvdata *drvdata, int trig, int ch)
{
uint32_t ctien;
CTI_UNLOCK(drvdata);
ctien = cti_readl(drvdata, CTIOUTEN(trig));
cti_writel(drvdata, (ctien | 0x1 << ch), CTIOUTEN(trig));
CTI_LOCK(drvdata);
}
int coresight_cti_map_trigout(struct coresight_cti *cti, int trig, int ch)
{
struct cti_drvdata *drvdata;
int ret = 0;
if (IS_ERR_OR_NULL(cti))
return -EINVAL;
ret = cti_verify_bounds(trig, ch);
if (ret)
return ret;
drvdata = to_cti_drvdata(cti);
mutex_lock(&drvdata->mutex);
if (drvdata->refcnt == 0) {
ret = cti_enable(drvdata);
if (ret)
goto err;
}
drvdata->refcnt++;
__cti_map_trigout(drvdata, trig, ch);
err:
mutex_unlock(&drvdata->mutex);
return ret;
}
EXPORT_SYMBOL(coresight_cti_map_trigout);
static void cti_disable(struct cti_drvdata *drvdata)
{
CTI_UNLOCK(drvdata);
cti_writel(drvdata, 0x1, CTICONTROL);
CTI_LOCK(drvdata);
}
static void __cti_unmap_trigin(struct cti_drvdata *drvdata, int trig, int ch)
{
uint32_t ctien;
CTI_UNLOCK(drvdata);
ctien = cti_readl(drvdata, CTIINEN(trig));
cti_writel(drvdata, (ctien & ~(0x1 << ch)), CTIINEN(trig));
CTI_LOCK(drvdata);
}
void coresight_cti_unmap_trigin(struct coresight_cti *cti, int trig, int ch)
{
struct cti_drvdata *drvdata;
if (IS_ERR_OR_NULL(cti))
return;
if (cti_verify_bounds(trig, ch))
return;
drvdata = to_cti_drvdata(cti);
mutex_lock(&drvdata->mutex);
__cti_unmap_trigin(drvdata, trig, ch);
if (drvdata->refcnt == 1)
cti_disable(drvdata);
drvdata->refcnt--;
mutex_unlock(&drvdata->mutex);
clk_disable_unprepare(drvdata->clk);
}
EXPORT_SYMBOL(coresight_cti_unmap_trigin);
static void __cti_unmap_trigout(struct cti_drvdata *drvdata, int trig, int ch)
{
uint32_t ctien;
CTI_UNLOCK(drvdata);
ctien = cti_readl(drvdata, CTIOUTEN(trig));
cti_writel(drvdata, (ctien & ~(0x1 << ch)), CTIOUTEN(trig));
CTI_LOCK(drvdata);
}
void coresight_cti_unmap_trigout(struct coresight_cti *cti, int trig, int ch)
{
struct cti_drvdata *drvdata;
if (IS_ERR_OR_NULL(cti))
return;
if (cti_verify_bounds(trig, ch))
return;
drvdata = to_cti_drvdata(cti);
mutex_lock(&drvdata->mutex);
__cti_unmap_trigout(drvdata, trig, ch);
if (drvdata->refcnt == 1)
cti_disable(drvdata);
drvdata->refcnt--;
mutex_unlock(&drvdata->mutex);
clk_disable_unprepare(drvdata->clk);
}
EXPORT_SYMBOL(coresight_cti_unmap_trigout);
struct coresight_cti *coresight_cti_get(const char *name)
{
struct coresight_cti *cti;
mutex_lock(&cti_lock);
list_for_each_entry(cti, &cti_list, link) {
if (!strncmp(cti->name, name, strlen(cti->name) + 1)) {
mutex_unlock(&cti_lock);
return cti;
}
}
mutex_unlock(&cti_lock);
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL(coresight_cti_get);
void coresight_cti_put(struct coresight_cti *cti)
{
}
EXPORT_SYMBOL(coresight_cti_put);
static ssize_t cti_store_map_trigin(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val1, val2;
int ret;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
return -EINVAL;
ret = coresight_cti_map_trigin(&drvdata->cti, val1, val2);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(map_trigin, S_IWUSR, NULL, cti_store_map_trigin);
static ssize_t cti_store_map_trigout(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val1, val2;
int ret;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
return -EINVAL;
ret = coresight_cti_map_trigout(&drvdata->cti, val1, val2);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(map_trigout, S_IWUSR, NULL, cti_store_map_trigout);
static ssize_t cti_store_unmap_trigin(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val1, val2;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
return -EINVAL;
coresight_cti_unmap_trigin(&drvdata->cti, val1, val2);
return size;
}
static DEVICE_ATTR(unmap_trigin, S_IWUSR, NULL, cti_store_unmap_trigin);
static ssize_t cti_store_unmap_trigout(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val1, val2;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
return -EINVAL;
coresight_cti_unmap_trigout(&drvdata->cti, val1, val2);
return size;
}
static DEVICE_ATTR(unmap_trigout, S_IWUSR, NULL, cti_store_unmap_trigout);
static struct attribute *cti_attrs[] = {
&dev_attr_map_trigin.attr,
&dev_attr_map_trigout.attr,
&dev_attr_unmap_trigin.attr,
&dev_attr_unmap_trigout.attr,
NULL,
};
static struct attribute_group cti_attr_grp = {
.attrs = cti_attrs,
};
static const struct attribute_group *cti_attr_grps[] = {
&cti_attr_grp,
NULL,
};
static int __devinit cti_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct cti_drvdata *drvdata;
struct resource *res;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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;
/* Store the driver data pointer for use in exported functions */
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cti-base");
if (!res)
return -ENODEV;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
mutex_init(&drvdata->mutex);
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
mutex_lock(&cti_lock);
drvdata->cti.name = ((struct coresight_platform_data *)
(pdev->dev.platform_data))->name;
list_add_tail(&drvdata->cti.link, &cti_list);
mutex_unlock(&cti_lock);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_NONE;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->groups = cti_attr_grps;
desc->owner = THIS_MODULE;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "CTI initialized\n");
return 0;
}
static int __devexit cti_remove(struct platform_device *pdev)
{
struct cti_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id cti_match[] = {
{.compatible = "arm,coresight-cti"},
{}
};
static struct platform_driver cti_driver = {
.probe = cti_probe,
.remove = __devexit_p(cti_remove),
.driver = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.of_match_table = cti_match,
},
};
static int __init cti_init(void)
{
return platform_driver_register(&cti_driver);
}
module_init(cti_init);
static void __exit cti_exit(void)
{
platform_driver_unregister(&cti_driver);
}
module_exit(cti_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight CTI driver");
+467
View File
@@ -0,0 +1,467 @@
/* Copyright (c) 2011-2013, 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/platform_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/delay.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
#define etb_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define etb_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define ETB_LOCK(drvdata) \
do { \
mb(); \
etb_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define ETB_UNLOCK(drvdata) \
do { \
etb_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#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)
#define BYTES_PER_WORD 4
#define ETB_SIZE_WORDS 4096
#define FRAME_SIZE_WORDS 4
struct etb_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
struct clk *clk;
spinlock_t spinlock;
bool reading;
atomic_t in_use;
uint8_t *buf;
bool enable;
uint32_t trigger_cntr;
};
static void __etb_enable(struct etb_drvdata *drvdata)
{
int i;
ETB_UNLOCK(drvdata);
etb_writel(drvdata, 0x0, ETB_RAM_WRITE_POINTER);
for (i = 0; i < ETB_SIZE_WORDS; i++)
etb_writel(drvdata, 0x0, ETB_RWD_REG);
etb_writel(drvdata, 0x0, ETB_RAM_WRITE_POINTER);
etb_writel(drvdata, 0x0, ETB_RAM_READ_POINTER);
etb_writel(drvdata, drvdata->trigger_cntr, ETB_TRG);
etb_writel(drvdata, BIT(13) | BIT(0), ETB_FFCR);
etb_writel(drvdata, BIT(0), ETB_CTL_REG);
ETB_LOCK(drvdata);
}
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(drvdata);
drvdata->enable = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "ETB enabled\n");
return 0;
}
static void __etb_disable(struct etb_drvdata *drvdata)
{
int count;
uint32_t ffcr;
ETB_UNLOCK(drvdata);
ffcr = etb_readl(drvdata, ETB_FFCR);
ffcr |= BIT(12);
etb_writel(drvdata, ffcr, ETB_FFCR);
ffcr |= BIT(6);
etb_writel(drvdata, ffcr, ETB_FFCR);
for (count = TIMEOUT_US; BVAL(etb_readl(drvdata, ETB_FFCR), 6) != 0
&& count > 0; count--)
udelay(1);
WARN(count == 0, "timeout while flushing DRVDATA, ETB_FFCR: %#x\n",
etb_readl(drvdata, ETB_FFCR));
etb_writel(drvdata, 0x0, ETB_CTL_REG);
for (count = TIMEOUT_US; BVAL(etb_readl(drvdata, ETB_FFSR), 1) != 1
&& count > 0; count--)
udelay(1);
WARN(count == 0, "timeout while disabling DRVDATA, ETB_FFSR: %#x\n",
etb_readl(drvdata, ETB_FFSR));
ETB_LOCK(drvdata);
}
static void __etb_dump(struct etb_drvdata *drvdata)
{
int i;
uint8_t *buf_ptr;
uint32_t read_data;
uint32_t read_ptr;
uint32_t write_ptr;
uint32_t frame_off;
uint32_t frame_endoff;
ETB_UNLOCK(drvdata);
read_ptr = etb_readl(drvdata, ETB_RAM_READ_POINTER);
write_ptr = etb_readl(drvdata, ETB_RAM_WRITE_POINTER);
frame_off = write_ptr % FRAME_SIZE_WORDS;
frame_endoff = 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 ((etb_readl(drvdata, ETB_STATUS_REG) & BIT(0)) == 0)
etb_writel(drvdata, 0x0, ETB_RAM_READ_POINTER);
else
etb_writel(drvdata, write_ptr, ETB_RAM_READ_POINTER);
buf_ptr = drvdata->buf;
for (i = 0; i < ETB_SIZE_WORDS; i++) {
read_data = etb_readl(drvdata, 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 * BYTES_PER_WORD);
for (i = 0; i < frame_endoff; i++) {
*buf_ptr++ = 0x0;
*buf_ptr++ = 0x0;
*buf_ptr++ = 0x0;
*buf_ptr++ = 0x0;
}
}
etb_writel(drvdata, read_ptr, ETB_RAM_READ_POINTER);
ETB_LOCK(drvdata);
}
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(drvdata);
__etb_dump(drvdata);
drvdata->enable = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "ETB disabled\n");
}
static void etb_abort(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(drvdata);
__etb_dump(drvdata);
drvdata->enable = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "ETB aborted\n");
}
static const struct coresight_ops_sink etb_sink_ops = {
.enable = etb_enable,
.disable = etb_disable,
.abort = etb_abort,
};
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(drvdata);
__etb_dump(drvdata);
__etb_enable(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)
{
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
if (drvdata->reading == false) {
etb_dump(drvdata);
drvdata->reading = true;
}
if (*ppos + len > ETB_SIZE_WORDS * BYTES_PER_WORD)
len = ETB_SIZE_WORDS * BYTES_PER_WORD - *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: %d bytes copied, %d bytes left\n",
__func__, len, (int) (ETB_SIZE_WORDS * BYTES_PER_WORD - *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);
drvdata->reading = false;
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 etb_show_trigger_cntr(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 scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t etb_store_trigger_cntr(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
drvdata->trigger_cntr = val;
return size;
}
static DEVICE_ATTR(trigger_cntr, S_IRUGO | S_IWUSR, etb_show_trigger_cntr,
etb_store_trigger_cntr);
static struct attribute *etb_attrs[] = {
&dev_attr_trigger_cntr.attr,
NULL,
};
static struct attribute_group etb_attr_grp = {
.attrs = etb_attrs,
};
static const struct attribute_group *etb_attr_grps[] = {
&etb_attr_grp,
NULL,
};
static int __devinit etb_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct etb_drvdata *drvdata;
struct resource *res;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "etb-base");
if (!res)
return -ENODEV;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
spin_lock_init(&drvdata->spinlock);
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
drvdata->buf = devm_kzalloc(dev, ETB_SIZE_WORDS * BYTES_PER_WORD,
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 = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->groups = etb_attr_grps;
desc->owner = THIS_MODULE;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
drvdata->miscdev.name = ((struct coresight_platform_data *)
(pdev->dev.platform_data))->name;
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
drvdata->miscdev.fops = &etb_fops;
ret = misc_register(&drvdata->miscdev);
if (ret)
goto err;
dev_info(dev, "ETB initialized\n");
return 0;
err:
coresight_unregister(drvdata->csdev);
return ret;
}
static int __devexit etb_remove(struct platform_device *pdev)
{
struct etb_drvdata *drvdata = platform_get_drvdata(pdev);
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id etb_match[] = {
{.compatible = "arm,coresight-etb"},
{}
};
static struct platform_driver etb_driver = {
.probe = etb_probe,
.remove = __devexit_p(etb_remove),
.driver = {
.name = "coresight-etb",
.owner = THIS_MODULE,
.of_match_table = etb_match,
},
};
static int __init etb_init(void)
{
return platform_driver_register(&etb_driver);
}
module_init(etb_init);
static void __exit etb_exit(void)
{
platform_driver_unregister(&etb_driver);
}
module_exit(etb_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver");
@@ -0,0 +1,510 @@
/* 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/types.h>
#include <asm/hardware/cp14.h>
static unsigned int etm_read_reg(uint32_t reg)
{
switch (reg) {
case 0x0:
return etm_read(ETMCR);
case 0x1:
return etm_read(ETMCCR);
case 0x2:
return etm_read(ETMTRIGGER);
case 0x4:
return etm_read(ETMSR);
case 0x5:
return etm_read(ETMSCR);
case 0x6:
return etm_read(ETMTSSCR);
case 0x8:
return etm_read(ETMTEEVR);
case 0x9:
return etm_read(ETMTECR1);
case 0xB:
return etm_read(ETMFFLR);
case 0x10:
return etm_read(ETMACVR0);
case 0x11:
return etm_read(ETMACVR1);
case 0x12:
return etm_read(ETMACVR2);
case 0x13:
return etm_read(ETMACVR3);
case 0x14:
return etm_read(ETMACVR4);
case 0x15:
return etm_read(ETMACVR5);
case 0x16:
return etm_read(ETMACVR6);
case 0x17:
return etm_read(ETMACVR7);
case 0x18:
return etm_read(ETMACVR8);
case 0x19:
return etm_read(ETMACVR9);
case 0x1A:
return etm_read(ETMACVR10);
case 0x1B:
return etm_read(ETMACVR11);
case 0x1C:
return etm_read(ETMACVR12);
case 0x1D:
return etm_read(ETMACVR13);
case 0x1E:
return etm_read(ETMACVR14);
case 0x1F:
return etm_read(ETMACVR15);
case 0x20:
return etm_read(ETMACTR0);
case 0x21:
return etm_read(ETMACTR1);
case 0x22:
return etm_read(ETMACTR2);
case 0x23:
return etm_read(ETMACTR3);
case 0x24:
return etm_read(ETMACTR4);
case 0x25:
return etm_read(ETMACTR5);
case 0x26:
return etm_read(ETMACTR6);
case 0x27:
return etm_read(ETMACTR7);
case 0x28:
return etm_read(ETMACTR8);
case 0x29:
return etm_read(ETMACTR9);
case 0x2A:
return etm_read(ETMACTR10);
case 0x2B:
return etm_read(ETMACTR11);
case 0x2C:
return etm_read(ETMACTR12);
case 0x2D:
return etm_read(ETMACTR13);
case 0x2E:
return etm_read(ETMACTR14);
case 0x2F:
return etm_read(ETMACTR15);
case 0x50:
return etm_read(ETMCNTRLDVR0);
case 0x51:
return etm_read(ETMCNTRLDVR1);
case 0x52:
return etm_read(ETMCNTRLDVR2);
case 0x53:
return etm_read(ETMCNTRLDVR3);
case 0x54:
return etm_read(ETMCNTENR0);
case 0x55:
return etm_read(ETMCNTENR1);
case 0x56:
return etm_read(ETMCNTENR2);
case 0x57:
return etm_read(ETMCNTENR3);
case 0x58:
return etm_read(ETMCNTRLDEVR0);
case 0x59:
return etm_read(ETMCNTRLDEVR1);
case 0x5A:
return etm_read(ETMCNTRLDEVR2);
case 0x5B:
return etm_read(ETMCNTRLDEVR3);
case 0x5C:
return etm_read(ETMCNTVR0);
case 0x5D:
return etm_read(ETMCNTVR1);
case 0x5E:
return etm_read(ETMCNTVR2);
case 0x5F:
return etm_read(ETMCNTVR3);
case 0x60:
return etm_read(ETMSQ12EVR);
case 0x61:
return etm_read(ETMSQ21EVR);
case 0x62:
return etm_read(ETMSQ23EVR);
case 0x63:
return etm_read(ETMSQ31EVR);
case 0x64:
return etm_read(ETMSQ32EVR);
case 0x65:
return etm_read(ETMSQ13EVR);
case 0x67:
return etm_read(ETMSQR);
case 0x68:
return etm_read(ETMEXTOUTEVR0);
case 0x69:
return etm_read(ETMEXTOUTEVR1);
case 0x6A:
return etm_read(ETMEXTOUTEVR2);
case 0x6B:
return etm_read(ETMEXTOUTEVR3);
case 0x6C:
return etm_read(ETMCIDCVR0);
case 0x6D:
return etm_read(ETMCIDCVR1);
case 0x6E:
return etm_read(ETMCIDCVR2);
case 0x6F:
return etm_read(ETMCIDCMR);
case 0x70:
return etm_read(ETMIMPSPEC0);
case 0x71:
return etm_read(ETMIMPSPEC1);
case 0x72:
return etm_read(ETMIMPSPEC2);
case 0x73:
return etm_read(ETMIMPSPEC3);
case 0x74:
return etm_read(ETMIMPSPEC4);
case 0x75:
return etm_read(ETMIMPSPEC5);
case 0x76:
return etm_read(ETMIMPSPEC6);
case 0x77:
return etm_read(ETMIMPSPEC7);
case 0x78:
return etm_read(ETMSYNCFR);
case 0x79:
return etm_read(ETMIDR);
case 0x7A:
return etm_read(ETMCCER);
case 0x7B:
return etm_read(ETMEXTINSELR);
case 0x7C:
return etm_read(ETMTESSEICR);
case 0x7D:
return etm_read(ETMEIBCR);
case 0x7E:
return etm_read(ETMTSEVR);
case 0x7F:
return etm_read(ETMAUXCR);
case 0x80:
return etm_read(ETMTRACEIDR);
case 0x90:
return etm_read(ETMVMIDCVR);
case 0xC1:
return etm_read(ETMOSLSR);
case 0xC2:
return etm_read(ETMOSSRR);
case 0xC4:
return etm_read(ETMPDCR);
case 0xC5:
return etm_read(ETMPDSR);
default:
WARN(1, "invalid CP14 access to ETM reg: %lx",
(unsigned long)reg);
return 0;
}
}
static void etm_write_reg(uint32_t val, uint32_t reg)
{
switch (reg) {
case 0x0:
etm_write(val, ETMCR);
return;
case 0x2:
etm_write(val, ETMTRIGGER);
return;
case 0x4:
etm_write(val, ETMSR);
return;
case 0x6:
etm_write(val, ETMTSSCR);
return;
case 0x8:
etm_write(val, ETMTEEVR);
return;
case 0x9:
etm_write(val, ETMTECR1);
return;
case 0xB:
etm_write(val, ETMFFLR);
return;
case 0x10:
etm_write(val, ETMACVR0);
return;
case 0x11:
etm_write(val, ETMACVR1);
return;
case 0x12:
etm_write(val, ETMACVR2);
return;
case 0x13:
etm_write(val, ETMACVR3);
return;
case 0x14:
etm_write(val, ETMACVR4);
return;
case 0x15:
etm_write(val, ETMACVR5);
return;
case 0x16:
etm_write(val, ETMACVR6);
return;
case 0x17:
etm_write(val, ETMACVR7);
return;
case 0x18:
etm_write(val, ETMACVR8);
return;
case 0x19:
etm_write(val, ETMACVR9);
return;
case 0x1A:
etm_write(val, ETMACVR10);
return;
case 0x1B:
etm_write(val, ETMACVR11);
return;
case 0x1C:
etm_write(val, ETMACVR12);
return;
case 0x1D:
etm_write(val, ETMACVR13);
return;
case 0x1E:
etm_write(val, ETMACVR14);
return;
case 0x1F:
etm_write(val, ETMACVR15);
return;
case 0x20:
etm_write(val, ETMACTR0);
return;
case 0x21:
etm_write(val, ETMACTR1);
return;
case 0x22:
etm_write(val, ETMACTR2);
return;
case 0x23:
etm_write(val, ETMACTR3);
return;
case 0x24:
etm_write(val, ETMACTR4);
return;
case 0x25:
etm_write(val, ETMACTR5);
return;
case 0x26:
etm_write(val, ETMACTR6);
return;
case 0x27:
etm_write(val, ETMACTR7);
return;
case 0x28:
etm_write(val, ETMACTR8);
return;
case 0x29:
etm_write(val, ETMACTR9);
return;
case 0x2A:
etm_write(val, ETMACTR10);
return;
case 0x2B:
etm_write(val, ETMACTR11);
return;
case 0x2C:
etm_write(val, ETMACTR12);
return;
case 0x2D:
etm_write(val, ETMACTR13);
return;
case 0x2E:
etm_write(val, ETMACTR14);
return;
case 0x2F:
etm_write(val, ETMACTR15);
return;
case 0x50:
etm_write(val, ETMCNTRLDVR0);
return;
case 0x51:
etm_write(val, ETMCNTRLDVR1);
return;
case 0x52:
etm_write(val, ETMCNTRLDVR2);
return;
case 0x53:
etm_write(val, ETMCNTRLDVR3);
return;
case 0x54:
etm_write(val, ETMCNTENR0);
return;
case 0x55:
etm_write(val, ETMCNTENR1);
return;
case 0x56:
etm_write(val, ETMCNTENR2);
return;
case 0x57:
etm_write(val, ETMCNTENR3);
return;
case 0x58:
etm_write(val, ETMCNTRLDEVR0);
return;
case 0x59:
etm_write(val, ETMCNTRLDEVR1);
return;
case 0x5A:
etm_write(val, ETMCNTRLDEVR2);
return;
case 0x5B:
etm_write(val, ETMCNTRLDEVR3);
return;
case 0x5C:
etm_write(val, ETMCNTVR0);
return;
case 0x5D:
etm_write(val, ETMCNTVR1);
return;
case 0x5E:
etm_write(val, ETMCNTVR2);
return;
case 0x5F:
etm_write(val, ETMCNTVR3);
return;
case 0x60:
etm_write(val, ETMSQ12EVR);
return;
case 0x61:
etm_write(val, ETMSQ21EVR);
return;
case 0x62:
etm_write(val, ETMSQ23EVR);
return;
case 0x63:
etm_write(val, ETMSQ31EVR);
return;
case 0x64:
etm_write(val, ETMSQ32EVR);
return;
case 0x65:
etm_write(val, ETMSQ13EVR);
return;
case 0x67:
etm_write(val, ETMSQR);
return;
case 0x68:
etm_write(val, ETMEXTOUTEVR0);
return;
case 0x69:
etm_write(val, ETMEXTOUTEVR1);
return;
case 0x6A:
etm_write(val, ETMEXTOUTEVR2);
return;
case 0x6B:
etm_write(val, ETMEXTOUTEVR3);
return;
case 0x6C:
etm_write(val, ETMCIDCVR0);
return;
case 0x6D:
etm_write(val, ETMCIDCVR1);
return;
case 0x6E:
etm_write(val, ETMCIDCVR2);
return;
case 0x6F:
etm_write(val, ETMCIDCMR);
return;
case 0x70:
etm_write(val, ETMIMPSPEC0);
return;
case 0x71:
etm_write(val, ETMIMPSPEC1);
return;
case 0x72:
etm_write(val, ETMIMPSPEC2);
return;
case 0x73:
etm_write(val, ETMIMPSPEC3);
return;
case 0x74:
etm_write(val, ETMIMPSPEC4);
return;
case 0x75:
etm_write(val, ETMIMPSPEC5);
return;
case 0x76:
etm_write(val, ETMIMPSPEC6);
return;
case 0x77:
etm_write(val, ETMIMPSPEC7);
return;
case 0x78:
etm_write(val, ETMSYNCFR);
return;
case 0x7B:
etm_write(val, ETMEXTINSELR);
return;
case 0x7C:
etm_write(val, ETMTESSEICR);
return;
case 0x7D:
etm_write(val, ETMEIBCR);
return;
case 0x7E:
etm_write(val, ETMTSEVR);
return;
case 0x7F:
etm_write(val, ETMAUXCR);
return;
case 0x80:
etm_write(val, ETMTRACEIDR);
return;
case 0x90:
etm_write(val, ETMVMIDCVR);
return;
case 0xC0:
etm_write(val, ETMOSLAR);
return;
case 0xC2:
etm_write(val, ETMOSSRR);
return;
case 0xC4:
etm_write(val, ETMPDCR);
return;
case 0xC5:
etm_write(val, ETMPDSR);
return;
default:
WARN(1, "invalid CP14 access to ETM reg: %lx",
(unsigned long)reg);
return;
}
}
static inline uint32_t offset_to_reg_num(uint32_t off)
{
return off >> 2;
}
unsigned int etm_readl_cp14(uint32_t off)
{
uint32_t reg = offset_to_reg_num(off);
return etm_read_reg(reg);
}
void etm_writel_cp14(uint32_t val, uint32_t off)
{
uint32_t reg = offset_to_reg_num(off);
etm_write_reg(val, reg);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
/* 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.
*/
/*
* DLKM to register a callback with a ftrace event
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/tracepoint.h>
#include <linux/coresight.h>
#include <trace/events/exception.h>
static void abort_coresight_tracing(void *ignore, struct task_struct *task,\
unsigned long addr, unsigned int fsr)
{
coresight_abort();
pr_debug("control_trace: task_name: %s, addr: %lu, fsr:%u",\
(char *)task->comm, addr, fsr);
}
static void abort_tracing_undef_instr(void *ignore, struct pt_regs *regs,\
void *pc)
{
if (user_mode(regs)) {
coresight_abort();
pr_debug("control_trace: pc: %p", pc);
}
}
static int __init control_trace_init(void)
{
int ret_user_fault, ret_undef_instr;
ret_user_fault = register_trace_user_fault(abort_coresight_tracing,\
NULL);
ret_undef_instr = register_trace_undef_instr(abort_tracing_undef_instr,\
NULL);
if (ret_user_fault != 0 || ret_undef_instr != 0) {
pr_info("control_trace: Module Not Registered\n");
return (ret_user_fault < 0 ?\
ret_user_fault : ret_undef_instr);
}
pr_info("control_trace: Module Registered\n");
return 0;
}
module_init(control_trace_init);
static void __exit control_trace_exit(void)
{
unregister_trace_user_fault(abort_coresight_tracing, NULL);
unregister_trace_undef_instr(abort_tracing_undef_instr, NULL);
pr_info("control_trace: Module Removed\n");
}
module_exit(control_trace_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Kernel Module to abort tracing");
+260
View File
@@ -0,0 +1,260 @@
/* Copyright (c) 2011-2013, 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/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
#define funnel_writel(drvdata, val, off) \
__raw_writel((val), drvdata->base + off)
#define funnel_readl(drvdata, off) \
__raw_readl(drvdata->base + off)
#define FUNNEL_LOCK(drvdata) \
do { \
mb(); \
funnel_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define FUNNEL_UNLOCK(drvdata) \
do { \
funnel_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#define FUNNEL_FUNCTL (0x000)
#define FUNNEL_PRICTL (0x004)
#define FUNNEL_ITATBDATA0 (0xEEC)
#define FUNNEL_ITATBCTR2 (0xEF0)
#define FUNNEL_ITATBCTR1 (0xEF4)
#define FUNNEL_ITATBCTR0 (0xEF8)
#define FUNNEL_HOLDTIME_MASK (0xF00)
#define FUNNEL_HOLDTIME_SHFT (0x8)
#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
struct funnel_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
uint32_t priority;
};
static void __funnel_enable(struct funnel_drvdata *drvdata, int port)
{
uint32_t functl;
FUNNEL_UNLOCK(drvdata);
functl = funnel_readl(drvdata, FUNNEL_FUNCTL);
functl &= ~FUNNEL_HOLDTIME_MASK;
functl |= FUNNEL_HOLDTIME;
functl |= (1 << port);
funnel_writel(drvdata, functl, FUNNEL_FUNCTL);
funnel_writel(drvdata, drvdata->priority, FUNNEL_PRICTL);
FUNNEL_LOCK(drvdata);
}
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(drvdata, inport);
dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
return 0;
}
static void __funnel_disable(struct funnel_drvdata *drvdata, int inport)
{
uint32_t functl;
FUNNEL_UNLOCK(drvdata);
functl = funnel_readl(drvdata, FUNNEL_FUNCTL);
functl &= ~(1 << inport);
funnel_writel(drvdata, functl, FUNNEL_FUNCTL);
FUNNEL_LOCK(drvdata);
}
static void funnel_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
__funnel_disable(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 funnel_show_priority(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->priority;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t funnel_store_priority(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
drvdata->priority = val;
return size;
}
static DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, funnel_show_priority,
funnel_store_priority);
static struct attribute *funnel_attrs[] = {
&dev_attr_priority.attr,
NULL,
};
static struct attribute_group funnel_attr_grp = {
.attrs = funnel_attrs,
};
static const struct attribute_group *funnel_attr_grps[] = {
&funnel_attr_grp,
NULL,
};
static int __devinit funnel_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct funnel_drvdata *drvdata;
struct resource *res;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "funnel-base");
if (!res)
return -ENODEV;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
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 = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->groups = funnel_attr_grps;
desc->owner = THIS_MODULE;
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 __devexit funnel_remove(struct platform_device *pdev)
{
struct funnel_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id funnel_match[] = {
{.compatible = "arm,coresight-funnel"},
{}
};
static struct platform_driver funnel_driver = {
.probe = funnel_probe,
.remove = __devexit_p(funnel_remove),
.driver = {
.name = "coresight-funnel",
.owner = THIS_MODULE,
.of_match_table = funnel_match,
},
};
static int __init funnel_init(void)
{
return platform_driver_register(&funnel_driver);
}
module_init(funnel_init);
static void __exit funnel_exit(void)
{
platform_driver_unregister(&funnel_driver);
}
module_exit(funnel_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Funnel driver");
@@ -0,0 +1,341 @@
/* Copyright (c) 2013, 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/mutex.h>
#include <linux/clk.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include <linux/of.h>
#include "coresight-priv.h"
struct hwevent_mux {
phys_addr_t start;
phys_addr_t end;
};
struct hwevent_drvdata {
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
struct mutex mutex;
int nr_hclk;
struct clk **hclk;
int nr_hmux;
struct hwevent_mux *hmux;
bool enable;
};
static int hwevent_enable(struct hwevent_drvdata *drvdata)
{
int ret, i;
mutex_lock(&drvdata->mutex);
if (drvdata->enable)
goto out;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
goto err0;
for (i = 0; i < drvdata->nr_hclk; i++) {
ret = clk_prepare_enable(drvdata->hclk[i]);
if (ret)
goto err1;
}
drvdata->enable = true;
dev_info(drvdata->dev, "Hardware Event driver enabled\n");
out:
mutex_unlock(&drvdata->mutex);
return 0;
err1:
clk_disable_unprepare(drvdata->clk);
for (i--; i >= 0; i--)
clk_disable_unprepare(drvdata->hclk[i]);
err0:
mutex_unlock(&drvdata->mutex);
return ret;
}
static void hwevent_disable(struct hwevent_drvdata *drvdata)
{
int i;
mutex_lock(&drvdata->mutex);
if (!drvdata->enable)
goto out;
drvdata->enable = false;
clk_disable_unprepare(drvdata->clk);
for (i = 0; i < drvdata->nr_hclk; i++)
clk_disable_unprepare(drvdata->hclk[i]);
dev_info(drvdata->dev, "Hardware Event driver disabled\n");
out:
mutex_unlock(&drvdata->mutex);
}
static ssize_t hwevent_show_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->enable;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t hwevent_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret = 0;
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
if (val)
ret = hwevent_enable(drvdata);
else
hwevent_disable(drvdata);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, hwevent_show_enable,
hwevent_store_enable);
static ssize_t hwevent_store_setreg(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
void *hwereg;
phys_addr_t addr;
uint32_t val;
int ret, i;
if (sscanf(buf, "%x %x", &addr, &val) != 2)
return -EINVAL;
mutex_lock(&drvdata->mutex);
if (!drvdata->enable) {
dev_err(dev, "Hardware Event driver not enabled\n");
ret = -EINVAL;
goto err;
}
for (i = 0; i < drvdata->nr_hmux; i++) {
if ((addr >= drvdata->hmux[i].start) &&
(addr < drvdata->hmux[i].end)) {
hwereg = devm_ioremap(dev,
drvdata->hmux[i].start,
drvdata->hmux[i].end -
drvdata->hmux[i].start);
if (!hwereg) {
dev_err(dev, "unable to map address 0x%x\n",
addr);
ret = -ENOMEM;
goto err;
}
writel_relaxed(val, hwereg + addr -
drvdata->hmux[i].start);
/* Ensure writes to hwevent control registers
are completed before unmapping the address
*/
mb();
devm_iounmap(dev, hwereg);
break;
}
}
if (i == drvdata->nr_hmux) {
ret = coresight_csr_hwctrl_set(addr, val);
if (ret) {
dev_err(dev, "invalid mux control register address\n");
ret = -EINVAL;
goto err;
}
}
mutex_unlock(&drvdata->mutex);
return size;
err:
mutex_unlock(&drvdata->mutex);
return ret;
}
static DEVICE_ATTR(setreg, S_IWUSR, NULL, hwevent_store_setreg);
static struct attribute *hwevent_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_setreg.attr,
NULL,
};
static struct attribute_group hwevent_attr_grp = {
.attrs = hwevent_attrs,
};
static const struct attribute_group *hwevent_attr_grps[] = {
&hwevent_attr_grp,
NULL,
};
static int __devinit hwevent_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct hwevent_drvdata *drvdata;
struct coresight_desc *desc;
struct coresight_platform_data *pdata;
struct resource *res;
int ret, i;
const char *hmux_name, *hclk_name;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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);
if (pdev->dev.of_node)
drvdata->nr_hmux = of_property_count_strings(pdev->dev.of_node,
"reg-names");
if (drvdata->nr_hmux > 0) {
drvdata->hmux = devm_kzalloc(dev, drvdata->nr_hmux *
sizeof(*drvdata->hmux),
GFP_KERNEL);
if (!drvdata->hmux)
return -ENOMEM;
for (i = 0; i < drvdata->nr_hmux; i++) {
ret = of_property_read_string_index(pdev->dev.of_node,
"reg-names", i,
&hmux_name);
if (ret)
return ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
hmux_name);
if (!res)
return -ENODEV;
drvdata->hmux[i].start = res->start;
drvdata->hmux[i].end = res->end;
}
} else if (drvdata->nr_hmux < 0) {
return drvdata->nr_hmux;
} else {
/* return error if reg-names in dt node is empty string */
return -ENODEV;
}
mutex_init(&drvdata->mutex);
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
if (pdev->dev.of_node)
drvdata->nr_hclk = of_property_count_strings(pdev->dev.of_node,
"qcom,hwevent-clks");
if (drvdata->nr_hclk > 0) {
drvdata->hclk = devm_kzalloc(dev, drvdata->nr_hclk *
sizeof(*drvdata->hclk),
GFP_KERNEL);
if (!drvdata->hclk)
return -ENOMEM;
for (i = 0; i < drvdata->nr_hclk; i++) {
ret = of_property_read_string_index(pdev->dev.of_node,
"qcom,hwevent-clks",
i, &hclk_name);
if (ret)
return ret;
drvdata->hclk[i] = devm_clk_get(dev, hclk_name);
if (IS_ERR(drvdata->hclk[i]))
return PTR_ERR(drvdata->hclk[i]);
}
}
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_NONE;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->groups = hwevent_attr_grps;
desc->owner = THIS_MODULE;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
dev_info(dev, "Hardware Event driver initialized\n");
return 0;
}
static int __devexit hwevent_remove(struct platform_device *pdev)
{
struct hwevent_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id hwevent_match[] = {
{.compatible = "qcom,coresight-hwevent"},
{}
};
static struct platform_driver hwevent_driver = {
.probe = hwevent_probe,
.remove = __devexit_p(hwevent_remove),
.driver = {
.name = "coresight-hwevent",
.owner = THIS_MODULE,
.of_match_table = hwevent_match,
},
};
static int __init hwevent_init(void)
{
return platform_driver_register(&hwevent_driver);
}
module_init(hwevent_init);
static void __exit hwevent_exit(void)
{
platform_driver_unregister(&hwevent_driver);
}
module_exit(hwevent_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Hardware Event driver");
+59
View File
@@ -0,0 +1,59 @@
/* Copyright (c) 2011-2013, 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>
/* 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 CORESIGHT_UNLOCK (0xC5ACCE55)
#define TIMEOUT_US (100)
#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb))
#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb)
#define BVAL(val, n) ((val & BIT(n)) >> n)
#ifdef CONFIG_CORESIGHT_CSR
extern void msm_qdss_csr_enable_bam_to_usb(void);
extern void msm_qdss_csr_disable_bam_to_usb(void);
extern void msm_qdss_csr_disable_flush(void);
extern int coresight_csr_hwctrl_set(phys_addr_t addr, uint32_t val);
#else
static inline void msm_qdss_csr_enable_bam_to_usb(void) {}
static inline void msm_qdss_csr_disable_bam_to_usb(void) {}
static inline void msm_qdss_csr_disable_flush(void) {}
static inline int coresight_csr_hwctrl_set(phys_addr_t addr,
uint32_t val) { return -ENOSYS; }
#endif
#ifdef CONFIG_CORESIGHT_ETM
extern unsigned int etm_readl_cp14(uint32_t off);
extern void etm_writel_cp14(uint32_t val, uint32_t off);
#else
static inline unsigned int etm_readl_cp14(uint32_t off) { return 0; }
static inline void etm_writel_cp14(uint32_t val, uint32_t off) {}
#endif
#endif
@@ -0,0 +1,212 @@
/* Copyright (c) 2011-2013, 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_coresight.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
#define replicator_writel(drvdata, val, off) \
__raw_writel((val), drvdata->base + off)
#define replicator_readl(drvdata, off) \
__raw_readl(drvdata->base + off)
#define REPLICATOR_LOCK(drvdata) \
do { \
mb(); \
replicator_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define REPLICATOR_UNLOCK(drvdata) \
do { \
replicator_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#define REPLICATOR_IDFILTER0 (0x000)
#define REPLICATOR_IDFILTER1 (0x004)
#define REPLICATOR_ITATBCTR0 (0xEFC)
#define REPLICATOR_ITATBCTR1 (0xEF8)
struct replicator_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
};
static void __replicator_enable(struct replicator_drvdata *drvdata, int outport)
{
REPLICATOR_UNLOCK(drvdata);
if (outport == 0) {
replicator_writel(drvdata, 0x0, REPLICATOR_IDFILTER0);
replicator_writel(drvdata, 0xFF, REPLICATOR_IDFILTER1);
} else {
replicator_writel(drvdata, 0x0, REPLICATOR_IDFILTER1);
replicator_writel(drvdata, 0xFF, REPLICATOR_IDFILTER0);
}
REPLICATOR_LOCK(drvdata);
}
static int replicator_enable(struct coresight_device *csdev, int inport,
int outport)
{
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
__replicator_enable(drvdata, outport);
dev_info(drvdata->dev, "REPLICATOR enabled\n");
return 0;
}
static void __replicator_disable(struct replicator_drvdata *drvdata,
int outport)
{
REPLICATOR_UNLOCK(drvdata);
if (outport == 0)
replicator_writel(drvdata, 0xFF, REPLICATOR_IDFILTER0);
else
replicator_writel(drvdata, 0xFF, REPLICATOR_IDFILTER1);
REPLICATOR_LOCK(drvdata);
}
static void replicator_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
__replicator_disable(drvdata, outport);
clk_disable_unprepare(drvdata->clk);
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 __devinit replicator_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct replicator_drvdata *drvdata;
struct resource *res;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"replicator-base");
if (!res)
return -ENODEV;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_LINK;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
desc->ops = &replicator_cs_ops;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
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 __devexit replicator_remove(struct platform_device *pdev)
{
struct replicator_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id replicator_match[] = {
{.compatible = "qcom,coresight-replicator"},
{}
};
static struct platform_driver replicator_driver = {
.probe = replicator_probe,
.remove = __devexit_p(replicator_remove),
.driver = {
.name = "coresight-replicator",
.owner = THIS_MODULE,
.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");
+924
View File
@@ -0,0 +1,924 @@
/* Copyright (c) 2012-2013, 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/platform_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/delay.h>
#include <linux/clk.h>
#include <linux/bitmap.h>
#include <linux/of.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include <linux/coresight-stm.h>
#include <asm/unaligned.h>
#include "coresight-priv.h"
#define stm_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define stm_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define stm_data_writeb(val, addr) __raw_writeb_no_log(val, addr)
#define stm_data_writew(val, addr) __raw_writew_no_log(val, addr)
#define stm_data_writel(val, addr) __raw_writel_no_log(val, addr)
#define STM_LOCK(drvdata) \
do { \
mb(); \
stm_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define STM_UNLOCK(drvdata) \
do { \
stm_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#define STMDMASTARTR (0xC04)
#define STMDMASTOPR (0xC08)
#define STMDMASTATR (0xC0C)
#define STMDMACTLR (0xC10)
#define STMDMAIDR (0xCFC)
#define STMHEER (0xD00)
#define STMHETER (0xD20)
#define STMHEMCR (0xD64)
#define STMHEMASTR (0xDF4)
#define STMHEFEAT1R (0xDF8)
#define STMHEIDR (0xDFC)
#define STMSPER (0xE00)
#define STMSPTER (0xE20)
#define STMSPSCR (0xE60)
#define STMSPMSCR (0xE64)
#define STMSPOVERRIDER (0xE68)
#define STMSPMOVERRIDER (0xE6C)
#define STMSPTRIGCSR (0xE70)
#define STMTCSR (0xE80)
#define STMTSSTIMR (0xE84)
#define STMTSFREQR (0xE8C)
#define STMSYNCR (0xE90)
#define STMAUXCR (0xE94)
#define STMSPFEAT1R (0xEA0)
#define STMSPFEAT2R (0xEA4)
#define STMSPFEAT3R (0xEA8)
#define STMITTRIGGER (0xEE8)
#define STMITATBDATA0 (0xEEC)
#define STMITATBCTR2 (0xEF0)
#define STMITATBID (0xEF4)
#define STMITATBCTR0 (0xEF8)
#define NR_STM_CHANNEL (32)
#define BYTES_PER_CHANNEL (256)
#define STM_TRACE_BUF_SIZE (4096)
#define STM_USERSPACE_HEADER_SIZE (8)
#define STM_USERSPACE_MAGIC1_VAL (0xf0)
#define STM_USERSPACE_MAGIC2_VAL (0xf1)
#define OST_TOKEN_STARTSIMPLE (0x10)
#define OST_TOKEN_STARTBASE (0x30)
#define OST_VERSION_PROP (1)
#define OST_VERSION_MIPI1 (16)
enum stm_pkt_type {
STM_PKT_TYPE_DATA = 0x98,
STM_PKT_TYPE_FLAG = 0xE8,
STM_PKT_TYPE_TRIG = 0xF8,
};
enum {
STM_OPTION_MARKED = 0x10,
};
#define stm_channel_addr(drvdata, ch) (drvdata->chs.base + \
(ch * BYTES_PER_CHANNEL))
#define stm_channel_off(type, opts) (type & ~opts)
#ifdef CONFIG_CORESIGHT_STM_DEFAULT_ENABLE
static int boot_enable = 1;
#else
static int boot_enable;
#endif
module_param_named(
boot_enable, boot_enable, int, S_IRUGO
);
static int boot_nr_channel;
module_param_named(
boot_nr_channel, boot_nr_channel, int, S_IRUGO
);
struct channel_space {
void __iomem *base;
unsigned long *bitmap;
};
struct stm_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
struct clk *clk;
spinlock_t spinlock;
struct channel_space chs;
bool enable;
DECLARE_BITMAP(entities, OST_ENTITY_MAX);
bool write_64bit;
};
static struct stm_drvdata *stmdrvdata;
static int stm_hwevent_isenable(struct stm_drvdata *drvdata)
{
int ret = 0;
spin_lock(&drvdata->spinlock);
if (drvdata->enable)
if (BVAL(stm_readl(drvdata, STMHEMCR), 0))
ret = stm_readl(drvdata, STMHEER) == 0 ? 0 : 1;
spin_unlock(&drvdata->spinlock);
return ret;
}
static void __stm_hwevent_enable(struct stm_drvdata *drvdata)
{
STM_UNLOCK(drvdata);
/* Program STMHETER to ensure TRIGOUTHETE (fed to CTI) is asserted
for HW events.
*/
stm_writel(drvdata, 0xFFFFFFFF, STMHETER);
stm_writel(drvdata, 0xFFFFFFFF, STMHEER);
stm_writel(drvdata, 0x5, STMHEMCR);
STM_LOCK(drvdata);
}
static int stm_hwevent_enable(struct stm_drvdata *drvdata)
{
int ret = 0;
spin_lock(&drvdata->spinlock);
if (drvdata->enable)
__stm_hwevent_enable(drvdata);
else
ret = -EINVAL;
spin_unlock(&drvdata->spinlock);
return ret;
}
static int stm_port_isenable(struct stm_drvdata *drvdata)
{
int ret = 0;
spin_lock(&drvdata->spinlock);
if (drvdata->enable)
ret = stm_readl(drvdata, STMSPER) == 0 ? 0 : 1;
spin_unlock(&drvdata->spinlock);
return ret;
}
static void __stm_port_enable(struct stm_drvdata *drvdata)
{
STM_UNLOCK(drvdata);
stm_writel(drvdata, 0x10, STMSPTRIGCSR);
stm_writel(drvdata, 0xFFFFFFFF, STMSPER);
STM_LOCK(drvdata);
}
static int stm_port_enable(struct stm_drvdata *drvdata)
{
int ret = 0;
spin_lock(&drvdata->spinlock);
if (drvdata->enable)
__stm_port_enable(drvdata);
else
ret = -EINVAL;
spin_unlock(&drvdata->spinlock);
return ret;
}
static void __stm_enable(struct stm_drvdata *drvdata)
{
__stm_hwevent_enable(drvdata);
__stm_port_enable(drvdata);
STM_UNLOCK(drvdata);
stm_writel(drvdata, 0xFFF, STMSYNCR);
/* SYNCEN is read-only and HWTEN is not implemented */
stm_writel(drvdata, 0x100003, STMTCSR);
STM_LOCK(drvdata);
}
static int stm_enable(struct coresight_device *csdev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
spin_lock(&drvdata->spinlock);
__stm_enable(drvdata);
drvdata->enable = true;
spin_unlock(&drvdata->spinlock);
dev_info(drvdata->dev, "STM tracing enabled\n");
return 0;
}
static void __stm_hwevent_disable(struct stm_drvdata *drvdata)
{
STM_UNLOCK(drvdata);
stm_writel(drvdata, 0x0, STMHEMCR);
stm_writel(drvdata, 0x0, STMHEER);
stm_writel(drvdata, 0x0, STMHETER);
STM_LOCK(drvdata);
}
static void stm_hwevent_disable(struct stm_drvdata *drvdata)
{
spin_lock(&drvdata->spinlock);
if (drvdata->enable)
__stm_hwevent_disable(drvdata);
spin_unlock(&drvdata->spinlock);
}
static void __stm_port_disable(struct stm_drvdata *drvdata)
{
STM_UNLOCK(drvdata);
stm_writel(drvdata, 0x0, STMSPER);
stm_writel(drvdata, 0x0, STMSPTRIGCSR);
STM_LOCK(drvdata);
}
static void stm_port_disable(struct stm_drvdata *drvdata)
{
spin_lock(&drvdata->spinlock);
if (drvdata->enable)
__stm_port_disable(drvdata);
spin_unlock(&drvdata->spinlock);
}
static void __stm_disable(struct stm_drvdata *drvdata)
{
STM_UNLOCK(drvdata);
stm_writel(drvdata, 0x100000, STMTCSR);
STM_LOCK(drvdata);
__stm_hwevent_disable(drvdata);
__stm_port_disable(drvdata);
}
static void stm_disable(struct coresight_device *csdev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
__stm_disable(drvdata);
drvdata->enable = false;
spin_unlock(&drvdata->spinlock);
/* Wait for 100ms so that pending data has been written to HW */
msleep(100);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "STM tracing disabled\n");
}
static const struct coresight_ops_source stm_source_ops = {
.enable = stm_enable,
.disable = stm_disable,
};
static const struct coresight_ops stm_cs_ops = {
.source_ops = &stm_source_ops,
};
static uint32_t stm_channel_alloc(uint32_t off)
{
struct stm_drvdata *drvdata = stmdrvdata;
uint32_t ch;
do {
ch = find_next_zero_bit(drvdata->chs.bitmap,
NR_STM_CHANNEL, off);
} while ((ch < NR_STM_CHANNEL) &&
test_and_set_bit(ch, drvdata->chs.bitmap));
return ch;
}
static void stm_channel_free(uint32_t ch)
{
struct stm_drvdata *drvdata = stmdrvdata;
clear_bit(ch, drvdata->chs.bitmap);
}
static int stm_send_64bit(void *addr, const void *data, uint32_t size)
{
uint64_t prepad = 0;
uint64_t postpad = 0;
char *pad;
uint8_t off, endoff;
uint32_t len = size;
/* only 64bit writes are supported, we rely on the compiler to
* generate STRD instruction for the casted 64bit assignments
*/
off = (unsigned long)data & 0x7;
if (off) {
endoff = 8 - off;
pad = (char *)&prepad;
pad += off;
while (endoff && size) {
*pad++ = *(char *)data++;
endoff--;
size--;
}
*(volatile uint64_t __force *)addr = prepad;
}
/* now we are 64bit aligned */
while (size >= 8) {
*(volatile uint64_t __force *)addr = *(uint64_t *)data;
data += 8;
size -= 8;
}
endoff = 0;
if (size) {
endoff = 8 - (uint8_t)size;
pad = (char *)&postpad;
while (size) {
*pad++ = *(char *)data++;
size--;
}
*(volatile uint64_t __force *)addr = postpad;
}
return len + off + endoff;
}
static int stm_trace_ost_header_64bit(unsigned long ch_addr, uint32_t options,
uint8_t entity_id, uint8_t proto_id,
const void *payload_data,
uint32_t payload_size)
{
void *addr;
uint8_t prepad_size;
uint64_t header;
char *hdr;
hdr = (char *)&header;
hdr[0] = OST_TOKEN_STARTBASE;
hdr[1] = OST_VERSION_PROP;
hdr[2] = entity_id;
hdr[3] = proto_id;
prepad_size = (unsigned long)payload_data & 0x7;
*(uint32_t *)(hdr + 4) = (prepad_size << 24) | payload_size;
/* for 64bit writes, header is expected to be D32M, D32M type */
options |= STM_OPTION_MARKED;
options &= ~STM_OPTION_TIMESTAMPED;
addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
return stm_send_64bit(addr, &header, sizeof(header));
}
static int stm_trace_data_64bit(unsigned long ch_addr, uint32_t options,
const void *data, uint32_t size)
{
void *addr;
options &= ~STM_OPTION_TIMESTAMPED;
addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
return stm_send_64bit(addr, data, size);
}
static int stm_trace_ost_tail_64bit(unsigned long ch_addr, uint32_t options)
{
void *addr;
uint64_t tail = 0x0;
addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_FLAG, options));
return stm_send_64bit(addr, &tail, sizeof(tail));
}
static int stm_send(void *addr, const void *data, uint32_t size)
{
if (((unsigned long)data & 0x1) && (size >= 1)) {
stm_data_writeb(*(uint8_t *)data, addr);
data++;
size--;
}
if (((unsigned long)data & 0x2) && (size >= 2)) {
stm_data_writew(*(uint16_t *)data, addr);
data += 2;
size -= 2;
}
/* now we are 32bit aligned */
while (size >= 4) {
stm_data_writel(*(uint32_t *)data, addr);
data += 4;
size -= 4;
}
if (size >= 2) {
stm_data_writew(*(uint16_t *)data, addr);
data += 2;
size -= 2;
}
if (size >= 1) {
stm_data_writeb(*(uint8_t *)data, addr);
data++;
size--;
}
return size;
}
static int stm_trace_ost_header(unsigned long ch_addr, uint32_t options,
uint8_t entity_id, uint8_t proto_id,
const void *payload_data, uint32_t payload_size)
{
void *addr;
uint32_t header;
char *hdr;
hdr = (char *)&header;
hdr[0] = OST_TOKEN_STARTSIMPLE;
hdr[1] = OST_VERSION_MIPI1;
hdr[2] = entity_id;
hdr[3] = proto_id;
/* header is expected to be D32M type */
options |= STM_OPTION_MARKED;
options &= ~STM_OPTION_TIMESTAMPED;
addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
return stm_send(addr, &header, sizeof(header));
}
static int stm_trace_data(unsigned long ch_addr, uint32_t options,
const void *data, uint32_t size)
{
void *addr;
options &= ~STM_OPTION_TIMESTAMPED;
addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
return stm_send(addr, data, size);
}
static int stm_trace_ost_tail(unsigned long ch_addr, uint32_t options)
{
void *addr;
uint32_t tail = 0x0;
addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_FLAG, options));
return stm_send(addr, &tail, sizeof(tail));
}
static inline int __stm_trace(uint32_t options, uint8_t entity_id,
uint8_t proto_id, const void *data, uint32_t size)
{
struct stm_drvdata *drvdata = stmdrvdata;
int len = 0;
uint32_t ch;
unsigned long ch_addr;
/* allocate channel and get the channel address */
ch = stm_channel_alloc(0);
ch_addr = (unsigned long)stm_channel_addr(drvdata, ch);
if (drvdata->write_64bit) {
/* send the ost header */
len += stm_trace_ost_header_64bit(ch_addr, options, entity_id,
proto_id, data, size);
/* send the payload data */
len += stm_trace_data_64bit(ch_addr, options, data, size);
/* send the ost tail */
len += stm_trace_ost_tail_64bit(ch_addr, options);
} else {
/* send the ost header */
len += stm_trace_ost_header(ch_addr, options, entity_id,
proto_id, data, size);
/* send the payload data */
len += stm_trace_data(ch_addr, options, data, size);
/* send the ost tail */
len += stm_trace_ost_tail(ch_addr, options);
}
/* we are done, free the channel */
stm_channel_free(ch);
return len;
}
/**
* stm_trace - trace the binary or string data through STM
* @options: tracing options - guaranteed, timestamped, etc
* @entity_id: entity representing the trace data
* @proto_id: protocol id to distinguish between different binary formats
* @data: pointer to binary or string data buffer
* @size: size of data to send
*
* Packetizes the data as the payload to an OST packet and sends it over STM
*
* CONTEXT:
* Can be called from any context.
*
* RETURNS:
* number of bytes transfered over STM
*/
int stm_trace(uint32_t options, uint8_t entity_id, uint8_t proto_id,
const void *data, uint32_t size)
{
struct stm_drvdata *drvdata = stmdrvdata;
/* we don't support sizes more than 24bits (0 to 23) */
if (!(drvdata && drvdata->enable &&
test_bit(entity_id, drvdata->entities) && size &&
(size < 0x1000000)))
return 0;
return __stm_trace(options, entity_id, proto_id, data, size);
}
EXPORT_SYMBOL(stm_trace);
static ssize_t stm_write(struct file *file, const char __user *data,
size_t size, loff_t *ppos)
{
struct stm_drvdata *drvdata = container_of(file->private_data,
struct stm_drvdata, miscdev);
char *buf;
uint8_t entity_id, proto_id;
uint32_t options;
if (!drvdata->enable || !size)
return -EINVAL;
if (size > STM_TRACE_BUF_SIZE)
size = STM_TRACE_BUF_SIZE;
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, data, size)) {
kfree(buf);
dev_dbg(drvdata->dev, "%s: copy_from_user failed\n", __func__);
return -EFAULT;
}
if (size >= STM_USERSPACE_HEADER_SIZE &&
buf[0] == STM_USERSPACE_MAGIC1_VAL &&
buf[1] == STM_USERSPACE_MAGIC2_VAL) {
entity_id = buf[2];
proto_id = buf[3];
options = *(uint32_t *)(buf + 4);
if (!test_bit(entity_id, drvdata->entities) ||
!(size - STM_USERSPACE_HEADER_SIZE)) {
kfree(buf);
return size;
}
__stm_trace(options, entity_id, proto_id,
buf + STM_USERSPACE_HEADER_SIZE,
size - STM_USERSPACE_HEADER_SIZE);
} else {
if (!test_bit(OST_ENTITY_DEV_NODE, drvdata->entities)) {
kfree(buf);
return size;
}
__stm_trace(STM_OPTION_TIMESTAMPED, OST_ENTITY_DEV_NODE, 0,
buf, size);
}
kfree(buf);
return size;
}
static const struct file_operations stm_fops = {
.owner = THIS_MODULE,
.open = nonseekable_open,
.write = stm_write,
.llseek = no_llseek,
};
static ssize_t stm_show_hwevent_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = stm_hwevent_isenable(drvdata);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t stm_store_hwevent_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret = 0;
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
if (val)
ret = stm_hwevent_enable(drvdata);
else
stm_hwevent_disable(drvdata);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(hwevent_enable, S_IRUGO | S_IWUSR, stm_show_hwevent_enable,
stm_store_hwevent_enable);
static ssize_t stm_show_port_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = stm_port_isenable(drvdata);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t stm_store_port_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret = 0;
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
if (val)
ret = stm_port_enable(drvdata);
else
stm_port_disable(drvdata);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(port_enable, S_IRUGO | S_IWUSR, stm_show_port_enable,
stm_store_port_enable);
static ssize_t stm_show_entities(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t len;
len = bitmap_scnprintf(buf, PAGE_SIZE, drvdata->entities,
OST_ENTITY_MAX);
if (PAGE_SIZE - len < 2)
len = -EINVAL;
else
len += scnprintf(buf + len, 2, "\n");
return len;
}
static ssize_t stm_store_entities(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val1, val2;
if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
return -EINVAL;
if (val1 >= OST_ENTITY_MAX)
return -EINVAL;
if (val2)
__set_bit(val1, drvdata->entities);
else
__clear_bit(val1, drvdata->entities);
return size;
}
static DEVICE_ATTR(entities, S_IRUGO | S_IWUSR, stm_show_entities,
stm_store_entities);
static struct attribute *stm_attrs[] = {
&dev_attr_hwevent_enable.attr,
&dev_attr_port_enable.attr,
&dev_attr_entities.attr,
NULL,
};
static struct attribute_group stm_attr_grp = {
.attrs = stm_attrs,
};
static const struct attribute_group *stm_attr_grps[] = {
&stm_attr_grp,
NULL,
};
static int __devinit stm_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct stm_drvdata *drvdata;
struct resource *res;
size_t res_size, bitmap_size;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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;
/* Store the driver data pointer for use in exported functions */
stmdrvdata = drvdata;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "stm-base");
if (!res)
return -ENODEV;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"stm-data-base");
if (!res)
return -ENODEV;
if (boot_nr_channel) {
res_size = min((resource_size_t)(boot_nr_channel *
BYTES_PER_CHANNEL), resource_size(res));
bitmap_size = boot_nr_channel * sizeof(long);
} else {
res_size = min((resource_size_t)(NR_STM_CHANNEL *
BYTES_PER_CHANNEL), resource_size(res));
bitmap_size = NR_STM_CHANNEL * sizeof(long);
}
drvdata->chs.base = devm_ioremap(dev, res->start, res_size);
if (!drvdata->chs.base)
return -ENOMEM;
drvdata->chs.bitmap = devm_kzalloc(dev, bitmap_size, GFP_KERNEL);
if (!drvdata->chs.bitmap)
return -ENOMEM;
spin_lock_init(&drvdata->spinlock);
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
bitmap_fill(drvdata->entities, OST_ENTITY_MAX);
if (pdev->dev.of_node)
drvdata->write_64bit = of_property_read_bool(pdev->dev.of_node,
"qcom,write-64bit");
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_SOURCE;
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
desc->ops = &stm_cs_ops;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->groups = stm_attr_grps;
desc->owner = THIS_MODULE;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
drvdata->miscdev.name = ((struct coresight_platform_data *)
(pdev->dev.platform_data))->name;
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
drvdata->miscdev.fops = &stm_fops;
ret = misc_register(&drvdata->miscdev);
if (ret)
goto err;
dev_info(drvdata->dev, "STM initialized\n");
if (boot_enable)
coresight_enable(drvdata->csdev);
return 0;
err:
coresight_unregister(drvdata->csdev);
return ret;
}
static int __devexit stm_remove(struct platform_device *pdev)
{
struct stm_drvdata *drvdata = platform_get_drvdata(pdev);
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id stm_match[] = {
{.compatible = "arm,coresight-stm"},
{}
};
static struct platform_driver stm_driver = {
.probe = stm_probe,
.remove = __devexit_p(stm_remove),
.driver = {
.name = "coresight-stm",
.owner = THIS_MODULE,
.of_match_table = stm_match,
},
};
static int __init stm_init(void)
{
return platform_driver_register(&stm_driver);
}
module_init(stm_init);
static void __exit stm_exit(void)
{
platform_driver_unregister(&stm_driver);
}
module_exit(stm_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight System Trace Macrocell driver");
File diff suppressed because it is too large Load Diff
+820
View File
@@ -0,0 +1,820 @@
/* Copyright (c) 2011-2013, 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/delay.h>
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <mach/gpiomux.h>
#include "coresight-priv.h"
#define tpiu_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define tpiu_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define TPIU_LOCK(drvdata) \
do { \
mb(); \
tpiu_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define TPIU_UNLOCK(drvdata) \
do { \
tpiu_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); \
} while (0)
#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)
enum tpiu_out_mode {
TPIU_OUT_MODE_NONE,
TPIU_OUT_MODE_MICTOR,
TPIU_OUT_MODE_SDC,
};
enum tpiu_set {
TPIU_SET_NONE,
TPIU_SET_A,
TPIU_SET_B,
};
struct tpiu_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
struct mutex mutex;
enum tpiu_out_mode out_mode;
struct regulator *reg;
unsigned int reg_low;
unsigned int reg_high;
unsigned int reg_lpm;
unsigned int reg_hpm;
enum tpiu_set set;
unsigned int seta_gpiocnt;
unsigned int *seta_gpios;
struct gpiomux_setting *seta_cfgs;
unsigned int setb_gpiocnt;
unsigned int *setb_gpios;
struct gpiomux_setting *setb_cfgs;
bool enable;
};
struct gpiomux_setting old_cfg;
static void tpiu_flush_and_stop(struct tpiu_drvdata *drvdata)
{
int count;
uint32_t ffcr;
ffcr = tpiu_readl(drvdata, TPIU_FFCR);
ffcr |= BIT(12);
tpiu_writel(drvdata, ffcr, TPIU_FFCR);
ffcr |= BIT(6);
tpiu_writel(drvdata, ffcr, TPIU_FFCR);
/* Ensure flush completes */
for (count = TIMEOUT_US; BVAL(tpiu_readl(drvdata, TPIU_FFCR), 6) != 0
&& count > 0; count--)
udelay(1);
WARN(count == 0, "timeout while flushing TPIU, TPIU_FFCR: %#x\n",
tpiu_readl(drvdata, TPIU_FFCR));
}
static int __tpiu_enable_seta(struct tpiu_drvdata *drvdata)
{
int i, ret;
if (!drvdata->seta_gpiocnt)
return -EINVAL;
for (i = 0; i < drvdata->seta_gpiocnt; i++) {
ret = gpio_request(drvdata->seta_gpios[i], NULL);
if (ret) {
dev_err(drvdata->dev,
"gpio_request failed for seta_gpio: %u\n",
drvdata->seta_gpios[i]);
goto err0;
}
ret = msm_gpiomux_write(drvdata->seta_gpios[i],
GPIOMUX_ACTIVE,
&drvdata->seta_cfgs[i],
&old_cfg);
if (ret < 0) {
dev_err(drvdata->dev,
"gpio write failed for seta_gpio: %u\n",
drvdata->seta_gpios[i]);
goto err1;
}
}
return 0;
err1:
gpio_free(drvdata->seta_gpios[i]);
err0:
i--;
while (i >= 0) {
gpio_free(drvdata->seta_gpios[i]);
i--;
}
return ret;
}
static int __tpiu_enable_setb(struct tpiu_drvdata *drvdata)
{
int i, ret;
if (!drvdata->setb_gpiocnt)
return -EINVAL;
for (i = 0; i < drvdata->setb_gpiocnt; i++) {
ret = gpio_request(drvdata->setb_gpios[i], NULL);
if (ret) {
dev_err(drvdata->dev,
"gpio_request failed for setb_gpio: %u\n",
drvdata->setb_gpios[i]);
goto err0;
}
ret = msm_gpiomux_write(drvdata->setb_gpios[i],
GPIOMUX_ACTIVE,
&drvdata->setb_cfgs[i],
&old_cfg);
if (ret < 0) {
dev_err(drvdata->dev,
"gpio write failed for setb_gpio: %u\n",
drvdata->setb_gpios[i]);
goto err1;
}
}
return 0;
err1:
gpio_free(drvdata->setb_gpios[i]);
err0:
i--;
while (i >= 0) {
gpio_free(drvdata->setb_gpios[i]);
i--;
}
return ret;
}
static int __tpiu_enable_to_mictor(struct tpiu_drvdata *drvdata)
{
int ret;
if (drvdata->set == TPIU_SET_A) {
ret = __tpiu_enable_seta(drvdata);
if (ret)
return ret;
} else if (drvdata->set == TPIU_SET_B) {
ret = __tpiu_enable_setb(drvdata);
if (ret)
return ret;
}
TPIU_UNLOCK(drvdata);
tpiu_writel(drvdata, 0x8000, TPIU_CURR_PORTSZ);
tpiu_writel(drvdata, 0x101, TPIU_FFCR);
TPIU_LOCK(drvdata);
return 0;
}
static int tpiu_reg_set_optimum_mode(struct regulator *reg,
unsigned int reg_hpm)
{
if (regulator_count_voltages(reg) <= 0)
return 0;
return regulator_set_optimum_mode(reg, reg_hpm);
}
static int tpiu_reg_set_voltage(struct regulator *reg, unsigned int reg_low,
unsigned int reg_high)
{
if (regulator_count_voltages(reg) <= 0)
return 0;
return regulator_set_voltage(reg, reg_low, reg_high);
}
static int __tpiu_enable_to_sdc(struct tpiu_drvdata *drvdata)
{
int ret;
if (!drvdata->reg)
return -EINVAL;
ret = tpiu_reg_set_optimum_mode(drvdata->reg, drvdata->reg_hpm);
if (ret < 0)
return ret;
ret = tpiu_reg_set_voltage(drvdata->reg, drvdata->reg_low,
drvdata->reg_high);
if (ret)
goto err0;
ret = regulator_enable(drvdata->reg);
if (ret)
goto err1;
msm_tlmm_misc_reg_write(TLMM_SDC2_HDRV_PULL_CTL, 0x16D);
msm_tlmm_misc_reg_write(TLMM_ETM_MODE_REG, 1);
TPIU_UNLOCK(drvdata);
tpiu_writel(drvdata, 0x8, TPIU_CURR_PORTSZ);
tpiu_writel(drvdata, 0x103, TPIU_FFCR);
TPIU_LOCK(drvdata);
return 0;
err1:
tpiu_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high);
err0:
tpiu_reg_set_optimum_mode(drvdata->reg, 0);
return ret;
}
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;
mutex_lock(&drvdata->mutex);
if (drvdata->out_mode == TPIU_OUT_MODE_MICTOR)
ret = __tpiu_enable_to_mictor(drvdata);
else
ret = __tpiu_enable_to_sdc(drvdata);
if (ret)
goto err;
drvdata->enable = true;
mutex_unlock(&drvdata->mutex);
dev_info(drvdata->dev, "TPIU enabled\n");
return 0;
err:
mutex_unlock(&drvdata->mutex);
clk_disable_unprepare(drvdata->clk);
return ret;
}
static void __tpiu_disable(struct tpiu_drvdata *drvdata)
{
TPIU_UNLOCK(drvdata);
tpiu_flush_and_stop(drvdata);
TPIU_LOCK(drvdata);
}
static void __tpiu_disable_seta(struct tpiu_drvdata *drvdata)
{
int i;
for (i = 0; i < drvdata->seta_gpiocnt; i++)
gpio_free(drvdata->seta_gpios[i]);
}
static void __tpiu_disable_setb(struct tpiu_drvdata *drvdata)
{
int i;
for (i = 0; i < drvdata->setb_gpiocnt; i++)
gpio_free(drvdata->setb_gpios[i]);
}
static void __tpiu_disable_to_mictor(struct tpiu_drvdata *drvdata)
{
__tpiu_disable(drvdata);
if (drvdata->set == TPIU_SET_A)
__tpiu_disable_seta(drvdata);
else if (drvdata->set == TPIU_SET_B)
__tpiu_disable_setb(drvdata);
}
static void __tpiu_disable_to_sdc(struct tpiu_drvdata *drvdata)
{
__tpiu_disable(drvdata);
msm_tlmm_misc_reg_write(TLMM_ETM_MODE_REG, 0);
regulator_disable(drvdata->reg);
tpiu_reg_set_optimum_mode(drvdata->reg, 0);
tpiu_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high);
}
static void tpiu_disable(struct coresight_device *csdev)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
mutex_lock(&drvdata->mutex);
if (drvdata->out_mode == TPIU_OUT_MODE_MICTOR)
__tpiu_disable_to_mictor(drvdata);
else
__tpiu_disable_to_sdc(drvdata);
drvdata->enable = false;
mutex_unlock(&drvdata->mutex);
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "TPIU disabled\n");
}
static void tpiu_abort(struct coresight_device *csdev)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
__tpiu_disable(drvdata);
dev_info(drvdata->dev, "TPIU aborted\n");
}
static const struct coresight_ops_sink tpiu_sink_ops = {
.enable = tpiu_enable,
.disable = tpiu_disable,
.abort = tpiu_abort,
};
static ssize_t tpiu_show_out_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%s\n",
drvdata->out_mode == TPIU_OUT_MODE_MICTOR ?
"mictor" : "sdc");
}
static ssize_t tpiu_store_out_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(dev->parent);
char str[10] = "";
int ret;
if (strlen(buf) >= 10)
return -EINVAL;
if (sscanf(buf, "%s", str) != 1)
return -EINVAL;
mutex_lock(&drvdata->mutex);
if (!strcmp(str, "mictor")) {
if (drvdata->out_mode == TPIU_OUT_MODE_MICTOR)
goto out;
if (!drvdata->enable) {
drvdata->out_mode = TPIU_OUT_MODE_MICTOR;
goto out;
}
__tpiu_disable_to_sdc(drvdata);
ret = __tpiu_enable_to_mictor(drvdata);
if (ret) {
dev_err(drvdata->dev, "failed to enable mictor\n");
goto err;
}
drvdata->out_mode = TPIU_OUT_MODE_MICTOR;
} else if (!strcmp(str, "sdc")) {
if (drvdata->out_mode == TPIU_OUT_MODE_SDC)
goto out;
if (!drvdata->enable) {
drvdata->out_mode = TPIU_OUT_MODE_SDC;
goto out;
}
__tpiu_disable_to_mictor(drvdata);
ret = __tpiu_enable_to_sdc(drvdata);
if (ret) {
dev_err(drvdata->dev, "failed to enable sdc\n");
goto err;
}
drvdata->out_mode = TPIU_OUT_MODE_SDC;
}
out:
mutex_unlock(&drvdata->mutex);
return size;
err:
mutex_unlock(&drvdata->mutex);
return ret;
}
static DEVICE_ATTR(out_mode, S_IRUGO | S_IWUSR, tpiu_show_out_mode,
tpiu_store_out_mode);
static const struct coresight_ops tpiu_cs_ops = {
.sink_ops = &tpiu_sink_ops,
};
static ssize_t tpiu_show_set(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%s\n",
drvdata->set == TPIU_SET_A ?
"a" : "b");
}
static ssize_t tpiu_store_set(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(dev->parent);
char str[10] = "";
int ret;
if (strlen(buf) >= 10)
return -EINVAL;
if (sscanf(buf, "%s", str) != 1)
return -EINVAL;
mutex_lock(&drvdata->mutex);
if (!strcmp(str, "a")) {
if (drvdata->set == TPIU_SET_A)
goto out;
if (!drvdata->enable || drvdata->out_mode !=
TPIU_OUT_MODE_MICTOR) {
drvdata->set = TPIU_SET_A;
goto out;
}
__tpiu_disable_setb(drvdata);
ret = __tpiu_enable_seta(drvdata);
if (ret) {
dev_err(drvdata->dev, "failed to enable set A\n");
goto err;
}
drvdata->set = TPIU_SET_A;
} else if (!strcmp(str, "b")) {
if (drvdata->set == TPIU_SET_B)
goto out;
if (!drvdata->enable || drvdata->out_mode !=
TPIU_OUT_MODE_MICTOR) {
drvdata->set = TPIU_SET_B;
goto out;
}
__tpiu_disable_seta(drvdata);
ret = __tpiu_enable_setb(drvdata);
if (ret) {
dev_err(drvdata->dev, "failed to enable set B\n");
goto err;
}
drvdata->set = TPIU_SET_B;
}
out:
mutex_unlock(&drvdata->mutex);
return size;
err:
mutex_unlock(&drvdata->mutex);
return ret;
}
static DEVICE_ATTR(set, S_IRUGO | S_IWUSR, tpiu_show_set, tpiu_store_set);
static struct attribute *tpiu_attrs[] = {
&dev_attr_out_mode.attr,
&dev_attr_set.attr,
NULL,
};
static struct attribute_group tpiu_attr_grp = {
.attrs = tpiu_attrs,
};
static const struct attribute_group *tpiu_attr_grps[] = {
&tpiu_attr_grp,
NULL,
};
static int __devinit tpiu_parse_of_data(struct platform_device *pdev,
struct tpiu_drvdata *drvdata)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *reg_node = NULL;
struct device *dev = &pdev->dev;
const __be32 *prop;
int i, len, gpio, ret;
uint32_t *seta_cfgs, *setb_cfgs;
reg_node = of_parse_phandle(node, "vdd-supply", 0);
if (reg_node) {
drvdata->reg = devm_regulator_get(dev, "vdd");
if (IS_ERR(drvdata->reg))
return PTR_ERR(drvdata->reg);
prop = of_get_property(node, "qcom,vdd-voltage-level", &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_err(dev, "sdc voltage levels not specified\n");
} else {
drvdata->reg_low = be32_to_cpup(&prop[0]);
drvdata->reg_high = be32_to_cpup(&prop[1]);
}
prop = of_get_property(node, "qcom,vdd-current-level", &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_err(dev, "sdc current levels not specified\n");
} else {
drvdata->reg_lpm = be32_to_cpup(&prop[0]);
drvdata->reg_hpm = be32_to_cpup(&prop[1]);
}
of_node_put(reg_node);
} else {
dev_err(dev, "sdc voltage supply not specified or available\n");
}
drvdata->out_mode = TPIU_OUT_MODE_MICTOR;
drvdata->set = TPIU_SET_B;
drvdata->seta_gpiocnt = of_gpio_named_count(node, "qcom,seta-gpios");
if (drvdata->seta_gpiocnt) {
drvdata->seta_gpios = devm_kzalloc(dev,
sizeof(*drvdata->seta_gpios) *
drvdata->seta_gpiocnt, GFP_KERNEL);
if (!drvdata->seta_gpios)
return -ENOMEM;
for (i = 0; i < drvdata->seta_gpiocnt; i++) {
gpio = of_get_named_gpio(node, "qcom,seta-gpios", i);
if (!gpio_is_valid(gpio))
return gpio;
drvdata->seta_gpios[i] = gpio;
}
drvdata->seta_cfgs = devm_kzalloc(dev,
sizeof(*drvdata->seta_cfgs) *
drvdata->seta_gpiocnt, GFP_KERNEL);
if (!drvdata->seta_cfgs)
return -ENOMEM;
seta_cfgs = devm_kzalloc(dev, sizeof(*seta_cfgs) *
drvdata->seta_gpiocnt, GFP_KERNEL);
if (!seta_cfgs)
return -ENOMEM;
ret = of_property_read_u32_array(node, "qcom,seta-gpios-func",
(u32 *)seta_cfgs,
drvdata->seta_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->seta_gpiocnt; i++)
drvdata->seta_cfgs[i].func = seta_cfgs[i];
ret = of_property_read_u32_array(node, "qcom,seta-gpios-drv",
(u32 *)seta_cfgs,
drvdata->seta_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->seta_gpiocnt; i++)
drvdata->seta_cfgs[i].drv = seta_cfgs[i];
ret = of_property_read_u32_array(node, "qcom,seta-gpios-pull",
(u32 *)seta_cfgs,
drvdata->seta_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->seta_gpiocnt; i++)
drvdata->seta_cfgs[i].pull = seta_cfgs[i];
ret = of_property_read_u32_array(node, "qcom,seta-gpios-dir",
(u32 *)seta_cfgs,
drvdata->seta_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->seta_gpiocnt; i++)
drvdata->seta_cfgs[i].dir = seta_cfgs[i];
devm_kfree(dev, seta_cfgs);
} else {
dev_err(dev, "seta gpios not specified\n");
}
drvdata->setb_gpiocnt = of_gpio_named_count(node, "qcom,setb-gpios");
if (drvdata->setb_gpiocnt) {
drvdata->setb_gpios = devm_kzalloc(dev,
sizeof(*drvdata->setb_gpios) *
drvdata->setb_gpiocnt, GFP_KERNEL);
if (!drvdata->setb_gpios)
return -ENOMEM;
for (i = 0; i < drvdata->setb_gpiocnt; i++) {
gpio = of_get_named_gpio(node, "qcom,setb-gpios", i);
if (!gpio_is_valid(gpio))
return gpio;
drvdata->setb_gpios[i] = gpio;
}
drvdata->setb_cfgs = devm_kzalloc(dev,
sizeof(*drvdata->setb_cfgs) *
drvdata->setb_gpiocnt, GFP_KERNEL);
if (!drvdata->setb_cfgs)
return -ENOMEM;
setb_cfgs = devm_kzalloc(dev, sizeof(*setb_cfgs) *
drvdata->setb_gpiocnt, GFP_KERNEL);
if (!setb_cfgs)
return -ENOMEM;
ret = of_property_read_u32_array(node, "qcom,setb-gpios-func",
(u32 *)setb_cfgs,
drvdata->setb_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->setb_gpiocnt; i++)
drvdata->setb_cfgs[i].func = setb_cfgs[i];
ret = of_property_read_u32_array(node, "qcom,setb-gpios-drv",
(u32 *)setb_cfgs,
drvdata->setb_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->setb_gpiocnt; i++)
drvdata->setb_cfgs[i].drv = setb_cfgs[i];
ret = of_property_read_u32_array(node, "qcom,setb-gpios-pull",
(u32 *)setb_cfgs,
drvdata->setb_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->setb_gpiocnt; i++)
drvdata->setb_cfgs[i].pull = setb_cfgs[i];
ret = of_property_read_u32_array(node, "qcom,setb-gpios-dir",
(u32 *)setb_cfgs,
drvdata->setb_gpiocnt);
if (ret)
return ret;
for (i = 0; i < drvdata->setb_gpiocnt; i++)
drvdata->setb_cfgs[i].dir = setb_cfgs[i];
devm_kfree(dev, setb_cfgs);
} else {
dev_err(dev, "setb gpios not specified\n");
}
return 0;
}
static int __devinit tpiu_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct tpiu_drvdata *drvdata;
struct resource *res;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
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);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tpiu-base");
if (!res)
return -ENODEV;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
mutex_init(&drvdata->mutex);
drvdata->clk = devm_clk_get(dev, "core_clk");
if (IS_ERR(drvdata->clk))
return PTR_ERR(drvdata->clk);
ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
/* Disable tpiu to support older targets that need this */
__tpiu_disable(drvdata);
clk_disable_unprepare(drvdata->clk);
if (pdev->dev.of_node) {
ret = tpiu_parse_of_data(pdev, drvdata);
if (ret)
return ret;
}
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 = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->groups = tpiu_attr_grps;
desc->owner = THIS_MODULE;
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 __devexit tpiu_remove(struct platform_device *pdev)
{
struct tpiu_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct of_device_id tpiu_match[] = {
{.compatible = "arm,coresight-tpiu"},
{}
};
static struct platform_driver tpiu_driver = {
.probe = tpiu_probe,
.remove = __devexit_p(tpiu_remove),
.driver = {
.name = "coresight-tpiu",
.owner = THIS_MODULE,
.of_match_table = tpiu_match,
},
};
static int __init tpiu_init(void)
{
return platform_driver_register(&tpiu_driver);
}
module_init(tpiu_init);
static void __exit tpiu_exit(void)
{
platform_driver_unregister(&tpiu_driver);
}
module_exit(tpiu_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver");
+710
View File
@@ -0,0 +1,710 @@
/* Copyright (c) 2012-2013, 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/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
#define NO_SINK (-1)
static int curr_sink = NO_SINK;
static LIST_HEAD(coresight_orph_conns);
static LIST_HEAD(coresight_devs);
static DEFINE_SEMAPHORE(coresight_mutex);
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_conns; i++) {
conn = &parent->conns[i];
if (conn->child_dev == csdev)
return conn->child_port;
}
pr_err("coresight: couldn't find inport, parent: %d, child: %d\n",
parent->id, csdev->id);
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_conns; i++) {
conn = &csdev->conns[i];
if (conn->child_dev == child)
return conn->outport;
}
pr_err("coresight: couldn't find outport, parent: %d, child: %d\n",
csdev->id, child->id);
return 0;
}
static int coresight_enable_sink(struct coresight_device *csdev)
{
int ret;
if (csdev->refcnt.sink_refcnt == 0) {
if (csdev->ops->sink_ops->enable) {
ret = csdev->ops->sink_ops->enable(csdev);
if (ret)
goto err;
csdev->enable = true;
}
}
csdev->refcnt.sink_refcnt++;
return 0;
err:
return ret;
}
static void coresight_disable_sink(struct coresight_device *csdev)
{
if (csdev->refcnt.sink_refcnt == 1) {
if (csdev->ops->sink_ops->disable) {
csdev->ops->sink_ops->disable(csdev);
csdev->enable = false;
}
}
csdev->refcnt.sink_refcnt--;
}
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 (csdev->refcnt.link_refcnts[refport] == 0) {
if (csdev->ops->link_ops->enable) {
ret = csdev->ops->link_ops->enable(csdev, inport,
outport);
if (ret)
goto err;
csdev->enable = true;
}
}
csdev->refcnt.link_refcnts[refport]++;
return 0;
err:
return ret;
}
static void coresight_disable_link(struct coresight_device *csdev)
{
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 (csdev->refcnt.link_refcnts[refport] == 1) {
if (csdev->ops->link_ops->disable) {
csdev->ops->link_ops->disable(csdev, inport, outport);
csdev->enable = false;
}
}
csdev->refcnt.link_refcnts[refport]--;
}
static int coresight_enable_source(struct coresight_device *csdev)
{
int ret;
if (csdev->refcnt.source_refcnt == 0) {
if (csdev->ops->source_ops->enable) {
ret = csdev->ops->source_ops->enable(csdev);
if (ret)
goto err;
csdev->enable = true;
}
}
csdev->refcnt.source_refcnt++;
return 0;
err:
return ret;
}
static void coresight_disable_source(struct coresight_device *csdev)
{
if (csdev->refcnt.source_refcnt == 1) {
if (csdev->ops->source_ops->disable) {
csdev->ops->source_ops->disable(csdev);
csdev->enable = false;
}
}
csdev->refcnt.source_refcnt--;
}
static struct list_head *coresight_build_path(struct coresight_device *csdev,
struct list_head *path)
{
int i;
struct list_head *p;
struct coresight_connection *conn;
if (!csdev)
return NULL;
if (csdev->id == curr_sink) {
list_add_tail(&csdev->path_link, path);
return path;
}
for (i = 0; i < csdev->nr_conns; i++) {
conn = &csdev->conns[i];
p = coresight_build_path(conn->child_dev, path);
if (p) {
list_add_tail(&csdev->path_link, p);
return p;
}
}
return NULL;
}
static void coresight_release_path(struct list_head *path)
{
struct coresight_device *cd, *temp;
list_for_each_entry_safe(cd, temp, path, path_link)
list_del(&cd->path_link);
}
static int coresight_enable_path(struct list_head *path, bool incl_source)
{
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)) {
if (incl_source)
ret = coresight_enable_source(cd);
} 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)) {
if (incl_source)
coresight_disable_source(cd);
} else {
coresight_disable_link(cd);
}
}
return ret;
}
static void coresight_disable_path(struct list_head *path, bool incl_source)
{
struct coresight_device *cd;
list_for_each_entry(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)) {
if (incl_source)
coresight_disable_source(cd);
} else {
coresight_disable_link(cd);
}
}
}
static int coresight_switch_sink(struct coresight_device *csdev)
{
int ret, prev_sink;
LIST_HEAD(path);
struct coresight_device *cd, *err_cd;
if (IS_ERR_OR_NULL(csdev))
return -EINVAL;
down(&coresight_mutex);
if (csdev->id == curr_sink)
goto out;
list_for_each_entry(cd, &coresight_devs, dev_link) {
if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
coresight_build_path(cd, &path);
coresight_disable_path(&path, false);
coresight_release_path(&path);
}
}
prev_sink = curr_sink;
curr_sink = csdev->id;
list_for_each_entry(cd, &coresight_devs, dev_link) {
if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
if (!coresight_build_path(cd, &path)) {
ret = -EINVAL;
pr_err("coresight: build path failed\n");
goto err;
}
ret = coresight_enable_path(&path, false);
coresight_release_path(&path);
if (ret)
goto err;
}
}
out:
up(&coresight_mutex);
return 0;
err:
err_cd = cd;
list_for_each_entry_continue_reverse(cd, &coresight_devs, dev_link) {
if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
coresight_build_path(cd, &path);
coresight_disable_path(&path, true);
coresight_release_path(&path);
}
}
cd = err_cd;
/* This should be an enabled source, so we can disable it directly */
coresight_disable_source(cd);
list_for_each_entry_continue(cd, &coresight_devs, dev_link) {
if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable)
coresight_disable_source(cd);
}
curr_sink = prev_sink;
up(&coresight_mutex);
pr_err("coresight: sink switch failed, sources disabled; try again\n");
return ret;
}
int coresight_enable(struct coresight_device *csdev)
{
int ret;
LIST_HEAD(path);
if (IS_ERR_OR_NULL(csdev))
return -EINVAL;
down(&coresight_mutex);
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
ret = -EINVAL;
pr_err("coresight: wrong device type in %s\n", __func__);
goto err;
}
if (csdev->enable)
goto out;
if (!coresight_build_path(csdev, &path)) {
ret = -EINVAL;
pr_err("coresight: build path failed\n");
goto err;
}
ret = coresight_enable_path(&path, true);
coresight_release_path(&path);
if (ret)
goto err;
out:
up(&coresight_mutex);
return 0;
err:
up(&coresight_mutex);
pr_err("coresight: enable failed\n");
return ret;
}
EXPORT_SYMBOL(coresight_enable);
void coresight_disable(struct coresight_device *csdev)
{
LIST_HEAD(path);
if (IS_ERR_OR_NULL(csdev))
return;
down(&coresight_mutex);
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
pr_err("coresight: wrong device type in %s\n", __func__);
goto out;
}
if (!csdev->enable)
goto out;
coresight_build_path(csdev, &path);
coresight_disable_path(&path, true);
coresight_release_path(&path);
out:
up(&coresight_mutex);
}
EXPORT_SYMBOL(coresight_disable);
void coresight_abort(void)
{
struct coresight_device *cd;
if (down_trylock(&coresight_mutex)) {
pr_err("coresight: abort could not be processed\n");
return;
}
if (curr_sink == NO_SINK)
goto out;
list_for_each_entry(cd, &coresight_devs, dev_link) {
if (cd->id == curr_sink) {
if (cd->enable && cd->ops->sink_ops->abort) {
cd->ops->sink_ops->abort(cd);
cd->enable = false;
}
}
}
out:
up(&coresight_mutex);
}
EXPORT_SYMBOL(coresight_abort);
static ssize_t coresight_show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", dev->type->name);
}
static struct device_attribute coresight_dev_attrs[] = {
__ATTR(type, S_IRUGO, coresight_show_type, NULL),
{ },
};
struct bus_type coresight_bus_type = {
.name = "coresight",
.dev_attrs = coresight_dev_attrs,
};
static ssize_t coresight_show_curr_sink(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n",
csdev->id == curr_sink ? 1 : 0);
}
static ssize_t coresight_store_curr_sink(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);
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
if (val)
ret = coresight_switch_sink(csdev);
else
ret = -EINVAL;
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(curr_sink, S_IRUGO | S_IWUSR, coresight_show_curr_sink,
coresight_store_curr_sink);
static ssize_t coresight_show_enable(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 coresight_store_enable(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);
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
if (val)
ret = coresight_enable(csdev);
else
coresight_disable(csdev);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, coresight_show_enable,
coresight_store_enable);
static struct attribute *coresight_attrs_sink[] = {
&dev_attr_curr_sink.attr,
NULL,
};
static struct attribute_group coresight_attr_grp_sink = {
.attrs = coresight_attrs_sink,
};
static const struct attribute_group *coresight_attr_grps_sink[] = {
&coresight_attr_grp_sink,
NULL,
};
static struct attribute *coresight_attrs_source[] = {
&dev_attr_enable.attr,
NULL,
};
static struct attribute_group coresight_attr_grp_source = {
.attrs = coresight_attrs_source,
};
static const struct attribute_group *coresight_attr_grps_source[] = {
&coresight_attr_grp_source,
NULL,
};
static struct device_type coresight_dev_type[] = {
{
.name = "none",
},
{
.name = "sink",
.groups = coresight_attr_grps_sink,
},
{
.name = "link",
},
{
.name = "linksink",
.groups = coresight_attr_grps_sink,
},
{
.name = "source",
.groups = coresight_attr_grps_source,
},
};
static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);
kfree(csdev);
}
static void coresight_fixup_orphan_conns(struct coresight_device *csdev)
{
struct coresight_connection *conn, *temp;
list_for_each_entry_safe(conn, temp, &coresight_orph_conns, link) {
if (conn->child_id == csdev->id) {
conn->child_dev = csdev;
list_del(&conn->link);
}
}
}
static void coresight_fixup_device_conns(struct coresight_device *csdev)
{
int i;
struct coresight_device *cd;
bool found;
for (i = 0; i < csdev->nr_conns; i++) {
found = false;
list_for_each_entry(cd, &coresight_devs, dev_link) {
if (csdev->conns[i].child_id == cd->id) {
csdev->conns[i].child_dev = cd;
found = true;
break;
}
}
if (!found)
list_add_tail(&csdev->conns[i].link,
&coresight_orph_conns);
}
}
struct coresight_device *coresight_register(struct coresight_desc *desc)
{
int i;
int ret;
int link_subtype;
int nr_refcnts;
int *refcnts = NULL;
struct coresight_device *csdev;
struct coresight_connection *conns;
if (IS_ERR_OR_NULL(desc))
return ERR_PTR(-EINVAL);
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
if (!csdev) {
ret = -ENOMEM;
goto err_kzalloc_csdev;
}
csdev->id = desc->pdata->id;
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_inports;
else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
nr_refcnts = desc->pdata->nr_outports;
else
nr_refcnts = 1;
refcnts = kzalloc(sizeof(*refcnts) * nr_refcnts, GFP_KERNEL);
if (!refcnts) {
ret = -ENOMEM;
goto err_kzalloc_refcnts;
}
csdev->refcnt.link_refcnts = refcnts;
}
csdev->nr_conns = desc->pdata->nr_outports;
conns = kzalloc(sizeof(*conns) * csdev->nr_conns, GFP_KERNEL);
if (!conns) {
ret = -ENOMEM;
goto err_kzalloc_conns;
}
for (i = 0; i < csdev->nr_conns; i++) {
conns[i].outport = desc->pdata->outports[i];
conns[i].child_id = desc->pdata->child_ids[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->owner = desc->owner;
csdev->dev.type = &coresight_dev_type[desc->type];
csdev->dev.groups = desc->groups;
csdev->dev.parent = desc->dev;
csdev->dev.bus = &coresight_bus_type;
csdev->dev.release = coresight_device_release;
dev_set_name(&csdev->dev, "%s", desc->pdata->name);
down(&coresight_mutex);
if (desc->pdata->default_sink) {
if (curr_sink == NO_SINK) {
curr_sink = csdev->id;
} else {
ret = -EINVAL;
goto err_default_sink;
}
}
coresight_fixup_device_conns(csdev);
ret = device_register(&csdev->dev);
if (ret)
goto err_dev_reg;
coresight_fixup_orphan_conns(csdev);
list_add_tail(&csdev->dev_link, &coresight_devs);
up(&coresight_mutex);
return csdev;
err_dev_reg:
put_device(&csdev->dev);
err_default_sink:
up(&coresight_mutex);
kfree(conns);
err_kzalloc_conns:
kfree(refcnts);
err_kzalloc_refcnts:
kfree(csdev);
err_kzalloc_csdev:
return ERR_PTR(ret);
}
EXPORT_SYMBOL(coresight_register);
void coresight_unregister(struct coresight_device *csdev)
{
if (IS_ERR_OR_NULL(csdev))
return;
if (get_device(&csdev->dev)) {
device_unregister(&csdev->dev);
put_device(&csdev->dev);
}
}
EXPORT_SYMBOL(coresight_unregister);
static int __init coresight_init(void)
{
return bus_register(&coresight_bus_type);
}
core_initcall(coresight_init);
static void __exit coresight_exit(void)
{
bus_unregister(&coresight_bus_type);
}
module_exit(coresight_exit);
MODULE_LICENSE("GPL v2");