Commit d45fed4f authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'coresight-next-v6.3' of...

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

Suzuki writes:

coresight: Updates for v6.3

 - Dynamic TraceID allocation scheme for CoreSight trace source. Allows systems
   with > 44 CPUs to use the ETMs. TraceID is advertised via AUX_OUTPUT_HWID
   packets in perf.data. Also allows allocating trace-ids for non-CPU bound trace
   components (e.g., Qualcomm TPDA).

- Support for Qualcomm TPDA and TPDM CoreSight devices.

- Support for Ultrasoc SMB CoreSight Sink buffer.

- Fixes for HiSilicon PTT driver

- MAINTAINERS update: Add Reviewer for HiSilicon PTT driver

- Bug fixes for CTI power management and sysfs mode

- Fix CoreSight ETM4x TRCSEQRSTEVRn access
Signed-off-by: default avatarSuzuki K Poulose <suzuki.poulose@arm.com>

* tag 'coresight-next-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux: (35 commits)
  coresight: tmc: Don't enable TMC when it's not ready.
  coresight: tpda: fix return value check in tpda_probe()
  Coresight: tpda/tpdm: remove incorrect __exit annotation
  coresight: perf: Output trace id only once
  coresight: Fix uninitialised variable use in coresight_disable
  Documentation: coresight: tpdm: Add dummy comment after sysfs list
  Documentation: coresight: Extend title heading syntax in TPDM and TPDA documentation
  Documentation: trace: Add documentation for TPDM and TPDA
  dt-bindings: arm: Adds CoreSight TPDA hardware definitions
  Coresight: Add TPDA link driver
  coresight-tpdm: Add integration test support
  coresight-tpdm: Add DSB dataset support
  dt-bindings: arm: Add CoreSight TPDM hardware
  Coresight: Add coresight TPDM source driver
  coresight: core: Use IDR for non-cpu bound sources' paths.
  coresight: trace-id: Add debug & test macros to Trace ID allocation
  coresight: events: PERF_RECORD_AUX_OUTPUT_HW_ID used for Trace ID
  kernel: events: Export perf_report_aux_output_id()
  coresight: trace id: Remove legacy get trace ID function.
  coresight: etmX.X: stm: Remove trace_id() callback
  ...
parents 5e6a5178 669c4614
......@@ -236,7 +236,7 @@ What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/traceid
Date: November 2014
KernelVersion: 3.19
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Holds the trace ID that will appear in the trace stream
Description: (RO) Holds the trace ID that will appear in the trace stream
coming from this trace entity.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/trigger_event
......
What: /sys/bus/coresight/devices/<tpdm-name>/integration_test
Date: January 2023
KernelVersion 6.2
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(Write) Run integration test for tpdm. Integration test
will generate test data for tpdm. It can help to make
sure that the trace path is enabled and the link configurations
are fine.
Accepts only one of the 2 values - 1 or 2.
1 : Generate 64 bits data
2 : Generate 32 bits data
What: /sys/bus/coresight/devices/ultra_smb<N>/enable_sink
Date: January 2023
KernelVersion: 6.3
Contact: Junhao He <hejunhao3@huawei.com>
Description: (RW) Add/remove a SMB device from a trace path. There can be
multiple sources for a single SMB device.
What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/buf_size
Date: January 2023
KernelVersion: 6.3
Contact: Junhao He <hejunhao3@huawei.com>
Description: (RO) Shows the buffer size of each UltraSoc SMB device.
What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/buf_status
Date: January 2023
KernelVersion: 6.3
Contact: Junhao He <hejunhao3@huawei.com>
Description: (RO) Shows the value of UltraSoc SMB status register.
BIT(0) is zero means buffer is empty.
What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/read_pos
Date: January 2023
KernelVersion: 6.3
Contact: Junhao He <hejunhao3@huawei.com>
Description: (RO) Shows the value of UltraSoc SMB Read Pointer register.
What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/write_pos
Date: January 2023
KernelVersion: 6.3
Contact: Junhao He <hejunhao3@huawei.com>
Description: (RO) Shows the value of UltraSoc SMB Write Pointer register.
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/qcom,coresight-tpda.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Trace, Profiling and Diagnostics Aggregator - TPDA
description: |
TPDAs are responsible for packetization and timestamping of data sets
utilizing the MIPI STPv2 packet protocol. Pulling data sets from one or
more attached TPDM and pushing the resultant (packetized) data out a
master ATB interface. Performing an arbitrated ATB interleaving (funneling)
task for free-flowing data from TPDM (i.e. CMB and DSB data set flows).
There is no strict binding between TPDM and TPDA. TPDA can have multiple
TPDMs connect to it. But There must be only one TPDA in the path from the
TPDM source to TMC sink. TPDM can directly connect to TPDA's inport or
connect to funnel which will connect to TPDA's inport.
We can use the commands are similar to the below to validate TPDMs.
Enable coresight sink first.
echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
The test data will be collected in the coresight sink which is enabled.
If rwp register of the sink is keeping updating when do integration_test
(by cat tmc_etf0/mgmt/rwp), it means there is data generated from TPDM
to sink.
maintainers:
- Mao Jinlong <quic_jinlmao@quicinc.com>
- Tao Zhang <quic_taozha@quicinc.com>
# Need a custom select here or 'arm,primecell' will match on lots of nodes
select:
properties:
compatible:
contains:
enum:
- qcom,coresight-tpda
required:
- compatible
properties:
$nodename:
pattern: "^tpda(@[0-9a-f]+)$"
compatible:
items:
- const: qcom,coresight-tpda
- const: arm,primecell
reg:
minItems: 1
maxItems: 2
clocks:
maxItems: 1
clock-names:
items:
- const: apb_pclk
in-ports:
type: object
description: |
Input connections from TPDM to TPDA
$ref: /schemas/graph.yaml#/properties/ports
out-ports:
type: object
description: |
Output connections from the TPDA to legacy CoreSight trace bus.
$ref: /schemas/graph.yaml#/properties/ports
properties:
port:
description:
Output connection from the TPDA to legacy CoreSight Trace bus.
$ref: /schemas/graph.yaml#/properties/port
required:
- compatible
- reg
- clocks
- clock-names
- in-ports
- out-ports
additionalProperties: false
examples:
# minimum tpda definition.
- |
tpda@6004000 {
compatible = "qcom,coresight-tpda", "arm,primecell";
reg = <0x6004000 0x1000>;
clocks = <&aoss_qmp>;
clock-names = "apb_pclk";
in-ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
tpda_qdss_0_in_tpdm_dcc: endpoint {
remote-endpoint =
<&tpdm_dcc_out_tpda_qdss_0>;
};
};
};
out-ports {
port {
tpda_qdss_out_funnel_in0: endpoint {
remote-endpoint =
<&funnel_in0_in_tpda_qdss>;
};
};
};
};
...
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/qcom,coresight-tpdm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Trace, Profiling and Diagnostics Monitor - TPDM
description: |
The TPDM or Monitor serves as data collection component for various dataset
types specified in the QPMDA spec. It covers Implementation defined ((ImplDef),
Basic Counts (BC), Tenure Counts (TC), Continuous Multi-Bit (CMB), and Discrete
Single Bit (DSB). It performs data collection in the data producing clock
domain and transfers it to the data collection time domain, generally ATB
clock domain.
The primary use case of the TPDM is to collect data from different data
sources and send it to a TPDA for packetization, timestamping, and funneling.
maintainers:
- Mao Jinlong <quic_jinlmao@quicinc.com>
- Tao Zhang <quic_taozha@quicinc.com>
# Need a custom select here or 'arm,primecell' will match on lots of nodes
select:
properties:
compatible:
contains:
enum:
- qcom,coresight-tpdm
required:
- compatible
properties:
$nodename:
pattern: "^tpdm(@[0-9a-f]+)$"
compatible:
items:
- const: qcom,coresight-tpdm
- const: arm,primecell
reg:
minItems: 1
maxItems: 2
clocks:
maxItems: 1
clock-names:
items:
- const: apb_pclk
out-ports:
description: |
Output connections from the TPDM to coresight funnel/TPDA.
$ref: /schemas/graph.yaml#/properties/ports
properties:
port:
description: Output connection from the TPDM to coresight
funnel/TPDA.
$ref: /schemas/graph.yaml#/properties/port
required:
- compatible
- reg
- clocks
- clock-names
additionalProperties: false
examples:
# minimum TPDM definition. TPDM connect to coresight TPDA.
- |
tpdm@684c000 {
compatible = "qcom,coresight-tpdm", "arm,primecell";
reg = <0x0684c000 0x1000>;
clocks = <&aoss_qmp>;
clock-names = "apb_pclk";
out-ports {
port {
tpdm_prng_out_tpda_qdss: endpoint {
remote-endpoint =
<&tpda_qdss_in_tpdm_prng>;
};
};
};
};
...
.. SPDX-License-Identifier: GPL-2.0
=================================================================
The trace performance monitoring and diagnostics aggregator(TPDA)
=================================================================
:Author: Jinlong Mao <quic_jinlmao@quicinc.com>
:Date: January 2023
Hardware Description
--------------------
TPDA - The trace performance monitoring and diagnostics aggregator or
TPDA in short serves as an arbitration and packetization engine for the
performance monitoring and diagnostics network specification.
The primary use case of the TPDA is to provide packetization, funneling
and timestamping of Monitor data.
Sysfs files and directories
---------------------------
Root: ``/sys/bus/coresight/devices/tpda<N>``
Config details
---------------------------
The tpdm and tpda nodes should be observed at the coresight path
"/sys/bus/coresight/devices".
e.g.
/sys/bus/coresight/devices # ls -l | grep tpd
tpda0 -> ../../../devices/platform/soc@0/6004000.tpda/tpda0
tpdm0 -> ../../../devices/platform/soc@0/6c08000.mm.tpdm/tpdm0
We can use the commands are similar to the below to validate TPDMs.
Enable coresight sink first. The port of tpda which is connected to
the tpdm will be enabled after commands below.
echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
The test data will be collected in the coresight sink which is enabled.
If rwp register of the sink is keeping updating when do
integration_test (by cat tmc_etf0/mgmt/rwp), it means there is data
generated from TPDM to sink.
There must be a tpda between tpdm and the sink. When there are some
other trace event hw components in the same HW block with tpdm, tpdm
and these hw components will connect to the coresight funnel. When
there is only tpdm trace hw in the HW block, tpdm will connect to
tpda directly.
.. SPDX-License-Identifier: GPL-2.0
==========================================================
Trace performance monitoring and diagnostics monitor(TPDM)
==========================================================
:Author: Jinlong Mao <quic_jinlmao@quicinc.com>
:Date: January 2023
Hardware Description
--------------------
TPDM - The trace performance monitoring and diagnostics monitor or TPDM in
short serves as data collection component for various dataset types.
The primary use case of the TPDM is to collect data from different data
sources and send it to a TPDA for packetization, timestamping and funneling.
Sysfs files and directories
---------------------------
Root: ``/sys/bus/coresight/devices/tpdm<N>``
----
:File: ``enable_source`` (RW)
:Notes:
- > 0 : enable the datasets of TPDM.
- = 0 : disable the datasets of TPDM.
:Syntax:
``echo 1 > enable_source``
----
:File: ``integration_test`` (wo)
:Notes:
Integration test will generate test data for tpdm.
:Syntax:
``echo value > integration_test``
value - 1 or 2.
----
.. This text is intentionally added to make Sphinx happy.
.. SPDX-License-Identifier: GPL-2.0
======================================
UltraSoc - HW Assisted Tracing on SoC
======================================
:Author: Qi Liu <liuqi115@huawei.com>
:Date: January 2023
Introduction
------------
UltraSoc SMB is a per SCCL (Super CPU Cluster) hardware. It provides a
way to buffer and store CPU trace messages in a region of shared system
memory. The device acts as a coresight sink device and the
corresponding trace generators (ETM) are attached as source devices.
Sysfs files and directories
---------------------------
The SMB devices appear on the existing coresight bus alongside other
devices::
$# ls /sys/bus/coresight/devices/
ultra_smb0 ultra_smb1 ultra_smb2 ultra_smb3
The ``ultra_smb<N>`` names SMB device associated with SCCL.::
$# ls /sys/bus/coresight/devices/ultra_smb0
enable_sink mgmt
$# ls /sys/bus/coresight/devices/ultra_smb0/mgmt
buf_size buf_status read_pos write_pos
Key file items are:
* ``read_pos``: Shows the value on the read pointer register.
* ``write_pos``: Shows the value on the write pointer register.
* ``buf_status``: Shows the value on the status register.
BIT(0) is zero value which means the buffer is empty.
* ``buf_size``: Shows the buffer size of each device.
Firmware Bindings
-----------------
The device is only supported with ACPI. Its binding describes device
identifier, resource information and graph structure.
The device is identified as ACPI HID "HISI03A1". Device resources are allocated
using the _CRS method. Each device must present two base address; the first one
is the configuration base address of the device, the second one is the 32-bit
base address of shared system memory.
Example::
Device(USMB) { \
Name(_HID, "HISI03A1") \
Name(_CRS, ResourceTemplate() { \
QWordMemory (ResourceConsumer, , MinFixed, MaxFixed, NonCacheable, \
ReadWrite, 0x0, 0x95100000, 0x951FFFFF, 0x0, 0x100000) \
QWordMemory (ResourceConsumer, , MinFixed, MaxFixed, Cacheable, \
ReadWrite, 0x0, 0x50000000, 0x53FFFFFF, 0x0, 0x4000000) \
}) \
Name(_DSD, Package() { \
ToUUID("ab02a46b-74c7-45a2-bd68-f7d344ef2153"), \
/* Use CoreSight Graph ACPI bindings to describe connections topology */
Package() { \
0, \
1, \
Package() { \
1, \
ToUUID("3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd"), \
8, \
Package() {0x8, 0, \_SB.S00.SL11.CL28.F008, 0}, \
Package() {0x9, 0, \_SB.S00.SL11.CL29.F009, 0}, \
Package() {0xa, 0, \_SB.S00.SL11.CL2A.F010, 0}, \
Package() {0xb, 0, \_SB.S00.SL11.CL2B.F011, 0}, \
Package() {0xc, 0, \_SB.S00.SL11.CL2C.F012, 0}, \
Package() {0xd, 0, \_SB.S00.SL11.CL2D.F013, 0}, \
Package() {0xe, 0, \_SB.S00.SL11.CL2E.F014, 0}, \
Package() {0xf, 0, \_SB.S00.SL11.CL2F.F015, 0}, \
} \
} \
}) \
}
......@@ -2123,6 +2123,7 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git
F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
F: Documentation/devicetree/bindings/arm/arm,coresight-*.yaml
F: Documentation/devicetree/bindings/arm/qcom,coresight-*.yaml
F: Documentation/devicetree/bindings/arm/arm,embedded-trace-extension.yaml
F: Documentation/devicetree/bindings/arm/arm,trace-buffer-extension.yaml
F: Documentation/trace/coresight/*
......@@ -9374,11 +9375,15 @@ F: drivers/perf/hisilicon/hns3_pmu.c
HISILICON PTT DRIVER
M: Yicong Yang <yangyicong@hisilicon.com>
M: Jonathan Cameron <jonathan.cameron@huawei.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-devices-hisi_ptt
F: Documentation/trace/hisi-ptt.rst
F: drivers/hwtracing/ptt/
F: tools/perf/arch/arm64/util/hisi-ptt.c
F: tools/perf/util/hisi-ptt*
F: tools/perf/util/hisi-ptt-decoder/*
HISILICON QM DRIVER
M: Weili Qian <qianweili@huawei.com>
......
......@@ -201,4 +201,39 @@ config CORESIGHT_TRBE
To compile this driver as a module, choose M here: the module will be
called coresight-trbe.
config ULTRASOC_SMB
tristate "Ultrasoc system memory buffer drivers"
depends on ACPI || COMPILE_TEST
depends on ARM64 && CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for the Ultrasoc system memory buffer (SMB).
SMB is responsible for receiving the trace data from Coresight ETM devices
and storing them to a system buffer.
To compile this driver as a module, choose M here: the module will be
called ultrasoc-smb.
config CORESIGHT_TPDM
tristate "CoreSight Trace, Profiling & Diagnostics Monitor driver"
select CORESIGHT_LINKS_AND_SINKS
select CORESIGHT_TPDA
help
This driver provides support for configuring monitor. Monitors are
primarily responsible for data set collection and support the
ability to collect any permutation of data set types.
To compile this driver as a module, choose M here: the module will be
called coresight-tpdm.
config CORESIGHT_TPDA
tristate "CoreSight Trace, Profiling & Diagnostics Aggregator driver"
help
This driver provides support for configuring aggregator. This is
primarily useful for pulling the data sets from one or more
attached monitors and pushing the resultant data out. Multiple
monitors are connected on different input ports of TPDA.
To compile this driver as a module, choose M here: the module will be
called coresight-tpda.
endif
......@@ -6,7 +6,7 @@ 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 \
coresight-cfg-preload.o coresight-cfg-afdo.o \
coresight-syscfg-configfs.o
coresight-syscfg-configfs.o coresight-trace-id.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
coresight-tmc-y := coresight-tmc-core.o coresight-tmc-etf.o \
coresight-tmc-etr.o
......@@ -25,5 +25,8 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o
coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
coresight-cti-sysfs.o
obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
......@@ -8,6 +8,7 @@
#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>
......@@ -26,6 +27,13 @@
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.
*/
static DEFINE_IDR(path_idr);
/**
* struct coresight_node - elements of a path, from source to sink
* @csdev: Address of an element.
......@@ -42,14 +50,6 @@ struct coresight_node {
*/
static DEFINE_PER_CPU(struct list_head *, tracer_path);
/*
* As of this writing only a single STM can be found in CS topologies. Since
* there is no way to know if we'll ever see more and what kind of
* configuration they will enact, for the time being only define a single path
* for STM.
*/
static struct list_head *stm_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
......@@ -112,45 +112,6 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
}
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
static int coresight_id_match(struct device *dev, void *data)
{
int trace_id, i_trace_id;
struct coresight_device *csdev, *i_csdev;
csdev = data;
i_csdev = to_coresight_device(dev);
/*
* No need to care about oneself and components that are not
* sources or not enabled
*/
if (i_csdev == csdev || !i_csdev->enable ||
i_csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
return 0;
/* Get the source ID for both components */
trace_id = source_ops(csdev)->trace_id(csdev);
i_trace_id = source_ops(i_csdev)->trace_id(i_csdev);
/* All you need is one */
if (trace_id == i_trace_id)
return 1;
return 0;
}
static int coresight_source_is_unique(struct coresight_device *csdev)
{
int trace_id = source_ops(csdev)->trace_id(csdev);
/* this shouldn't happen */
if (trace_id < 0)
return 0;
return !bus_for_each_dev(&coresight_bustype, NULL,
csdev, coresight_id_match);
}
static int coresight_find_link_inport(struct coresight_device *csdev,
struct coresight_device *parent)
{
......@@ -459,12 +420,6 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
{
int ret;
if (!coresight_source_is_unique(csdev)) {
dev_warn(&csdev->dev, "traceID %d not unique\n",
source_ops(csdev)->trace_id(csdev));
return -EINVAL;
}
if (!csdev->enable) {
if (source_ops(csdev)->enable) {
ret = coresight_control_assoc_ectdev(csdev, true);
......@@ -1106,7 +1061,8 @@ static int coresight_validate_source(struct coresight_device *csdev,
}
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) {
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
}
......@@ -1120,6 +1076,7 @@ int coresight_enable(struct coresight_device *csdev)
struct coresight_device *sink;
struct list_head *path;
enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
......@@ -1174,7 +1131,15 @@ int coresight_enable(struct coresight_device *csdev)
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
stm_path = path;
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 */
......@@ -1198,6 +1163,7 @@ void coresight_disable(struct coresight_device *csdev)
{
int cpu, ret;
struct list_head *path = NULL;
u32 hash;
mutex_lock(&coresight_mutex);
......@@ -1215,8 +1181,15 @@ void coresight_disable(struct coresight_device *csdev)
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
path = stm_path;
stm_path = NULL;
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 */
......
......@@ -107,12 +107,12 @@ static int cti_enable_hw(struct cti_drvdata *drvdata)
cti_write_all_hw_regs(drvdata);
config->hw_enabled = true;
atomic_inc(&drvdata->config.enable_req_count);
drvdata->config.enable_req_count++;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return rc;
cti_state_unchanged:
atomic_inc(&drvdata->config.enable_req_count);
drvdata->config.enable_req_count++;
/* cannot enable due to error */
cti_err_not_enabled:
......@@ -129,7 +129,7 @@ static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata)
config->hw_powered = true;
/* no need to do anything if no enable request */
if (!atomic_read(&drvdata->config.enable_req_count))
if (!drvdata->config.enable_req_count)
goto cti_hp_not_enabled;
/* try to claim the device */
......@@ -151,11 +151,18 @@ static int cti_disable_hw(struct cti_drvdata *drvdata)
{
struct cti_config *config = &drvdata->config;
struct coresight_device *csdev = drvdata->csdev;
int ret = 0;
spin_lock(&drvdata->spinlock);
/* don't allow negative refcounts, return an error */
if (!drvdata->config.enable_req_count) {
ret = -EINVAL;
goto cti_not_disabled;
}
/* check refcount - disable on 0 */
if (atomic_dec_return(&drvdata->config.enable_req_count) > 0)
if (--drvdata->config.enable_req_count > 0)
goto cti_not_disabled;
/* no need to do anything if disabled or cpu unpowered */
......@@ -171,12 +178,12 @@ static int cti_disable_hw(struct cti_drvdata *drvdata)
coresight_disclaim_device_unlocked(csdev);
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
return 0;
return ret;
/* not disabled this call */
cti_not_disabled:
spin_unlock(&drvdata->spinlock);
return 0;
return ret;
}
void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value)
......@@ -232,7 +239,7 @@ static void cti_set_default_config(struct device *dev,
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = true;
config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
atomic_set(&config->enable_req_count, 0);
config->enable_req_count = 0;
}
/*
......@@ -689,7 +696,7 @@ static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
drvdata->config.hw_enabled = false;
/* check enable reference count to enable HW */
if (atomic_read(&drvdata->config.enable_req_count)) {
if (drvdata->config.enable_req_count) {
/* check we can claim the device as we re-power */
if (coresight_claim_device(csdev))
goto cti_notify_exit;
......
......@@ -84,8 +84,8 @@ static ssize_t enable_show(struct device *dev,
bool enabled, powered;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
enable_req = drvdata->config.enable_req_count;
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
spin_unlock(&drvdata->spinlock);
......@@ -108,10 +108,19 @@ static ssize_t enable_store(struct device *dev,
if (ret)
return ret;
if (val)
if (val) {
ret = pm_runtime_resume_and_get(dev->parent);
if (ret)
return ret;
ret = cti_enable(drvdata->csdev);
else
if (ret)
pm_runtime_put(dev->parent);
} else {
ret = cti_disable(drvdata->csdev);
if (!ret)
pm_runtime_put(dev->parent);
}
if (ret)
return ret;
return size;
......
......@@ -141,7 +141,7 @@ struct cti_config {
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
int enable_req_count;
bool hw_enabled;
bool hw_powered;
......
......@@ -4,6 +4,7 @@
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/coresight.h>
#include <linux/coresight-pmu.h>
#include <linux/cpumask.h>
......@@ -22,6 +23,7 @@
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
#include "coresight-syscfg.h"
#include "coresight-trace-id.h"
static struct pmu etm_pmu;
static bool etm_perf_up;
......@@ -228,8 +230,12 @@ static void free_event_data(struct work_struct *work)
if (!(IS_ERR_OR_NULL(*ppath)))
coresight_release_path(*ppath);
*ppath = NULL;
coresight_trace_id_put_cpu_id(cpu);
}
/* mark perf event as done for trace id allocator */
coresight_trace_id_perf_stop();
free_percpu(event_data->path);
kfree(event_data);
}
......@@ -300,6 +306,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
{
u32 id, cfg_hash;
int cpu = event->cpu;
int trace_id;
cpumask_t *mask;
struct coresight_device *sink = NULL;
struct coresight_device *user_sink = NULL, *last_sink = NULL;
......@@ -316,6 +323,9 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
sink = user_sink = coresight_get_sink_by_id(id);
}
/* tell the trace ID allocator that a perf event is starting up */
coresight_trace_id_perf_start();
/* check if user wants a coresight configuration selected */
cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32);
if (cfg_hash) {
......@@ -388,6 +398,13 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
continue;
}
/* ensure we can allocate a trace ID for this CPU */
trace_id = coresight_trace_id_get_cpu_id(cpu);
if (!IS_VALID_CS_TRACE_ID(trace_id)) {
cpumask_clear_cpu(cpu, mask);
continue;
}
*etm_event_cpu_path_ptr(event_data, cpu) = path;
}
......@@ -432,6 +449,7 @@ static void etm_event_start(struct perf_event *event, int flags)
struct perf_output_handle *handle = &ctxt->handle;
struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
struct list_head *path;
u64 hw_id;
if (!csdev)
goto fail;
......@@ -477,6 +495,19 @@ static void etm_event_start(struct perf_event *event, int flags)
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
goto fail_disable_path;
/*
* output cpu / trace ID in perf record, once for the lifetime
* of the event.
*/
if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) {
cpumask_set_cpu(cpu, &event_data->aux_hwid_done);
hw_id = FIELD_PREP(CS_AUX_HW_ID_VERSION_MASK,
CS_AUX_HW_ID_CURR_VERSION);
hw_id |= FIELD_PREP(CS_AUX_HW_ID_TRACE_ID_MASK,
coresight_trace_id_read_cpu_id(cpu));
perf_report_aux_output_id(event, hw_id);
}
out:
/* Tell the perf core the event is alive */
event->hw.state = 0;
......
......@@ -48,6 +48,7 @@ struct etm_filters {
* struct etm_event_data - Coresight specifics associated to an event
* @work: Handle to free allocated memory outside IRQ context.
* @mask: Hold the CPU(s) this event was set for.
* @aux_hwid_done: Whether a CPU has emitted the TraceID packet or not.
* @snk_config: The sink configuration.
* @cfg_hash: The hash id of any coresight config selected.
* @path: An array of path, each slot for one CPU.
......@@ -55,6 +56,7 @@ struct etm_filters {
struct etm_event_data {
struct work_struct work;
cpumask_t mask;
cpumask_t aux_hwid_done;
void *snk_config;
u32 cfg_hash;
struct list_head * __percpu *path;
......
......@@ -283,8 +283,9 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
}
extern const struct attribute_group *coresight_etm_groups[];
int etm_get_trace_id(struct etm_drvdata *drvdata);
void etm_set_default(struct etm_config *config);
void etm_config_trace_mode(struct etm_config *config);
struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
int etm_read_alloc_trace_id(struct etm_drvdata *drvdata);
void etm_release_trace_id(struct etm_drvdata *drvdata);
#endif
......@@ -32,6 +32,7 @@
#include "coresight-etm.h"
#include "coresight-etm-perf.h"
#include "coresight-trace-id.h"
/*
* Not really modular but using module_param is the easiest way to
......@@ -454,52 +455,59 @@ static int etm_cpu_id(struct coresight_device *csdev)
return drvdata->cpu;
}
int etm_get_trace_id(struct etm_drvdata *drvdata)
int etm_read_alloc_trace_id(struct etm_drvdata *drvdata)
{
unsigned long flags;
int trace_id = -1;
struct device *etm_dev;
int trace_id;
if (!drvdata)
goto out;
etm_dev = drvdata->csdev->dev.parent;
if (!local_read(&drvdata->mode))
return drvdata->traceid;
pm_runtime_get_sync(etm_dev);
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
trace_id = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
pm_runtime_put(etm_dev);
out:
/*
* This will allocate a trace ID to the cpu,
* or return the one currently allocated.
*
* trace id function has its own lock
*/
trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu);
if (IS_VALID_CS_TRACE_ID(trace_id))
drvdata->traceid = (u8)trace_id;
else
dev_err(&drvdata->csdev->dev,
"Failed to allocate trace ID for %s on CPU%d\n",
dev_name(&drvdata->csdev->dev), drvdata->cpu);
return trace_id;
}
static int etm_trace_id(struct coresight_device *csdev)
void etm_release_trace_id(struct etm_drvdata *drvdata)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return etm_get_trace_id(drvdata);
coresight_trace_id_put_cpu_id(drvdata->cpu);
}
static int etm_enable_perf(struct coresight_device *csdev,
struct perf_event *event)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int trace_id;
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
return -EINVAL;
/* Configure the tracer based on the session's specifics */
etm_parse_event_config(drvdata, event);
/*
* perf allocates cpu ids as part of _setup_aux() - device needs to use
* the allocated ID. This reads the current version without allocation.
*
* This does not use the trace id lock to prevent lock_dep issues
* with perf locks - we know the ID cannot change until perf shuts down
* the session
*/
trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu);
if (!IS_VALID_CS_TRACE_ID(trace_id)) {
dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n",
dev_name(&drvdata->csdev->dev), drvdata->cpu);
return -EINVAL;
}
drvdata->traceid = (u8)trace_id;
/* And enable it */
return etm_enable_hw(drvdata);
}
......@@ -512,6 +520,11 @@ static int etm_enable_sysfs(struct coresight_device *csdev)
spin_lock(&drvdata->spinlock);
/* sysfs needs to allocate and set a trace ID */
ret = etm_read_alloc_trace_id(drvdata);
if (ret < 0)
goto unlock_enable_sysfs;
/*
* Configure the ETM only if the CPU is online. If it isn't online
* hw configuration will take place on the local CPU during bring up.
......@@ -528,6 +541,10 @@ static int etm_enable_sysfs(struct coresight_device *csdev)
ret = -ENODEV;
}
if (ret)
etm_release_trace_id(drvdata);
unlock_enable_sysfs:
spin_unlock(&drvdata->spinlock);
if (!ret)
......@@ -611,6 +628,12 @@ static void etm_disable_perf(struct coresight_device *csdev)
coresight_disclaim_device_unlocked(csdev);
CS_LOCK(drvdata->base);
/*
* perf will release trace ids when _free_aux()
* is called at the end of the session
*/
}
static void etm_disable_sysfs(struct coresight_device *csdev)
......@@ -635,6 +658,13 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
spin_unlock(&drvdata->spinlock);
cpus_read_unlock();
/*
* we only release trace IDs when resetting sysfs.
* This permits sysfs users to read the trace ID after the trace
* session has completed. This maintains operational behaviour with
* prior trace id allocation method
*/
dev_dbg(&csdev->dev, "ETM tracing disabled\n");
}
......@@ -671,7 +701,6 @@ static void etm_disable(struct coresight_device *csdev,
static const struct coresight_ops_source etm_source_ops = {
.cpu_id = etm_cpu_id,
.trace_id = etm_trace_id,
.enable = etm_enable,
.disable = etm_disable,
};
......@@ -781,11 +810,6 @@ static void etm_init_arch_data(void *info)
CS_LOCK(drvdata->base);
}
static void etm_init_trace_id(struct etm_drvdata *drvdata)
{
drvdata->traceid = coresight_get_trace_id(drvdata->cpu);
}
static int __init etm_hp_setup(void)
{
int ret;
......@@ -871,7 +895,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
if (etm_arch_supported(drvdata->arch) == false)
return -EINVAL;
etm_init_trace_id(drvdata);
etm_set_default(&drvdata->config);
pdata = coresight_get_platform_data(dev);
......
......@@ -85,6 +85,7 @@ static ssize_t reset_store(struct device *dev,
}
etm_set_default(config);
etm_release_trace_id(drvdata);
spin_unlock(&drvdata->spinlock);
}
......@@ -1189,30 +1190,16 @@ static DEVICE_ATTR_RO(cpu);
static ssize_t traceid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long val;
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = etm_get_trace_id(drvdata);
return sprintf(buf, "%#lx\n", val);
}
static ssize_t traceid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
int trace_id;
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
trace_id = etm_read_alloc_trace_id(drvdata);
if (trace_id < 0)
return trace_id;
drvdata->traceid = val & ETM_TRACEID_MASK;
return size;
return sysfs_emit(buf, "%#x\n", trace_id);
}
static DEVICE_ATTR_RW(traceid);
static DEVICE_ATTR_RO(traceid);
static struct attribute *coresight_etm_attrs[] = {
&dev_attr_nr_addr_cmp.attr,
......
......@@ -42,6 +42,7 @@
#include "coresight-etm4x-cfg.h"
#include "coresight-self-hosted-trace.h"
#include "coresight-syscfg.h"
#include "coresight-trace-id.h"
static int boot_enable;
module_param(boot_enable, int, 0444);
......@@ -230,11 +231,28 @@ static int etm4_cpu_id(struct coresight_device *csdev)
return drvdata->cpu;
}
static int etm4_trace_id(struct coresight_device *csdev)
int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int trace_id;
/*
* This will allocate a trace ID to the cpu,
* or return the one currently allocated.
* The trace id function has its own lock
*/
trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu);
if (IS_VALID_CS_TRACE_ID(trace_id))
drvdata->trcid = (u8)trace_id;
else
dev_err(&drvdata->csdev->dev,
"Failed to allocate trace ID for %s on CPU%d\n",
dev_name(&drvdata->csdev->dev), drvdata->cpu);
return trace_id;
}
return drvdata->trcid;
void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
{
coresight_trace_id_put_cpu_id(drvdata->cpu);
}
struct etm4_enable_arg {
......@@ -427,8 +445,10 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR);
for (i = 0; i < drvdata->nrseqstate - 1; i++)
etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i));
etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR);
etm4x_relaxed_write32(csa, config->seq_state, TRCSEQSTR);
if (drvdata->nrseqstate) {
etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR);
etm4x_relaxed_write32(csa, config->seq_state, TRCSEQSTR);
}
etm4x_relaxed_write32(csa, config->ext_inp, TRCEXTINSELR);
for (i = 0; i < drvdata->nr_cntr; i++) {
etm4x_relaxed_write32(csa, config->cntrldvr[i], TRCCNTRLDVRn(i));
......@@ -720,7 +740,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
static int etm4_enable_perf(struct coresight_device *csdev,
struct perf_event *event)
{
int ret = 0;
int ret = 0, trace_id;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
......@@ -732,6 +752,24 @@ static int etm4_enable_perf(struct coresight_device *csdev,
ret = etm4_parse_event_config(csdev, event);
if (ret)
goto out;
/*
* perf allocates cpu ids as part of _setup_aux() - device needs to use
* the allocated ID. This reads the current version without allocation.
*
* This does not use the trace id lock to prevent lock_dep issues
* with perf locks - we know the ID cannot change until perf shuts down
* the session
*/
trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu);
if (!IS_VALID_CS_TRACE_ID(trace_id)) {
dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n",
dev_name(&drvdata->csdev->dev), drvdata->cpu);
ret = -EINVAL;
goto out;
}
drvdata->trcid = (u8)trace_id;
/* And enable it */
ret = etm4_enable_hw(drvdata);
......@@ -756,6 +794,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
spin_lock(&drvdata->spinlock);
/* sysfs needs to read and allocate a trace ID */
ret = etm4_read_alloc_trace_id(drvdata);
if (ret < 0)
goto unlock_sysfs_enable;
/*
* Executing etm4_enable_hw on the cpu whose ETM is being enabled
* ensures that register writes occur when cpu is powered.
......@@ -767,6 +810,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
ret = arg.rc;
if (!ret)
drvdata->sticky_enable = true;
if (ret)
etm4_release_trace_id(drvdata);
unlock_sysfs_enable:
spin_unlock(&drvdata->spinlock);
if (!ret)
......@@ -898,6 +946,11 @@ static int etm4_disable_perf(struct coresight_device *csdev,
/* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
/*
* perf will release trace ids when _free_aux() is
* called at the end of the session.
*/
return 0;
}
......@@ -923,6 +976,13 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
spin_unlock(&drvdata->spinlock);
cpus_read_unlock();
/*
* we only release trace IDs when resetting sysfs.
* This permits sysfs users to read the trace ID after the trace
* session has completed. This maintains operational behaviour with
* prior trace id allocation method
*/
dev_dbg(&csdev->dev, "ETM tracing disabled\n");
}
......@@ -956,7 +1016,6 @@ static void etm4_disable(struct coresight_device *csdev,
static const struct coresight_ops_source etm4_source_ops = {
.cpu_id = etm4_cpu_id,
.trace_id = etm4_trace_id,
.enable = etm4_enable,
.disable = etm4_disable,
};
......@@ -1565,11 +1624,6 @@ static int etm4_dying_cpu(unsigned int cpu)
return 0;
}
static void etm4_init_trace_id(struct etmv4_drvdata *drvdata)
{
drvdata->trcid = coresight_get_trace_id(drvdata->cpu);
}
static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
{
int i, ret = 0;
......@@ -1634,8 +1688,10 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
for (i = 0; i < drvdata->nrseqstate - 1; i++)
state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i));
state->trcseqrstevr = etm4x_read32(csa, TRCSEQRSTEVR);
state->trcseqstr = etm4x_read32(csa, TRCSEQSTR);
if (drvdata->nrseqstate) {
state->trcseqrstevr = etm4x_read32(csa, TRCSEQRSTEVR);
state->trcseqstr = etm4x_read32(csa, TRCSEQSTR);
}
state->trcextinselr = etm4x_read32(csa, TRCEXTINSELR);
for (i = 0; i < drvdata->nr_cntr; i++) {
......@@ -1763,8 +1819,10 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
for (i = 0; i < drvdata->nrseqstate - 1; i++)
etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i));
etm4x_relaxed_write32(csa, state->trcseqrstevr, TRCSEQRSTEVR);
etm4x_relaxed_write32(csa, state->trcseqstr, TRCSEQSTR);
if (drvdata->nrseqstate) {
etm4x_relaxed_write32(csa, state->trcseqrstevr, TRCSEQRSTEVR);
etm4x_relaxed_write32(csa, state->trcseqstr, TRCSEQSTR);
}
etm4x_relaxed_write32(csa, state->trcextinselr, TRCEXTINSELR);
for (i = 0; i < drvdata->nr_cntr; i++) {
......@@ -1946,7 +2004,6 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
if (!desc.name)
return -ENOMEM;
etm4_init_trace_id(drvdata);
etm4_set_default(&drvdata->config);
pdata = coresight_get_platform_data(dev);
......
......@@ -266,10 +266,11 @@ static ssize_t reset_store(struct device *dev,
config->vmid_mask0 = 0x0;
config->vmid_mask1 = 0x0;
drvdata->trcid = drvdata->cpu + 1;
spin_unlock(&drvdata->spinlock);
/* for sysfs - only release trace id when resetting */
etm4_release_trace_id(drvdata);
cscfg_csdev_reset_feats(to_coresight_device(dev));
return size;
......@@ -2392,6 +2393,26 @@ static struct attribute *coresight_etmv4_attrs[] = {
NULL,
};
/*
* Trace ID allocated dynamically on enable - but also allocate on read
* in case sysfs or perf read before enable to ensure consistent metadata
* information for trace decode
*/
static ssize_t trctraceid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int trace_id;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
trace_id = etm4_read_alloc_trace_id(drvdata);
if (trace_id < 0)
return trace_id;
return sysfs_emit(buf, "0x%x\n", trace_id);
}
static DEVICE_ATTR_RO(trctraceid);
struct etmv4_reg {
struct coresight_device *csdev;
u32 offset;
......@@ -2528,7 +2549,7 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = {
coresight_etm4x_reg(trcpidr3, TRCPIDR3),
coresight_etm4x_reg(trcoslsr, TRCOSLSR),
coresight_etm4x_reg(trcconfig, TRCCONFIGR),
coresight_etm4x_reg(trctraceid, TRCTRACEIDR),
&dev_attr_trctraceid.attr,
coresight_etm4x_reg(trcdevarch, TRCDEVARCH),
NULL,
};
......
......@@ -1095,4 +1095,7 @@ static inline bool etm4x_is_ete(struct etmv4_drvdata *drvdata)
{
return drvdata->arch >= ETM_ARCH_ETE;
}
int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata);
void etm4_release_trace_id(struct etmv4_drvdata *drvdata);
#endif
......@@ -31,6 +31,7 @@
#include <linux/stm.h>
#include "coresight-priv.h"
#include "coresight-trace-id.h"
#define STMDMASTARTR 0xc04
#define STMDMASTOPR 0xc08
......@@ -280,15 +281,7 @@ static void stm_disable(struct coresight_device *csdev,
}
}
static int stm_trace_id(struct coresight_device *csdev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return drvdata->traceid;
}
static const struct coresight_ops_source stm_source_ops = {
.trace_id = stm_trace_id,
.enable = stm_enable,
.disable = stm_disable,
};
......@@ -615,24 +608,7 @@ static ssize_t traceid_show(struct device *dev,
val = drvdata->traceid;
return sprintf(buf, "%#lx\n", val);
}
static ssize_t traceid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
/* traceid field is 7bit wide on STM32 */
drvdata->traceid = val & 0x7f;
return size;
}
static DEVICE_ATTR_RW(traceid);
static DEVICE_ATTR_RO(traceid);
static struct attribute *coresight_stm_attrs[] = {
&dev_attr_hwevent_enable.attr,
......@@ -803,14 +779,6 @@ static void stm_init_default_data(struct stm_drvdata *drvdata)
*/
drvdata->stmsper = ~0x0;
/*
* The trace ID value for *ETM* tracers start at CPU_ID * 2 + 0x10 and
* anything equal to or higher than 0x70 is reserved. Since 0x00 is
* also reserved the STM trace ID needs to be higher than 0x00 and
* lowner than 0x10.
*/
drvdata->traceid = 0x1;
/* Set invariant transaction timing on all channels */
bitmap_clear(drvdata->chs.guaranteed, 0, drvdata->numsp);
}
......@@ -838,7 +806,7 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata,
static int stm_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
int ret, trace_id;
void __iomem *base;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
......@@ -922,12 +890,22 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
goto stm_unregister;
}
trace_id = coresight_trace_id_get_system_id();
if (trace_id < 0) {
ret = trace_id;
goto cs_unregister;
}
drvdata->traceid = (u8)trace_id;
pm_runtime_put(&adev->dev);
dev_info(&drvdata->csdev->dev, "%s initialized\n",
(char *)coresight_get_uci_data(id));
return 0;
cs_unregister:
coresight_unregister(drvdata->csdev);
stm_unregister:
stm_unregister_device(&drvdata->stm);
return ret;
......@@ -937,6 +915,7 @@ static void stm_remove(struct amba_device *adev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_trace_id_put_system_id(drvdata->traceid);
coresight_unregister(drvdata->csdev);
stm_unregister_device(&drvdata->stm);
......
......@@ -31,7 +31,7 @@ DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb");
DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf");
DEFINE_CORESIGHT_DEVLIST(etr_devs, "tmc_etr");
void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
{
struct coresight_device *csdev = drvdata->csdev;
struct csdev_access *csa = &csdev->access;
......@@ -40,7 +40,9 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
if (coresight_timeout(csa, TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
dev_err(&csdev->dev,
"timeout while waiting for TMC to be Ready\n");
return -EBUSY;
}
return 0;
}
void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
......
......@@ -16,12 +16,20 @@
static int tmc_set_etf_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle);
static void __tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
static int __tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
{
int rc = 0;
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
tmc_wait_for_tmcready(drvdata);
rc = tmc_wait_for_tmcready(drvdata);
if (rc) {
dev_err(&drvdata->csdev->dev,
"Failed to enable: TMC not ready\n");
CS_LOCK(drvdata->base);
return rc;
}
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
......@@ -33,6 +41,7 @@ static void __tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
return rc;
}
static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
......@@ -42,8 +51,10 @@ static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
if (rc)
return rc;
__tmc_etb_enable_hw(drvdata);
return 0;
rc = __tmc_etb_enable_hw(drvdata);
if (rc)
coresight_disclaim_device(drvdata->csdev);
return rc;
}
static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
......@@ -91,12 +102,20 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
coresight_disclaim_device(drvdata->csdev);
}
static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
static int __tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
{
int rc = 0;
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
tmc_wait_for_tmcready(drvdata);
rc = tmc_wait_for_tmcready(drvdata);
if (rc) {
dev_err(&drvdata->csdev->dev,
"Failed to enable : TMC is not ready\n");
CS_LOCK(drvdata->base);
return rc;
}
writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
......@@ -105,6 +124,7 @@ static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
return rc;
}
static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
......@@ -114,8 +134,10 @@ static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
if (rc)
return rc;
__tmc_etf_enable_hw(drvdata);
return 0;
rc = __tmc_etf_enable_hw(drvdata);
if (rc)
coresight_disclaim_device(drvdata->csdev);
return rc;
}
static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
......@@ -639,6 +661,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
char *buf = NULL;
enum tmc_mode mode;
unsigned long flags;
int rc = 0;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
......@@ -664,7 +687,11 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
* can't be NULL.
*/
memset(drvdata->buf, 0, drvdata->size);
__tmc_etb_enable_hw(drvdata);
rc = __tmc_etb_enable_hw(drvdata);
if (rc) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return rc;
}
} else {
/*
* The ETB/ETF is not tracing and the buffer was just read.
......
......@@ -983,15 +983,22 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata)
etr_buf->ops->sync(etr_buf, rrp, rwp);
}
static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
static int __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
u32 axictl, sts;
struct etr_buf *etr_buf = drvdata->etr_buf;
int rc = 0;
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
tmc_wait_for_tmcready(drvdata);
rc = tmc_wait_for_tmcready(drvdata);
if (rc) {
dev_err(&drvdata->csdev->dev,
"Failed to enable : TMC not ready\n");
CS_LOCK(drvdata->base);
return rc;
}
writel_relaxed(etr_buf->size / 4, drvdata->base + TMC_RSZ);
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
......@@ -1032,6 +1039,7 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
return rc;
}
static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
......@@ -1060,7 +1068,12 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
rc = coresight_claim_device(drvdata->csdev);
if (!rc) {
drvdata->etr_buf = etr_buf;
__tmc_etr_enable_hw(drvdata);
rc = __tmc_etr_enable_hw(drvdata);
if (rc) {
drvdata->etr_buf = NULL;
coresight_disclaim_device(drvdata->csdev);
tmc_etr_disable_catu(drvdata);
}
}
return rc;
......
......@@ -255,7 +255,7 @@ struct tmc_sg_table {
};
/* Generic functions */
void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata);
int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata);
void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
void tmc_enable_hw(struct tmc_drvdata *drvdata);
void tmc_disable_hw(struct tmc_drvdata *drvdata);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/amba/bus.h>
#include <linux/bitfield.h>
#include <linux/coresight.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include "coresight-priv.h"
#include "coresight-tpda.h"
#include "coresight-trace-id.h"
DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
/* Settings pre enabling port control register */
static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
{
u32 val;
val = readl_relaxed(drvdata->base + TPDA_CR);
val &= ~TPDA_CR_ATID;
val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid);
writel_relaxed(val, drvdata->base + TPDA_CR);
}
static void tpda_enable_port(struct tpda_drvdata *drvdata, int port)
{
u32 val;
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
/* Enable the port */
val |= TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
}
static void __tpda_enable(struct tpda_drvdata *drvdata, int port)
{
CS_UNLOCK(drvdata->base);
if (!drvdata->csdev->enable)
tpda_enable_pre_port(drvdata);
tpda_enable_port(drvdata, port);
CS_LOCK(drvdata->base);
}
static int tpda_enable(struct coresight_device *csdev, int inport, int outport)
{
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (atomic_read(&csdev->refcnt[inport]) == 0)
__tpda_enable(drvdata, inport);
atomic_inc(&csdev->refcnt[inport]);
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", inport);
return 0;
}
static void __tpda_disable(struct tpda_drvdata *drvdata, int port)
{
u32 val;
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
val &= ~TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
CS_LOCK(drvdata->base);
}
static void tpda_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (atomic_dec_return(&csdev->refcnt[inport]) == 0)
__tpda_disable(drvdata, inport);
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", inport);
}
static const struct coresight_ops_link tpda_link_ops = {
.enable = tpda_enable,
.disable = tpda_disable,
};
static const struct coresight_ops tpda_cs_ops = {
.link_ops = &tpda_link_ops,
};
static int tpda_init_default_data(struct tpda_drvdata *drvdata)
{
int atid;
/*
* TPDA must has a unique atid. This atid can uniquely
* identify the TPDM trace source connected to the TPDA.
* The TPDMs which are connected to same TPDA share the
* same trace-id. When TPDA does packetization, different
* port will have unique channel number for decoding.
*/
atid = coresight_trace_id_get_system_id();
if (atid < 0)
return atid;
drvdata->atid = atid;
return 0;
}
static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata;
struct tpda_drvdata *drvdata;
struct coresight_desc desc = { 0 };
void __iomem *base;
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
base = devm_ioremap_resource(dev, &adev->res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
ret = tpda_init_default_data(drvdata);
if (ret)
return ret;
desc.name = coresight_alloc_device_name(&tpda_devs, dev);
if (!desc.name)
return -ENOMEM;
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
desc.ops = &tpda_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
desc.access = CSDEV_ACCESS_IOMEM(base);
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
pm_runtime_put(&adev->dev);
dev_dbg(drvdata->dev, "TPDA initialized\n");
return 0;
}
static void tpda_remove(struct amba_device *adev)
{
struct tpda_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_trace_id_put_system_id(drvdata->atid);
coresight_unregister(drvdata->csdev);
}
/*
* Different TPDA has different periph id.
* The difference is 0-7 bits' value. So ignore 0-7 bits.
*/
static struct amba_id tpda_ids[] = {
{
.id = 0x000f0f00,
.mask = 0x000fff00,
},
{ 0, 0},
};
static struct amba_driver tpda_driver = {
.drv = {
.name = "coresight-tpda",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = tpda_probe,
.remove = tpda_remove,
.id_table = tpda_ids,
};
module_amba_driver(tpda_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _CORESIGHT_CORESIGHT_TPDA_H
#define _CORESIGHT_CORESIGHT_TPDA_H
#define TPDA_CR (0x000)
#define TPDA_Pn_CR(n) (0x004 + (n * 4))
/* Aggregator port enable bit */
#define TPDA_Pn_CR_ENA BIT(0)
#define TPDA_MAX_INPORTS 32
/* Bits 6 ~ 12 is for atid value */
#define TPDA_CR_ATID GENMASK(12, 6)
/**
* struct tpda_drvdata - specifics associated to an TPDA component
* @base: memory mapped base address for this component.
* @dev: The device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @spinlock: lock for the drvdata value.
* @enable: enable status of the component.
*/
struct tpda_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
spinlock_t spinlock;
u8 atid;
};
#endif /* _CORESIGHT_CORESIGHT_TPDA_H */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/amba/bus.h>
#include <linux/bitmap.h>
#include <linux/coresight.h>
#include <linux/coresight-pmu.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include "coresight-priv.h"
#include "coresight-tpdm.h"
DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val;
/* Set the enable bit of DSB control register to 1 */
val = readl_relaxed(drvdata->base + TPDM_DSB_CR);
val |= TPDM_DSB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_DSB_CR);
}
/* TPDM enable operations */
static void __tpdm_enable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Check if DSB datasets is present for TPDM. */
if (drvdata->datasets & TPDM_PIDR0_DS_DSB)
tpdm_enable_dsb(drvdata);
CS_LOCK(drvdata->base);
}
static int tpdm_enable(struct coresight_device *csdev,
struct perf_event *event, u32 mode)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (drvdata->enable) {
spin_unlock(&drvdata->spinlock);
return -EBUSY;
}
__tpdm_enable(drvdata);
drvdata->enable = true;
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDM tracing enabled\n");
return 0;
}
static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val;
/* 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);
}
/* TPDM disable operations */
static void __tpdm_disable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Check if DSB datasets is present for TPDM. */
if (drvdata->datasets & TPDM_PIDR0_DS_DSB)
tpdm_disable_dsb(drvdata);
CS_LOCK(drvdata->base);
}
static void tpdm_disable(struct coresight_device *csdev,
struct perf_event *event)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (!drvdata->enable) {
spin_unlock(&drvdata->spinlock);
return;
}
__tpdm_disable(drvdata);
drvdata->enable = false;
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDM tracing disabled\n");
}
static const struct coresight_ops_source tpdm_source_ops = {
.enable = tpdm_enable,
.disable = tpdm_disable,
};
static const struct coresight_ops tpdm_cs_ops = {
.source_ops = &tpdm_source_ops,
};
static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
{
u32 pidr;
CS_UNLOCK(drvdata->base);
/* Get the datasets present on the TPDM. */
pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0);
drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0);
CS_LOCK(drvdata->base);
}
/*
* value 1: 64 bits test data
* value 2: 32 bits test data
*/
static ssize_t integration_test_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
int i, ret = 0;
unsigned long val;
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val != 1 && val != 2)
return -EINVAL;
if (!drvdata->enable)
return -EINVAL;
if (val == 1)
val = ATBCNTRL_VAL_64;
else
val = ATBCNTRL_VAL_32;
CS_UNLOCK(drvdata->base);
writel_relaxed(0x1, drvdata->base + TPDM_ITCNTRL);
for (i = 0; i < INTEGRATION_TEST_CYCLE; i++)
writel_relaxed(val, drvdata->base + TPDM_ITATBCNTRL);
writel_relaxed(0, drvdata->base + TPDM_ITCNTRL);
CS_LOCK(drvdata->base);
return size;
}
static DEVICE_ATTR_WO(integration_test);
static struct attribute *tpdm_attrs[] = {
&dev_attr_integration_test.attr,
NULL,
};
static struct attribute_group tpdm_attr_grp = {
.attrs = tpdm_attrs,
};
static const struct attribute_group *tpdm_attr_grps[] = {
&tpdm_attr_grp,
NULL,
};
static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
{
void __iomem *base;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata;
struct tpdm_drvdata *drvdata;
struct coresight_desc desc = { 0 };
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
base = devm_ioremap_resource(dev, &adev->res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
/* Set up coresight component description */
desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
if (!desc.name)
return -ENOMEM;
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS;
desc.ops = &tpdm_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
desc.access = CSDEV_ACCESS_IOMEM(base);
desc.groups = tpdm_attr_grps;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
spin_lock_init(&drvdata->spinlock);
tpdm_init_default_data(drvdata);
/* Decrease pm refcount when probe is done.*/
pm_runtime_put(&adev->dev);
return 0;
}
static void tpdm_remove(struct amba_device *adev)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_unregister(drvdata->csdev);
}
/*
* Different TPDM has different periph id.
* The difference is 0-7 bits' value. So ignore 0-7 bits.
*/
static struct amba_id tpdm_ids[] = {
{
.id = 0x000f0e00,
.mask = 0x000fff00,
},
{ 0, 0},
};
static struct amba_driver tpdm_driver = {
.drv = {
.name = "coresight-tpdm",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = tpdm_probe,
.id_table = tpdm_ids,
.remove = tpdm_remove,
};
module_amba_driver(tpdm_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _CORESIGHT_CORESIGHT_TPDM_H
#define _CORESIGHT_CORESIGHT_TPDM_H
/* The max number of the datasets that TPDM supports */
#define TPDM_DATASETS 7
/* DSB Subunit Registers */
#define TPDM_DSB_CR (0x780)
/* Enable bit for DSB subunit */
#define TPDM_DSB_CR_ENA BIT(0)
/* TPDM integration test registers */
#define TPDM_ITATBCNTRL (0xEF0)
#define TPDM_ITCNTRL (0xF00)
/* Register value for integration test */
#define ATBCNTRL_VAL_32 0xC00F1409
#define ATBCNTRL_VAL_64 0xC01F1409
/*
* Number of cycles to write value when
* integration test.
*/
#define INTEGRATION_TEST_CYCLE 10
/**
* The bits of PERIPHIDR0 register.
* The fields [6:0] of PERIPHIDR0 are used to determine what
* interfaces and subunits are present on a given TPDM.
*
* PERIPHIDR0[0] : Fix to 1 if ImplDef subunit present, else 0
* PERIPHIDR0[1] : Fix to 1 if DSB subunit present, else 0
*/
#define TPDM_PIDR0_DS_IMPDEF BIT(0)
#define TPDM_PIDR0_DS_DSB BIT(1)
/**
* struct tpdm_drvdata - specifics associated to an TPDM component
* @base: memory mapped base address for this component.
* @dev: The device entity associated to this component.
* @csdev: component vitals needed by the framework.
* @spinlock: lock for the drvdata value.
* @enable: enable status of the component.
* @datasets: The datasets types present of the TPDM.
*/
struct tpdm_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
spinlock_t spinlock;
bool enable;
unsigned long datasets;
};
#endif /* _CORESIGHT_CORESIGHT_TPDM_H */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022, Linaro Limited, All rights reserved.
* Author: Mike Leach <mike.leach@linaro.org>
*/
#include <linux/coresight-pmu.h>
#include <linux/cpumask.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include "coresight-trace-id.h"
/* Default trace ID map. Used on systems that don't require per sink mappings */
static struct coresight_trace_id_map id_map_default;
/* maintain a record of the mapping of IDs and pending releases per cpu */
static DEFINE_PER_CPU(atomic_t, cpu_id) = ATOMIC_INIT(0);
static cpumask_t cpu_id_release_pending;
/* perf session active counter */
static atomic_t perf_cs_etm_session_active = ATOMIC_INIT(0);
/* lock to protect id_map and cpu data */
static DEFINE_SPINLOCK(id_map_lock);
/* #define TRACE_ID_DEBUG 1 */
#if defined(TRACE_ID_DEBUG) || defined(CONFIG_COMPILE_TEST)
static void coresight_trace_id_dump_table(struct coresight_trace_id_map *id_map,
const char *func_name)
{
pr_debug("%s id_map::\n", func_name);
pr_debug("Used = %*pb\n", CORESIGHT_TRACE_IDS_MAX, id_map->used_ids);
pr_debug("Pend = %*pb\n", CORESIGHT_TRACE_IDS_MAX, id_map->pend_rel_ids);
}
#define DUMP_ID_MAP(map) coresight_trace_id_dump_table(map, __func__)
#define DUMP_ID_CPU(cpu, id) pr_debug("%s called; cpu=%d, id=%d\n", __func__, cpu, id)
#define DUMP_ID(id) pr_debug("%s called; id=%d\n", __func__, id)
#define PERF_SESSION(n) pr_debug("%s perf count %d\n", __func__, n)
#else
#define DUMP_ID_MAP(map)
#define DUMP_ID(id)
#define DUMP_ID_CPU(cpu, id)
#define PERF_SESSION(n)
#endif
/* unlocked read of current trace ID value for given CPU */
static int _coresight_trace_id_read_cpu_id(int cpu)
{
return atomic_read(&per_cpu(cpu_id, cpu));
}
/* look for next available odd ID, return 0 if none found */
static int coresight_trace_id_find_odd_id(struct coresight_trace_id_map *id_map)
{
int found_id = 0, bit = 1, next_id;
while ((bit < CORESIGHT_TRACE_ID_RES_TOP) && !found_id) {
/*
* bitmap length of CORESIGHT_TRACE_ID_RES_TOP,
* search from offset `bit`.
*/
next_id = find_next_zero_bit(id_map->used_ids,
CORESIGHT_TRACE_ID_RES_TOP, bit);
if ((next_id < CORESIGHT_TRACE_ID_RES_TOP) && (next_id & 0x1))
found_id = next_id;
else
bit = next_id + 1;
}
return found_id;
}
/*
* Allocate new ID and set in use
*
* if @preferred_id is a valid id then try to use that value if available.
* if @preferred_id is not valid and @prefer_odd_id is true, try for odd id.
*
* Otherwise allocate next available ID.
*/
static int coresight_trace_id_alloc_new_id(struct coresight_trace_id_map *id_map,
int preferred_id, bool prefer_odd_id)
{
int id = 0;
/* for backwards compatibility, cpu IDs may use preferred value */
if (IS_VALID_CS_TRACE_ID(preferred_id) &&
!test_bit(preferred_id, id_map->used_ids)) {
id = preferred_id;
goto trace_id_allocated;
} else if (prefer_odd_id) {
/* may use odd ids to avoid preferred legacy cpu IDs */
id = coresight_trace_id_find_odd_id(id_map);
if (id)
goto trace_id_allocated;
}
/*
* skip reserved bit 0, look at bitmap length of
* CORESIGHT_TRACE_ID_RES_TOP from offset of bit 1.
*/
id = find_next_zero_bit(id_map->used_ids, CORESIGHT_TRACE_ID_RES_TOP, 1);
if (id >= CORESIGHT_TRACE_ID_RES_TOP)
return -EINVAL;
/* mark as used */
trace_id_allocated:
set_bit(id, id_map->used_ids);
return id;
}
static void coresight_trace_id_free(int id, struct coresight_trace_id_map *id_map)
{
if (WARN(!IS_VALID_CS_TRACE_ID(id), "Invalid Trace ID %d\n", id))
return;
if (WARN(!test_bit(id, id_map->used_ids), "Freeing unused ID %d\n", id))
return;
clear_bit(id, id_map->used_ids);
}
static void coresight_trace_id_set_pend_rel(int id, struct coresight_trace_id_map *id_map)
{
if (WARN(!IS_VALID_CS_TRACE_ID(id), "Invalid Trace ID %d\n", id))
return;
set_bit(id, id_map->pend_rel_ids);
}
/*
* release all pending IDs for all current maps & clear CPU associations
*
* This currently operates on the default id map, but may be extended to
* operate on all registered id maps if per sink id maps are used.
*/
static void coresight_trace_id_release_all_pending(void)
{
struct coresight_trace_id_map *id_map = &id_map_default;
unsigned long flags;
int cpu, bit;
spin_lock_irqsave(&id_map_lock, flags);
for_each_set_bit(bit, id_map->pend_rel_ids, CORESIGHT_TRACE_ID_RES_TOP) {
clear_bit(bit, id_map->used_ids);
clear_bit(bit, id_map->pend_rel_ids);
}
for_each_cpu(cpu, &cpu_id_release_pending) {
atomic_set(&per_cpu(cpu_id, cpu), 0);
cpumask_clear_cpu(cpu, &cpu_id_release_pending);
}
spin_unlock_irqrestore(&id_map_lock, flags);
DUMP_ID_MAP(id_map);
}
static int coresight_trace_id_map_get_cpu_id(int cpu, struct coresight_trace_id_map *id_map)
{
unsigned long flags;
int id;
spin_lock_irqsave(&id_map_lock, flags);
/* check for existing allocation for this CPU */
id = _coresight_trace_id_read_cpu_id(cpu);
if (id)
goto get_cpu_id_clr_pend;
/*
* Find a new ID.
*
* Use legacy values where possible in the dynamic trace ID allocator to
* allow older tools to continue working if they are not upgraded at the
* same time as the kernel drivers.
*
* If the generated legacy ID is invalid, or not available then the next
* available dynamic ID will be used.
*/
id = coresight_trace_id_alloc_new_id(id_map,
CORESIGHT_LEGACY_CPU_TRACE_ID(cpu),
false);
if (!IS_VALID_CS_TRACE_ID(id))
goto get_cpu_id_out_unlock;
/* allocate the new id to the cpu */
atomic_set(&per_cpu(cpu_id, cpu), id);
get_cpu_id_clr_pend:
/* we are (re)using this ID - so ensure it is not marked for release */
cpumask_clear_cpu(cpu, &cpu_id_release_pending);
clear_bit(id, id_map->pend_rel_ids);
get_cpu_id_out_unlock:
spin_unlock_irqrestore(&id_map_lock, flags);
DUMP_ID_CPU(cpu, id);
DUMP_ID_MAP(id_map);
return id;
}
static void coresight_trace_id_map_put_cpu_id(int cpu, struct coresight_trace_id_map *id_map)
{
unsigned long flags;
int id;
/* check for existing allocation for this CPU */
id = _coresight_trace_id_read_cpu_id(cpu);
if (!id)
return;
spin_lock_irqsave(&id_map_lock, flags);
if (atomic_read(&perf_cs_etm_session_active)) {
/* set release at pending if perf still active */
coresight_trace_id_set_pend_rel(id, id_map);
cpumask_set_cpu(cpu, &cpu_id_release_pending);
} else {
/* otherwise clear id */
coresight_trace_id_free(id, id_map);
atomic_set(&per_cpu(cpu_id, cpu), 0);
}
spin_unlock_irqrestore(&id_map_lock, flags);
DUMP_ID_CPU(cpu, id);
DUMP_ID_MAP(id_map);
}
static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map)
{
unsigned long flags;
int id;
spin_lock_irqsave(&id_map_lock, flags);
/* prefer odd IDs for system components to avoid legacy CPU IDS */
id = coresight_trace_id_alloc_new_id(id_map, 0, true);
spin_unlock_irqrestore(&id_map_lock, flags);
DUMP_ID(id);
DUMP_ID_MAP(id_map);
return id;
}
static void coresight_trace_id_map_put_system_id(struct coresight_trace_id_map *id_map, int id)
{
unsigned long flags;
spin_lock_irqsave(&id_map_lock, flags);
coresight_trace_id_free(id, id_map);
spin_unlock_irqrestore(&id_map_lock, flags);
DUMP_ID(id);
DUMP_ID_MAP(id_map);
}
/* API functions */
int coresight_trace_id_get_cpu_id(int cpu)
{
return coresight_trace_id_map_get_cpu_id(cpu, &id_map_default);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_get_cpu_id);
void coresight_trace_id_put_cpu_id(int cpu)
{
coresight_trace_id_map_put_cpu_id(cpu, &id_map_default);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_put_cpu_id);
int coresight_trace_id_read_cpu_id(int cpu)
{
return _coresight_trace_id_read_cpu_id(cpu);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id);
int coresight_trace_id_get_system_id(void)
{
return coresight_trace_id_map_get_system_id(&id_map_default);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_get_system_id);
void coresight_trace_id_put_system_id(int id)
{
coresight_trace_id_map_put_system_id(&id_map_default, id);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_put_system_id);
void coresight_trace_id_perf_start(void)
{
atomic_inc(&perf_cs_etm_session_active);
PERF_SESSION(atomic_read(&perf_cs_etm_session_active));
}
EXPORT_SYMBOL_GPL(coresight_trace_id_perf_start);
void coresight_trace_id_perf_stop(void)
{
if (!atomic_dec_return(&perf_cs_etm_session_active))
coresight_trace_id_release_all_pending();
PERF_SESSION(atomic_read(&perf_cs_etm_session_active));
}
EXPORT_SYMBOL_GPL(coresight_trace_id_perf_stop);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright(C) 2022 Linaro Limited. All rights reserved.
* Author: Mike Leach <mike.leach@linaro.org>
*/
#ifndef _CORESIGHT_TRACE_ID_H
#define _CORESIGHT_TRACE_ID_H
/*
* Coresight trace ID allocation API
*
* With multi cpu systems, and more additional trace sources a scalable
* trace ID reservation system is required.
*
* The system will allocate Ids on a demand basis, and allow them to be
* released when done.
*
* In order to ensure that a consistent cpu / ID matching is maintained
* throughout a perf cs_etm event session - a session in progress flag will
* be maintained, and released IDs not cleared until the perf session is
* complete. This allows the same CPU to be re-allocated its prior ID.
*
*
* Trace ID maps will be created and initialised to prevent architecturally
* reserved IDs from being allocated.
*
* API permits multiple maps to be maintained - for large systems where
* different sets of cpus trace into different independent sinks.
*/
#include <linux/bitops.h>
#include <linux/types.h>
/* architecturally we have 128 IDs some of which are reserved */
#define CORESIGHT_TRACE_IDS_MAX 128
/* ID 0 is reserved */
#define CORESIGHT_TRACE_ID_RES_0 0
/* ID 0x70 onwards are reserved */
#define CORESIGHT_TRACE_ID_RES_TOP 0x70
/* check an ID is in the valid range */
#define IS_VALID_CS_TRACE_ID(id) \
((id > CORESIGHT_TRACE_ID_RES_0) && (id < CORESIGHT_TRACE_ID_RES_TOP))
/**
* Trace ID map.
*
* @used_ids: Bitmap to register available (bit = 0) and in use (bit = 1) IDs.
* Initialised so that the reserved IDs are permanently marked as
* in use.
* @pend_rel_ids: CPU IDs that have been released by the trace source but not
* yet marked as available, to allow re-allocation to the same
* CPU during a perf session.
*/
struct coresight_trace_id_map {
DECLARE_BITMAP(used_ids, CORESIGHT_TRACE_IDS_MAX);
DECLARE_BITMAP(pend_rel_ids, CORESIGHT_TRACE_IDS_MAX);
};
/* Allocate and release IDs for a single default trace ID map */
/**
* Read and optionally allocate a CoreSight trace ID and associate with a CPU.
*
* Function will read the current trace ID for the associated CPU,
* allocating an new ID if one is not currently allocated.
*
* Numeric ID values allocated use legacy allocation algorithm if possible,
* otherwise any available ID is used.
*
* @cpu: The CPU index to allocate for.
*
* return: CoreSight trace ID or -EINVAL if allocation impossible.
*/
int coresight_trace_id_get_cpu_id(int cpu);
/**
* Release an allocated trace ID associated with the CPU.
*
* This will release the CoreSight trace ID associated with the CPU,
* unless a perf session is in operation.
*
* If a perf session is in operation then the ID will be marked as pending
* release.
*
* @cpu: The CPU index to release the associated trace ID.
*/
void coresight_trace_id_put_cpu_id(int cpu);
/**
* Read the current allocated CoreSight Trace ID value for the CPU.
*
* Fast read of the current value that does not allocate if no ID allocated
* for the CPU.
*
* Used in perf context where it is known that the value for the CPU will not
* be changing, when perf starts and event on a core and outputs the Trace ID
* for the CPU as a packet in the data file. IDs cannot change during a perf
* session.
*
* This function does not take the lock protecting the ID lists, avoiding
* locking dependency issues with perf locks.
*
* @cpu: The CPU index to read.
*
* return: current value, will be 0 if unallocated.
*/
int coresight_trace_id_read_cpu_id(int cpu);
/**
* Allocate a CoreSight trace ID for a system component.
*
* Unconditionally allocates a Trace ID, without associating the ID with a CPU.
*
* Used to allocate IDs for system trace sources such as STM.
*
* return: Trace ID or -EINVAL if allocation is impossible.
*/
int coresight_trace_id_get_system_id(void);
/**
* Release an allocated system trace ID.
*
* Unconditionally release a trace ID allocated to a system component.
*
* @id: value of trace ID allocated.
*/
void coresight_trace_id_put_system_id(int id);
/* notifiers for perf session start and stop */
/**
* Notify the Trace ID allocator that a perf session is starting.
*
* Increase the perf session reference count - called by perf when setting up
* a trace event.
*
* This reference count is used by the ID allocator to ensure that trace IDs
* associated with a CPU cannot change or be released during a perf session.
*/
void coresight_trace_id_perf_start(void);
/**
* Notify the ID allocator that a perf session is stopping.
*
* Decrease the perf session reference count.
* if this causes the count to go to zero, then all Trace IDs marked as pending
* release, will be released.
*/
void coresight_trace_id_perf_stop(void);
#endif /* _CORESIGHT_TRACE_ID_H */
This diff is collapsed.
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Siemens System Memory Buffer driver.
* Copyright(c) 2022, HiSilicon Limited.
*/
#ifndef _ULTRASOC_SMB_H
#define _ULTRASOC_SMB_H
#include <linux/miscdevice.h>
#include <linux/mutex.h>
/* Offset of SMB global registers */
#define SMB_GLB_CFG_REG 0x00
#define SMB_GLB_EN_REG 0x04
#define SMB_GLB_INT_REG 0x08
/* Offset of SMB logical buffer registers */
#define SMB_LB_CFG_LO_REG 0x40
#define SMB_LB_CFG_HI_REG 0x44
#define SMB_LB_INT_CTRL_REG 0x48
#define SMB_LB_INT_STS_REG 0x4c
#define SMB_LB_RD_ADDR_REG 0x5c
#define SMB_LB_WR_ADDR_REG 0x60
#define SMB_LB_PURGE_REG 0x64
/* Set global config register */
#define SMB_GLB_CFG_BURST_LEN_MSK GENMASK(11, 4)
#define SMB_GLB_CFG_IDLE_PRD_MSK GENMASK(15, 12)
#define SMB_GLB_CFG_MEM_WR_MSK GENMASK(21, 16)
#define SMB_GLB_CFG_MEM_RD_MSK GENMASK(27, 22)
#define SMB_GLB_CFG_DEFAULT (FIELD_PREP(SMB_GLB_CFG_BURST_LEN_MSK, 0xf) | \
FIELD_PREP(SMB_GLB_CFG_IDLE_PRD_MSK, 0xf) | \
FIELD_PREP(SMB_GLB_CFG_MEM_WR_MSK, 0x3) | \
FIELD_PREP(SMB_GLB_CFG_MEM_RD_MSK, 0x1b))
#define SMB_GLB_EN_HW_ENABLE BIT(0)
/* Set global interrupt control register */
#define SMB_GLB_INT_EN BIT(0)
#define SMB_GLB_INT_PULSE BIT(1) /* Interrupt type: 1 - Pulse */
#define SMB_GLB_INT_ACT_H BIT(2) /* Interrupt polarity: 1 - Active high */
#define SMB_GLB_INT_CFG (SMB_GLB_INT_EN | SMB_GLB_INT_PULSE | \
SMB_GLB_INT_ACT_H)
/* Set logical buffer config register lower 32 bits */
#define SMB_LB_CFG_LO_EN BIT(0)
#define SMB_LB_CFG_LO_SINGLE_END BIT(1)
#define SMB_LB_CFG_LO_INIT BIT(8)
#define SMB_LB_CFG_LO_CONT BIT(11)
#define SMB_LB_CFG_LO_FLOW_MSK GENMASK(19, 16)
#define SMB_LB_CFG_LO_DEFAULT (SMB_LB_CFG_LO_EN | SMB_LB_CFG_LO_SINGLE_END | \
SMB_LB_CFG_LO_INIT | SMB_LB_CFG_LO_CONT | \
FIELD_PREP(SMB_LB_CFG_LO_FLOW_MSK, 0xf))
/* Set logical buffer config register upper 32 bits */
#define SMB_LB_CFG_HI_RANGE_UP_MSK GENMASK(15, 8)
#define SMB_LB_CFG_HI_DEFAULT FIELD_PREP(SMB_LB_CFG_HI_RANGE_UP_MSK, 0xff)
/*
* Set logical buffer interrupt control register.
* The register control the validity of both real-time events and
* interrupts. When logical buffer status changes causes to issue
* an interrupt at the same time as it issues a real-time event.
* Real-time events are used in SMB driver, which needs to get the buffer
* status. Interrupts are used in debugger mode.
* SMB_LB_INT_CTRL_BUF_NOTE_MASK control which events flags or interrupts
* are valid.
*/
#define SMB_LB_INT_CTRL_EN BIT(0)
#define SMB_LB_INT_CTRL_BUF_NOTE_MSK GENMASK(11, 8)
#define SMB_LB_INT_CTRL_CFG (SMB_LB_INT_CTRL_EN | \
FIELD_PREP(SMB_LB_INT_CTRL_BUF_NOTE_MSK, 0xf))
/* Set logical buffer interrupt status register */
#define SMB_LB_INT_STS_NOT_EMPTY_MSK BIT(0)
#define SMB_LB_INT_STS_BUF_RESET_MSK GENMASK(3, 0)
#define SMB_LB_INT_STS_RESET FIELD_PREP(SMB_LB_INT_STS_BUF_RESET_MSK, 0xf)
#define SMB_LB_PURGE_PURGED BIT(0)
#define SMB_REG_ADDR_RES 0
#define SMB_BUF_ADDR_RES 1
#define SMB_BUF_ADDR_LO_MSK GENMASK(31, 0)
/**
* struct smb_data_buffer - Details of the buffer used by SMB
* @buf_base: Memory mapped base address of SMB.
* @buf_hw_base: SMB buffer start Physical base address, only used 32bits.
* @buf_size: Size of the buffer.
* @data_size: Size of the available trace data for SMB.
* @buf_rdptr: Current read position (index) within the buffer.
*/
struct smb_data_buffer {
void *buf_base;
u32 buf_hw_base;
unsigned long buf_size;
unsigned long data_size;
unsigned long buf_rdptr;
};
/**
* struct smb_drv_data - specifics associated to an SMB component
* @base: Memory mapped base address for SMB component.
* @csdev: Component vitals needed by the framework.
* @sdb: Data buffer for SMB.
* @miscdev: Specifics to handle "/dev/xyz.smb" entry.
* @mutex: Control data access to one at a time.
* @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;
struct coresight_device *csdev;
struct smb_data_buffer sdb;
struct miscdevice miscdev;
struct mutex mutex;
bool reading;
pid_t pid;
u32 mode;
};
#endif
......@@ -356,8 +356,18 @@ static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt)
static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data)
{
struct pci_dev *root_port = pcie_find_root_port(pdev);
struct hisi_ptt_filter_desc *filter;
struct hisi_ptt *hisi_ptt = data;
u32 port_devid;
if (!root_port)
return 0;
port_devid = PCI_DEVID(root_port->bus->number, root_port->devfn);
if (port_devid < hisi_ptt->lower_bdf ||
port_devid > hisi_ptt->upper_bdf)
return 0;
/*
* We won't fail the probe if filter allocation failed here. The filters
......
......@@ -7,8 +7,19 @@
#ifndef _LINUX_CORESIGHT_PMU_H
#define _LINUX_CORESIGHT_PMU_H
#include <linux/bits.h>
#define CORESIGHT_ETM_PMU_NAME "cs_etm"
#define CORESIGHT_ETM_PMU_SEED 0x10
/*
* The legacy Trace ID system based on fixed calculation from the cpu
* number. This has been replaced by drivers using a dynamic allocation
* system - but need to retain the legacy algorithm for backward comparibility
* in certain situations:-
* a) new perf running on older systems that generate the legacy mapping
* b) older tools that may not update at the same time as the kernel.
*/
#define CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) (0x10 + (cpu * 2))
/*
* Below are the definition of bit offsets for perf option, and works as
......@@ -34,15 +45,16 @@
#define ETM4_CFG_BIT_RETSTK 12
#define ETM4_CFG_BIT_VMID_OPT 15
static inline int coresight_get_trace_id(int cpu)
{
/*
* A trace ID of value 0 is invalid, so let's start at some
* random value that fits in 7 bits and go from there. Since
* the common convention is to have data trace IDs be I(N) + 1,
* set instruction trace IDs as a function of the CPU number.
*/
return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
}
/*
* Interpretation of the PERF_RECORD_AUX_OUTPUT_HW_ID payload.
* Used to associate a CPU with the CoreSight Trace ID.
* [07:00] - Trace ID - uses 8 bits to make value easy to read in file.
* [59:08] - Unused (SBZ)
* [63:60] - Version
*/
#define CS_AUX_HW_ID_TRACE_ID_MASK GENMASK_ULL(7, 0)
#define CS_AUX_HW_ID_VERSION_MASK GENMASK_ULL(63, 60)
#define CS_AUX_HW_ID_CURR_VERSION 0
#endif
......@@ -61,6 +61,7 @@ enum coresight_dev_subtype_source {
CORESIGHT_DEV_SUBTYPE_SOURCE_PROC,
CORESIGHT_DEV_SUBTYPE_SOURCE_BUS,
CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE,
CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS,
};
enum coresight_dev_subtype_helper {
......@@ -314,14 +315,11 @@ struct coresight_ops_link {
* Operations available for sources.
* @cpu_id: returns the value of the CPU number this component
* is associated to.
* @trace_id: returns the value of the component's trace ID as known
* to the HW.
* @enable: enables tracing for a source.
* @disable: disables tracing for a source.
*/
struct coresight_ops_source {
int (*cpu_id)(struct coresight_device *csdev);
int (*trace_id)(struct coresight_device *csdev);
int (*enable)(struct coresight_device *csdev,
struct perf_event *event, u32 mode);
void (*disable)(struct coresight_device *csdev,
......
......@@ -9399,6 +9399,7 @@ void perf_report_aux_output_id(struct perf_event *event, u64 hw_id)
perf_output_end(&handle);
}
EXPORT_SYMBOL_GPL(perf_report_aux_output_id);
static int
__perf_event_account_interrupt(struct perf_event *event, int throttle)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment