coresight: hwtracing subsystem updates for v6.9

Changes targeting Linux v6.9 include:
  - CoreSight: Enable W=1 warnings as default
  - CoreSight: Clean up sysfs/perf mode handling for tracing
  - Support for Qualcomm TPDM CMB Dataset
  - Miscellaneous fixes to the CoreSight subsystem
  - Fix for hisi_ptt PMU to reject events targeting other PMUs
 
 Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuFy0byloRoXZHaWBxcXRZPKyBqEFAmXfD1gACgkQxcXRZPKy
 BqEbiRAAks3YgMt1PC610hhvDFps3GoedEGnJBLajtdh7v/gWP6a/LYIP7YU9dkA
 pskkhvvZm87PdCu9vc7r2NaYVb+8rVyTdafd2DjA1HAdOK2/BoT6tJQzwG3W62EE
 0hvRTAgoq40jU4pix1s/wiQjFYa0l98cN85mu6HmMaWu+ulDgEAudcCyqX4kiY+k
 S1llq7m5JlQdobcDa2a2IbU8L6t6m9hPittgHZwvXBy9OxFJ6GDLoKO0YOn2mp8W
 yZa1fMAlkCG4asRZyhLb/mRubzVRHfYrJOvgZUJGV67fRUPU/Lwsx5WAwy4NECv5
 rooiXEw5TVaQ9/l18W0Zj2WAveVWLGI5HGmIywZ4HEc8fukLWsgLG78Bomu2FkGD
 Is9mgeXL8oXfGufEHvCxOvI53rHg3tBLsX13mPylkFH+DAD3EPZ8ASoQOa0SbCpV
 fp8SBznv8q8mHBiFR6Mb4qSDkIjq7h9ygNXzTh1j5BMEf06oTbasNiHmi62dSZsa
 uYlnPBmSZ0OgvQjRhvdlVKRQIbbni2Ddt93Wnl/6tPvwcv6MmnTKi2cmTZ9IrFPn
 QFeSeU19gLlAjNAF/1BtubaV0Z4Sj3Tks6TpCcLT4em+Z57efRW9ZGwOGrRsHHiB
 P0SXCNLiVsAkpZP9wVryEYjb0CnlRljKqiNSO9p3AGhkPJpDhyc=
 =cz5l
 -----END PGP SIGNATURE-----

Merge tag 'coresight-next-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux into char-misc-next

Suzuki writes:

coresight: hwtracing subsystem updates for v6.9

Changes targeting Linux v6.9 include:
 - CoreSight: Enable W=1 warnings as default
 - CoreSight: Clean up sysfs/perf mode handling for tracing
 - Support for Qualcomm TPDM CMB Dataset
 - Miscellaneous fixes to the CoreSight subsystem
 - Fix for hisi_ptt PMU to reject events targeting other PMUs

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>

* tag 'coresight-next-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux: (32 commits)
  coresight-tpda: Change qcom,dsb-element-size to qcom,dsb-elem-bits
  dt-bindings: arm: qcom,coresight-tpdm: Rename qcom,dsb-element-size
  hwtracing: hisi_ptt: Move type check to the beginning of hisi_ptt_pmu_event_init()
  coresight: tpdm: Fix build break due to uninitialised field
  coresight: etm4x: Set skip_power_up in etm4_init_arch_data function
  coresight-tpdm: Add msr register support for CMB
  dt-bindings: arm: qcom,coresight-tpdm: Add support for TPDM CMB MSR register
  coresight-tpdm: Add timestamp control register support for the CMB
  coresight-tpdm: Add pattern registers support for CMB
  coresight-tpdm: Add support to configure CMB
  coresight-tpda: Add support to configure CMB element
  coresight-tpdm: Add CMB dataset support
  dt-bindings: arm: qcom,coresight-tpdm: Add support for CMB element size
  coresight-tpdm: Optimize the useage of tpdm_has_dsb_dataset
  coresight-tpdm: Optimize the store function of tpdm simple dataset
  coresight: Add helper for setting csdev->mode
  coresight: Add a helper for getting csdev->mode
  coresight: Add helper for atomically taking the device
  coresight: Add explicit member initializers to coresight_dev_type
  coresight: Remove unused stubs
  ...
This commit is contained in:
Greg Kroah-Hartman 2024-03-02 19:58:26 +01:00
commit bac2f2cfe2
31 changed files with 1387 additions and 813 deletions

View File

@ -170,3 +170,90 @@ Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_t
Description:
(RW) Set/Get the MSR(mux select register) for the DSB subunit
TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_mode
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description: (Write) Set the data collection mode of CMB tpdm. Continuous
change creates CMB data set elements on every CMBCLK edge.
Trace-on-change creates CMB data set elements only when a new
data set element differs in value from the previous element
in a CMB data set.
Accepts only one of the 2 values - 0 or 1.
0 : Continuous CMB collection mode.
1 : Trace-on-change CMB collection mode.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_patt/xpr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the value of the trigger pattern for the CMB
subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_patt/xpmr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the mask of the trigger pattern for the CMB
subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the value of the pattern for the CMB subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpmr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the mask of the pattern for the CMB subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_patt/enable_ts
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(Write) Set the pattern timestamp of CMB tpdm. Read
the pattern timestamp of CMB tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Disable CMB pattern timestamp.
1 : Enable CMB pattern timestamp.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_ts
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the trigger timestamp of the CMB for tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Set the CMB trigger type to false
1 : Set the CMB trigger type to true
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_ts_all
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Read or write the status of timestamp upon all interface.
Only value 0 and 1 can be written to this node. Set this node to 1 to requeset
timestamp to all trace packet.
Accepts only one of the 2 values - 0 or 1.
0 : Disable the timestamp of all trace packets.
1 : Enable the timestamp of all trace packets.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_msr/msr[0:31]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the MSR(mux select register) for the CMB subunit
TPDM.

View File

@ -44,14 +44,21 @@ properties:
minItems: 1
maxItems: 2
qcom,dsb-element-size:
qcom,dsb-element-bits:
description:
Specifies the DSB(Discrete Single Bit) element size supported by
the monitor. The associated aggregator will read this size before it
is enabled. DSB element size currently only supports 32-bit and 64-bit.
$ref: /schemas/types.yaml#/definitions/uint8
enum: [32, 64]
qcom,cmb-element-bits:
description:
Specifies the CMB(Continuous Multi-Bit) element size supported by
the monitor. The associated aggregator will read this size before it
is enabled. CMB element size currently only supports 8-bit, 32-bit
and 64-bit.
enum: [8, 32, 64]
qcom,dsb-msrs-num:
description:
Specifies the number of DSB(Discrete Single Bit) MSR(mux select register)
@ -61,6 +68,15 @@ properties:
minimum: 0
maximum: 32
qcom,cmb-msrs-num:
description:
Specifies the number of CMB MSR(mux select register) registers supported
by the monitor. If this property is not configured or set to 0, it means
this TPDM doesn't support CMB MSR.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 32
clocks:
maxItems: 1
@ -94,7 +110,7 @@ examples:
compatible = "qcom,coresight-tpdm", "arm,primecell";
reg = <0x0684c000 0x1000>;
qcom,dsb-element-size = /bits/ 8 <32>;
qcom,dsb-element-bits = <32>;
qcom,dsb-msrs-num = <16>;
clocks = <&aoss_qmp>;
@ -110,4 +126,22 @@ examples:
};
};
tpdm@6c29000 {
compatible = "qcom,coresight-tpdm", "arm,primecell";
reg = <0x06c29000 0x1000>;
qcom,cmb-element-bits = <64>;
qcom,cmb-msrs-num = <32>;
clocks = <&aoss_qmp>;
clock-names = "apb_pclk";
out-ports {
port {
tpdm_ipcc_out_funnel_center: endpoint {
remote-endpoint = <&funnel_center_in_tpdm_ipcc>;
};
};
};
};
...

View File

@ -2,6 +2,26 @@
#
# Makefile for CoreSight drivers.
#
# Current W=1 warnings
subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter
subdir-ccflags-y += -Wmissing-declarations
subdir-ccflags-y += -Wmissing-format-attribute
subdir-ccflags-y += -Wmissing-prototypes
subdir-ccflags-y += -Wold-style-definition
subdir-ccflags-y += -Wmissing-include-dirs
subdir-ccflags-y += -Wno-sign-compare
condflags := \
$(call cc-option, -Wrestrict) \
$(call cc-option, -Wunused-but-set-variable) \
$(call cc-option, -Wunused-const-variable) \
$(call cc-option, -Wpacked-not-aligned) \
$(call cc-option, -Wformat-overflow) \
$(call cc-option, -Wformat-truncation) \
$(call cc-option, -Wstringop-overflow) \
$(call cc-option, -Wstringop-truncation)
subdir-ccflags-y += $(condflags)
obj-$(CONFIG_CORESIGHT) += coresight.o
coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \
coresight-sysfs.o coresight-syscfg.o coresight-config.o \

View File

@ -9,6 +9,7 @@
/* ETMv4 includes and features */
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
#include "coresight-etm4x-cfg.h"
#include "coresight-cfg-preload.h"
/* preload configurations and features */

View File

@ -9,7 +9,6 @@
#include <linux/types.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/idr.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/slab.h>
@ -25,15 +24,12 @@
#include "coresight-priv.h"
#include "coresight-syscfg.h"
static DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
/*
* Use IDR to map the hash of the source's device name
* to the pointer of path for the source. The idr is for
* the sources which aren't associated with CPU.
* Mutex used to lock all sysfs enable and disable actions and loading and
* unloading devices by the Coresight core.
*/
static DEFINE_IDR(path_idr);
DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
/**
* struct coresight_node - elements of a path, from source to sink
@ -45,12 +41,6 @@ struct coresight_node {
struct list_head link;
};
/*
* When operating Coresight drivers from the sysFS interface, only a single
* path can exist from a tracer (associated to a CPU) to a sink.
*/
static DEFINE_PER_CPU(struct list_head *, tracer_path);
/*
* When losing synchronisation a new barrier packet needs to be inserted at the
* beginning of the data collected in a buffer. That way the decoder knows that
@ -61,34 +51,6 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
static const struct cti_assoc_op *cti_assoc_ops;
ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
ssize_t coresight_simple_show32(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@ -279,42 +241,18 @@ EXPORT_SYMBOL_GPL(coresight_add_helper);
static int coresight_enable_sink(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
int ret;
/*
* We need to make sure the "new" session is compatible with the
* existing "mode" of operation.
*/
if (!sink_ops(csdev)->enable)
return -EINVAL;
ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
csdev->enable = true;
return 0;
return sink_ops(csdev)->enable(csdev, mode, data);
}
static void coresight_disable_sink(struct coresight_device *csdev)
{
int ret;
if (!sink_ops(csdev)->disable)
return;
ret = sink_ops(csdev)->disable(csdev);
if (ret)
return;
csdev->enable = false;
sink_ops(csdev)->disable(csdev);
}
static int coresight_enable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
{
int ret = 0;
int link_subtype;
struct coresight_connection *inconn, *outconn;
@ -330,21 +268,13 @@ static int coresight_enable_link(struct coresight_device *csdev,
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && IS_ERR(outconn))
return PTR_ERR(outconn);
if (link_ops(csdev)->enable) {
ret = link_ops(csdev)->enable(csdev, inconn, outconn);
if (!ret)
csdev->enable = true;
}
return ret;
return link_ops(csdev)->enable(csdev, inconn, outconn);
}
static void coresight_disable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
{
int i;
int link_subtype;
struct coresight_connection *inconn, *outconn;
if (!parent || !child)
@ -352,50 +282,10 @@ static void coresight_disable_link(struct coresight_device *csdev,
inconn = coresight_find_out_connection(parent, csdev);
outconn = coresight_find_out_connection(csdev, child);
link_subtype = csdev->subtype.link_subtype;
if (link_ops(csdev)->disable) {
link_ops(csdev)->disable(csdev, inconn, outconn);
}
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
for (i = 0; i < csdev->pdata->nr_inconns; i++)
if (atomic_read(&csdev->pdata->in_conns[i]->dest_refcnt) !=
0)
return;
} else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
for (i = 0; i < csdev->pdata->nr_outconns; i++)
if (atomic_read(&csdev->pdata->out_conns[i]->src_refcnt) !=
0)
return;
} else {
if (atomic_read(&csdev->refcnt) != 0)
return;
}
csdev->enable = false;
link_ops(csdev)->disable(csdev, inconn, outconn);
}
int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
void *data)
{
int ret;
if (!csdev->enable) {
if (source_ops(csdev)->enable) {
ret = source_ops(csdev)->enable(csdev, data, mode);
if (ret)
return ret;
}
csdev->enable = true;
}
atomic_inc(&csdev->refcnt);
return 0;
}
EXPORT_SYMBOL_GPL(coresight_enable_source);
static bool coresight_is_helper(struct coresight_device *csdev)
{
return csdev->type == CORESIGHT_DEV_TYPE_HELPER;
@ -404,29 +294,12 @@ static bool coresight_is_helper(struct coresight_device *csdev)
static int coresight_enable_helper(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
int ret;
if (!helper_ops(csdev)->enable)
return 0;
ret = helper_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
csdev->enable = true;
return 0;
return helper_ops(csdev)->enable(csdev, mode, data);
}
static void coresight_disable_helper(struct coresight_device *csdev)
{
int ret;
if (!helper_ops(csdev)->disable)
return;
ret = helper_ops(csdev)->disable(csdev, NULL);
if (ret)
return;
csdev->enable = false;
helper_ops(csdev)->disable(csdev, NULL);
}
static void coresight_disable_helpers(struct coresight_device *csdev)
@ -441,25 +314,20 @@ static void coresight_disable_helpers(struct coresight_device *csdev)
}
}
/**
* coresight_disable_source - Drop the reference count by 1 and disable
* the device if there are no users left.
/*
* Helper function to call source_ops(csdev)->disable and also disable the
* helpers.
*
* @csdev: The coresight device to disable
* @data: Opaque data to pass on to the disable function of the source device.
* For example in perf mode this is a pointer to the struct perf_event.
*
* Returns true if the device has been disabled.
* There is an imbalance between coresight_enable_path() and
* coresight_disable_path(). Enabling also enables the source's helpers as part
* of the path, but disabling always skips the first item in the path (which is
* the source), so sources and their helpers don't get disabled as part of that
* function and we need the extra step here.
*/
bool coresight_disable_source(struct coresight_device *csdev, void *data)
void coresight_disable_source(struct coresight_device *csdev, void *data)
{
if (atomic_dec_return(&csdev->refcnt) == 0) {
if (source_ops(csdev)->disable)
source_ops(csdev)->disable(csdev, data);
coresight_disable_helpers(csdev);
csdev->enable = false;
}
return !csdev->enable;
source_ops(csdev)->disable(csdev, data);
coresight_disable_helpers(csdev);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
@ -484,7 +352,7 @@ static void coresight_disable_path_from(struct list_head *path,
/*
* ETF devices are tricky... They can be a link or a sink,
* depending on how they are configured. If an ETF has been
* "activated" it will be configured as a sink, otherwise
* selected as a sink it will be configured as a sink, otherwise
* go ahead with the link configuration.
*/
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
@ -562,7 +430,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode,
/*
* ETF devices are tricky... They can be a link or a sink,
* depending on how they are configured. If an ETF has been
* "activated" it will be configured as a sink, otherwise
* selected as a sink it will be configured as a sink, otherwise
* go ahead with the link configuration.
*/
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
@ -619,48 +487,6 @@ struct coresight_device *coresight_get_sink(struct list_head *path)
return csdev;
}
static struct coresight_device *
coresight_find_enabled_sink(struct coresight_device *csdev)
{
int i;
struct coresight_device *sink = NULL;
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->activated)
return csdev;
/*
* Recursively explore each port found on this element.
*/
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
struct coresight_device *child_dev;
child_dev = csdev->pdata->out_conns[i]->dest_dev;
if (child_dev)
sink = coresight_find_enabled_sink(child_dev);
if (sink)
return sink;
}
return NULL;
}
/**
* coresight_get_enabled_sink - returns the first enabled sink using
* connection based search starting from the source reference
*
* @source: Coresight source device reference
*/
struct coresight_device *
coresight_get_enabled_sink(struct coresight_device *source)
{
if (!source)
return NULL;
return coresight_find_enabled_sink(source);
}
static int coresight_sink_by_id(struct device *dev, const void *data)
{
struct coresight_device *csdev = to_coresight_device(dev);
@ -794,11 +620,10 @@ static void coresight_drop_device(struct coresight_device *csdev)
* @sink: The final sink we want in this path.
* @path: The list to add devices to.
*
* The tree of Coresight device is traversed until an activated sink is
* found. From there the sink is added to the list along with all the
* devices that led to that point - the end result is a list from source
* to sink. In that list the source is the first device and the sink the
* last one.
* The tree of Coresight device is traversed until @sink is found.
* From there the sink is added to the list along with all the devices that led
* to that point - the end result is a list from source to sink. In that list
* the source is the first device and the sink the last one.
*/
static int _coresight_build_path(struct coresight_device *csdev,
struct coresight_device *sink,
@ -808,7 +633,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
bool found = false;
struct coresight_node *node;
/* An activated sink has been found. Enqueue the element */
/* The sink has been found. Enqueue the element */
if (csdev == sink)
goto out;
@ -1072,269 +897,6 @@ static void coresight_clear_default_sink(struct coresight_device *csdev)
}
}
/** coresight_validate_source - make sure a source has the right credentials
* @csdev: the device structure for a source.
* @function: the function this was called from.
*
* Assumes the coresight_mutex is held.
*/
static int coresight_validate_source(struct coresight_device *csdev,
const char *function)
{
u32 type, subtype;
type = csdev->type;
subtype = csdev->subtype.source_subtype;
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", function);
return -EINVAL;
}
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
}
return 0;
}
int coresight_enable(struct coresight_device *csdev)
{
int cpu, ret = 0;
struct coresight_device *sink;
struct list_head *path;
enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source(csdev, __func__);
if (ret)
goto out;
if (csdev->enable) {
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*/
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
atomic_inc(&csdev->refcnt);
goto out;
}
sink = coresight_get_enabled_sink(csdev);
if (!sink) {
ret = -EINVAL;
goto out;
}
path = coresight_build_path(csdev, sink);
if (IS_ERR(path)) {
pr_err("building path(s) failed\n");
ret = PTR_ERR(path);
goto out;
}
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
if (ret)
goto err_path;
ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL);
if (ret)
goto err_source;
switch (subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
/*
* Use the hash of source's device name as ID
* and map the ID to the pointer of the path.
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
if (ret)
goto err_source;
break;
default:
/* We can't be here */
break;
}
out:
mutex_unlock(&coresight_mutex);
return ret;
err_source:
coresight_disable_path(path);
err_path:
coresight_release_path(path);
goto out;
}
EXPORT_SYMBOL_GPL(coresight_enable);
void coresight_disable(struct coresight_device *csdev)
{
int cpu, ret;
struct list_head *path = NULL;
u32 hash;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source(csdev, __func__);
if (ret)
goto out;
if (!csdev->enable || !coresight_disable_source(csdev, NULL))
goto out;
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
/* Find the path by the hash. */
path = idr_find(&path_idr, hash);
if (path == NULL) {
pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
goto out;
}
idr_remove(&path_idr, hash);
break;
default:
/* We can't be here */
break;
}
coresight_disable_path(path);
coresight_release_path(path);
out:
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_disable);
static ssize_t enable_sink_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated);
}
static ssize_t enable_sink_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val)
csdev->activated = true;
else
csdev->activated = false;
return size;
}
static DEVICE_ATTR_RW(enable_sink);
static ssize_t enable_source_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable);
}
static ssize_t enable_source_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret = 0;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val) {
ret = coresight_enable(csdev);
if (ret)
return ret;
} else {
coresight_disable(csdev);
}
return size;
}
static DEVICE_ATTR_RW(enable_source);
static struct attribute *coresight_sink_attrs[] = {
&dev_attr_enable_sink.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_sink);
static struct attribute *coresight_source_attrs[] = {
&dev_attr_enable_source.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_source);
static struct device_type coresight_dev_type[] = {
{
.name = "sink",
.groups = coresight_sink_groups,
},
{
.name = "link",
},
{
.name = "linksink",
.groups = coresight_sink_groups,
},
{
.name = "source",
.groups = coresight_source_groups,
},
{
.name = "helper",
}
};
/* Ensure the enum matches the names and groups */
static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);
@ -1799,7 +1361,7 @@ done:
}
EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
struct bus_type coresight_bustype = {
const struct bus_type coresight_bustype = {
.name = "coresight",
};

View File

@ -974,7 +974,7 @@ static const struct amba_id cti_ids[] = {
CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */
CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, cti_ids);

View File

@ -76,7 +76,6 @@ DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb");
* @pid: Process ID of the process being monitored by the session
* that is using this component.
* @buf: area of memory where ETB buffer content gets sent.
* @mode: this ETB is being used.
* @buffer_depth: size of @buf.
* @trigger_cntr: amount of words to store after a trigger.
*/
@ -89,7 +88,6 @@ struct etb_drvdata {
local_t reading;
pid_t pid;
u8 *buf;
u32 mode;
u32 buffer_depth;
u32 trigger_cntr;
};
@ -150,20 +148,20 @@ static int etb_enable_sysfs(struct coresight_device *csdev)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't messup with perf sessions. */
if (drvdata->mode == CS_MODE_PERF) {
if (coresight_get_mode(csdev) == CS_MODE_PERF) {
ret = -EBUSY;
goto out;
}
if (drvdata->mode == CS_MODE_DISABLED) {
if (coresight_get_mode(csdev) == CS_MODE_DISABLED) {
ret = etb_enable_hw(drvdata);
if (ret)
goto out;
drvdata->mode = CS_MODE_SYSFS;
coresight_set_mode(csdev, CS_MODE_SYSFS);
}
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
@ -181,7 +179,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* No need to continue if the component is already in used by sysFS. */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
ret = -EBUSY;
goto out;
}
@ -199,7 +197,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
* use for this session.
*/
if (drvdata->pid == pid) {
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
goto out;
}
@ -216,8 +214,8 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
if (!ret) {
/* Associate with monitored process. */
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
atomic_inc(&csdev->refcnt);
coresight_set_mode(drvdata->csdev, CS_MODE_PERF);
csdev->refcnt++;
}
out:
@ -356,17 +354,18 @@ static int etb_disable(struct coresight_device *csdev)
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_dec_return(&csdev->refcnt)) {
csdev->refcnt--;
if (csdev->refcnt) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
etb_disable_hw(drvdata);
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_dbg(&csdev->dev, "ETB disabled\n");
@ -447,7 +446,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't do anything if another tracer is using this sink */
if (atomic_read(&csdev->refcnt) != 1)
if (csdev->refcnt != 1)
goto out;
__etb_disable_hw(drvdata);
@ -589,7 +588,7 @@ static void etb_dump(struct etb_drvdata *drvdata)
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
__etb_disable_hw(drvdata);
etb_dump_hw(drvdata);
__etb_enable_hw(drvdata);
@ -837,7 +836,7 @@ static const struct amba_id etb_ids[] = {
.id = 0x000bb907,
.mask = 0x000fffff,
},
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, etb_ids);

View File

@ -589,7 +589,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
return;
/* stop tracer */
source_ops(csdev)->disable(csdev, event);
coresight_disable_source(csdev, event);
/* tell the core */
event->hw.state = PERF_HES_STOPPED;

View File

@ -215,7 +215,6 @@ struct etm_config {
* @port_size: port size as reported by ETMCR bit 4-6 and 21.
* @arch: ETM/PTM version number.
* @use_cpu14: true if management registers need to be accessed via CP14.
* @mode: this tracer's mode, i.e sysFS, Perf or disabled.
* @sticky_enable: true if ETM base configuration has been done.
* @boot_enable:true if we should start tracing at boot time.
* @os_unlock: true if access to management registers is allowed.
@ -238,7 +237,6 @@ struct etm_drvdata {
int port_size;
u8 arch;
bool use_cp14;
local_t mode;
bool sticky_enable;
bool boot_enable;
bool os_unlock;

View File

@ -115,7 +115,7 @@ static void etm_clr_pwrup(struct etm_drvdata *drvdata)
*
* Basically the same as @coresight_timeout except for the register access
* method where we have to account for CP14 configurations.
*
* Return: 0 as soon as the bit has taken the desired state or -EAGAIN if
* TIMEOUT_US has elapsed, which ever happens first.
*/
@ -556,14 +556,12 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode)
{
int ret;
u32 val;
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
if (!coresight_take_mode(csdev, mode)) {
/* Someone is already using the tracer */
return -EBUSY;
}
switch (mode) {
case CS_MODE_SYSFS:
@ -578,7 +576,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
/* The tracer didn't start */
if (ret)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
return ret;
}
@ -672,14 +670,13 @@ static void etm_disable(struct coresight_device *csdev,
struct perf_event *event)
{
enum cs_mode mode;
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* For as long as the tracer isn't disabled another entity can't
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
mode = local_read(&drvdata->mode);
mode = coresight_get_mode(csdev);
switch (mode) {
case CS_MODE_DISABLED:
@ -696,7 +693,7 @@ static void etm_disable(struct coresight_device *csdev,
}
if (mode)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
static const struct coresight_ops_source etm_source_ops = {
@ -715,7 +712,7 @@ static int etm_online_cpu(unsigned int cpu)
return 0;
if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
coresight_enable(etmdrvdata[cpu]->csdev);
coresight_enable_sysfs(etmdrvdata[cpu]->csdev);
return 0;
}
@ -730,7 +727,7 @@ static int etm_starting_cpu(unsigned int cpu)
etmdrvdata[cpu]->os_unlock = true;
}
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm_enable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -742,7 +739,7 @@ static int etm_dying_cpu(unsigned int cpu)
return 0;
spin_lock(&etmdrvdata[cpu]->spinlock);
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm_disable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -925,7 +922,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
dev_info(&drvdata->csdev->dev,
"%s initialized\n", (char *)coresight_get_uci_data(id));
if (boot_enable) {
coresight_enable(drvdata->csdev);
coresight_enable_sysfs(drvdata->csdev);
drvdata->boot_enable = true;
}
@ -1003,7 +1000,7 @@ static const struct amba_id etm_ids[] = {
CS_AMBA_ID_DATA(0x000bb95f, "PTM 1.1"),
/* PTM 1.1 Qualcomm */
CS_AMBA_ID_DATA(0x000b006f, "PTM 1.1"),
{ 0, 0},
{ 0, 0, NULL},
};
MODULE_DEVICE_TABLE(amba, etm_ids);

View File

@ -722,7 +722,7 @@ static ssize_t cntr_val_show(struct device *dev,
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etm_config *config = &drvdata->config;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
spin_lock(&drvdata->spinlock);
for (i = 0; i < drvdata->nr_cntr; i++)
ret += sprintf(buf, "counter %d: %x\n",
@ -941,7 +941,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etm_config *config = &drvdata->config;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
val = config->seq_curr_state;
goto out;
}

View File

@ -840,14 +840,11 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode)
{
int ret;
u32 val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
if (!coresight_take_mode(csdev, mode)) {
/* Someone is already using the tracer */
return -EBUSY;
}
switch (mode) {
case CS_MODE_SYSFS:
@ -862,7 +859,7 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
/* The tracer didn't start */
if (ret)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
return ret;
}
@ -1004,14 +1001,13 @@ static void etm4_disable(struct coresight_device *csdev,
struct perf_event *event)
{
enum cs_mode mode;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* For as long as the tracer isn't disabled another entity can't
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
mode = local_read(&drvdata->mode);
mode = coresight_get_mode(csdev);
switch (mode) {
case CS_MODE_DISABLED:
@ -1025,7 +1021,7 @@ static void etm4_disable(struct coresight_device *csdev,
}
if (mode)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
static const struct coresight_ops_source etm4_source_ops = {
@ -1200,6 +1196,7 @@ static void etm4_init_arch_data(void *info)
struct etm4_init_arg *init_arg = info;
struct etmv4_drvdata *drvdata;
struct csdev_access *csa;
struct device *dev = init_arg->dev;
int i;
drvdata = dev_get_drvdata(init_arg->dev);
@ -1213,6 +1210,10 @@ static void etm4_init_arch_data(void *info)
if (!etm4_init_csdev_access(drvdata, csa))
return;
if (!csa->io_mem ||
fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
drvdata->skip_power_up = true;
/* Detect the support for OS Lock before we actually use it */
etm_detect_os_lock(drvdata, csa);
@ -1650,7 +1651,7 @@ static int etm4_online_cpu(unsigned int cpu)
return etm4_probe_cpu(cpu);
if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
coresight_enable(etmdrvdata[cpu]->csdev);
coresight_enable_sysfs(etmdrvdata[cpu]->csdev);
return 0;
}
@ -1663,7 +1664,7 @@ static int etm4_starting_cpu(unsigned int cpu)
if (!etmdrvdata[cpu]->os_unlock)
etm4_os_unlock(etmdrvdata[cpu]);
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_enable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -1675,7 +1676,7 @@ static int etm4_dying_cpu(unsigned int cpu)
return 0;
spin_lock(&etmdrvdata[cpu]->spinlock);
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_disable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -1833,7 +1834,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
* Save and restore the ETM Trace registers only if
* the ETM is active.
*/
if (local_read(&drvdata->mode) && drvdata->save_state)
if (coresight_get_mode(drvdata->csdev) && drvdata->save_state)
ret = __etm4_cpu_save(drvdata);
return ret;
}
@ -2040,11 +2041,6 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
if (!drvdata->arch)
return -EINVAL;
/* TRCPDCR is not accessible with system instructions. */
if (!desc.access.io_mem ||
fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
drvdata->skip_power_up = true;
major = ETM_ARCH_MAJOR_VERSION(drvdata->arch);
minor = ETM_ARCH_MINOR_VERSION(drvdata->arch);
@ -2098,7 +2094,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
drvdata->cpu, type_name, major, minor);
if (boot_enable) {
coresight_enable(drvdata->csdev);
coresight_enable_sysfs(drvdata->csdev);
drvdata->boot_enable = true;
}
@ -2390,7 +2386,7 @@ static const struct of_device_id etm4_sysreg_match[] = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id etm4x_acpi_ids[] = {
{"ARMHC500", 0}, /* ARM CoreSight ETM4x */
{"ARMHC500", 0, 0, 0}, /* ARM CoreSight ETM4x */
{}
};
MODULE_DEVICE_TABLE(acpi, etm4x_acpi_ids);

View File

@ -1016,7 +1016,6 @@ struct etmv4_drvdata {
void __iomem *base;
struct coresight_device *csdev;
spinlock_t spinlock;
local_t mode;
int cpu;
u8 arch;
u8 nr_pe;

View File

@ -350,7 +350,7 @@ MODULE_DEVICE_TABLE(of, static_funnel_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id static_funnel_ids[] = {
{"ARMHC9FE", 0},
{"ARMHC9FE", 0, 0, 0},
{},
};
@ -391,7 +391,7 @@ static const struct amba_id dynamic_funnel_ids[] = {
.id = 0x000bb9eb,
.mask = 0x000fffff,
},
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids);

View File

@ -12,6 +12,9 @@
#include <linux/coresight.h>
#include <linux/pm_runtime.h>
extern struct mutex coresight_mutex;
extern struct device_type coresight_dev_type[];
/*
* Coresight management registers (0xf00-0xfcc)
* 0xfa0 - 0xfa4: Management registers in PFTv1.0
@ -130,8 +133,6 @@ void coresight_disable_path(struct list_head *path);
int coresight_enable_path(struct list_head *path, enum cs_mode mode,
void *sink_data);
struct coresight_device *coresight_get_sink(struct list_head *path);
struct coresight_device *
coresight_get_enabled_sink(struct coresight_device *source);
struct coresight_device *coresight_get_sink_by_id(u32 id);
struct coresight_device *
coresight_find_default_sink(struct coresight_device *csdev);
@ -231,8 +232,6 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
struct coresight_device *coresight_get_percpu_sink(int cpu);
int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
void *data);
bool coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_disable_source(struct coresight_device *csdev, void *data);
#endif

View File

@ -363,7 +363,7 @@ MODULE_DEVICE_TABLE(of, static_replicator_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id static_replicator_acpi_ids[] = {
{"ARMHC985", 0}, /* ARM CoreSight Static Replicator */
{"ARMHC985", 0, 0, 0}, /* ARM CoreSight Static Replicator */
{}
};

View File

@ -119,7 +119,6 @@ DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm");
* @spinlock: only one at a time pls.
* @chs: the channels accociated to this STM.
* @stm: structure associated to the generic STM interface.
* @mode: this tracer's mode (enum cs_mode), i.e sysFS, or disabled.
* @traceid: value of the current ID for this component.
* @write_bytes: Maximus bytes this STM can write at a time.
* @stmsper: settings for register STMSPER.
@ -136,7 +135,6 @@ struct stm_drvdata {
spinlock_t spinlock;
struct channel_space chs;
struct stm_data stm;
local_t mode;
u8 traceid;
u32 write_bytes;
u32 stmsper;
@ -195,17 +193,15 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
static int stm_enable(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode)
{
u32 val;
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (mode != CS_MODE_SYSFS)
return -EINVAL;
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
if (!coresight_take_mode(csdev, mode)) {
/* Someone is already using the tracer */
return -EBUSY;
}
pm_runtime_get_sync(csdev->dev.parent);
@ -266,7 +262,7 @@ static void stm_disable(struct coresight_device *csdev,
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
spin_lock(&drvdata->spinlock);
stm_disable_hw(drvdata);
spin_unlock(&drvdata->spinlock);
@ -276,7 +272,7 @@ static void stm_disable(struct coresight_device *csdev,
pm_runtime_put(csdev->dev.parent);
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
dev_dbg(&csdev->dev, "STM tracing disabled\n");
}
}
@ -334,7 +330,7 @@ static int stm_generic_link(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev)
return -EINVAL;
return coresight_enable(drvdata->csdev);
return coresight_enable_sysfs(drvdata->csdev);
}
static void stm_generic_unlink(struct stm_data *stm_data,
@ -345,7 +341,7 @@ static void stm_generic_unlink(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev)
return;
coresight_disable(drvdata->csdev);
coresight_disable_sysfs(drvdata->csdev);
}
static phys_addr_t
@ -373,7 +369,7 @@ static long stm_generic_set_options(struct stm_data *stm_data,
{
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
if (!(drvdata && local_read(&drvdata->mode)))
if (!(drvdata && coresight_get_mode(drvdata->csdev)))
return -EINVAL;
if (channel >= drvdata->numsp)
@ -408,7 +404,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
struct stm_drvdata, stm);
unsigned int stm_flags;
if (!(drvdata && local_read(&drvdata->mode)))
if (!(drvdata && coresight_get_mode(drvdata->csdev)))
return -EACCES;
if (channel >= drvdata->numsp)
@ -515,7 +511,7 @@ static ssize_t port_select_show(struct device *dev,
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
val = drvdata->stmspscr;
} else {
spin_lock(&drvdata->spinlock);
@ -541,7 +537,7 @@ static ssize_t port_select_store(struct device *dev,
spin_lock(&drvdata->spinlock);
drvdata->stmspscr = val;
if (local_read(&drvdata->mode)) {
if (coresight_get_mode(drvdata->csdev)) {
CS_UNLOCK(drvdata->base);
/* Process as per ARM's TRM recommendation */
stmsper = readl_relaxed(drvdata->base + STMSPER);
@ -562,7 +558,7 @@ static ssize_t port_enable_show(struct device *dev,
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
val = drvdata->stmsper;
} else {
spin_lock(&drvdata->spinlock);
@ -588,7 +584,7 @@ static ssize_t port_enable_store(struct device *dev,
spin_lock(&drvdata->spinlock);
drvdata->stmsper = val;
if (local_read(&drvdata->mode)) {
if (coresight_get_mode(drvdata->csdev)) {
CS_UNLOCK(drvdata->base);
writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER);
CS_LOCK(drvdata->base);
@ -950,7 +946,7 @@ static const struct dev_pm_ops stm_dev_pm_ops = {
static const struct amba_id stm_ids[] = {
CS_AMBA_ID_DATA(0x000bb962, "STM32"),
CS_AMBA_ID_DATA(0x000bb963, "STM500"),
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, stm_ids);

View File

@ -5,10 +5,401 @@
*/
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include "coresight-priv.h"
/*
* Use IDR to map the hash of the source's device name
* to the pointer of path for the source. The idr is for
* the sources which aren't associated with CPU.
*/
static DEFINE_IDR(path_idr);
/*
* When operating Coresight drivers from the sysFS interface, only a single
* path can exist from a tracer (associated to a CPU) to a sink.
*/
static DEFINE_PER_CPU(struct list_head *, tracer_path);
ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
ssize_t coresight_simple_show32(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);
static int coresight_enable_source_sysfs(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
int ret;
/*
* Comparison with CS_MODE_SYSFS works without taking any device
* specific spinlock because the truthyness of that comparison can only
* change with coresight_mutex held, which we already have here.
*/
lockdep_assert_held(&coresight_mutex);
if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
ret = source_ops(csdev)->enable(csdev, data, mode);
if (ret)
return ret;
}
csdev->refcnt++;
return 0;
}
/**
* coresight_disable_source_sysfs - Drop the reference count by 1 and disable
* the device if there are no users left.
*
* @csdev: The coresight device to disable
* @data: Opaque data to pass on to the disable function of the source device.
* For example in perf mode this is a pointer to the struct perf_event.
*
* Returns true if the device has been disabled.
*/
static bool coresight_disable_source_sysfs(struct coresight_device *csdev,
void *data)
{
lockdep_assert_held(&coresight_mutex);
if (coresight_get_mode(csdev) != CS_MODE_SYSFS)
return false;
csdev->refcnt--;
if (csdev->refcnt == 0) {
coresight_disable_source(csdev, data);
return true;
}
return false;
}
/**
* coresight_find_activated_sysfs_sink - returns the first sink activated via
* sysfs using connection based search starting from the source reference.
*
* @csdev: Coresight source device reference
*/
static struct coresight_device *
coresight_find_activated_sysfs_sink(struct coresight_device *csdev)
{
int i;
struct coresight_device *sink = NULL;
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->sysfs_sink_activated)
return csdev;
/*
* Recursively explore each port found on this element.
*/
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
struct coresight_device *child_dev;
child_dev = csdev->pdata->out_conns[i]->dest_dev;
if (child_dev)
sink = coresight_find_activated_sysfs_sink(child_dev);
if (sink)
return sink;
}
return NULL;
}
/** coresight_validate_source - make sure a source has the right credentials to
* be used via sysfs.
* @csdev: the device structure for a source.
* @function: the function this was called from.
*
* Assumes the coresight_mutex is held.
*/
static int coresight_validate_source_sysfs(struct coresight_device *csdev,
const char *function)
{
u32 type, subtype;
type = csdev->type;
subtype = csdev->subtype.source_subtype;
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", function);
return -EINVAL;
}
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
}
return 0;
}
int coresight_enable_sysfs(struct coresight_device *csdev)
{
int cpu, ret = 0;
struct coresight_device *sink;
struct list_head *path;
enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source_sysfs(csdev, __func__);
if (ret)
goto out;
/*
* mode == SYSFS implies that it's already enabled. Don't look at the
* refcount to determine this because we don't claim the source until
* coresight_enable_source() so can still race with Perf mode which
* doesn't hold coresight_mutex.
*/
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*/
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
csdev->refcnt++;
goto out;
}
sink = coresight_find_activated_sysfs_sink(csdev);
if (!sink) {
ret = -EINVAL;
goto out;
}
path = coresight_build_path(csdev, sink);
if (IS_ERR(path)) {
pr_err("building path(s) failed\n");
ret = PTR_ERR(path);
goto out;
}
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
if (ret)
goto err_path;
ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL);
if (ret)
goto err_source;
switch (subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
/*
* Use the hash of source's device name as ID
* and map the ID to the pointer of the path.
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
if (ret)
goto err_source;
break;
default:
/* We can't be here */
break;
}
out:
mutex_unlock(&coresight_mutex);
return ret;
err_source:
coresight_disable_path(path);
err_path:
coresight_release_path(path);
goto out;
}
EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
void coresight_disable_sysfs(struct coresight_device *csdev)
{
int cpu, ret;
struct list_head *path = NULL;
u32 hash;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source_sysfs(csdev, __func__);
if (ret)
goto out;
if (!coresight_disable_source_sysfs(csdev, NULL))
goto out;
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
/* Find the path by the hash. */
path = idr_find(&path_idr, hash);
if (path == NULL) {
pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
goto out;
}
idr_remove(&path_idr, hash);
break;
default:
/* We can't be here */
break;
}
coresight_disable_path(path);
coresight_release_path(path);
out:
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_disable_sysfs);
static ssize_t enable_sink_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated);
}
static ssize_t enable_sink_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
csdev->sysfs_sink_activated = !!val;
return size;
}
static DEVICE_ATTR_RW(enable_sink);
static ssize_t enable_source_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
guard(mutex)(&coresight_mutex);
return scnprintf(buf, PAGE_SIZE, "%u\n",
coresight_get_mode(csdev) == CS_MODE_SYSFS);
}
static ssize_t enable_source_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret = 0;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val) {
ret = coresight_enable_sysfs(csdev);
if (ret)
return ret;
} else {
coresight_disable_sysfs(csdev);
}
return size;
}
static DEVICE_ATTR_RW(enable_source);
static struct attribute *coresight_sink_attrs[] = {
&dev_attr_enable_sink.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_sink);
static struct attribute *coresight_source_attrs[] = {
&dev_attr_enable_source.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_source);
struct device_type coresight_dev_type[] = {
[CORESIGHT_DEV_TYPE_SINK] = {
.name = "sink",
.groups = coresight_sink_groups,
},
[CORESIGHT_DEV_TYPE_LINK] = {
.name = "link",
},
[CORESIGHT_DEV_TYPE_LINKSINK] = {
.name = "linksink",
.groups = coresight_sink_groups,
},
[CORESIGHT_DEV_TYPE_SOURCE] = {
.name = "source",
.groups = coresight_source_groups,
},
[CORESIGHT_DEV_TYPE_HELPER] = {
.name = "helper",
}
};
/* Ensure the enum matches the names and groups */
static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
/*
* Connections group - links attribute.
* Count of created links between coresight components in the group.

View File

@ -558,7 +558,7 @@ static void tmc_shutdown(struct amba_device *adev)
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->mode == CS_MODE_DISABLED)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
@ -594,7 +594,7 @@ static const struct amba_id tmc_ids[] = {
CS_AMBA_ID(0x000bb9e9),
/* Coresight SoC 600 TMC-ETF */
CS_AMBA_ID(0x000bb9ea),
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, tmc_ids);

View File

@ -89,7 +89,7 @@ static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
* When operating in sysFS mode the content of the buffer needs to be
* read before the TMC is disabled.
*/
if (drvdata->mode == CS_MODE_SYSFS)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
tmc_etb_dump_hw(drvdata);
tmc_disable_hw(drvdata);
@ -205,8 +205,8 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
* sink is already enabled no memory is needed and the HW need not be
* touched.
*/
if (drvdata->mode == CS_MODE_SYSFS) {
atomic_inc(&csdev->refcnt);
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
csdev->refcnt++;
goto out;
}
@ -228,8 +228,8 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
ret = tmc_etb_enable_hw(drvdata);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(&csdev->refcnt);
coresight_set_mode(csdev, CS_MODE_SYSFS);
csdev->refcnt++;
} else {
/* Free up the buffer if we failed to enable */
used = false;
@ -262,7 +262,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
* No need to continue if the ETB/ETF is already operated
* from sysFS.
*/
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
ret = -EBUSY;
break;
}
@ -284,7 +284,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
* use for this session.
*/
if (drvdata->pid == pid) {
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
break;
}
@ -292,8 +292,8 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
if (!ret) {
/* Associate with monitored process. */
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
atomic_inc(&csdev->refcnt);
coresight_set_mode(csdev, CS_MODE_PERF);
csdev->refcnt++;
}
} while (0);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -338,17 +338,18 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
return -EBUSY;
}
if (atomic_dec_return(&csdev->refcnt)) {
csdev->refcnt--;
if (csdev->refcnt) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
tmc_etb_disable_hw(drvdata);
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -371,15 +372,15 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
return -EBUSY;
}
if (atomic_read(&csdev->refcnt) == 0) {
if (csdev->refcnt == 0) {
ret = tmc_etf_enable_hw(drvdata);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
coresight_set_mode(csdev, CS_MODE_SYSFS);
first_enable = true;
}
}
if (!ret)
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (first_enable)
@ -401,9 +402,10 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
return;
}
if (atomic_dec_return(&csdev->refcnt) == 0) {
csdev->refcnt--;
if (csdev->refcnt == 0) {
tmc_etf_disable_hw(drvdata);
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
last_disable = true;
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -483,13 +485,13 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
return 0;
/* This shouldn't happen */
if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF))
if (WARN_ON_ONCE(coresight_get_mode(csdev) != CS_MODE_PERF))
return 0;
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't do anything if another tracer is using this sink */
if (atomic_read(&csdev->refcnt) != 1)
if (csdev->refcnt != 1)
goto out;
CS_UNLOCK(drvdata->base);
@ -629,7 +631,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
}
/* Don't interfere if operated from Perf */
if (drvdata->mode == CS_MODE_PERF) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_PERF) {
ret = -EINVAL;
goto out;
}
@ -641,7 +643,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
}
/* Disable the TMC if need be */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
/* There is no point in reading a TMC in HW FIFO mode */
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode != TMC_MODE_CIRCULAR_BUFFER) {
@ -673,7 +675,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Re-enable the TMC if need be */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
/* There is no point in reading a TMC in HW FIFO mode */
mode = readl_relaxed(drvdata->base + TMC_MODE);
if (mode != TMC_MODE_CIRCULAR_BUFFER) {

View File

@ -1143,7 +1143,7 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
* When operating in sysFS mode the content of the buffer needs to be
* read before the TMC is disabled.
*/
if (drvdata->mode == CS_MODE_SYSFS)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
tmc_etr_sync_sysfs_buf(drvdata);
tmc_disable_hw(drvdata);
@ -1189,7 +1189,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
spin_lock_irqsave(&drvdata->spinlock, flags);
}
if (drvdata->reading || drvdata->mode == CS_MODE_PERF) {
if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
ret = -EBUSY;
goto out;
}
@ -1230,15 +1230,15 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* sink is already enabled no memory is needed and the HW need not be
* touched, even if the buffer size has changed.
*/
if (drvdata->mode == CS_MODE_SYSFS) {
atomic_inc(&csdev->refcnt);
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
csdev->refcnt++;
goto out;
}
ret = tmc_etr_enable_hw(drvdata, sysfs_buf);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(&csdev->refcnt);
coresight_set_mode(csdev, CS_MODE_SYSFS);
csdev->refcnt++;
}
out:
@ -1564,7 +1564,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't do anything if another tracer is using this sink */
if (atomic_read(&csdev->refcnt) != 1) {
if (csdev->refcnt != 1) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
goto out;
}
@ -1652,7 +1652,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't use this sink if it is already claimed by sysFS */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
rc = -EBUSY;
goto unlock_out;
}
@ -1676,7 +1676,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
* use for this session.
*/
if (drvdata->pid == pid) {
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
goto unlock_out;
}
@ -1684,9 +1684,9 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
if (!rc) {
/* Associate with monitored process. */
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
coresight_set_mode(csdev, CS_MODE_PERF);
drvdata->perf_buf = etr_perf->etr_buf;
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
}
unlock_out:
@ -1719,17 +1719,18 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
return -EBUSY;
}
if (atomic_dec_return(&csdev->refcnt)) {
csdev->refcnt--;
if (csdev->refcnt) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
tmc_etr_disable_hw(drvdata);
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
/* Reset perf specific data */
drvdata->perf_buf = NULL;
@ -1777,7 +1778,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
}
/* Disable the TMC if we are trying to read from a running session. */
if (drvdata->mode == CS_MODE_SYSFS)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
__tmc_etr_disable_hw(drvdata);
drvdata->reading = true;
@ -1799,7 +1800,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
/*
* The trace run will continue with the same allocated trace
* buffer. Since the tracer is still enabled drvdata::buf can't

View File

@ -178,7 +178,6 @@ struct etr_buf {
* @size: trace buffer size for this TMC (common for all modes).
* @max_burst_size: The maximum burst size that can be initiated by
* TMC-ETR on AXI bus.
* @mode: how this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type.
* @memwidth: width of the memory interface databus, in bytes.
* @trigger_cntr: amount of words to store after a trigger.
@ -203,7 +202,6 @@ struct tmc_drvdata {
u32 len;
u32 size;
u32 max_burst_size;
u32 mode;
enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth;
u32 trigger_cntr;

View File

@ -18,6 +18,7 @@
#include "coresight-priv.h"
#include "coresight-tpda.h"
#include "coresight-trace-id.h"
#include "coresight-tpdm.h"
DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
@ -28,24 +29,59 @@ static bool coresight_device_is_tpdm(struct coresight_device *csdev)
CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM);
}
/*
* Read the DSB element size from the TPDM device
* Returns
* The dsb element size read from the devicetree if available.
* 0 - Otherwise, with a warning once.
*/
static int tpdm_read_dsb_element_size(struct coresight_device *csdev)
static void tpda_clear_element_size(struct coresight_device *csdev)
{
int rc = 0;
u8 size = 0;
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
drvdata->dsb_esize = 0;
drvdata->cmb_esize = 0;
}
static void tpda_set_element_size(struct tpda_drvdata *drvdata, u32 *val)
{
/* Clear all relevant fields */
*val &= ~(TPDA_Pn_CR_DSBSIZE | TPDA_Pn_CR_CMBSIZE);
if (drvdata->dsb_esize == 64)
*val |= TPDA_Pn_CR_DSBSIZE;
else if (drvdata->dsb_esize == 32)
*val &= ~TPDA_Pn_CR_DSBSIZE;
if (drvdata->cmb_esize == 64)
*val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x2);
else if (drvdata->cmb_esize == 32)
*val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x1);
else if (drvdata->cmb_esize == 8)
*val &= ~TPDA_Pn_CR_CMBSIZE;
}
/*
* Read the element size from the TPDM device. One TPDM must have at least one of the
* element size property.
* Returns
* 0 - The element size property is read
* Others - Cannot read the property of the element size
*/
static int tpdm_read_element_size(struct tpda_drvdata *drvdata,
struct coresight_device *csdev)
{
int rc = -EINVAL;
struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent);
if (tpdm_has_dsb_dataset(tpdm_data)) {
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
"qcom,dsb-element-bits", &drvdata->dsb_esize);
}
if (tpdm_has_cmb_dataset(tpdm_data)) {
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
"qcom,cmb-element-bits", &drvdata->cmb_esize);
}
rc = fwnode_property_read_u8(dev_fwnode(csdev->dev.parent),
"qcom,dsb-element-size", &size);
if (rc)
dev_warn_once(&csdev->dev,
"Failed to read TPDM DSB Element size: %d\n", rc);
"Failed to read TPDM Element size: %d\n", rc);
return size;
return rc;
}
/*
@ -56,11 +92,12 @@ static int tpdm_read_dsb_element_size(struct coresight_device *csdev)
* Parameter "inport" is used to pass in the input port number
* of TPDA, and it is set to -1 in the recursize call.
*/
static int tpda_get_element_size(struct coresight_device *csdev,
static int tpda_get_element_size(struct tpda_drvdata *drvdata,
struct coresight_device *csdev,
int inport)
{
int dsb_size = -ENOENT;
int i, size;
int rc = 0;
int i;
struct coresight_device *in;
for (i = 0; i < csdev->pdata->nr_inconns; i++) {
@ -69,30 +106,26 @@ static int tpda_get_element_size(struct coresight_device *csdev,
continue;
/* Ignore the paths that do not match port */
if (inport > 0 &&
if (inport >= 0 &&
csdev->pdata->in_conns[i]->dest_port != inport)
continue;
if (coresight_device_is_tpdm(in)) {
size = tpdm_read_dsb_element_size(in);
if (drvdata->dsb_esize || drvdata->cmb_esize)
return -EEXIST;
rc = tpdm_read_element_size(drvdata, in);
if (rc)
return rc;
} else {
/* Recurse down the path */
size = tpda_get_element_size(in, -1);
}
if (size < 0)
return size;
if (dsb_size < 0) {
/* Found a size, save it. */
dsb_size = size;
} else {
/* Found duplicate TPDMs */
return -EEXIST;
rc = tpda_get_element_size(drvdata, in, -1);
if (rc)
return rc;
}
}
return dsb_size;
return rc;
}
/* Settings pre enabling port control register */
@ -109,37 +142,24 @@ static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
{
u32 val;
int size;
int rc;
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
/*
* Configure aggregator port n DSB data set element size
* Set the bit to 0 if the size is 32
* Set the bit to 1 if the size is 64
*/
size = tpda_get_element_size(drvdata->csdev, port);
switch (size) {
case 32:
val &= ~TPDA_Pn_CR_DSBSIZE;
break;
case 64:
val |= TPDA_Pn_CR_DSBSIZE;
break;
case 0:
return -EEXIST;
case -EEXIST:
tpda_clear_element_size(drvdata->csdev);
rc = tpda_get_element_size(drvdata, drvdata->csdev, port);
if (!rc && (drvdata->dsb_esize || drvdata->cmb_esize)) {
tpda_set_element_size(drvdata, &val);
/* Enable the port */
val |= TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
} else if (rc == -EEXIST)
dev_warn_once(&drvdata->csdev->dev,
"Detected multiple TPDMs on port %d", -EEXIST);
return -EEXIST;
default:
return -EINVAL;
}
"Detected multiple TPDMs on port %d", port);
else
dev_warn_once(&drvdata->csdev->dev,
"Didn't find TPDM element size");
/* Enable the port */
val |= TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
return 0;
return rc;
}
static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
@ -148,7 +168,12 @@ static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
CS_UNLOCK(drvdata->base);
if (!drvdata->csdev->enable)
/*
* Only do pre-port enable for first port that calls enable when the
* device's main refcount is still 0
*/
lockdep_assert_held(&drvdata->spinlock);
if (!drvdata->csdev->refcnt)
tpda_enable_pre_port(drvdata);
ret = tpda_enable_port(drvdata, port);
@ -169,6 +194,7 @@ static int tpda_enable(struct coresight_device *csdev,
ret = __tpda_enable(drvdata, in->dest_port);
if (!ret) {
atomic_inc(&in->dest_refcnt);
csdev->refcnt++;
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
}
}
@ -197,9 +223,10 @@ static void tpda_disable(struct coresight_device *csdev,
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (atomic_dec_return(&in->dest_refcnt) == 0)
if (atomic_dec_return(&in->dest_refcnt) == 0) {
__tpda_disable(drvdata, in->dest_port);
csdev->refcnt--;
}
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
@ -300,7 +327,7 @@ static struct amba_id tpda_ids[] = {
.id = 0x000f0f00,
.mask = 0x000fff00,
},
{ 0, 0},
{ 0, 0, NULL },
};
static struct amba_driver tpda_driver = {

View File

@ -10,6 +10,8 @@
#define TPDA_Pn_CR(n) (0x004 + (n * 4))
/* Aggregator port enable bit */
#define TPDA_Pn_CR_ENA BIT(0)
/* Aggregator port CMB data set element size bit */
#define TPDA_Pn_CR_CMBSIZE GENMASK(7, 6)
/* Aggregator port DSB data set element size bit */
#define TPDA_Pn_CR_DSBSIZE BIT(8)
@ -25,6 +27,8 @@
* @csdev: component vitals needed by the framework.
* @spinlock: lock for the drvdata value.
* @enable: enable status of the component.
* @dsb_esize Record the DSB element size.
* @cmb_esize Record the CMB element size.
*/
struct tpda_drvdata {
void __iomem *base;
@ -32,6 +36,8 @@ struct tpda_drvdata {
struct coresight_device *csdev;
spinlock_t spinlock;
u8 atid;
u32 dsb_esize;
u32 cmb_esize;
};
#endif /* _CORESIGHT_CORESIGHT_TPDA_H */

View File

@ -66,6 +66,31 @@ static ssize_t tpdm_simple_dataset_show(struct device *dev,
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->msr[tpdm_attr->idx]);
case CMB_TRIG_PATT:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->trig_patt[tpdm_attr->idx]);
case CMB_TRIG_PATT_MASK:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->trig_patt_mask[tpdm_attr->idx]);
case CMB_PATT:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->patt_val[tpdm_attr->idx]);
case CMB_PATT_MASK:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->patt_mask[tpdm_attr->idx]);
case CMB_MSR:
if (tpdm_attr->idx >= drvdata->cmb_msr_num)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->msr[tpdm_attr->idx]);
}
return -EINVAL;
}
@ -77,60 +102,84 @@ static ssize_t tpdm_simple_dataset_store(struct device *dev,
size_t size)
{
unsigned long val;
ssize_t ret = size;
ssize_t ret = -EINVAL;
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
if (kstrtoul(buf, 0, &val))
return -EINVAL;
return ret;
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
switch (tpdm_attr->mem) {
case DSB_TRIG_PATT:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->trig_patt[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_TRIG_PATT_MASK:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->trig_patt_mask[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_PATT:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->patt_val[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_PATT_MASK:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->patt_mask[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_MSR:
if (tpdm_attr->idx < drvdata->dsb_msr_num)
if (tpdm_attr->idx < drvdata->dsb_msr_num) {
drvdata->dsb->msr[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case CMB_TRIG_PATT:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->trig_patt[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_TRIG_PATT_MASK:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->trig_patt_mask[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_PATT:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->patt_val[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_PATT_MASK:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->patt_mask[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_MSR:
if (tpdm_attr->idx < drvdata->cmb_msr_num) {
drvdata->cmb->msr[tpdm_attr->idx] = val;
ret = size;
}
break;
default:
ret = -EINVAL;
break;
}
spin_unlock(&drvdata->spinlock);
return ret;
}
static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata)
{
return (drvdata->datasets & TPDM_PIDR0_DS_DSB);
}
static umode_t tpdm_dsb_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
@ -143,6 +192,18 @@ static umode_t tpdm_dsb_is_visible(struct kobject *kobj,
return 0;
}
static umode_t tpdm_cmb_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (drvdata && tpdm_has_cmb_dataset(drvdata))
return attr->mode;
return 0;
}
static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
@ -159,6 +220,23 @@ static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj,
return 0;
}
static umode_t tpdm_cmb_msr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct device_attribute *dev_attr =
container_of(attr, struct device_attribute, attr);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(dev_attr, struct tpdm_dataset_attribute, attr);
if (tpdm_attr->idx < drvdata->cmb_msr_num)
return attr->mode;
return 0;
}
static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)
{
if (tpdm_has_dsb_dataset(drvdata)) {
@ -167,6 +245,9 @@ static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)
drvdata->dsb->trig_ts = true;
drvdata->dsb->trig_type = false;
}
if (drvdata->cmb)
memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
}
static void set_dsb_mode(struct tpdm_drvdata *drvdata, u32 *val)
@ -233,25 +314,27 @@ static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val, i;
if (!tpdm_has_dsb_dataset(drvdata))
return;
for (i = 0; i < TPDM_DSB_MAX_EDCR; i++)
writel_relaxed(drvdata->dsb->edge_ctrl[i],
drvdata->base + TPDM_DSB_EDCR(i));
drvdata->base + TPDM_DSB_EDCR(i));
for (i = 0; i < TPDM_DSB_MAX_EDCMR; i++)
writel_relaxed(drvdata->dsb->edge_ctrl_mask[i],
drvdata->base + TPDM_DSB_EDCMR(i));
drvdata->base + TPDM_DSB_EDCMR(i));
for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
writel_relaxed(drvdata->dsb->patt_val[i],
drvdata->base + TPDM_DSB_TPR(i));
drvdata->base + TPDM_DSB_TPR(i));
writel_relaxed(drvdata->dsb->patt_mask[i],
drvdata->base + TPDM_DSB_TPMR(i));
drvdata->base + TPDM_DSB_TPMR(i));
writel_relaxed(drvdata->dsb->trig_patt[i],
drvdata->base + TPDM_DSB_XPR(i));
drvdata->base + TPDM_DSB_XPR(i));
writel_relaxed(drvdata->dsb->trig_patt_mask[i],
drvdata->base + TPDM_DSB_XPMR(i));
drvdata->base + TPDM_DSB_XPMR(i));
}
set_dsb_tier(drvdata);
set_dsb_msr(drvdata);
val = readl_relaxed(drvdata->base + TPDM_DSB_CR);
@ -267,6 +350,76 @@ static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
writel_relaxed(val, drvdata->base + TPDM_DSB_CR);
}
static void set_cmb_tier(struct tpdm_drvdata *drvdata)
{
u32 val;
val = readl_relaxed(drvdata->base + TPDM_CMB_TIER);
/* Clear all relevant fields */
val &= ~(TPDM_CMB_TIER_PATT_TSENAB | TPDM_CMB_TIER_TS_ALL |
TPDM_CMB_TIER_XTRIG_TSENAB);
/* Set pattern timestamp type and enablement */
if (drvdata->cmb->patt_ts)
val |= TPDM_CMB_TIER_PATT_TSENAB;
/* Set trigger timestamp */
if (drvdata->cmb->trig_ts)
val |= TPDM_CMB_TIER_XTRIG_TSENAB;
/* Set all timestamp enablement*/
if (drvdata->cmb->ts_all)
val |= TPDM_CMB_TIER_TS_ALL;
writel_relaxed(val, drvdata->base + TPDM_CMB_TIER);
}
static void set_cmb_msr(struct tpdm_drvdata *drvdata)
{
int i;
for (i = 0; i < drvdata->cmb_msr_num; i++)
writel_relaxed(drvdata->cmb->msr[i],
drvdata->base + TPDM_CMB_MSR(i));
}
static void tpdm_enable_cmb(struct tpdm_drvdata *drvdata)
{
u32 val, i;
if (!tpdm_has_cmb_dataset(drvdata))
return;
/* Configure pattern registers */
for (i = 0; i < TPDM_CMB_MAX_PATT; i++) {
writel_relaxed(drvdata->cmb->patt_val[i],
drvdata->base + TPDM_CMB_TPR(i));
writel_relaxed(drvdata->cmb->patt_mask[i],
drvdata->base + TPDM_CMB_TPMR(i));
writel_relaxed(drvdata->cmb->trig_patt[i],
drvdata->base + TPDM_CMB_XPR(i));
writel_relaxed(drvdata->cmb->trig_patt_mask[i],
drvdata->base + TPDM_CMB_XPMR(i));
}
set_cmb_tier(drvdata);
set_cmb_msr(drvdata);
val = readl_relaxed(drvdata->base + TPDM_CMB_CR);
/*
* Set to 0 for continuous CMB collection mode,
* 1 for trace-on-change CMB collection mode.
*/
if (drvdata->cmb->trace_mode)
val |= TPDM_CMB_CR_MODE;
else
val &= ~TPDM_CMB_CR_MODE;
/* Set the enable bit of CMB control register to 1 */
val |= TPDM_CMB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_CMB_CR);
}
/*
* TPDM enable operations
* The TPDM or Monitor serves as data collection component for various
@ -279,8 +432,8 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
if (tpdm_has_dsb_dataset(drvdata))
tpdm_enable_dsb(drvdata);
tpdm_enable_dsb(drvdata);
tpdm_enable_cmb(drvdata);
CS_LOCK(drvdata->base);
}
@ -308,19 +461,35 @@ static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val;
if (!tpdm_has_dsb_dataset(drvdata))
return;
/* Set the enable bit of DSB control register to 0 */
val = readl_relaxed(drvdata->base + TPDM_DSB_CR);
val &= ~TPDM_DSB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_DSB_CR);
}
static void tpdm_disable_cmb(struct tpdm_drvdata *drvdata)
{
u32 val;
if (!tpdm_has_cmb_dataset(drvdata))
return;
val = readl_relaxed(drvdata->base + TPDM_CMB_CR);
/* Set the enable bit of CMB control register to 0 */
val &= ~TPDM_CMB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_CMB_CR);
}
/* TPDM disable operations */
static void __tpdm_disable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
if (tpdm_has_dsb_dataset(drvdata))
tpdm_disable_dsb(drvdata);
tpdm_disable_dsb(drvdata);
tpdm_disable_cmb(drvdata);
CS_LOCK(drvdata->base);
}
@ -366,6 +535,12 @@ static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata)
if (!drvdata->dsb)
return -ENOMEM;
}
if (tpdm_has_cmb_dataset(drvdata) && (!drvdata->cmb)) {
drvdata->cmb = devm_kzalloc(drvdata->dev,
sizeof(*drvdata->cmb), GFP_KERNEL);
if (!drvdata->cmb)
return -ENOMEM;
}
tpdm_reset_datasets(drvdata);
return 0;
@ -577,9 +752,18 @@ static ssize_t enable_ts_show(struct device *dev,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
ssize_t size = -EINVAL;
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->patt_ts);
if (tpdm_attr->mem == DSB_PATT)
size = sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->patt_ts);
else if (tpdm_attr->mem == CMB_PATT)
size = sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->cmb->patt_ts);
return size;
}
/*
@ -591,17 +775,23 @@ static ssize_t enable_ts_store(struct device *dev,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->dsb->patt_ts = !!val;
spin_unlock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
if (tpdm_attr->mem == DSB_PATT)
drvdata->dsb->patt_ts = !!val;
else if (tpdm_attr->mem == CMB_PATT)
drvdata->cmb->patt_ts = !!val;
else
return -EINVAL;
return size;
}
static DEVICE_ATTR_RW(enable_ts);
static ssize_t set_type_show(struct device *dev,
struct device_attribute *attr,
@ -704,6 +894,96 @@ static ssize_t dsb_trig_ts_store(struct device *dev,
}
static DEVICE_ATTR_RW(dsb_trig_ts);
static ssize_t cmb_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%x\n", drvdata->cmb->trace_mode);
}
static ssize_t cmb_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long trace_mode;
if (kstrtoul(buf, 0, &trace_mode) || (trace_mode & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->cmb->trace_mode = trace_mode;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(cmb_mode);
static ssize_t cmb_ts_all_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->cmb->ts_all);
}
static ssize_t cmb_ts_all_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
guard(spinlock)(&drvdata->spinlock);
if (val)
drvdata->cmb->ts_all = true;
else
drvdata->cmb->ts_all = false;
return size;
}
static DEVICE_ATTR_RW(cmb_ts_all);
static ssize_t cmb_trig_ts_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->cmb->trig_ts);
}
static ssize_t cmb_trig_ts_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
guard(spinlock)(&drvdata->spinlock);
if (val)
drvdata->cmb->trig_ts = true;
else
drvdata->cmb->trig_ts = false;
return size;
}
static DEVICE_ATTR_RW(cmb_trig_ts);
static struct attribute *tpdm_dsb_edge_attrs[] = {
&dev_attr_ctrl_idx.attr,
&dev_attr_ctrl_val.attr,
@ -772,7 +1052,7 @@ static struct attribute *tpdm_dsb_patt_attrs[] = {
DSB_PATT_MASK_ATTR(5),
DSB_PATT_MASK_ATTR(6),
DSB_PATT_MASK_ATTR(7),
&dev_attr_enable_ts.attr,
DSB_PATT_ENABLE_TS,
&dev_attr_set_type.attr,
NULL,
};
@ -813,6 +1093,59 @@ static struct attribute *tpdm_dsb_msr_attrs[] = {
NULL,
};
static struct attribute *tpdm_cmb_trig_patt_attrs[] = {
CMB_TRIG_PATT_ATTR(0),
CMB_TRIG_PATT_ATTR(1),
CMB_TRIG_PATT_MASK_ATTR(0),
CMB_TRIG_PATT_MASK_ATTR(1),
NULL,
};
static struct attribute *tpdm_cmb_patt_attrs[] = {
CMB_PATT_ATTR(0),
CMB_PATT_ATTR(1),
CMB_PATT_MASK_ATTR(0),
CMB_PATT_MASK_ATTR(1),
CMB_PATT_ENABLE_TS,
NULL,
};
static struct attribute *tpdm_cmb_msr_attrs[] = {
CMB_MSR_ATTR(0),
CMB_MSR_ATTR(1),
CMB_MSR_ATTR(2),
CMB_MSR_ATTR(3),
CMB_MSR_ATTR(4),
CMB_MSR_ATTR(5),
CMB_MSR_ATTR(6),
CMB_MSR_ATTR(7),
CMB_MSR_ATTR(8),
CMB_MSR_ATTR(9),
CMB_MSR_ATTR(10),
CMB_MSR_ATTR(11),
CMB_MSR_ATTR(12),
CMB_MSR_ATTR(13),
CMB_MSR_ATTR(14),
CMB_MSR_ATTR(15),
CMB_MSR_ATTR(16),
CMB_MSR_ATTR(17),
CMB_MSR_ATTR(18),
CMB_MSR_ATTR(19),
CMB_MSR_ATTR(20),
CMB_MSR_ATTR(21),
CMB_MSR_ATTR(22),
CMB_MSR_ATTR(23),
CMB_MSR_ATTR(24),
CMB_MSR_ATTR(25),
CMB_MSR_ATTR(26),
CMB_MSR_ATTR(27),
CMB_MSR_ATTR(28),
CMB_MSR_ATTR(29),
CMB_MSR_ATTR(30),
CMB_MSR_ATTR(31),
NULL,
};
static struct attribute *tpdm_dsb_attrs[] = {
&dev_attr_dsb_mode.attr,
&dev_attr_dsb_trig_ts.attr,
@ -820,6 +1153,13 @@ static struct attribute *tpdm_dsb_attrs[] = {
NULL,
};
static struct attribute *tpdm_cmb_attrs[] = {
&dev_attr_cmb_mode.attr,
&dev_attr_cmb_ts_all.attr,
&dev_attr_cmb_trig_ts.attr,
NULL,
};
static struct attribute_group tpdm_dsb_attr_grp = {
.attrs = tpdm_dsb_attrs,
.is_visible = tpdm_dsb_is_visible,
@ -849,6 +1189,29 @@ static struct attribute_group tpdm_dsb_msr_grp = {
.name = "dsb_msr",
};
static struct attribute_group tpdm_cmb_attr_grp = {
.attrs = tpdm_cmb_attrs,
.is_visible = tpdm_cmb_is_visible,
};
static struct attribute_group tpdm_cmb_trig_patt_grp = {
.attrs = tpdm_cmb_trig_patt_attrs,
.is_visible = tpdm_cmb_is_visible,
.name = "cmb_trig_patt",
};
static struct attribute_group tpdm_cmb_patt_grp = {
.attrs = tpdm_cmb_patt_attrs,
.is_visible = tpdm_cmb_is_visible,
.name = "cmb_patt",
};
static struct attribute_group tpdm_cmb_msr_grp = {
.attrs = tpdm_cmb_msr_attrs,
.is_visible = tpdm_cmb_msr_is_visible,
.name = "cmb_msr",
};
static const struct attribute_group *tpdm_attr_grps[] = {
&tpdm_attr_grp,
&tpdm_dsb_attr_grp,
@ -856,6 +1219,10 @@ static const struct attribute_group *tpdm_attr_grps[] = {
&tpdm_dsb_trig_patt_grp,
&tpdm_dsb_patt_grp,
&tpdm_dsb_msr_grp,
&tpdm_cmb_attr_grp,
&tpdm_cmb_trig_patt_grp,
&tpdm_cmb_patt_grp,
&tpdm_cmb_msr_grp,
NULL,
};
@ -894,6 +1261,10 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
of_property_read_u32(drvdata->dev->of_node,
"qcom,dsb-msrs-num", &drvdata->dsb_msr_num);
if (drvdata && tpdm_has_cmb_dataset(drvdata))
of_property_read_u32(drvdata->dev->of_node,
"qcom,cmb-msrs-num", &drvdata->cmb_msr_num);
/* Set up coresight component description */
desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
if (!desc.name)
@ -933,7 +1304,7 @@ static struct amba_id tpdm_ids[] = {
.id = 0x000f0e00,
.mask = 0x000fff00,
},
{ 0, 0},
{ 0, 0, NULL },
};
static struct amba_driver tpdm_driver = {

View File

@ -9,6 +9,38 @@
/* The max number of the datasets that TPDM supports */
#define TPDM_DATASETS 7
/* CMB Subunit Registers */
#define TPDM_CMB_CR (0xA00)
/* CMB subunit timestamp insertion enable register */
#define TPDM_CMB_TIER (0xA04)
/* CMB subunit timestamp pattern registers */
#define TPDM_CMB_TPR(n) (0xA08 + (n * 4))
/* CMB subunit timestamp pattern mask registers */
#define TPDM_CMB_TPMR(n) (0xA10 + (n * 4))
/* CMB subunit trigger pattern registers */
#define TPDM_CMB_XPR(n) (0xA18 + (n * 4))
/* CMB subunit trigger pattern mask registers */
#define TPDM_CMB_XPMR(n) (0xA20 + (n * 4))
/* CMB MSR register */
#define TPDM_CMB_MSR(n) (0xA80 + (n * 4))
/* Enable bit for CMB subunit */
#define TPDM_CMB_CR_ENA BIT(0)
/* Trace collection mode for CMB subunit */
#define TPDM_CMB_CR_MODE BIT(1)
/* Timestamp control for pattern match */
#define TPDM_CMB_TIER_PATT_TSENAB BIT(0)
/* CMB CTI timestamp request */
#define TPDM_CMB_TIER_XTRIG_TSENAB BIT(1)
/* For timestamp fo all trace */
#define TPDM_CMB_TIER_TS_ALL BIT(2)
/* Patten register number */
#define TPDM_CMB_MAX_PATT 2
/* MAX number of DSB MSR */
#define TPDM_CMB_MAX_MSR 32
/* DSB Subunit Registers */
#define TPDM_DSB_CR (0x780)
#define TPDM_DSB_TIER (0x784)
@ -79,10 +111,12 @@
*
* PERIPHIDR0[0] : Fix to 1 if ImplDef subunit present, else 0
* PERIPHIDR0[1] : Fix to 1 if DSB subunit present, else 0
* PERIPHIDR0[2] : Fix to 1 if CMB subunit present, else 0
*/
#define TPDM_PIDR0_DS_IMPDEF BIT(0)
#define TPDM_PIDR0_DS_DSB BIT(1)
#define TPDM_PIDR0_DS_CMB BIT(2)
#define TPDM_DSB_MAX_LINES 256
/* MAX number of EDCR registers */
@ -113,6 +147,16 @@
} \
})[0].attr.attr)
#define tpdm_patt_enable_ts(name, mem) \
(&((struct tpdm_dataset_attribute[]) { \
{ \
__ATTR(name, 0644, enable_ts_show, \
enable_ts_store), \
mem, \
0, \
} \
})[0].attr.attr)
#define DSB_EDGE_CTRL_ATTR(nr) \
tpdm_simple_dataset_ro(edcr##nr, \
DSB_EDGE_CTRL, nr)
@ -137,10 +181,38 @@
tpdm_simple_dataset_rw(tpmr##nr, \
DSB_PATT_MASK, nr)
#define DSB_PATT_ENABLE_TS \
tpdm_patt_enable_ts(enable_ts, \
DSB_PATT)
#define DSB_MSR_ATTR(nr) \
tpdm_simple_dataset_rw(msr##nr, \
DSB_MSR, nr)
#define CMB_TRIG_PATT_ATTR(nr) \
tpdm_simple_dataset_rw(xpr##nr, \
CMB_TRIG_PATT, nr)
#define CMB_TRIG_PATT_MASK_ATTR(nr) \
tpdm_simple_dataset_rw(xpmr##nr, \
CMB_TRIG_PATT_MASK, nr)
#define CMB_PATT_ATTR(nr) \
tpdm_simple_dataset_rw(tpr##nr, \
CMB_PATT, nr)
#define CMB_PATT_MASK_ATTR(nr) \
tpdm_simple_dataset_rw(tpmr##nr, \
CMB_PATT_MASK, nr)
#define CMB_PATT_ENABLE_TS \
tpdm_patt_enable_ts(enable_ts, \
CMB_PATT)
#define CMB_MSR_ATTR(nr) \
tpdm_simple_dataset_rw(msr##nr, \
CMB_MSR, nr)
/**
* struct dsb_dataset - specifics associated to dsb dataset
* @mode: DSB programming mode
@ -173,6 +245,30 @@ struct dsb_dataset {
bool trig_type;
};
/**
* struct cmb_dataset
* @trace_mode: Dataset collection mode
* @patt_val: Save value for pattern
* @patt_mask: Save value for pattern mask
* @trig_patt: Save value for trigger pattern
* @trig_patt_mask: Save value for trigger pattern mask
* @msr Save value for MSR
* @patt_ts: Indicates if pattern match for timestamp is enabled.
* @trig_ts: Indicates if CTI trigger for timestamp is enabled.
* @ts_all: Indicates if timestamp is enabled for all packets.
*/
struct cmb_dataset {
u32 trace_mode;
u32 patt_val[TPDM_CMB_MAX_PATT];
u32 patt_mask[TPDM_CMB_MAX_PATT];
u32 trig_patt[TPDM_CMB_MAX_PATT];
u32 trig_patt_mask[TPDM_CMB_MAX_PATT];
u32 msr[TPDM_CMB_MAX_MSR];
bool patt_ts;
bool trig_ts;
bool ts_all;
};
/**
* struct tpdm_drvdata - specifics associated to an TPDM component
* @base: memory mapped base address for this component.
@ -182,7 +278,9 @@ struct dsb_dataset {
* @enable: enable status of the component.
* @datasets: The datasets types present of the TPDM.
* @dsb Specifics associated to TPDM DSB.
* @cmb Specifics associated to TPDM CMB.
* @dsb_msr_num Number of MSR supported by DSB TPDM
* @cmb_msr_num Number of MSR supported by CMB TPDM
*/
struct tpdm_drvdata {
@ -193,7 +291,9 @@ struct tpdm_drvdata {
bool enable;
unsigned long datasets;
struct dsb_dataset *dsb;
struct cmb_dataset *cmb;
u32 dsb_msr_num;
u32 cmb_msr_num;
};
/* Enumerate members of various datasets */
@ -205,6 +305,11 @@ enum dataset_mem {
DSB_PATT,
DSB_PATT_MASK,
DSB_MSR,
CMB_TRIG_PATT,
CMB_TRIG_PATT_MASK,
CMB_PATT,
CMB_PATT_MASK,
CMB_MSR
};
/**
@ -220,4 +325,13 @@ struct tpdm_dataset_attribute {
u32 idx;
};
static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata)
{
return (drvdata->datasets & TPDM_PIDR0_DS_DSB);
}
static bool tpdm_has_cmb_dataset(struct tpdm_drvdata *drvdata)
{
return (drvdata->datasets & TPDM_PIDR0_DS_CMB);
}
#endif /* _CORESIGHT_CORESIGHT_TPDM_H */

View File

@ -58,6 +58,7 @@ struct tpiu_drvdata {
void __iomem *base;
struct clk *atclk;
struct coresight_device *csdev;
spinlock_t spinlock;
};
static void tpiu_enable_hw(struct csdev_access *csa)
@ -72,8 +73,11 @@ static void tpiu_enable_hw(struct csdev_access *csa)
static int tpiu_enable(struct coresight_device *csdev, enum cs_mode mode,
void *__unused)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
guard(spinlock)(&drvdata->spinlock);
tpiu_enable_hw(&csdev->access);
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
dev_dbg(&csdev->dev, "TPIU enabled\n");
return 0;
}
@ -96,7 +100,11 @@ static void tpiu_disable_hw(struct csdev_access *csa)
static int tpiu_disable(struct coresight_device *csdev)
{
if (atomic_dec_return(&csdev->refcnt))
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
guard(spinlock)(&drvdata->spinlock);
csdev->refcnt--;
if (csdev->refcnt)
return -EBUSY;
tpiu_disable_hw(&csdev->access);
@ -132,6 +140,8 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
if (!drvdata)
return -ENOMEM;
spin_lock_init(&drvdata->spinlock);
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
if (!IS_ERR(drvdata->atclk)) {
ret = clk_prepare_enable(drvdata->atclk);
@ -218,7 +228,7 @@ static const struct amba_id tpiu_ids[] = {
.id = 0x000bb9e7,
.mask = 0x000fffff,
},
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, tpiu_ids);

View File

@ -103,7 +103,7 @@ static int smb_open(struct inode *inode, struct file *file)
if (drvdata->reading)
return -EBUSY;
if (atomic_read(&drvdata->csdev->refcnt))
if (drvdata->csdev->refcnt)
return -EBUSY;
smb_update_data_size(drvdata);
@ -207,11 +207,11 @@ static void smb_enable_sysfs(struct coresight_device *csdev)
{
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
if (drvdata->mode != CS_MODE_DISABLED)
if (coresight_get_mode(csdev) != CS_MODE_DISABLED)
return;
smb_enable_hw(drvdata);
drvdata->mode = CS_MODE_SYSFS;
coresight_set_mode(csdev, CS_MODE_SYSFS);
}
static int smb_enable_perf(struct coresight_device *csdev, void *data)
@ -234,7 +234,7 @@ static int smb_enable_perf(struct coresight_device *csdev, void *data)
if (drvdata->pid == -1) {
smb_enable_hw(drvdata);
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
coresight_set_mode(csdev, CS_MODE_PERF);
}
return 0;
@ -253,7 +253,8 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
return -EBUSY;
/* Do nothing, the SMB is already enabled as other mode */
if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode)
if (coresight_get_mode(csdev) != CS_MODE_DISABLED &&
coresight_get_mode(csdev) != mode)
return -EBUSY;
switch (mode) {
@ -270,7 +271,7 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
if (ret)
return ret;
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n");
return ret;
@ -285,17 +286,18 @@ static int smb_disable(struct coresight_device *csdev)
if (drvdata->reading)
return -EBUSY;
if (atomic_dec_return(&csdev->refcnt))
csdev->refcnt--;
if (csdev->refcnt)
return -EBUSY;
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
smb_disable_hw(drvdata);
/* Dissociate from the target process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n");
return 0;
@ -380,7 +382,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev,
guard(spinlock)(&drvdata->spinlock);
/* Don't do anything if another tracer is using this sink. */
if (atomic_read(&csdev->refcnt) != 1)
if (csdev->refcnt != 1)
return 0;
smb_disable_hw(drvdata);
@ -586,7 +588,7 @@ static void smb_remove(struct platform_device *pdev)
#ifdef CONFIG_ACPI
static const struct acpi_device_id ultrasoc_smb_acpi_match[] = {
{"HISI03A1", 0},
{"HISI03A1", 0, 0, 0},
{}
};
MODULE_DEVICE_TABLE(acpi, ultrasoc_smb_acpi_match);

View File

@ -109,7 +109,6 @@ struct smb_data_buffer {
* @reading: Synchronise user space access to SMB buffer.
* @pid: Process ID of the process being monitored by the
* session that is using this component.
* @mode: How this SMB is being used, perf mode or sysfs mode.
*/
struct smb_drv_data {
void __iomem *base;
@ -119,7 +118,6 @@ struct smb_drv_data {
spinlock_t spinlock;
bool reading;
pid_t pid;
enum cs_mode mode;
};
#endif

View File

@ -998,6 +998,9 @@ static int hisi_ptt_pmu_event_init(struct perf_event *event)
int ret;
u32 val;
if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type)
return -ENOENT;
if (event->cpu < 0) {
dev_dbg(event->pmu->dev, "Per-task mode not supported\n");
return -EOPNOTSUPP;
@ -1006,9 +1009,6 @@ static int hisi_ptt_pmu_event_init(struct perf_event *event)
if (event->attach_state & PERF_ATTACH_TASK)
return -EOPNOTSUPP;
if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type)
return -ENOENT;
ret = hisi_ptt_trace_valid_filter(hisi_ptt, event->attr.config);
if (ret < 0)
return ret;

View File

@ -35,7 +35,7 @@
#define CORESIGHT_UNLOCK 0xc5acce55
extern struct bus_type coresight_bustype;
extern const struct bus_type coresight_bustype;
enum coresight_dev_type {
CORESIGHT_DEV_TYPE_SINK,
@ -226,13 +226,26 @@ struct coresight_sysfs_link {
* by @coresight_ops.
* @access: Device i/o access abstraction for this device.
* @dev: The device entity associated to this component.
* @refcnt: keep track of what is in use.
* @mode: This tracer's mode, i.e sysFS, Perf or disabled. This is
* actually an 'enum cs_mode', but is stored in an atomic type.
* This is always accessed through local_read() and local_set(),
* but wherever it's done from within the Coresight device's lock,
* a non-atomic read would also work. This is the main point of
* synchronisation between code happening inside the sysfs mode's
* coresight_mutex and outside when running in Perf mode. A compare
* and exchange swap is done to atomically claim one mode or the
* other.
* @refcnt: keep track of what is in use. Only access this outside of the
* device's spinlock when the coresight_mutex held and mode ==
* CS_MODE_SYSFS. Otherwise it must be accessed from inside the
* spinlock.
* @orphan: true if the component has connections that haven't been linked.
* @enable: 'true' if component is currently part of an active path.
* @activated: 'true' only if a _sink_ has been activated. A sink can be
* activated but not yet enabled. Enabling for a _sink_
* happens when a source has been selected and a path is enabled
* from source to that sink.
* @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs
* by writing a 1 to the 'enable_sink' file. A sink can be
* activated but not yet enabled. Enabling for a _sink_ happens
* when a source has been selected and a path is enabled from
* source to that sink. A sink can also become enabled but not
* activated if it's used via Perf.
* @ea: Device attribute for sink representation under PMU directory.
* @def_sink: cached reference to default sink found for this device.
* @nr_links: number of sysfs links created to other components from this
@ -250,11 +263,11 @@ struct coresight_device {
const struct coresight_ops *ops;
struct csdev_access access;
struct device dev;
atomic_t refcnt;
local_t mode;
int refcnt;
bool orphan;
bool enable; /* true only if configured as part of a path */
/* sink specific fields */
bool activated; /* true only if a sink is part of a path */
bool sysfs_sink_activated;
struct dev_ext_attribute *ea;
struct coresight_device *def_sink;
/* sysfs links between components */
@ -378,8 +391,6 @@ struct coresight_ops {
const struct coresight_ops_helper *helper_ops;
};
#if IS_ENABLED(CONFIG_CORESIGHT)
static inline u32 csdev_access_relaxed_read32(struct csdev_access *csa,
u32 offset)
{
@ -569,11 +580,43 @@ static inline bool coresight_is_percpu_sink(struct coresight_device *csdev)
(csdev->subtype.sink_subtype == CORESIGHT_DEV_SUBTYPE_SINK_PERCPU_SYSMEM);
}
/*
* Atomically try to take the device and set a new mode. Returns true on
* success, false if the device is already taken by someone else.
*/
static inline bool coresight_take_mode(struct coresight_device *csdev,
enum cs_mode new_mode)
{
return local_cmpxchg(&csdev->mode, CS_MODE_DISABLED, new_mode) ==
CS_MODE_DISABLED;
}
static inline enum cs_mode coresight_get_mode(struct coresight_device *csdev)
{
return local_read(&csdev->mode);
}
static inline void coresight_set_mode(struct coresight_device *csdev,
enum cs_mode new_mode)
{
enum cs_mode current_mode = coresight_get_mode(csdev);
/*
* Changing to a new mode must be done from an already disabled state
* unless it's synchronized with coresight_take_mode(). Otherwise the
* device is already in use and signifies a locking issue.
*/
WARN(new_mode != CS_MODE_DISABLED && current_mode != CS_MODE_DISABLED &&
current_mode != new_mode, "Device already in use\n");
local_set(&csdev->mode, new_mode);
}
extern struct coresight_device *
coresight_register(struct coresight_desc *desc);
extern void coresight_unregister(struct coresight_device *csdev);
extern int coresight_enable(struct coresight_device *csdev);
extern void coresight_disable(struct coresight_device *csdev);
extern int coresight_enable_sysfs(struct coresight_device *csdev);
extern void coresight_disable_sysfs(struct coresight_device *csdev);
extern int coresight_timeout(struct csdev_access *csa, u32 offset,
int position, int value);
@ -598,83 +641,6 @@ void coresight_relaxed_write64(struct coresight_device *csdev,
u64 val, u32 offset);
void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset);
#else
static inline struct coresight_device *
coresight_register(struct coresight_desc *desc) { return NULL; }
static inline void coresight_unregister(struct coresight_device *csdev) {}
static inline int
coresight_enable(struct coresight_device *csdev) { return -ENOSYS; }
static inline void coresight_disable(struct coresight_device *csdev) {}
static inline int coresight_timeout(struct csdev_access *csa, u32 offset,
int position, int value)
{
return 1;
}
static inline int coresight_claim_device_unlocked(struct coresight_device *csdev)
{
return -EINVAL;
}
static inline int coresight_claim_device(struct coresight_device *csdev)
{
return -EINVAL;
}
static inline void coresight_disclaim_device(struct coresight_device *csdev) {}
static inline void coresight_disclaim_device_unlocked(struct coresight_device *csdev) {}
static inline bool coresight_loses_context_with_cpu(struct device *dev)
{
return false;
}
static inline u32 coresight_relaxed_read32(struct coresight_device *csdev, u32 offset)
{
WARN_ON_ONCE(1);
return 0;
}
static inline u32 coresight_read32(struct coresight_device *csdev, u32 offset)
{
WARN_ON_ONCE(1);
return 0;
}
static inline void coresight_write32(struct coresight_device *csdev, u32 val, u32 offset)
{
}
static inline void coresight_relaxed_write32(struct coresight_device *csdev,
u32 val, u32 offset)
{
}
static inline u64 coresight_relaxed_read64(struct coresight_device *csdev,
u32 offset)
{
WARN_ON_ONCE(1);
return 0;
}
static inline u64 coresight_read64(struct coresight_device *csdev, u32 offset)
{
WARN_ON_ONCE(1);
return 0;
}
static inline void coresight_relaxed_write64(struct coresight_device *csdev,
u64 val, u32 offset)
{
}
static inline void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset)
{
}
#endif /* IS_ENABLED(CONFIG_CORESIGHT) */
extern int coresight_get_cpu(struct device *dev);
struct coresight_platform_data *coresight_get_platform_data(struct device *dev);