mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 14:50:19 +00:00
coresight: Updates for v6.5
CoreSight and hwtracing subsystem updates for v6.5 includes: - Fixes to the CTI module reference leaks. This involves, redesign of how the helper devices are tracked and CTI devices have been converted to helper devices. - Fix removal of the trctraceidr file from sysfs for ETMs. - Match all ETMv4 instances based on the ETMv4 architected registers and the CoreSight Component ID (CID), than having to add individual PIDs for CPUs. - Add support for Dummy CoreSight source and sink drivers. - Add James Clark as Reviewer for the CoreSight kernel drivers - Fixes to HiSilicon PCIe Tune and Trace Device driver Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com> -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEuFy0byloRoXZHaWBxcXRZPKyBqEFAmSS/goACgkQxcXRZPKy BqFShQ//Z25Qnf0y2VdOBgZ1xWYeOTjkeQ2AVYE2hepV78N5rnI8BgcwlBNrF5IA uTu2U+nSenkCurWk+wOrmXaQ2SXkEEp2Gsm866WzeL4OjWsqxpdoZ1l2u7/YqxMK 4QIP2ELS71KcQMIIJ31eYDSMro/gA8xDPh2QGhZKihUQAsoVQOghX7Y1eoT+4a/V pvsngu71iM45jHR1eFkp9/rQCKhy9OA58Q8gtg21uotOja9jvHQpRZ4TGN7en0CP RDVmIaxRDh3sPWoVpIPYs3nL8DX2NeSX5BVC/xq2P0UAHN6C9rp+Kom1XN7VZqS6 UdgyNw1iulwtGW0zF5jwZrj5ZGMY4CFQhS6R3/DF5ohzuSwtSOY32cYyLKrUjmpx W0Nj7Pu/UaHU/kTu5+qItgTp0FP6du9p2VnZZuhroGLkGRSi2u392gKmPnKbErx5 8tLo2ucAw1Kasm7pef2rj9M9etcWJws+dD1qWg96fvuKvJQX9+milweyg0I4NTXy p8GHpITZ65chWUJjqlxgnbvhB2V1eKP6bpG3sjzhCC2h9yXyzn4grOoSu/XNVQdx W3ldxRMlsoIFBbUb42yJQROSVezaYVC+5sk+fufRVbNR3b5ZmJOGYiCUtM+MMvtj q/1M/liPOYIf6Ix94EzxujdU12Ki5XLb5rWZqS3Gvebc8OG+o9E= =XWfo -----END PGP SIGNATURE----- Merge tag 'coresight-next-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux into char-misc-next Suzuki writes: coresight: Updates for v6.5 CoreSight and hwtracing subsystem updates for v6.5 includes: - Fixes to the CTI module reference leaks. This involves, redesign of how the helper devices are tracked and CTI devices have been converted to helper devices. - Fix removal of the trctraceidr file from sysfs for ETMs. - Match all ETMv4 instances based on the ETMv4 architected registers and the CoreSight Component ID (CID), than having to add individual PIDs for CPUs. - Add support for Dummy CoreSight source and sink drivers. - Add James Clark as Reviewer for the CoreSight kernel drivers - Fixes to HiSilicon PCIe Tune and Trace Device driver Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com> * tag 'coresight-next-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux: (27 commits) hwtracing: hisi_ptt: Fix potential sleep in atomic context hwtracing: hisi_ptt: Advertise PERF_PMU_CAP_NO_EXCLUDE for PTT PMU hwtracing: hisi_ptt: Export available filters through sysfs hwtracing: hisi_ptt: Add support for dynamically updating the filter list hwtracing: hisi_ptt: Factor out filter allocation and release operation coresight: dummy: Update type of mode parameter in dummy_{sink,source}_enable() Documentation: trace: Add documentation for Coresight Dummy Trace dt-bindings: arm: Add support for Coresight dummy trace Coresight: Add coresight dummy driver MAINTAINERS: coresight: Add James Clark as Reviewer coresight: etm4x: Match all ETM4 instances based on DEVARCH and DEVTYPE coresight: etm4x: Make etm4_remove_dev() return void coresight: etm4x: Fix missing trctraceidr file in sysfs coresight: Fix CTI module refcount leak by making it a helper device coresight: Enable and disable helper devices adjacent to the path coresight: Refactor out buffer allocation function for ETR coresight: Make refcount a property of the connection coresight: Store in-connections as well as out-connections coresight: Simplify connection fixup mechanism coresight: Store pointers to connections rather than an array of them ...
This commit is contained in:
commit
fa50d6b8a5
@ -59,3 +59,55 @@ Description: (RW) Control the allocated buffer watermark of outbound packets.
|
||||
The available tune data is [0, 1, 2]. Writing a negative value
|
||||
will return an error, and out of range values will be converted
|
||||
to 2. The value indicates a probable level of the event.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/root_port_filters
|
||||
Date: May 2023
|
||||
KernelVersion: 6.5
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
Description: This directory contains the files providing the PCIe Root Port filters
|
||||
information used for PTT trace. Each file is named after the supported
|
||||
Root Port device name <domain>:<bus>:<device>.<function>.
|
||||
|
||||
See the description of the "filter" in Documentation/trace/hisi-ptt.rst
|
||||
for more information.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/root_port_filters/multiselect
|
||||
Date: May 2023
|
||||
KernelVersion: 6.5
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
Description: (Read) Indicates if this kind of filter can be selected at the same
|
||||
time as others filters, or must be used on it's own. 1 indicates
|
||||
the former case and 0 indicates the latter.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/root_port_filters/<bdf>
|
||||
Date: May 2023
|
||||
KernelVersion: 6.5
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
Description: (Read) Indicates the filter value of this Root Port filter, which
|
||||
can be used to control the TLP headers to trace by the PTT trace.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/requester_filters
|
||||
Date: May 2023
|
||||
KernelVersion: 6.5
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
Description: This directory contains the files providing the PCIe Requester filters
|
||||
information used for PTT trace. Each file is named after the supported
|
||||
Endpoint device name <domain>:<bus>:<device>.<function>.
|
||||
|
||||
See the description of the "filter" in Documentation/trace/hisi-ptt.rst
|
||||
for more information.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/requester_filters/multiselect
|
||||
Date: May 2023
|
||||
KernelVersion: 6.5
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
Description: (Read) Indicates if this kind of filter can be selected at the same
|
||||
time as others filters, or must be used on it's own. 1 indicates
|
||||
the former case and 0 indicates the latter.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/requester_filters/<bdf>
|
||||
Date: May 2023
|
||||
KernelVersion: 6.5
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
Description: (Read) Indicates the filter value of this Requester filter, which
|
||||
can be used to control the TLP headers to trace by the PTT trace.
|
||||
|
@ -0,0 +1,73 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/arm/arm,coresight-dummy-sink.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM Coresight Dummy sink component
|
||||
|
||||
description: |
|
||||
CoreSight components are compliant with the ARM CoreSight architecture
|
||||
specification and can be connected in various topologies to suit a particular
|
||||
SoCs tracing needs. These trace components can generally be classified as
|
||||
sinks, links and sources. Trace data produced by one or more sources flows
|
||||
through the intermediate links connecting the source to the currently selected
|
||||
sink.
|
||||
|
||||
The Coresight dummy sink component is for the specific coresight sink devices
|
||||
kernel don't have permission to access or configure, e.g., CoreSight EUD on
|
||||
Qualcomm platforms. It is a mini-USB hub implemented to support the USB-based
|
||||
debug and trace capabilities. For this device, a dummy driver is needed to
|
||||
register it as Coresight sink device in kernel side, so that path can be
|
||||
created in the driver. Then the trace flow would be transferred to EUD via
|
||||
coresight link of AP processor. It provides Coresight API for operations on
|
||||
dummy source devices, such as enabling and disabling them. It also provides
|
||||
the Coresight dummy source paths for debugging.
|
||||
|
||||
The primary use case of the coresight dummy sink is to build path in kernel
|
||||
side for dummy sink component.
|
||||
|
||||
maintainers:
|
||||
- Mike Leach <mike.leach@linaro.org>
|
||||
- Suzuki K Poulose <suzuki.poulose@arm.com>
|
||||
- James Clark <james.clark@arm.com>
|
||||
- Mao Jinlong <quic_jinlmao@quicinc.com>
|
||||
- Hao Zhang <quic_hazha@quicinc.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- arm,coresight-dummy-sink
|
||||
|
||||
in-ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port:
|
||||
description: Input connection from the Coresight Trace bus to
|
||||
dummy sink, such as Embedded USB debugger(EUD).
|
||||
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- in-ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
# Minimum dummy sink definition. Dummy sink connect to coresight replicator.
|
||||
- |
|
||||
sink {
|
||||
compatible = "arm,coresight-dummy-sink";
|
||||
|
||||
in-ports {
|
||||
port {
|
||||
eud_in_replicator_swao: endpoint {
|
||||
remote-endpoint = <&replicator_swao_out_eud>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/arm/arm,coresight-dummy-source.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM Coresight Dummy source component
|
||||
|
||||
description: |
|
||||
CoreSight components are compliant with the ARM CoreSight architecture
|
||||
specification and can be connected in various topologies to suit a particular
|
||||
SoCs tracing needs. These trace components can generally be classified as
|
||||
sinks, links and sources. Trace data produced by one or more sources flows
|
||||
through the intermediate links connecting the source to the currently selected
|
||||
sink.
|
||||
|
||||
The Coresight dummy source component is for the specific coresight source
|
||||
devices kernel don't have permission to access or configure. For some SOCs,
|
||||
there would be Coresight source trace components on sub-processor which
|
||||
are conneted to AP processor via debug bus. For these devices, a dummy driver
|
||||
is needed to register them as Coresight source devices, so that paths can be
|
||||
created in the driver. It provides Coresight API for operations on dummy
|
||||
source devices, such as enabling and disabling them. It also provides the
|
||||
Coresight dummy source paths for debugging.
|
||||
|
||||
The primary use case of the coresight dummy source is to build path in kernel
|
||||
side for dummy source component.
|
||||
|
||||
maintainers:
|
||||
- Mike Leach <mike.leach@linaro.org>
|
||||
- Suzuki K Poulose <suzuki.poulose@arm.com>
|
||||
- James Clark <james.clark@arm.com>
|
||||
- Mao Jinlong <quic_jinlmao@quicinc.com>
|
||||
- Hao Zhang <quic_hazha@quicinc.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- arm,coresight-dummy-source
|
||||
|
||||
out-ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port:
|
||||
description: Output connection from the source to Coresight
|
||||
Trace bus.
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- out-ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
# Minimum dummy source definition. Dummy source connect to coresight funnel.
|
||||
- |
|
||||
source {
|
||||
compatible = "arm,coresight-dummy-source";
|
||||
|
||||
out-ports {
|
||||
port {
|
||||
dummy_riscv_out_funnel_swao: endpoint {
|
||||
remote-endpoint = <&funnel_swao_in_dummy_riscv>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -364,6 +364,7 @@ MEM
|
||||
devm_kmalloc_array()
|
||||
devm_kmemdup()
|
||||
devm_krealloc()
|
||||
devm_krealloc_array()
|
||||
devm_kstrdup()
|
||||
devm_kstrdup_const()
|
||||
devm_kvasprintf()
|
||||
|
32
Documentation/trace/coresight/coresight-dummy.rst
Normal file
32
Documentation/trace/coresight/coresight-dummy.rst
Normal file
@ -0,0 +1,32 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=============================
|
||||
Coresight Dummy Trace Module
|
||||
=============================
|
||||
|
||||
:Author: Hao Zhang <quic_hazha@quicinc.com>
|
||||
:Date: June 2023
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Coresight dummy trace module is for the specific devices that kernel don't
|
||||
have permission to access or configure, e.g., CoreSight TPDMs on Qualcomm
|
||||
platforms. For these devices, a dummy driver is needed to register them as
|
||||
Coresight devices. The module may also be used to define components that may
|
||||
not have any programming interfaces, so that paths can be created in the driver.
|
||||
It provides Coresight API for operations on dummy devices, such as enabling and
|
||||
disabling them. It also provides the Coresight dummy sink/source paths for
|
||||
debugging.
|
||||
|
||||
Config details
|
||||
--------------
|
||||
|
||||
There are two types of nodes, dummy sink and dummy source. These nodes
|
||||
are available at ``/sys/bus/coresight/devices``.
|
||||
|
||||
Example output::
|
||||
|
||||
$ ls -l /sys/bus/coresight/devices | grep dummy
|
||||
dummy_sink0 -> ../../../devices/platform/soc@0/soc@0:sink/dummy_sink0
|
||||
dummy_source0 -> ../../../devices/platform/soc@0/soc@0:source/dummy_source0
|
@ -148,14 +148,20 @@ For example, if the desired filter is Endpoint function 0000:01:00.1 the filter
|
||||
value will be 0x00101. If the desired filter is Root Port 0000:00:10.0 then
|
||||
then filter value is calculated as 0x80001.
|
||||
|
||||
The driver also presents every supported Root Port and Requester filter through
|
||||
sysfs. Each filter will be an individual file with name of its related PCIe
|
||||
device name (domain:bus:device.function). The files of Root Port filters are
|
||||
under $(PTT PMU dir)/root_port_filters and files of Requester filters
|
||||
are under $(PTT PMU dir)/requester_filters.
|
||||
|
||||
Note that multiple Root Ports can be specified at one time, but only one
|
||||
Endpoint function can be specified in one trace. Specifying both Root Port
|
||||
and function at the same time is not supported. Driver maintains a list of
|
||||
available filters and will check the invalid inputs.
|
||||
|
||||
Currently the available filters are detected in driver's probe. If the supported
|
||||
devices are removed/added after probe, you may need to reload the driver to update
|
||||
the filters.
|
||||
The available filters will be dynamically updated, which means you will always
|
||||
get correct filter information when hotplug events happen, or when you manually
|
||||
remove/rescan the devices.
|
||||
|
||||
2. Type
|
||||
-------
|
||||
|
@ -2098,6 +2098,7 @@ N: digicolor
|
||||
ARM/CORESIGHT FRAMEWORK AND DRIVERS
|
||||
M: Suzuki K Poulose <suzuki.poulose@arm.com>
|
||||
R: Mike Leach <mike.leach@linaro.org>
|
||||
R: James Clark <james.clark@arm.com>
|
||||
R: Leo Yan <leo.yan@linaro.org>
|
||||
L: coresight@lists.linaro.org (moderated for non-subscribers)
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
@ -236,4 +236,15 @@ config CORESIGHT_TPDA
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called coresight-tpda.
|
||||
|
||||
config CORESIGHT_DUMMY
|
||||
tristate "Dummy driver support"
|
||||
help
|
||||
Enables support for dummy driver. Dummy driver can be used for
|
||||
CoreSight sources/sinks that are owned and configured by some
|
||||
other subsystem and use Linux drivers to configure rest of trace
|
||||
path.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called coresight-dummy.
|
||||
endif
|
||||
|
@ -30,3 +30,4 @@ 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
|
||||
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
|
||||
|
@ -395,13 +395,18 @@ static inline int catu_wait_for_ready(struct catu_drvdata *drvdata)
|
||||
return coresight_timeout(csa, CATU_STATUS, CATU_STATUS_READY, 1);
|
||||
}
|
||||
|
||||
static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
|
||||
static int catu_enable_hw(struct catu_drvdata *drvdata, enum cs_mode cs_mode,
|
||||
void *data)
|
||||
{
|
||||
int rc;
|
||||
u32 control, mode;
|
||||
struct etr_buf *etr_buf = data;
|
||||
struct etr_buf *etr_buf = NULL;
|
||||
struct device *dev = &drvdata->csdev->dev;
|
||||
struct coresight_device *csdev = drvdata->csdev;
|
||||
struct coresight_device *etrdev;
|
||||
union coresight_dev_subtype etr_subtype = {
|
||||
.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM
|
||||
};
|
||||
|
||||
if (catu_wait_for_ready(drvdata))
|
||||
dev_warn(dev, "Timeout while waiting for READY\n");
|
||||
@ -416,6 +421,13 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
etrdev = coresight_find_input_type(
|
||||
csdev->pdata, CORESIGHT_DEV_TYPE_SINK, etr_subtype);
|
||||
if (etrdev) {
|
||||
etr_buf = tmc_etr_get_buffer(etrdev, cs_mode, data);
|
||||
if (IS_ERR(etr_buf))
|
||||
return PTR_ERR(etr_buf);
|
||||
}
|
||||
control |= BIT(CATU_CONTROL_ENABLE);
|
||||
|
||||
if (etr_buf && etr_buf->mode == ETR_MODE_CATU) {
|
||||
@ -441,13 +453,14 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int catu_enable(struct coresight_device *csdev, void *data)
|
||||
static int catu_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data)
|
||||
{
|
||||
int rc;
|
||||
struct catu_drvdata *catu_drvdata = csdev_to_catu_drvdata(csdev);
|
||||
|
||||
CS_UNLOCK(catu_drvdata->base);
|
||||
rc = catu_enable_hw(catu_drvdata, data);
|
||||
rc = catu_enable_hw(catu_drvdata, mode, data);
|
||||
CS_LOCK(catu_drvdata->base);
|
||||
return rc;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
@ -112,40 +113,24 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
|
||||
|
||||
static int coresight_find_link_inport(struct coresight_device *csdev,
|
||||
struct coresight_device *parent)
|
||||
static struct coresight_connection *
|
||||
coresight_find_out_connection(struct coresight_device *src_dev,
|
||||
struct coresight_device *dest_dev)
|
||||
{
|
||||
int i;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
for (i = 0; i < parent->pdata->nr_outport; i++) {
|
||||
conn = &parent->pdata->conns[i];
|
||||
if (conn->child_dev == csdev)
|
||||
return conn->child_port;
|
||||
for (i = 0; i < src_dev->pdata->nr_outconns; i++) {
|
||||
conn = src_dev->pdata->out_conns[i];
|
||||
if (conn->dest_dev == dest_dev)
|
||||
return conn;
|
||||
}
|
||||
|
||||
dev_err(&csdev->dev, "couldn't find inport, parent: %s, child: %s\n",
|
||||
dev_name(&parent->dev), dev_name(&csdev->dev));
|
||||
dev_err(&src_dev->dev,
|
||||
"couldn't find output connection, src_dev: %s, dest_dev: %s\n",
|
||||
dev_name(&src_dev->dev), dev_name(&dest_dev->dev));
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int coresight_find_link_outport(struct coresight_device *csdev,
|
||||
struct coresight_device *child)
|
||||
{
|
||||
int i;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
conn = &csdev->pdata->conns[i];
|
||||
if (conn->child_dev == child)
|
||||
return conn->outport;
|
||||
}
|
||||
|
||||
dev_err(&csdev->dev, "couldn't find outport, parent: %s, child: %s\n",
|
||||
dev_name(&csdev->dev), dev_name(&child->dev));
|
||||
|
||||
return -ENODEV;
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline u32 coresight_read_claim_tags(struct coresight_device *csdev)
|
||||
@ -252,63 +237,47 @@ void coresight_disclaim_device(struct coresight_device *csdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_disclaim_device);
|
||||
|
||||
/* enable or disable an associated CTI device of the supplied CS device */
|
||||
static int
|
||||
coresight_control_assoc_ectdev(struct coresight_device *csdev, bool enable)
|
||||
{
|
||||
int ect_ret = 0;
|
||||
struct coresight_device *ect_csdev = csdev->ect_dev;
|
||||
struct module *mod;
|
||||
|
||||
if (!ect_csdev)
|
||||
return 0;
|
||||
if ((!ect_ops(ect_csdev)->enable) || (!ect_ops(ect_csdev)->disable))
|
||||
return 0;
|
||||
|
||||
mod = ect_csdev->dev.parent->driver->owner;
|
||||
if (enable) {
|
||||
if (try_module_get(mod)) {
|
||||
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev);
|
||||
if (ect_ret) {
|
||||
module_put(mod);
|
||||
} else {
|
||||
get_device(ect_csdev->dev.parent);
|
||||
csdev->ect_enabled = true;
|
||||
}
|
||||
} else
|
||||
ect_ret = -ENODEV;
|
||||
} else {
|
||||
if (csdev->ect_enabled) {
|
||||
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev);
|
||||
put_device(ect_csdev->dev.parent);
|
||||
module_put(mod);
|
||||
csdev->ect_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* output warning if ECT enable is preventing trace operation */
|
||||
if (ect_ret)
|
||||
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
|
||||
dev_name(&ect_csdev->dev),
|
||||
enable ? "enable" : "disable");
|
||||
return ect_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the associated ect / cti device while holding the coresight_mutex
|
||||
* to avoid a race with coresight_enable that may try to use this value.
|
||||
* Add a helper as an output device. This function takes the @coresight_mutex
|
||||
* because it's assumed that it's called from the helper device, outside of the
|
||||
* core code where the mutex would already be held. Don't add new calls to this
|
||||
* from inside the core code, instead try to add the new helper to the DT and
|
||||
* ACPI where it will be picked up and linked automatically.
|
||||
*/
|
||||
void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
|
||||
struct coresight_device *ect_csdev)
|
||||
void coresight_add_helper(struct coresight_device *csdev,
|
||||
struct coresight_device *helper)
|
||||
{
|
||||
int i;
|
||||
struct coresight_connection conn = {};
|
||||
struct coresight_connection *new_conn;
|
||||
|
||||
mutex_lock(&coresight_mutex);
|
||||
csdev->ect_dev = ect_csdev;
|
||||
conn.dest_fwnode = fwnode_handle_get(dev_fwnode(&helper->dev));
|
||||
conn.dest_dev = helper;
|
||||
conn.dest_port = conn.src_port = -1;
|
||||
conn.src_dev = csdev;
|
||||
|
||||
/*
|
||||
* Check for duplicates because this is called every time a helper
|
||||
* device is re-loaded. Existing connections will get re-linked
|
||||
* automatically.
|
||||
*/
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; ++i)
|
||||
if (csdev->pdata->out_conns[i]->dest_fwnode == conn.dest_fwnode)
|
||||
goto unlock;
|
||||
|
||||
new_conn = coresight_add_out_conn(csdev->dev.parent, csdev->pdata,
|
||||
&conn);
|
||||
if (!IS_ERR(new_conn))
|
||||
coresight_add_in_conn(new_conn);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&coresight_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_set_assoc_ectdev_mutex);
|
||||
EXPORT_SYMBOL_GPL(coresight_add_helper);
|
||||
|
||||
static int coresight_enable_sink(struct coresight_device *csdev,
|
||||
u32 mode, void *data)
|
||||
enum cs_mode mode, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -319,14 +288,10 @@ static int coresight_enable_sink(struct coresight_device *csdev,
|
||||
if (!sink_ops(csdev)->enable)
|
||||
return -EINVAL;
|
||||
|
||||
ret = coresight_control_assoc_ectdev(csdev, true);
|
||||
ret = sink_ops(csdev)->enable(csdev, mode, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sink_ops(csdev)->enable(csdev, mode, data);
|
||||
if (ret) {
|
||||
coresight_control_assoc_ectdev(csdev, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
csdev->enable = true;
|
||||
|
||||
return 0;
|
||||
@ -342,7 +307,6 @@ static void coresight_disable_sink(struct coresight_device *csdev)
|
||||
ret = sink_ops(csdev)->disable(csdev);
|
||||
if (ret)
|
||||
return;
|
||||
coresight_control_assoc_ectdev(csdev, false);
|
||||
csdev->enable = false;
|
||||
}
|
||||
|
||||
@ -352,31 +316,25 @@ static int coresight_enable_link(struct coresight_device *csdev,
|
||||
{
|
||||
int ret = 0;
|
||||
int link_subtype;
|
||||
int inport, outport;
|
||||
struct coresight_connection *inconn, *outconn;
|
||||
|
||||
if (!parent || !child)
|
||||
return -EINVAL;
|
||||
|
||||
inport = coresight_find_link_inport(csdev, parent);
|
||||
outport = coresight_find_link_outport(csdev, child);
|
||||
inconn = coresight_find_out_connection(parent, csdev);
|
||||
outconn = coresight_find_out_connection(csdev, child);
|
||||
link_subtype = csdev->subtype.link_subtype;
|
||||
|
||||
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && inport < 0)
|
||||
return inport;
|
||||
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && outport < 0)
|
||||
return outport;
|
||||
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && IS_ERR(inconn))
|
||||
return PTR_ERR(inconn);
|
||||
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && IS_ERR(outconn))
|
||||
return PTR_ERR(outconn);
|
||||
|
||||
if (link_ops(csdev)->enable) {
|
||||
ret = coresight_control_assoc_ectdev(csdev, true);
|
||||
if (!ret) {
|
||||
ret = link_ops(csdev)->enable(csdev, inport, outport);
|
||||
if (ret)
|
||||
coresight_control_assoc_ectdev(csdev, false);
|
||||
}
|
||||
}
|
||||
|
||||
ret = link_ops(csdev)->enable(csdev, inconn, outconn);
|
||||
if (!ret)
|
||||
csdev->enable = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -385,78 +343,125 @@ static void coresight_disable_link(struct coresight_device *csdev,
|
||||
struct coresight_device *parent,
|
||||
struct coresight_device *child)
|
||||
{
|
||||
int i, nr_conns;
|
||||
int i;
|
||||
int link_subtype;
|
||||
int inport, outport;
|
||||
struct coresight_connection *inconn, *outconn;
|
||||
|
||||
if (!parent || !child)
|
||||
return;
|
||||
|
||||
inport = coresight_find_link_inport(csdev, parent);
|
||||
outport = coresight_find_link_outport(csdev, child);
|
||||
inconn = coresight_find_out_connection(parent, csdev);
|
||||
outconn = coresight_find_out_connection(csdev, child);
|
||||
link_subtype = csdev->subtype.link_subtype;
|
||||
|
||||
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
|
||||
nr_conns = csdev->pdata->nr_inport;
|
||||
} else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
|
||||
nr_conns = csdev->pdata->nr_outport;
|
||||
} else {
|
||||
nr_conns = 1;
|
||||
}
|
||||
|
||||
if (link_ops(csdev)->disable) {
|
||||
link_ops(csdev)->disable(csdev, inport, outport);
|
||||
coresight_control_assoc_ectdev(csdev, false);
|
||||
link_ops(csdev)->disable(csdev, inconn, outconn);
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_conns; i++)
|
||||
if (atomic_read(&csdev->refcnt[i]) != 0)
|
||||
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;
|
||||
}
|
||||
|
||||
static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
|
||||
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 = coresight_control_assoc_ectdev(csdev, true);
|
||||
ret = source_ops(csdev)->enable(csdev, data, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = source_ops(csdev)->enable(csdev, NULL, mode);
|
||||
if (ret) {
|
||||
coresight_control_assoc_ectdev(csdev, false);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
csdev->enable = true;
|
||||
}
|
||||
|
||||
atomic_inc(csdev->refcnt);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void coresight_disable_helpers(struct coresight_device *csdev)
|
||||
{
|
||||
int i;
|
||||
struct coresight_device *helper;
|
||||
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
|
||||
helper = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (helper && coresight_is_helper(helper))
|
||||
coresight_disable_helper(helper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* coresight_disable_source - 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(struct coresight_device *csdev)
|
||||
bool coresight_disable_source(struct coresight_device *csdev, void *data)
|
||||
{
|
||||
if (atomic_dec_return(csdev->refcnt) == 0) {
|
||||
if (atomic_dec_return(&csdev->refcnt) == 0) {
|
||||
if (source_ops(csdev)->disable)
|
||||
source_ops(csdev)->disable(csdev, NULL);
|
||||
coresight_control_assoc_ectdev(csdev, false);
|
||||
source_ops(csdev)->disable(csdev, data);
|
||||
coresight_disable_helpers(csdev);
|
||||
csdev->enable = false;
|
||||
}
|
||||
return !csdev->enable;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_disable_source);
|
||||
|
||||
/*
|
||||
* coresight_disable_path_from : Disable components in the given path beyond
|
||||
@ -507,6 +512,9 @@ static void coresight_disable_path_from(struct list_head *path,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable all helpers adjacent along the path last */
|
||||
coresight_disable_helpers(csdev);
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,9 +524,28 @@ void coresight_disable_path(struct list_head *path)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_disable_path);
|
||||
|
||||
int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
|
||||
static int coresight_enable_helpers(struct coresight_device *csdev,
|
||||
enum cs_mode mode, void *data)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct coresight_device *helper;
|
||||
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
|
||||
helper = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (!helper || !coresight_is_helper(helper))
|
||||
continue;
|
||||
|
||||
ret = coresight_enable_helper(helper, mode, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coresight_enable_path(struct list_head *path, enum cs_mode mode,
|
||||
void *sink_data)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 type;
|
||||
struct coresight_node *nd;
|
||||
@ -528,6 +555,10 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
|
||||
csdev = nd->csdev;
|
||||
type = csdev->type;
|
||||
|
||||
/* Enable all helpers adjacent to the path first */
|
||||
ret = coresight_enable_helpers(csdev, mode, sink_data);
|
||||
if (ret)
|
||||
goto err;
|
||||
/*
|
||||
* ETF devices are tricky... They can be a link or a sink,
|
||||
* depending on how they are configured. If an ETF has been
|
||||
@ -602,10 +633,10 @@ coresight_find_enabled_sink(struct coresight_device *csdev)
|
||||
/*
|
||||
* Recursively explore each port found on this element.
|
||||
*/
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
|
||||
struct coresight_device *child_dev;
|
||||
|
||||
child_dev = csdev->pdata->conns[i].child_dev;
|
||||
child_dev = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (child_dev)
|
||||
sink = coresight_find_enabled_sink(child_dev);
|
||||
if (sink)
|
||||
@ -718,11 +749,11 @@ static int coresight_grab_device(struct coresight_device *csdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
|
||||
struct coresight_device *child;
|
||||
|
||||
child = csdev->pdata->conns[i].child_dev;
|
||||
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
|
||||
child = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (child && coresight_is_helper(child))
|
||||
if (!coresight_get_ref(child))
|
||||
goto err;
|
||||
}
|
||||
@ -732,8 +763,8 @@ err:
|
||||
for (i--; i >= 0; i--) {
|
||||
struct coresight_device *child;
|
||||
|
||||
child = csdev->pdata->conns[i].child_dev;
|
||||
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
|
||||
child = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (child && coresight_is_helper(child))
|
||||
coresight_put_ref(child);
|
||||
}
|
||||
return -ENODEV;
|
||||
@ -748,11 +779,11 @@ static void coresight_drop_device(struct coresight_device *csdev)
|
||||
int i;
|
||||
|
||||
coresight_put_ref(csdev);
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
|
||||
struct coresight_device *child;
|
||||
|
||||
child = csdev->pdata->conns[i].child_dev;
|
||||
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
|
||||
child = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (child && coresight_is_helper(child))
|
||||
coresight_put_ref(child);
|
||||
}
|
||||
}
|
||||
@ -790,10 +821,10 @@ static int _coresight_build_path(struct coresight_device *csdev,
|
||||
}
|
||||
|
||||
/* Not a sink - recursively explore each port found on this element */
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
|
||||
struct coresight_device *child_dev;
|
||||
|
||||
child_dev = csdev->pdata->conns[i].child_dev;
|
||||
child_dev = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (child_dev &&
|
||||
_coresight_build_path(child_dev, sink, path) == 0) {
|
||||
found = true;
|
||||
@ -959,11 +990,11 @@ coresight_find_sink(struct coresight_device *csdev, int *depth)
|
||||
* Not a sink we want - or possible child sink may be better.
|
||||
* recursively explore each port found on this element.
|
||||
*/
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
|
||||
struct coresight_device *child_dev, *sink = NULL;
|
||||
int child_depth = curr_depth;
|
||||
|
||||
child_dev = csdev->pdata->conns[i].child_dev;
|
||||
child_dev = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (child_dev)
|
||||
sink = coresight_find_sink(child_dev, &child_depth);
|
||||
|
||||
@ -1093,7 +1124,7 @@ int coresight_enable(struct coresight_device *csdev)
|
||||
* source is already enabled.
|
||||
*/
|
||||
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1114,7 +1145,7 @@ int coresight_enable(struct coresight_device *csdev)
|
||||
if (ret)
|
||||
goto err_path;
|
||||
|
||||
ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
|
||||
ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL);
|
||||
if (ret)
|
||||
goto err_source;
|
||||
|
||||
@ -1171,7 +1202,7 @@ void coresight_disable(struct coresight_device *csdev)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!csdev->enable || !coresight_disable_source(csdev))
|
||||
if (!csdev->enable || !coresight_disable_source(csdev, NULL))
|
||||
goto out;
|
||||
|
||||
switch (csdev->subtype.source_subtype) {
|
||||
@ -1296,18 +1327,16 @@ static struct device_type coresight_dev_type[] = {
|
||||
},
|
||||
{
|
||||
.name = "helper",
|
||||
},
|
||||
{
|
||||
.name = "ect",
|
||||
},
|
||||
}
|
||||
};
|
||||
/* 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);
|
||||
|
||||
fwnode_handle_put(csdev->dev.fwnode);
|
||||
kfree(csdev->refcnt);
|
||||
kfree(csdev);
|
||||
}
|
||||
|
||||
@ -1315,35 +1344,48 @@ static int coresight_orphan_match(struct device *dev, void *data)
|
||||
{
|
||||
int i, ret = 0;
|
||||
bool still_orphan = false;
|
||||
struct coresight_device *csdev, *i_csdev;
|
||||
struct coresight_device *dst_csdev = data;
|
||||
struct coresight_device *src_csdev = to_coresight_device(dev);
|
||||
struct coresight_connection *conn;
|
||||
|
||||
csdev = data;
|
||||
i_csdev = to_coresight_device(dev);
|
||||
|
||||
/* No need to check oneself */
|
||||
if (csdev == i_csdev)
|
||||
return 0;
|
||||
bool fixup_self = (src_csdev == dst_csdev);
|
||||
|
||||
/* Move on to another component if no connection is orphan */
|
||||
if (!i_csdev->orphan)
|
||||
if (!src_csdev->orphan)
|
||||
return 0;
|
||||
/*
|
||||
* Circle throuch all the connection of that component. If we find
|
||||
* an orphan connection whose name matches @csdev, link it.
|
||||
* Circle through all the connections of that component. If we find
|
||||
* an orphan connection whose name matches @dst_csdev, link it.
|
||||
*/
|
||||
for (i = 0; i < i_csdev->pdata->nr_outport; i++) {
|
||||
conn = &i_csdev->pdata->conns[i];
|
||||
for (i = 0; i < src_csdev->pdata->nr_outconns; i++) {
|
||||
conn = src_csdev->pdata->out_conns[i];
|
||||
|
||||
/* Skip the port if FW doesn't describe it */
|
||||
if (!conn->child_fwnode)
|
||||
/* Skip the port if it's already connected. */
|
||||
if (conn->dest_dev)
|
||||
continue;
|
||||
/* We have found at least one orphan connection */
|
||||
if (conn->child_dev == NULL) {
|
||||
|
||||
/*
|
||||
* If we are at the "new" device, which triggered this search,
|
||||
* we must find the remote device from the fwnode in the
|
||||
* connection.
|
||||
*/
|
||||
if (fixup_self)
|
||||
dst_csdev = coresight_find_csdev_by_fwnode(
|
||||
conn->dest_fwnode);
|
||||
|
||||
/* Does it match this newly added device? */
|
||||
if (conn->child_fwnode == csdev->dev.fwnode) {
|
||||
ret = coresight_make_links(i_csdev,
|
||||
conn, csdev);
|
||||
if (dst_csdev && conn->dest_fwnode == dst_csdev->dev.fwnode) {
|
||||
ret = coresight_make_links(src_csdev, conn, dst_csdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Install the device connection. This also indicates that
|
||||
* the links are operational on both ends.
|
||||
*/
|
||||
conn->dest_dev = dst_csdev;
|
||||
conn->src_dev = src_csdev;
|
||||
|
||||
ret = coresight_add_in_conn(conn);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
@ -1351,9 +1393,8 @@ static int coresight_orphan_match(struct device *dev, void *data)
|
||||
still_orphan = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i_csdev->orphan = still_orphan;
|
||||
src_csdev->orphan = still_orphan;
|
||||
|
||||
/*
|
||||
* Returning '0' in case we didn't encounter any error,
|
||||
@ -1368,91 +1409,43 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
|
||||
csdev, coresight_orphan_match);
|
||||
}
|
||||
|
||||
|
||||
static int coresight_fixup_device_conns(struct coresight_device *csdev)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < csdev->pdata->nr_outport; i++) {
|
||||
struct coresight_connection *conn = &csdev->pdata->conns[i];
|
||||
|
||||
if (!conn->child_fwnode)
|
||||
continue;
|
||||
conn->child_dev =
|
||||
coresight_find_csdev_by_fwnode(conn->child_fwnode);
|
||||
if (conn->child_dev && conn->child_dev->has_conns_grp) {
|
||||
ret = coresight_make_links(csdev, conn,
|
||||
conn->child_dev);
|
||||
if (ret)
|
||||
break;
|
||||
} else {
|
||||
csdev->orphan = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coresight_remove_match(struct device *dev, void *data)
|
||||
{
|
||||
int i;
|
||||
struct coresight_device *csdev, *iterator;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
csdev = data;
|
||||
iterator = to_coresight_device(dev);
|
||||
|
||||
/* No need to check oneself */
|
||||
if (csdev == iterator)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Circle throuch all the connection of that component. If we find
|
||||
* a connection whose name matches @csdev, remove it.
|
||||
*/
|
||||
for (i = 0; i < iterator->pdata->nr_outport; i++) {
|
||||
conn = &iterator->pdata->conns[i];
|
||||
|
||||
if (conn->child_dev == NULL || conn->child_fwnode == NULL)
|
||||
continue;
|
||||
|
||||
if (csdev->dev.fwnode == conn->child_fwnode) {
|
||||
iterator->orphan = true;
|
||||
coresight_remove_links(iterator, conn);
|
||||
/*
|
||||
* Drop the reference to the handle for the remote
|
||||
* device acquired in parsing the connections from
|
||||
* platform data.
|
||||
*/
|
||||
fwnode_handle_put(conn->child_fwnode);
|
||||
conn->child_fwnode = NULL;
|
||||
/* No need to continue */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returning '0' ensures that all known component on the
|
||||
* bus will be checked.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* coresight_remove_conns - Remove references to this given devices
|
||||
* from the connections of other devices.
|
||||
*/
|
||||
/* coresight_remove_conns - Remove other device's references to this device */
|
||||
static void coresight_remove_conns(struct coresight_device *csdev)
|
||||
{
|
||||
int i, j;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
/*
|
||||
* Another device will point to this device only if there is
|
||||
* an output port connected to this one. i.e, if the device
|
||||
* doesn't have at least one input port, there is no point
|
||||
* in searching all the devices.
|
||||
* Remove the input connection references from the destination device
|
||||
* for each output connection.
|
||||
*/
|
||||
if (csdev->pdata->nr_inport)
|
||||
bus_for_each_dev(&coresight_bustype, NULL,
|
||||
csdev, coresight_remove_match);
|
||||
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
|
||||
conn = csdev->pdata->out_conns[i];
|
||||
if (!conn->dest_dev)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < conn->dest_dev->pdata->nr_inconns; ++j)
|
||||
if (conn->dest_dev->pdata->in_conns[j] == conn) {
|
||||
conn->dest_dev->pdata->in_conns[j] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For all input connections, remove references to this device.
|
||||
* Connection objects are shared so modifying this device's input
|
||||
* connections affects the other device's output connection.
|
||||
*/
|
||||
for (i = 0; i < csdev->pdata->nr_inconns; ++i) {
|
||||
conn = csdev->pdata->in_conns[i];
|
||||
/* Input conns array is sparse */
|
||||
if (!conn)
|
||||
continue;
|
||||
|
||||
conn->src_dev->orphan = true;
|
||||
coresight_remove_links(conn->src_dev, conn);
|
||||
conn->dest_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1544,24 +1537,27 @@ void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset)
|
||||
* to the output port of this device.
|
||||
*/
|
||||
void coresight_release_platform_data(struct coresight_device *csdev,
|
||||
struct device *dev,
|
||||
struct coresight_platform_data *pdata)
|
||||
{
|
||||
int i;
|
||||
struct coresight_connection *conns = pdata->conns;
|
||||
struct coresight_connection **conns = pdata->out_conns;
|
||||
|
||||
for (i = 0; i < pdata->nr_outport; i++) {
|
||||
for (i = 0; i < pdata->nr_outconns; i++) {
|
||||
/* If we have made the links, remove them now */
|
||||
if (csdev && conns[i].child_dev)
|
||||
coresight_remove_links(csdev, &conns[i]);
|
||||
if (csdev && conns[i]->dest_dev)
|
||||
coresight_remove_links(csdev, conns[i]);
|
||||
/*
|
||||
* Drop the refcount and clear the handle as this device
|
||||
* is going away
|
||||
*/
|
||||
if (conns[i].child_fwnode) {
|
||||
fwnode_handle_put(conns[i].child_fwnode);
|
||||
pdata->conns[i].child_fwnode = NULL;
|
||||
}
|
||||
fwnode_handle_put(conns[i]->dest_fwnode);
|
||||
conns[i]->dest_fwnode = NULL;
|
||||
devm_kfree(dev, conns[i]);
|
||||
}
|
||||
devm_kfree(dev, pdata->out_conns);
|
||||
devm_kfree(dev, pdata->in_conns);
|
||||
devm_kfree(dev, pdata);
|
||||
if (csdev)
|
||||
coresight_remove_conns_sysfs_group(csdev);
|
||||
}
|
||||
@ -1569,9 +1565,6 @@ void coresight_release_platform_data(struct coresight_device *csdev,
|
||||
struct coresight_device *coresight_register(struct coresight_desc *desc)
|
||||
{
|
||||
int ret;
|
||||
int link_subtype;
|
||||
int nr_refcnts = 1;
|
||||
atomic_t *refcnts = NULL;
|
||||
struct coresight_device *csdev;
|
||||
bool registered = false;
|
||||
|
||||
@ -1581,32 +1574,13 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (desc->type == CORESIGHT_DEV_TYPE_LINK ||
|
||||
desc->type == CORESIGHT_DEV_TYPE_LINKSINK) {
|
||||
link_subtype = desc->subtype.link_subtype;
|
||||
|
||||
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
|
||||
nr_refcnts = desc->pdata->nr_inport;
|
||||
else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
|
||||
nr_refcnts = desc->pdata->nr_outport;
|
||||
}
|
||||
|
||||
refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL);
|
||||
if (!refcnts) {
|
||||
ret = -ENOMEM;
|
||||
kfree(csdev);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
csdev->refcnt = refcnts;
|
||||
|
||||
csdev->pdata = desc->pdata;
|
||||
|
||||
csdev->type = desc->type;
|
||||
csdev->subtype = desc->subtype;
|
||||
csdev->ops = desc->ops;
|
||||
csdev->access = desc->access;
|
||||
csdev->orphan = false;
|
||||
csdev->orphan = true;
|
||||
|
||||
csdev->dev.type = &coresight_dev_type[desc->type];
|
||||
csdev->dev.groups = desc->groups;
|
||||
@ -1656,8 +1630,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
||||
registered = true;
|
||||
|
||||
ret = coresight_create_conns_sysfs_group(csdev);
|
||||
if (!ret)
|
||||
ret = coresight_fixup_device_conns(csdev);
|
||||
if (!ret)
|
||||
ret = coresight_fixup_orphan_conns(csdev);
|
||||
|
||||
@ -1678,7 +1650,7 @@ out_unlock:
|
||||
|
||||
err_out:
|
||||
/* Cleanup the connection information */
|
||||
coresight_release_platform_data(NULL, desc->pdata);
|
||||
coresight_release_platform_data(NULL, desc->dev, desc->pdata);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_register);
|
||||
@ -1691,7 +1663,7 @@ void coresight_unregister(struct coresight_device *csdev)
|
||||
cti_assoc_ops->remove(csdev);
|
||||
coresight_remove_conns(csdev);
|
||||
coresight_clear_default_sink(csdev);
|
||||
coresight_release_platform_data(csdev, csdev->pdata);
|
||||
coresight_release_platform_data(csdev, csdev->dev.parent, csdev->pdata);
|
||||
device_unregister(&csdev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_unregister);
|
||||
@ -1714,6 +1686,69 @@ static inline int coresight_search_device_idx(struct coresight_dev_list *dict,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static bool coresight_compare_type(enum coresight_dev_type type_a,
|
||||
union coresight_dev_subtype subtype_a,
|
||||
enum coresight_dev_type type_b,
|
||||
union coresight_dev_subtype subtype_b)
|
||||
{
|
||||
if (type_a != type_b)
|
||||
return false;
|
||||
|
||||
switch (type_a) {
|
||||
case CORESIGHT_DEV_TYPE_SINK:
|
||||
return subtype_a.sink_subtype == subtype_b.sink_subtype;
|
||||
case CORESIGHT_DEV_TYPE_LINK:
|
||||
return subtype_a.link_subtype == subtype_b.link_subtype;
|
||||
case CORESIGHT_DEV_TYPE_LINKSINK:
|
||||
return subtype_a.link_subtype == subtype_b.link_subtype &&
|
||||
subtype_a.sink_subtype == subtype_b.sink_subtype;
|
||||
case CORESIGHT_DEV_TYPE_SOURCE:
|
||||
return subtype_a.source_subtype == subtype_b.source_subtype;
|
||||
case CORESIGHT_DEV_TYPE_HELPER:
|
||||
return subtype_a.helper_subtype == subtype_b.helper_subtype;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct coresight_device *
|
||||
coresight_find_input_type(struct coresight_platform_data *pdata,
|
||||
enum coresight_dev_type type,
|
||||
union coresight_dev_subtype subtype)
|
||||
{
|
||||
int i;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
for (i = 0; i < pdata->nr_inconns; ++i) {
|
||||
conn = pdata->in_conns[i];
|
||||
if (conn &&
|
||||
coresight_compare_type(type, subtype, conn->src_dev->type,
|
||||
conn->src_dev->subtype))
|
||||
return conn->src_dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_find_input_type);
|
||||
|
||||
struct coresight_device *
|
||||
coresight_find_output_type(struct coresight_platform_data *pdata,
|
||||
enum coresight_dev_type type,
|
||||
union coresight_dev_subtype subtype)
|
||||
{
|
||||
int i;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
for (i = 0; i < pdata->nr_outconns; ++i) {
|
||||
conn = pdata->out_conns[i];
|
||||
if (conn->dest_dev &&
|
||||
coresight_compare_type(type, subtype, conn->dest_dev->type,
|
||||
conn->dest_dev->subtype))
|
||||
return conn->dest_dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_find_output_type);
|
||||
|
||||
bool coresight_loses_context_with_cpu(struct device *dev)
|
||||
{
|
||||
return fwnode_property_present(dev_fwnode(dev),
|
||||
|
@ -555,7 +555,10 @@ static void cti_add_assoc_to_csdev(struct coresight_device *csdev)
|
||||
mutex_lock(&ect_mutex);
|
||||
|
||||
/* exit if current is an ECT device.*/
|
||||
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
|
||||
if ((csdev->type == CORESIGHT_DEV_TYPE_HELPER &&
|
||||
csdev->subtype.helper_subtype ==
|
||||
CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI) ||
|
||||
list_empty(&ect_net))
|
||||
goto cti_add_done;
|
||||
|
||||
/* if we didn't find the csdev previously we used the fwnode name */
|
||||
@ -571,8 +574,7 @@ static void cti_add_assoc_to_csdev(struct coresight_device *csdev)
|
||||
* if we found a matching csdev then update the ECT
|
||||
* association pointer for the device with this CTI.
|
||||
*/
|
||||
coresight_set_assoc_ectdev_mutex(csdev,
|
||||
ect_item->csdev);
|
||||
coresight_add_helper(csdev, ect_item->csdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -582,17 +584,23 @@ cti_add_done:
|
||||
|
||||
/*
|
||||
* Removing the associated devices is easier.
|
||||
* A CTI will not have a value for csdev->ect_dev.
|
||||
*/
|
||||
static void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
|
||||
{
|
||||
struct cti_drvdata *ctidrv;
|
||||
struct cti_trig_con *tc;
|
||||
struct cti_device *ctidev;
|
||||
union coresight_dev_subtype cti_subtype = {
|
||||
.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI
|
||||
};
|
||||
struct coresight_device *cti_csdev = coresight_find_output_type(
|
||||
csdev->pdata, CORESIGHT_DEV_TYPE_HELPER, cti_subtype);
|
||||
|
||||
if (!cti_csdev)
|
||||
return;
|
||||
|
||||
mutex_lock(&ect_mutex);
|
||||
if (csdev->ect_dev) {
|
||||
ctidrv = csdev_to_cti_drvdata(csdev->ect_dev);
|
||||
ctidrv = csdev_to_cti_drvdata(cti_csdev);
|
||||
ctidev = &ctidrv->ctidev;
|
||||
list_for_each_entry(tc, &ctidev->trig_cons, node) {
|
||||
if (tc->con_dev == csdev) {
|
||||
@ -601,8 +609,6 @@ static void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
csdev->ect_dev = NULL;
|
||||
}
|
||||
mutex_unlock(&ect_mutex);
|
||||
}
|
||||
|
||||
@ -630,7 +636,7 @@ static void cti_update_conn_xrefs(struct cti_drvdata *drvdata)
|
||||
/* if we can set the sysfs link */
|
||||
if (cti_add_sysfs_link(drvdata, tc))
|
||||
/* set the CTI/csdev association */
|
||||
coresight_set_assoc_ectdev_mutex(tc->con_dev,
|
||||
coresight_add_helper(tc->con_dev,
|
||||
drvdata->csdev);
|
||||
else
|
||||
/* otherwise remove reference from CTI */
|
||||
@ -646,8 +652,6 @@ static void cti_remove_conn_xrefs(struct cti_drvdata *drvdata)
|
||||
|
||||
list_for_each_entry(tc, &ctidev->trig_cons, node) {
|
||||
if (tc->con_dev) {
|
||||
coresight_set_assoc_ectdev_mutex(tc->con_dev,
|
||||
NULL);
|
||||
cti_remove_sysfs_link(drvdata, tc);
|
||||
tc->con_dev = NULL;
|
||||
}
|
||||
@ -795,27 +799,27 @@ static void cti_pm_release(struct cti_drvdata *drvdata)
|
||||
}
|
||||
|
||||
/** cti ect operations **/
|
||||
int cti_enable(struct coresight_device *csdev)
|
||||
int cti_enable(struct coresight_device *csdev, enum cs_mode mode, void *data)
|
||||
{
|
||||
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
|
||||
|
||||
return cti_enable_hw(drvdata);
|
||||
}
|
||||
|
||||
int cti_disable(struct coresight_device *csdev)
|
||||
int cti_disable(struct coresight_device *csdev, void *data)
|
||||
{
|
||||
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
|
||||
|
||||
return cti_disable_hw(drvdata);
|
||||
}
|
||||
|
||||
static const struct coresight_ops_ect cti_ops_ect = {
|
||||
static const struct coresight_ops_helper cti_ops_ect = {
|
||||
.enable = cti_enable,
|
||||
.disable = cti_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops cti_ops = {
|
||||
.ect_ops = &cti_ops_ect,
|
||||
.helper_ops = &cti_ops_ect,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -922,8 +926,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
/* set up coresight component description */
|
||||
cti_desc.pdata = pdata;
|
||||
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
|
||||
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
|
||||
cti_desc.type = CORESIGHT_DEV_TYPE_HELPER;
|
||||
cti_desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI;
|
||||
cti_desc.ops = &cti_ops;
|
||||
cti_desc.groups = drvdata->ctidev.con_groups;
|
||||
cti_desc.dev = dev;
|
||||
|
@ -112,11 +112,11 @@ static ssize_t enable_store(struct device *dev,
|
||||
ret = pm_runtime_resume_and_get(dev->parent);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = cti_enable(drvdata->csdev);
|
||||
ret = cti_enable(drvdata->csdev, CS_MODE_SYSFS, NULL);
|
||||
if (ret)
|
||||
pm_runtime_put(dev->parent);
|
||||
} else {
|
||||
ret = cti_disable(drvdata->csdev);
|
||||
ret = cti_disable(drvdata->csdev, NULL);
|
||||
if (!ret)
|
||||
pm_runtime_put(dev->parent);
|
||||
}
|
||||
|
@ -215,8 +215,8 @@ int cti_add_connection_entry(struct device *dev, struct cti_drvdata *drvdata,
|
||||
const char *assoc_dev_name);
|
||||
struct cti_trig_con *cti_allocate_trig_con(struct device *dev, int in_sigs,
|
||||
int out_sigs);
|
||||
int cti_enable(struct coresight_device *csdev);
|
||||
int cti_disable(struct coresight_device *csdev);
|
||||
int cti_enable(struct coresight_device *csdev, enum cs_mode mode, void *data);
|
||||
int cti_disable(struct coresight_device *csdev, void *data);
|
||||
void cti_write_all_hw_regs(struct cti_drvdata *drvdata);
|
||||
void cti_write_intack(struct device *dev, u32 ackval);
|
||||
void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
|
||||
|
163
drivers/hwtracing/coresight/coresight-dummy.c
Normal file
163
drivers/hwtracing/coresight/coresight-dummy.c
Normal file
@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
struct dummy_drvdata {
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
};
|
||||
|
||||
DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source");
|
||||
DEFINE_CORESIGHT_DEVLIST(sink_devs, "dummy_sink");
|
||||
|
||||
static int dummy_source_enable(struct coresight_device *csdev,
|
||||
struct perf_event *event, enum cs_mode mode)
|
||||
{
|
||||
dev_dbg(csdev->dev.parent, "Dummy source enabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dummy_source_disable(struct coresight_device *csdev,
|
||||
struct perf_event *event)
|
||||
{
|
||||
dev_dbg(csdev->dev.parent, "Dummy source disabled\n");
|
||||
}
|
||||
|
||||
static int dummy_sink_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data)
|
||||
{
|
||||
dev_dbg(csdev->dev.parent, "Dummy sink enabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_sink_disable(struct coresight_device *csdev)
|
||||
{
|
||||
dev_dbg(csdev->dev.parent, "Dummy sink disabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct coresight_ops_source dummy_source_ops = {
|
||||
.enable = dummy_source_enable,
|
||||
.disable = dummy_source_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops dummy_source_cs_ops = {
|
||||
.source_ops = &dummy_source_ops,
|
||||
};
|
||||
|
||||
static const struct coresight_ops_sink dummy_sink_ops = {
|
||||
.enable = dummy_sink_enable,
|
||||
.disable = dummy_sink_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops dummy_sink_cs_ops = {
|
||||
.sink_ops = &dummy_sink_ops,
|
||||
};
|
||||
|
||||
static int dummy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct coresight_platform_data *pdata;
|
||||
struct dummy_drvdata *drvdata;
|
||||
struct coresight_desc desc = { 0 };
|
||||
|
||||
if (of_device_is_compatible(node, "arm,coresight-dummy-source")) {
|
||||
|
||||
desc.name = coresight_alloc_device_name(&source_devs, dev);
|
||||
if (!desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
desc.subtype.source_subtype =
|
||||
CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS;
|
||||
desc.ops = &dummy_source_cs_ops;
|
||||
} else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) {
|
||||
desc.name = coresight_alloc_device_name(&sink_devs, dev);
|
||||
if (!desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
desc.type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_DUMMY;
|
||||
desc.ops = &dummy_sink_cs_ops;
|
||||
} else {
|
||||
dev_err(dev, "Device type not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = coresight_get_platform_data(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
pdev->dev.platform_data = pdata;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
desc.pdata = pdev->dev.platform_data;
|
||||
desc.dev = &pdev->dev;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
dev_dbg(dev, "Dummy device initialized\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dummy_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dummy_match[] = {
|
||||
{.compatible = "arm,coresight-dummy-source"},
|
||||
{.compatible = "arm,coresight-dummy-sink"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver dummy_driver = {
|
||||
.probe = dummy_probe,
|
||||
.remove = dummy_remove,
|
||||
.driver = {
|
||||
.name = "coresight-dummy",
|
||||
.of_match_table = dummy_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dummy_init(void)
|
||||
{
|
||||
return platform_driver_register(&dummy_driver);
|
||||
}
|
||||
module_init(dummy_init);
|
||||
|
||||
static void __exit dummy_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&dummy_driver);
|
||||
}
|
||||
module_exit(dummy_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("CoreSight dummy driver");
|
@ -163,7 +163,7 @@ static int etb_enable_sysfs(struct coresight_device *csdev)
|
||||
drvdata->mode = CS_MODE_SYSFS;
|
||||
}
|
||||
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return ret;
|
||||
@ -199,7 +199,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
|
||||
* use for this session.
|
||||
*/
|
||||
if (drvdata->pid == pid) {
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
|
||||
/* Associate with monitored process. */
|
||||
drvdata->pid = pid;
|
||||
drvdata->mode = CS_MODE_PERF;
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -225,7 +225,8 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etb_enable(struct coresight_device *csdev, u32 mode, void *data)
|
||||
static int etb_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -355,7 +356,7 @@ static int etb_disable(struct coresight_device *csdev)
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
if (atomic_dec_return(csdev->refcnt)) {
|
||||
if (atomic_dec_return(&csdev->refcnt)) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -446,7 +447,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 (atomic_read(&csdev->refcnt) != 1)
|
||||
goto out;
|
||||
|
||||
__etb_disable_hw(drvdata);
|
||||
|
@ -493,7 +493,7 @@ static void etm_event_start(struct perf_event *event, int flags)
|
||||
goto fail_end_stop;
|
||||
|
||||
/* Finally enable the tracer */
|
||||
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
|
||||
if (coresight_enable_source(csdev, CS_MODE_PERF, event))
|
||||
goto fail_disable_path;
|
||||
|
||||
/*
|
||||
@ -587,7 +587,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;
|
||||
|
@ -552,8 +552,8 @@ unlock_enable_sysfs:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etm_enable(struct coresight_device *csdev,
|
||||
struct perf_event *event, u32 mode)
|
||||
static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
@ -671,7 +671,7 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
|
||||
static void etm_disable(struct coresight_device *csdev,
|
||||
struct perf_event *event)
|
||||
{
|
||||
u32 mode;
|
||||
enum cs_mode mode;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
/*
|
||||
|
@ -822,8 +822,8 @@ unlock_sysfs_enable:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etm4_enable(struct coresight_device *csdev,
|
||||
struct perf_event *event, u32 mode)
|
||||
static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
@ -989,7 +989,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
|
||||
static void etm4_disable(struct coresight_device *csdev,
|
||||
struct perf_event *event)
|
||||
{
|
||||
u32 mode;
|
||||
enum cs_mode mode;
|
||||
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
/*
|
||||
@ -2190,7 +2190,7 @@ static void clear_etmdrvdata(void *info)
|
||||
per_cpu(delayed_probe, cpu) = NULL;
|
||||
}
|
||||
|
||||
static int __exit etm4_remove_dev(struct etmv4_drvdata *drvdata)
|
||||
static void __exit etm4_remove_dev(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
bool had_delayed_probe;
|
||||
/*
|
||||
@ -2217,8 +2217,6 @@ static int __exit etm4_remove_dev(struct etmv4_drvdata *drvdata)
|
||||
cscfg_unregister_csdev(drvdata->csdev);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit etm4_remove_amba(struct amba_device *adev)
|
||||
@ -2231,13 +2229,12 @@ static void __exit etm4_remove_amba(struct amba_device *adev)
|
||||
|
||||
static int __exit etm4_remove_platform_dev(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct etmv4_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (drvdata)
|
||||
ret = etm4_remove_dev(drvdata);
|
||||
etm4_remove_dev(drvdata);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct amba_id etm4_ids[] = {
|
||||
@ -2260,6 +2257,11 @@ static const struct amba_id etm4_ids[] = {
|
||||
CS_AMBA_UCI_ID(0x000cc0af, uci_id_etm4),/* Marvell ThunderX2 */
|
||||
CS_AMBA_UCI_ID(0x000b6d01, uci_id_etm4),/* HiSilicon-Hip08 */
|
||||
CS_AMBA_UCI_ID(0x000b6d02, uci_id_etm4),/* HiSilicon-Hip09 */
|
||||
/*
|
||||
* Match all PIDs with ETM4 DEVARCH. No need for adding any of the new
|
||||
* CPUs to the list here.
|
||||
*/
|
||||
CS_AMBA_MATCH_ALL_UCI(uci_id_etm4),
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -2411,7 +2411,6 @@ static ssize_t trctraceid_show(struct device *dev,
|
||||
|
||||
return sysfs_emit(buf, "0x%x\n", trace_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(trctraceid);
|
||||
|
||||
struct etmv4_reg {
|
||||
struct coresight_device *csdev;
|
||||
@ -2528,13 +2527,23 @@ coresight_etm4x_attr_reg_implemented(struct kobject *kobj,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define coresight_etm4x_reg(name, offset) \
|
||||
/*
|
||||
* Macro to set an RO ext attribute with offset and show function.
|
||||
* Offset is used in mgmt group to ensure only correct registers for
|
||||
* the ETM / ETE variant are visible.
|
||||
*/
|
||||
#define coresight_etm4x_reg_showfn(name, offset, showfn) ( \
|
||||
&((struct dev_ext_attribute[]) { \
|
||||
{ \
|
||||
__ATTR(name, 0444, coresight_etm4x_reg_show, NULL), \
|
||||
__ATTR(name, 0444, showfn, NULL), \
|
||||
(void *)(unsigned long)offset \
|
||||
} \
|
||||
})[0].attr.attr
|
||||
})[0].attr.attr \
|
||||
)
|
||||
|
||||
/* macro using the default coresight_etm4x_reg_show function */
|
||||
#define coresight_etm4x_reg(name, offset) \
|
||||
coresight_etm4x_reg_showfn(name, offset, coresight_etm4x_reg_show)
|
||||
|
||||
static struct attribute *coresight_etmv4_mgmt_attrs[] = {
|
||||
coresight_etm4x_reg(trcpdcr, TRCPDCR),
|
||||
@ -2549,7 +2558,7 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = {
|
||||
coresight_etm4x_reg(trcpidr3, TRCPIDR3),
|
||||
coresight_etm4x_reg(trcoslsr, TRCOSLSR),
|
||||
coresight_etm4x_reg(trcconfig, TRCCONFIGR),
|
||||
&dev_attr_trctraceid.attr,
|
||||
coresight_etm4x_reg_showfn(trctraceid, TRCTRACEIDR, trctraceid_show),
|
||||
coresight_etm4x_reg(trcdevarch, TRCDEVARCH),
|
||||
NULL,
|
||||
};
|
||||
|
@ -74,8 +74,9 @@ done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int funnel_enable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
static int funnel_enable(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
int rc = 0;
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
@ -83,18 +84,19 @@ static int funnel_enable(struct coresight_device *csdev, int inport,
|
||||
bool first_enable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (atomic_read(&csdev->refcnt[inport]) == 0) {
|
||||
if (atomic_read(&in->dest_refcnt) == 0) {
|
||||
if (drvdata->base)
|
||||
rc = dynamic_funnel_enable_hw(drvdata, inport);
|
||||
rc = dynamic_funnel_enable_hw(drvdata, in->dest_port);
|
||||
if (!rc)
|
||||
first_enable = true;
|
||||
}
|
||||
if (!rc)
|
||||
atomic_inc(&csdev->refcnt[inport]);
|
||||
atomic_inc(&in->dest_refcnt);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (first_enable)
|
||||
dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport);
|
||||
dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n",
|
||||
in->dest_port);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -117,23 +119,25 @@ static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata,
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void funnel_disable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
static void funnel_disable(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
unsigned long flags;
|
||||
bool last_disable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (atomic_dec_return(&csdev->refcnt[inport]) == 0) {
|
||||
if (atomic_dec_return(&in->dest_refcnt) == 0) {
|
||||
if (drvdata->base)
|
||||
dynamic_funnel_disable_hw(drvdata, inport);
|
||||
dynamic_funnel_disable_hw(drvdata, in->dest_port);
|
||||
last_disable = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (last_disable)
|
||||
dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport);
|
||||
dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n",
|
||||
in->dest_port);
|
||||
}
|
||||
|
||||
static const struct coresight_ops_link funnel_link_ops = {
|
||||
|
@ -19,22 +19,85 @@
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
/*
|
||||
* coresight_alloc_conns: Allocate connections record for each output
|
||||
* port from the device.
|
||||
* Add an entry to the connection list and assign @conn's contents to it.
|
||||
*
|
||||
* If the output port is already assigned on this device, return -EINVAL
|
||||
*/
|
||||
static int coresight_alloc_conns(struct device *dev,
|
||||
struct coresight_platform_data *pdata)
|
||||
struct coresight_connection *
|
||||
coresight_add_out_conn(struct device *dev,
|
||||
struct coresight_platform_data *pdata,
|
||||
const struct coresight_connection *new_conn)
|
||||
{
|
||||
if (pdata->nr_outport) {
|
||||
pdata->conns = devm_kcalloc(dev, pdata->nr_outport,
|
||||
sizeof(*pdata->conns), GFP_KERNEL);
|
||||
if (!pdata->conns)
|
||||
return -ENOMEM;
|
||||
int i;
|
||||
struct coresight_connection *conn;
|
||||
|
||||
/*
|
||||
* Warn on any existing duplicate output port.
|
||||
*/
|
||||
for (i = 0; i < pdata->nr_outconns; ++i) {
|
||||
conn = pdata->out_conns[i];
|
||||
/* Output == -1 means ignore the port for example for helpers */
|
||||
if (conn->src_port != -1 &&
|
||||
conn->src_port == new_conn->src_port) {
|
||||
dev_warn(dev, "Duplicate output port %d\n",
|
||||
conn->src_port);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
pdata->nr_outconns++;
|
||||
pdata->out_conns =
|
||||
devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns,
|
||||
sizeof(*pdata->out_conns), GFP_KERNEL);
|
||||
if (!pdata->out_conns)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
conn = devm_kmalloc(dev, sizeof(struct coresight_connection),
|
||||
GFP_KERNEL);
|
||||
if (!conn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Copy the new connection into the allocation, save the pointer to the
|
||||
* end of the connection array and also return it in case it needs to be
|
||||
* used right away.
|
||||
*/
|
||||
*conn = *new_conn;
|
||||
pdata->out_conns[pdata->nr_outconns - 1] = conn;
|
||||
return conn;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_add_out_conn);
|
||||
|
||||
/*
|
||||
* Add an input connection reference to @out_conn in the target's in_conns array
|
||||
*
|
||||
* @out_conn: Existing output connection to store as an input on the
|
||||
* connection's remote device.
|
||||
*/
|
||||
int coresight_add_in_conn(struct coresight_connection *out_conn)
|
||||
{
|
||||
int i;
|
||||
struct device *dev = out_conn->dest_dev->dev.parent;
|
||||
struct coresight_platform_data *pdata = out_conn->dest_dev->pdata;
|
||||
|
||||
for (i = 0; i < pdata->nr_inconns; ++i)
|
||||
if (!pdata->in_conns[i]) {
|
||||
pdata->in_conns[i] = out_conn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pdata->nr_inconns++;
|
||||
pdata->in_conns =
|
||||
devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns,
|
||||
sizeof(*pdata->in_conns), GFP_KERNEL);
|
||||
if (!pdata->in_conns)
|
||||
return -ENOMEM;
|
||||
pdata->in_conns[pdata->nr_inconns - 1] = out_conn;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_add_in_conn);
|
||||
|
||||
static struct device *
|
||||
coresight_find_device_by_fwnode(struct fwnode_handle *fwnode)
|
||||
@ -83,41 +146,6 @@ static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep)
|
||||
return of_property_read_bool(ep, "slave-mode");
|
||||
}
|
||||
|
||||
static void of_coresight_get_ports_legacy(const struct device_node *node,
|
||||
int *nr_inport, int *nr_outport)
|
||||
{
|
||||
struct device_node *ep = NULL;
|
||||
struct of_endpoint endpoint;
|
||||
int in = 0, out = 0;
|
||||
|
||||
/*
|
||||
* Avoid warnings in of_graph_get_next_endpoint()
|
||||
* if the device doesn't have any graph connections
|
||||
*/
|
||||
if (!of_graph_is_present(node))
|
||||
return;
|
||||
do {
|
||||
ep = of_graph_get_next_endpoint(node, ep);
|
||||
if (!ep)
|
||||
break;
|
||||
|
||||
if (of_graph_parse_endpoint(ep, &endpoint))
|
||||
continue;
|
||||
|
||||
if (of_coresight_legacy_ep_is_input(ep)) {
|
||||
in = (endpoint.port + 1 > in) ?
|
||||
endpoint.port + 1 : in;
|
||||
} else {
|
||||
out = (endpoint.port + 1) > out ?
|
||||
endpoint.port + 1 : out;
|
||||
}
|
||||
|
||||
} while (ep);
|
||||
|
||||
*nr_inport = in;
|
||||
*nr_outport = out;
|
||||
}
|
||||
|
||||
static struct device_node *of_coresight_get_port_parent(struct device_node *ep)
|
||||
{
|
||||
struct device_node *parent = of_graph_get_port_parent(ep);
|
||||
@ -133,59 +161,12 @@ static struct device_node *of_coresight_get_port_parent(struct device_node *ep)
|
||||
return parent;
|
||||
}
|
||||
|
||||
static inline struct device_node *
|
||||
of_coresight_get_input_ports_node(const struct device_node *node)
|
||||
{
|
||||
return of_get_child_by_name(node, "in-ports");
|
||||
}
|
||||
|
||||
static inline struct device_node *
|
||||
of_coresight_get_output_ports_node(const struct device_node *node)
|
||||
{
|
||||
return of_get_child_by_name(node, "out-ports");
|
||||
}
|
||||
|
||||
static inline int
|
||||
of_coresight_count_ports(struct device_node *port_parent)
|
||||
{
|
||||
int i = 0;
|
||||
struct device_node *ep = NULL;
|
||||
struct of_endpoint endpoint;
|
||||
|
||||
while ((ep = of_graph_get_next_endpoint(port_parent, ep))) {
|
||||
/* Defer error handling to parsing */
|
||||
if (of_graph_parse_endpoint(ep, &endpoint))
|
||||
continue;
|
||||
if (endpoint.port + 1 > i)
|
||||
i = endpoint.port + 1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void of_coresight_get_ports(const struct device_node *node,
|
||||
int *nr_inport, int *nr_outport)
|
||||
{
|
||||
struct device_node *input_ports = NULL, *output_ports = NULL;
|
||||
|
||||
input_ports = of_coresight_get_input_ports_node(node);
|
||||
output_ports = of_coresight_get_output_ports_node(node);
|
||||
|
||||
if (input_ports || output_ports) {
|
||||
if (input_ports) {
|
||||
*nr_inport = of_coresight_count_ports(input_ports);
|
||||
of_node_put(input_ports);
|
||||
}
|
||||
if (output_ports) {
|
||||
*nr_outport = of_coresight_count_ports(output_ports);
|
||||
of_node_put(output_ports);
|
||||
}
|
||||
} else {
|
||||
/* Fall back to legacy DT bindings parsing */
|
||||
of_coresight_get_ports_legacy(node, nr_inport, nr_outport);
|
||||
}
|
||||
}
|
||||
|
||||
static int of_coresight_get_cpu(struct device *dev)
|
||||
{
|
||||
int cpu;
|
||||
@ -206,7 +187,7 @@ static int of_coresight_get_cpu(struct device *dev)
|
||||
|
||||
/*
|
||||
* of_coresight_parse_endpoint : Parse the given output endpoint @ep
|
||||
* and fill the connection information in @conn
|
||||
* and fill the connection information in @pdata->out_conns
|
||||
*
|
||||
* Parses the local port, remote device name and the remote port.
|
||||
*
|
||||
@ -224,7 +205,8 @@ static int of_coresight_parse_endpoint(struct device *dev,
|
||||
struct device_node *rep = NULL;
|
||||
struct device *rdev = NULL;
|
||||
struct fwnode_handle *rdev_fwnode;
|
||||
struct coresight_connection *conn;
|
||||
struct coresight_connection conn = {};
|
||||
struct coresight_connection *new_conn;
|
||||
|
||||
do {
|
||||
/* Parse the local port details */
|
||||
@ -251,14 +233,7 @@ static int of_coresight_parse_endpoint(struct device *dev,
|
||||
break;
|
||||
}
|
||||
|
||||
conn = &pdata->conns[endpoint.port];
|
||||
if (conn->child_fwnode) {
|
||||
dev_warn(dev, "Duplicate output port %d\n",
|
||||
endpoint.port);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
conn->outport = endpoint.port;
|
||||
conn.src_port = endpoint.port;
|
||||
/*
|
||||
* Hold the refcount to the target device. This could be
|
||||
* released via:
|
||||
@ -267,8 +242,14 @@ static int of_coresight_parse_endpoint(struct device *dev,
|
||||
* 2) While removing the target device via
|
||||
* coresight_remove_match()
|
||||
*/
|
||||
conn->child_fwnode = fwnode_handle_get(rdev_fwnode);
|
||||
conn->child_port = rendpoint.port;
|
||||
conn.dest_fwnode = fwnode_handle_get(rdev_fwnode);
|
||||
conn.dest_port = rendpoint.port;
|
||||
|
||||
new_conn = coresight_add_out_conn(dev, pdata, &conn);
|
||||
if (IS_ERR_VALUE(new_conn)) {
|
||||
fwnode_handle_put(conn.dest_fwnode);
|
||||
return PTR_ERR(new_conn);
|
||||
}
|
||||
/* Connection record updated */
|
||||
} while (0);
|
||||
|
||||
@ -288,17 +269,6 @@ static int of_get_coresight_platform_data(struct device *dev,
|
||||
bool legacy_binding = false;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
/* Get the number of input and output port for this component */
|
||||
of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport);
|
||||
|
||||
/* If there are no output connections, we are done */
|
||||
if (!pdata->nr_outport)
|
||||
return 0;
|
||||
|
||||
ret = coresight_alloc_conns(dev, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parent = of_coresight_get_output_ports_node(node);
|
||||
/*
|
||||
* If the DT uses obsoleted bindings, the ports are listed
|
||||
@ -306,6 +276,12 @@ static int of_get_coresight_platform_data(struct device *dev,
|
||||
* ports.
|
||||
*/
|
||||
if (!parent) {
|
||||
/*
|
||||
* Avoid warnings in of_graph_get_next_endpoint()
|
||||
* if the device doesn't have any graph connections
|
||||
*/
|
||||
if (!of_graph_is_present(node))
|
||||
return 0;
|
||||
legacy_binding = true;
|
||||
parent = node;
|
||||
dev_warn_once(dev, "Uses obsolete Coresight DT bindings\n");
|
||||
@ -649,8 +625,8 @@ static int acpi_coresight_parse_link(struct acpi_device *adev,
|
||||
|
||||
dir = fields[3].integer.value;
|
||||
if (dir == ACPI_CORESIGHT_LINK_MASTER) {
|
||||
conn->outport = fields[0].integer.value;
|
||||
conn->child_port = fields[1].integer.value;
|
||||
conn->src_port = fields[0].integer.value;
|
||||
conn->dest_port = fields[1].integer.value;
|
||||
rdev = coresight_find_device_by_fwnode(&r_adev->fwnode);
|
||||
if (!rdev)
|
||||
return -EPROBE_DEFER;
|
||||
@ -662,14 +638,14 @@ static int acpi_coresight_parse_link(struct acpi_device *adev,
|
||||
* 2) While removing the target device via
|
||||
* coresight_remove_match().
|
||||
*/
|
||||
conn->child_fwnode = fwnode_handle_get(&r_adev->fwnode);
|
||||
conn->dest_fwnode = fwnode_handle_get(&r_adev->fwnode);
|
||||
} else if (dir == ACPI_CORESIGHT_LINK_SLAVE) {
|
||||
/*
|
||||
* We are only interested in the port number
|
||||
* for the input ports at this component.
|
||||
* Store the port number in child_port.
|
||||
*/
|
||||
conn->child_port = fields[0].integer.value;
|
||||
conn->dest_port = fields[0].integer.value;
|
||||
} else {
|
||||
/* Invalid direction */
|
||||
return -EINVAL;
|
||||
@ -683,14 +659,15 @@ static int acpi_coresight_parse_link(struct acpi_device *adev,
|
||||
* connection information and populate the supplied coresight_platform_data
|
||||
* instance.
|
||||
*/
|
||||
static int acpi_coresight_parse_graph(struct acpi_device *adev,
|
||||
static int acpi_coresight_parse_graph(struct device *dev,
|
||||
struct acpi_device *adev,
|
||||
struct coresight_platform_data *pdata)
|
||||
{
|
||||
int rc, i, nlinks;
|
||||
int i, nlinks;
|
||||
const union acpi_object *graph;
|
||||
struct coresight_connection *conns, *ptr;
|
||||
struct coresight_connection conn, zero_conn = {};
|
||||
struct coresight_connection *new_conn;
|
||||
|
||||
pdata->nr_inport = pdata->nr_outport = 0;
|
||||
graph = acpi_get_coresight_graph(adev);
|
||||
if (!graph)
|
||||
return -ENOENT;
|
||||
@ -699,56 +676,22 @@ static int acpi_coresight_parse_graph(struct acpi_device *adev,
|
||||
if (!nlinks)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* To avoid scanning the table twice (once for finding the number of
|
||||
* output links and then later for parsing the output links),
|
||||
* cache the links information in one go and then later copy
|
||||
* it to the pdata.
|
||||
*/
|
||||
conns = devm_kcalloc(&adev->dev, nlinks, sizeof(*conns), GFP_KERNEL);
|
||||
if (!conns)
|
||||
return -ENOMEM;
|
||||
ptr = conns;
|
||||
for (i = 0; i < nlinks; i++) {
|
||||
const union acpi_object *link = &graph->package.elements[3 + i];
|
||||
int dir;
|
||||
|
||||
dir = acpi_coresight_parse_link(adev, link, ptr);
|
||||
conn = zero_conn;
|
||||
dir = acpi_coresight_parse_link(adev, link, &conn);
|
||||
if (dir < 0)
|
||||
return dir;
|
||||
|
||||
if (dir == ACPI_CORESIGHT_LINK_MASTER) {
|
||||
if (ptr->outport >= pdata->nr_outport)
|
||||
pdata->nr_outport = ptr->outport + 1;
|
||||
ptr++;
|
||||
} else {
|
||||
WARN_ON(pdata->nr_inport == ptr->child_port + 1);
|
||||
/*
|
||||
* We do not track input port connections for a device.
|
||||
* However we need the highest port number described,
|
||||
* which can be recorded now and reuse this connection
|
||||
* record for an output connection. Hence, do not move
|
||||
* the ptr for input connections
|
||||
*/
|
||||
if (ptr->child_port >= pdata->nr_inport)
|
||||
pdata->nr_inport = ptr->child_port + 1;
|
||||
new_conn = coresight_add_out_conn(dev, pdata, &conn);
|
||||
if (IS_ERR(new_conn))
|
||||
return PTR_ERR(new_conn);
|
||||
}
|
||||
}
|
||||
|
||||
rc = coresight_alloc_conns(&adev->dev, pdata);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Copy the connection information to the final location */
|
||||
for (i = 0; conns + i < ptr; i++) {
|
||||
int port = conns[i].outport;
|
||||
|
||||
/* Duplicate output port */
|
||||
WARN_ON(pdata->conns[port].child_fwnode);
|
||||
pdata->conns[port] = conns[i];
|
||||
}
|
||||
|
||||
devm_kfree(&adev->dev, conns);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -809,7 +752,7 @@ acpi_get_coresight_platform_data(struct device *dev,
|
||||
if (!adev)
|
||||
return -EINVAL;
|
||||
|
||||
return acpi_coresight_parse_graph(adev, pdata);
|
||||
return acpi_coresight_parse_graph(dev, adev, pdata);
|
||||
}
|
||||
|
||||
#else
|
||||
@ -863,7 +806,7 @@ coresight_get_platform_data(struct device *dev)
|
||||
error:
|
||||
if (!IS_ERR_OR_NULL(pdata))
|
||||
/* Cleanup the connection information */
|
||||
coresight_release_platform_data(NULL, pdata);
|
||||
coresight_release_platform_data(NULL, dev, pdata);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_get_platform_data);
|
||||
|
@ -82,12 +82,6 @@ enum etm_addr_type {
|
||||
ETM_ADDR_TYPE_STOP,
|
||||
};
|
||||
|
||||
enum cs_mode {
|
||||
CS_MODE_DISABLED,
|
||||
CS_MODE_SYSFS,
|
||||
CS_MODE_PERF,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cs_buffer - keep track of a recording session' specifics
|
||||
* @cur: index of the current buffer
|
||||
@ -133,7 +127,8 @@ static inline void CS_UNLOCK(void __iomem *addr)
|
||||
}
|
||||
|
||||
void coresight_disable_path(struct list_head *path);
|
||||
int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data);
|
||||
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);
|
||||
@ -193,12 +188,27 @@ extern void coresight_remove_cti_ops(void);
|
||||
}
|
||||
|
||||
/* coresight AMBA ID, full UCI structure: id table entry. */
|
||||
#define CS_AMBA_UCI_ID(pid, uci_ptr) \
|
||||
#define __CS_AMBA_UCI_ID(pid, m, uci_ptr) \
|
||||
{ \
|
||||
.id = pid, \
|
||||
.mask = 0x000fffff, \
|
||||
.mask = m, \
|
||||
.data = (void *)uci_ptr \
|
||||
}
|
||||
#define CS_AMBA_UCI_ID(pid, uci) __CS_AMBA_UCI_ID(pid, 0x000fffff, uci)
|
||||
/*
|
||||
* PIDR2[JEDEC], BIT(3) must be 1 (Read As One) to indicate that rest of the
|
||||
* PIDR1, PIDR2 DES_* fields follow JEDEC encoding for the designer. Use that
|
||||
* as a match value for blanket matching all devices in the given CoreSight
|
||||
* device type and architecture.
|
||||
*/
|
||||
#define PIDR2_JEDEC BIT(3)
|
||||
#define PID_PIDR2_JEDEC (PIDR2_JEDEC << 16)
|
||||
/*
|
||||
* Match all PIDs in a given CoreSight device type and architecture, defined
|
||||
* by the uci.
|
||||
*/
|
||||
#define CS_AMBA_MATCH_ALL_UCI(uci) \
|
||||
__CS_AMBA_UCI_ID(PID_PIDR2_JEDEC, PID_PIDR2_JEDEC, uci)
|
||||
|
||||
/* extract the data value from a UCI structure given amba_id pointer. */
|
||||
static inline void *coresight_get_uci_data(const struct amba_id *id)
|
||||
@ -212,13 +222,17 @@ static inline void *coresight_get_uci_data(const struct amba_id *id)
|
||||
}
|
||||
|
||||
void coresight_release_platform_data(struct coresight_device *csdev,
|
||||
struct device *dev,
|
||||
struct coresight_platform_data *pdata);
|
||||
struct coresight_device *
|
||||
coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode);
|
||||
void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
|
||||
struct coresight_device *ect_csdev);
|
||||
void coresight_add_helper(struct coresight_device *csdev,
|
||||
struct coresight_device *helper);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
@ -114,8 +114,9 @@ static int dynamic_replicator_enable(struct replicator_drvdata *drvdata,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int replicator_enable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
static int replicator_enable(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
int rc = 0;
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
@ -123,15 +124,15 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
|
||||
bool first_enable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (atomic_read(&csdev->refcnt[outport]) == 0) {
|
||||
if (atomic_read(&out->src_refcnt) == 0) {
|
||||
if (drvdata->base)
|
||||
rc = dynamic_replicator_enable(drvdata, inport,
|
||||
outport);
|
||||
rc = dynamic_replicator_enable(drvdata, in->dest_port,
|
||||
out->src_port);
|
||||
if (!rc)
|
||||
first_enable = true;
|
||||
}
|
||||
if (!rc)
|
||||
atomic_inc(&csdev->refcnt[outport]);
|
||||
atomic_inc(&out->src_refcnt);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (first_enable)
|
||||
@ -168,17 +169,19 @@ static void dynamic_replicator_disable(struct replicator_drvdata *drvdata,
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void replicator_disable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
static void replicator_disable(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
unsigned long flags;
|
||||
bool last_disable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (atomic_dec_return(&csdev->refcnt[outport]) == 0) {
|
||||
if (atomic_dec_return(&out->src_refcnt) == 0) {
|
||||
if (drvdata->base)
|
||||
dynamic_replicator_disable(drvdata, inport, outport);
|
||||
dynamic_replicator_disable(drvdata, in->dest_port,
|
||||
out->src_port);
|
||||
last_disable = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
@ -119,7 +119,7 @@ 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, i.e sysFS, or disabled.
|
||||
* @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.
|
||||
@ -192,8 +192,8 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int stm_enable(struct coresight_device *csdev,
|
||||
struct perf_event *event, u32 mode)
|
||||
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);
|
||||
|
@ -148,13 +148,17 @@ int coresight_make_links(struct coresight_device *orig,
|
||||
char *outs = NULL, *ins = NULL;
|
||||
struct coresight_sysfs_link *link = NULL;
|
||||
|
||||
/* Helper devices aren't shown in sysfs */
|
||||
if (conn->dest_port == -1 && conn->src_port == -1)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
outs = devm_kasprintf(&orig->dev, GFP_KERNEL,
|
||||
"out:%d", conn->outport);
|
||||
"out:%d", conn->src_port);
|
||||
if (!outs)
|
||||
break;
|
||||
ins = devm_kasprintf(&target->dev, GFP_KERNEL,
|
||||
"in:%d", conn->child_port);
|
||||
"in:%d", conn->dest_port);
|
||||
if (!ins)
|
||||
break;
|
||||
link = devm_kzalloc(&orig->dev,
|
||||
@ -173,12 +177,6 @@ int coresight_make_links(struct coresight_device *orig,
|
||||
break;
|
||||
|
||||
conn->link = link;
|
||||
|
||||
/*
|
||||
* Install the device connection. This also indicates that
|
||||
* the links are operational on both ends.
|
||||
*/
|
||||
conn->child_dev = target;
|
||||
return 0;
|
||||
} while (0);
|
||||
|
||||
@ -198,9 +196,8 @@ void coresight_remove_links(struct coresight_device *orig,
|
||||
|
||||
coresight_remove_sysfs_link(conn->link);
|
||||
|
||||
devm_kfree(&conn->child_dev->dev, conn->link->target_name);
|
||||
devm_kfree(&conn->dest_dev->dev, conn->link->target_name);
|
||||
devm_kfree(&orig->dev, conn->link->orig_name);
|
||||
devm_kfree(&orig->dev, conn->link);
|
||||
conn->link = NULL;
|
||||
conn->child_dev = NULL;
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
||||
* touched.
|
||||
*/
|
||||
if (drvdata->mode == CS_MODE_SYSFS) {
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ 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);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
} else {
|
||||
/* Free up the buffer if we failed to enable */
|
||||
used = false;
|
||||
@ -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);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -293,7 +293,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
|
||||
/* Associate with monitored process. */
|
||||
drvdata->pid = pid;
|
||||
drvdata->mode = CS_MODE_PERF;
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
}
|
||||
} while (0);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
@ -302,7 +302,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
|
||||
}
|
||||
|
||||
static int tmc_enable_etf_sink(struct coresight_device *csdev,
|
||||
u32 mode, void *data)
|
||||
enum cs_mode mode, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -338,7 +338,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (atomic_dec_return(csdev->refcnt)) {
|
||||
if (atomic_dec_return(&csdev->refcnt)) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -357,7 +357,8 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
|
||||
}
|
||||
|
||||
static int tmc_enable_etf_link(struct coresight_device *csdev,
|
||||
int inport, int outport)
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
@ -370,7 +371,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (atomic_read(&csdev->refcnt[0]) == 0) {
|
||||
if (atomic_read(&csdev->refcnt) == 0) {
|
||||
ret = tmc_etf_enable_hw(drvdata);
|
||||
if (!ret) {
|
||||
drvdata->mode = CS_MODE_SYSFS;
|
||||
@ -378,7 +379,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
|
||||
}
|
||||
}
|
||||
if (!ret)
|
||||
atomic_inc(&csdev->refcnt[0]);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (first_enable)
|
||||
@ -387,7 +388,8 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
|
||||
}
|
||||
|
||||
static void tmc_disable_etf_link(struct coresight_device *csdev,
|
||||
int inport, int outport)
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
@ -399,7 +401,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_dec_return(&csdev->refcnt[0]) == 0) {
|
||||
if (atomic_dec_return(&csdev->refcnt) == 0) {
|
||||
tmc_etf_disable_hw(drvdata);
|
||||
drvdata->mode = CS_MODE_DISABLED;
|
||||
last_disable = true;
|
||||
@ -487,7 +489,7 @@ static unsigned long tmc_update_etf_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 (atomic_read(&csdev->refcnt) != 1)
|
||||
goto out;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
@ -775,40 +775,19 @@ static const struct etr_buf_operations etr_sg_buf_ops = {
|
||||
struct coresight_device *
|
||||
tmc_etr_get_catu_device(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
int i;
|
||||
struct coresight_device *tmp, *etr = drvdata->csdev;
|
||||
struct coresight_device *etr = drvdata->csdev;
|
||||
union coresight_dev_subtype catu_subtype = {
|
||||
.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CATU
|
||||
};
|
||||
|
||||
if (!IS_ENABLED(CONFIG_CORESIGHT_CATU))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < etr->pdata->nr_outport; i++) {
|
||||
tmp = etr->pdata->conns[i].child_dev;
|
||||
if (tmp && coresight_is_catu_device(tmp))
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return coresight_find_output_type(etr->pdata, CORESIGHT_DEV_TYPE_HELPER,
|
||||
catu_subtype);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tmc_etr_get_catu_device);
|
||||
|
||||
static inline int tmc_etr_enable_catu(struct tmc_drvdata *drvdata,
|
||||
struct etr_buf *etr_buf)
|
||||
{
|
||||
struct coresight_device *catu = tmc_etr_get_catu_device(drvdata);
|
||||
|
||||
if (catu && helper_ops(catu)->enable)
|
||||
return helper_ops(catu)->enable(catu, etr_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tmc_etr_disable_catu(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
struct coresight_device *catu = tmc_etr_get_catu_device(drvdata);
|
||||
|
||||
if (catu && helper_ops(catu)->disable)
|
||||
helper_ops(catu)->disable(catu, drvdata->etr_buf);
|
||||
}
|
||||
|
||||
static const struct etr_buf_operations *etr_buf_ops[] = {
|
||||
[ETR_MODE_FLAT] = &etr_flat_buf_ops,
|
||||
[ETR_MODE_ETR_SG] = &etr_sg_buf_ops,
|
||||
@ -1058,13 +1037,6 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
|
||||
if (WARN_ON(drvdata->etr_buf))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* If this ETR is connected to a CATU, enable it before we turn
|
||||
* this on.
|
||||
*/
|
||||
rc = tmc_etr_enable_catu(drvdata, etr_buf);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = coresight_claim_device(drvdata->csdev);
|
||||
if (!rc) {
|
||||
drvdata->etr_buf = etr_buf;
|
||||
@ -1072,7 +1044,6 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
|
||||
if (rc) {
|
||||
drvdata->etr_buf = NULL;
|
||||
coresight_disclaim_device(drvdata->csdev);
|
||||
tmc_etr_disable_catu(drvdata);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1162,14 +1133,12 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
|
||||
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
__tmc_etr_disable_hw(drvdata);
|
||||
/* Disable CATU device if this ETR is connected to one */
|
||||
tmc_etr_disable_catu(drvdata);
|
||||
coresight_disclaim_device(drvdata->csdev);
|
||||
/* Reset the ETR buf used by hardware */
|
||||
drvdata->etr_buf = NULL;
|
||||
}
|
||||
|
||||
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||
static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
@ -1192,7 +1161,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||
/* Allocate memory with the locks released */
|
||||
free_buf = new_buf = tmc_etr_setup_sysfs_buf(drvdata);
|
||||
if (IS_ERR(new_buf))
|
||||
return PTR_ERR(new_buf);
|
||||
return new_buf;
|
||||
|
||||
/* Let's try again */
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
@ -1209,7 +1178,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||
* touched, even if the buffer size has changed.
|
||||
*/
|
||||
if (drvdata->mode == CS_MODE_SYSFS) {
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1223,17 +1192,33 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||
drvdata->sysfs_buf = new_buf;
|
||||
}
|
||||
|
||||
ret = tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
|
||||
if (!ret) {
|
||||
drvdata->mode = CS_MODE_SYSFS;
|
||||
atomic_inc(csdev->refcnt);
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Free memory outside the spinlock if need be */
|
||||
if (free_buf)
|
||||
tmc_etr_free_sysfs_buf(free_buf);
|
||||
return ret ? ERR_PTR(ret) : drvdata->sysfs_buf;
|
||||
}
|
||||
|
||||
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
struct etr_buf *sysfs_buf = tmc_etr_get_sysfs_buffer(csdev);
|
||||
|
||||
if (IS_ERR(sysfs_buf))
|
||||
return PTR_ERR(sysfs_buf);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
ret = tmc_etr_enable_hw(drvdata, sysfs_buf);
|
||||
if (!ret) {
|
||||
drvdata->mode = CS_MODE_SYSFS;
|
||||
atomic_inc(&csdev->refcnt);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (!ret)
|
||||
dev_dbg(&csdev->dev, "TMC-ETR enabled\n");
|
||||
@ -1241,6 +1226,26 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
|
||||
enum cs_mode mode, void *data)
|
||||
{
|
||||
struct perf_output_handle *handle = data;
|
||||
struct etr_perf_buffer *etr_perf;
|
||||
|
||||
switch (mode) {
|
||||
case CS_MODE_SYSFS:
|
||||
return tmc_etr_get_sysfs_buffer(csdev);
|
||||
case CS_MODE_PERF:
|
||||
etr_perf = etm_perf_sink_config(handle);
|
||||
if (WARN_ON(!etr_perf || !etr_perf->etr_buf))
|
||||
return ERR_PTR(-EINVAL);
|
||||
return etr_perf->etr_buf;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tmc_etr_get_buffer);
|
||||
|
||||
/*
|
||||
* alloc_etr_buf: Allocate ETR buffer for use by perf.
|
||||
* The size of the hardware buffer is dependent on the size configured
|
||||
@ -1535,7 +1540,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 (atomic_read(&csdev->refcnt) != 1) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
goto out;
|
||||
}
|
||||
@ -1647,7 +1652,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);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
@ -1657,7 +1662,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
|
||||
drvdata->pid = pid;
|
||||
drvdata->mode = CS_MODE_PERF;
|
||||
drvdata->perf_buf = etr_perf->etr_buf;
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
}
|
||||
|
||||
unlock_out:
|
||||
@ -1666,17 +1671,16 @@ unlock_out:
|
||||
}
|
||||
|
||||
static int tmc_enable_etr_sink(struct coresight_device *csdev,
|
||||
u32 mode, void *data)
|
||||
enum cs_mode mode, void *data)
|
||||
{
|
||||
switch (mode) {
|
||||
case CS_MODE_SYSFS:
|
||||
return tmc_enable_etr_sink_sysfs(csdev);
|
||||
case CS_MODE_PERF:
|
||||
return tmc_enable_etr_sink_perf(csdev, data);
|
||||
}
|
||||
|
||||
/* We shouldn't be here */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int tmc_disable_etr_sink(struct coresight_device *csdev)
|
||||
@ -1691,7 +1695,7 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (atomic_dec_return(csdev->refcnt)) {
|
||||
if (atomic_dec_return(&csdev->refcnt)) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
@ -332,5 +332,7 @@ struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata);
|
||||
|
||||
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
|
||||
void tmc_etr_remove_catu_ops(void);
|
||||
struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
|
||||
enum cs_mode mode, void *data);
|
||||
|
||||
#endif
|
||||
|
@ -54,18 +54,20 @@ static void __tpda_enable(struct tpda_drvdata *drvdata, int port)
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int tpda_enable(struct coresight_device *csdev, int inport, int outport)
|
||||
static int tpda_enable(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
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);
|
||||
if (atomic_read(&in->dest_refcnt) == 0)
|
||||
__tpda_enable(drvdata, in->dest_port);
|
||||
|
||||
atomic_inc(&csdev->refcnt[inport]);
|
||||
atomic_inc(&in->dest_refcnt);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
|
||||
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", inport);
|
||||
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -82,18 +84,19 @@ static void __tpda_disable(struct tpda_drvdata *drvdata, int port)
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tpda_disable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
static void tpda_disable(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out)
|
||||
{
|
||||
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);
|
||||
if (atomic_dec_return(&in->dest_refcnt) == 0)
|
||||
__tpda_disable(drvdata, in->dest_port);
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
|
||||
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", inport);
|
||||
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
|
||||
}
|
||||
|
||||
static const struct coresight_ops_link tpda_link_ops = {
|
||||
|
@ -42,8 +42,8 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int tpdm_enable(struct coresight_device *csdev,
|
||||
struct perf_event *event, u32 mode)
|
||||
static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode)
|
||||
{
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
|
@ -69,10 +69,11 @@ static void tpiu_enable_hw(struct csdev_access *csa)
|
||||
CS_LOCK(csa->base);
|
||||
}
|
||||
|
||||
static int tpiu_enable(struct coresight_device *csdev, u32 mode, void *__unused)
|
||||
static int tpiu_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *__unused)
|
||||
{
|
||||
tpiu_enable_hw(&csdev->access);
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
dev_dbg(&csdev->dev, "TPIU enabled\n");
|
||||
return 0;
|
||||
}
|
||||
@ -95,7 +96,7 @@ static void tpiu_disable_hw(struct csdev_access *csa)
|
||||
|
||||
static int tpiu_disable(struct coresight_device *csdev)
|
||||
{
|
||||
if (atomic_dec_return(csdev->refcnt))
|
||||
if (atomic_dec_return(&csdev->refcnt))
|
||||
return -EBUSY;
|
||||
|
||||
tpiu_disable_hw(&csdev->access);
|
||||
|
@ -1005,7 +1005,8 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_trbe_enable(struct coresight_device *csdev, u32 mode, void *data)
|
||||
static int arm_trbe_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data)
|
||||
{
|
||||
struct trbe_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
|
||||
|
@ -106,7 +106,7 @@ static int smb_open(struct inode *inode, struct file *file)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (atomic_read(drvdata->csdev->refcnt)) {
|
||||
if (atomic_read(&drvdata->csdev->refcnt)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
@ -256,7 +256,8 @@ static int smb_enable_perf(struct coresight_device *csdev, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_enable(struct coresight_device *csdev, u32 mode, void *data)
|
||||
static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data)
|
||||
{
|
||||
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret = 0;
|
||||
@ -289,7 +290,7 @@ static int smb_enable(struct coresight_device *csdev, u32 mode, void *data)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
atomic_inc(csdev->refcnt);
|
||||
atomic_inc(&csdev->refcnt);
|
||||
|
||||
dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n");
|
||||
out:
|
||||
@ -310,7 +311,7 @@ static int smb_disable(struct coresight_device *csdev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (atomic_dec_return(csdev->refcnt)) {
|
||||
if (atomic_dec_return(&csdev->refcnt)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
@ -410,7 +411,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev,
|
||||
mutex_lock(&drvdata->mutex);
|
||||
|
||||
/* Don't do anything if another tracer is using this sink. */
|
||||
if (atomic_read(csdev->refcnt) != 1)
|
||||
if (atomic_read(&csdev->refcnt) != 1)
|
||||
goto out;
|
||||
|
||||
smb_disable_hw(drvdata);
|
||||
|
@ -119,7 +119,7 @@ struct smb_drv_data {
|
||||
struct mutex mutex;
|
||||
bool reading;
|
||||
pid_t pid;
|
||||
u32 mode;
|
||||
enum cs_mode mode;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -341,19 +341,319 @@ static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ),
|
||||
hisi_ptt->trace_irq = pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, hisi_ptt->trace_irq,
|
||||
NULL, hisi_ptt_isr, 0,
|
||||
DRV_NAME, hisi_ptt);
|
||||
if (ret) {
|
||||
pci_err(pdev, "failed to request irq %d, ret = %d\n",
|
||||
pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ), ret);
|
||||
hisi_ptt->trace_irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hisi_ptt_del_free_filter(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
if (filter->is_port)
|
||||
hisi_ptt->port_mask &= ~hisi_ptt_get_filter_val(filter->devid, true);
|
||||
|
||||
list_del(&filter->list);
|
||||
kfree(filter->name);
|
||||
kfree(filter);
|
||||
}
|
||||
|
||||
static struct hisi_ptt_filter_desc *
|
||||
hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port)
|
||||
{
|
||||
struct hisi_ptt_filter_desc *filter;
|
||||
u8 devfn = devid & 0xff;
|
||||
char *filter_name;
|
||||
|
||||
filter_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d", pci_domain_nr(hisi_ptt->pdev->bus),
|
||||
PCI_BUS_NUM(devid), PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
if (!filter_name) {
|
||||
pci_err(hisi_ptt->pdev, "failed to allocate name for filter %04x:%02x:%02x.%d\n",
|
||||
pci_domain_nr(hisi_ptt->pdev->bus), PCI_BUS_NUM(devid),
|
||||
PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
|
||||
if (!filter) {
|
||||
pci_err(hisi_ptt->pdev, "failed to add filter for %s\n",
|
||||
filter_name);
|
||||
kfree(filter_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filter->name = filter_name;
|
||||
filter->is_port = is_port;
|
||||
filter->devid = devid;
|
||||
|
||||
if (filter->is_port) {
|
||||
list_add_tail(&filter->list, &hisi_ptt->port_filters);
|
||||
|
||||
/* Update the available port mask */
|
||||
hisi_ptt->port_mask |= hisi_ptt_get_filter_val(filter->devid, true);
|
||||
} else {
|
||||
list_add_tail(&filter->list, &hisi_ptt->req_filters);
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
static ssize_t hisi_ptt_filter_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hisi_ptt_filter_desc *filter;
|
||||
unsigned long filter_val;
|
||||
|
||||
filter = container_of(attr, struct hisi_ptt_filter_desc, attr);
|
||||
filter_val = hisi_ptt_get_filter_val(filter->devid, filter->is_port) |
|
||||
(filter->is_port ? HISI_PTT_PMU_FILTER_IS_PORT : 0);
|
||||
|
||||
return sysfs_emit(buf, "0x%05lx\n", filter_val);
|
||||
}
|
||||
|
||||
static int hisi_ptt_create_rp_filter_attr(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj;
|
||||
|
||||
sysfs_attr_init(&filter->attr.attr);
|
||||
filter->attr.attr.name = filter->name;
|
||||
filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */
|
||||
filter->attr.show = hisi_ptt_filter_show;
|
||||
|
||||
return sysfs_add_file_to_group(kobj, &filter->attr.attr,
|
||||
HISI_PTT_RP_FILTERS_GRP_NAME);
|
||||
}
|
||||
|
||||
static void hisi_ptt_remove_rp_filter_attr(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj;
|
||||
|
||||
sysfs_remove_file_from_group(kobj, &filter->attr.attr,
|
||||
HISI_PTT_RP_FILTERS_GRP_NAME);
|
||||
}
|
||||
|
||||
static int hisi_ptt_create_req_filter_attr(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj;
|
||||
|
||||
sysfs_attr_init(&filter->attr.attr);
|
||||
filter->attr.attr.name = filter->name;
|
||||
filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */
|
||||
filter->attr.show = hisi_ptt_filter_show;
|
||||
|
||||
return sysfs_add_file_to_group(kobj, &filter->attr.attr,
|
||||
HISI_PTT_REQ_FILTERS_GRP_NAME);
|
||||
}
|
||||
|
||||
static void hisi_ptt_remove_req_filter_attr(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj;
|
||||
|
||||
sysfs_remove_file_from_group(kobj, &filter->attr.attr,
|
||||
HISI_PTT_REQ_FILTERS_GRP_NAME);
|
||||
}
|
||||
|
||||
static int hisi_ptt_create_filter_attr(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (filter->is_port)
|
||||
ret = hisi_ptt_create_rp_filter_attr(hisi_ptt, filter);
|
||||
else
|
||||
ret = hisi_ptt_create_req_filter_attr(hisi_ptt, filter);
|
||||
|
||||
if (ret)
|
||||
pci_err(hisi_ptt->pdev, "failed to create sysfs attribute for filter %s\n",
|
||||
filter->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hisi_ptt_remove_filter_attr(struct hisi_ptt *hisi_ptt,
|
||||
struct hisi_ptt_filter_desc *filter)
|
||||
{
|
||||
if (filter->is_port)
|
||||
hisi_ptt_remove_rp_filter_attr(hisi_ptt, filter);
|
||||
else
|
||||
hisi_ptt_remove_req_filter_attr(hisi_ptt, filter);
|
||||
}
|
||||
|
||||
static void hisi_ptt_remove_all_filter_attributes(void *data)
|
||||
{
|
||||
struct hisi_ptt_filter_desc *filter;
|
||||
struct hisi_ptt *hisi_ptt = data;
|
||||
|
||||
mutex_lock(&hisi_ptt->filter_lock);
|
||||
|
||||
list_for_each_entry(filter, &hisi_ptt->req_filters, list)
|
||||
hisi_ptt_remove_filter_attr(hisi_ptt, filter);
|
||||
|
||||
list_for_each_entry(filter, &hisi_ptt->port_filters, list)
|
||||
hisi_ptt_remove_filter_attr(hisi_ptt, filter);
|
||||
|
||||
hisi_ptt->sysfs_inited = false;
|
||||
mutex_unlock(&hisi_ptt->filter_lock);
|
||||
}
|
||||
|
||||
static int hisi_ptt_init_filter_attributes(struct hisi_ptt *hisi_ptt)
|
||||
{
|
||||
struct hisi_ptt_filter_desc *filter;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&hisi_ptt->filter_lock);
|
||||
|
||||
/*
|
||||
* Register the reset callback in the first stage. In reset we traverse
|
||||
* the filters list to remove the sysfs attributes so the callback can
|
||||
* be called safely even without below filter attributes creation.
|
||||
*/
|
||||
ret = devm_add_action(&hisi_ptt->pdev->dev,
|
||||
hisi_ptt_remove_all_filter_attributes,
|
||||
hisi_ptt);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(filter, &hisi_ptt->port_filters, list) {
|
||||
ret = hisi_ptt_create_filter_attr(hisi_ptt, filter);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(filter, &hisi_ptt->req_filters, list) {
|
||||
ret = hisi_ptt_create_filter_attr(hisi_ptt, filter);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
hisi_ptt->sysfs_inited = true;
|
||||
out:
|
||||
mutex_unlock(&hisi_ptt->filter_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hisi_ptt_update_filters(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *delayed_work = to_delayed_work(work);
|
||||
struct hisi_ptt_filter_update_info info;
|
||||
struct hisi_ptt_filter_desc *filter;
|
||||
struct hisi_ptt *hisi_ptt;
|
||||
|
||||
hisi_ptt = container_of(delayed_work, struct hisi_ptt, work);
|
||||
|
||||
if (!mutex_trylock(&hisi_ptt->filter_lock)) {
|
||||
schedule_delayed_work(&hisi_ptt->work, HISI_PTT_WORK_DELAY_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
while (kfifo_get(&hisi_ptt->filter_update_kfifo, &info)) {
|
||||
if (info.is_add) {
|
||||
/*
|
||||
* Notify the users if failed to add this filter, others
|
||||
* still work and available. See the comments in
|
||||
* hisi_ptt_init_filters().
|
||||
*/
|
||||
filter = hisi_ptt_alloc_add_filter(hisi_ptt, info.devid, info.is_port);
|
||||
if (!filter)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If filters' sysfs entries hasn't been initialized,
|
||||
* then we're still at probe stage. Add the filters to
|
||||
* the list and later hisi_ptt_init_filter_attributes()
|
||||
* will create sysfs attributes for all the filters.
|
||||
*/
|
||||
if (hisi_ptt->sysfs_inited &&
|
||||
hisi_ptt_create_filter_attr(hisi_ptt, filter)) {
|
||||
hisi_ptt_del_free_filter(hisi_ptt, filter);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
struct hisi_ptt_filter_desc *tmp;
|
||||
struct list_head *target_list;
|
||||
|
||||
target_list = info.is_port ? &hisi_ptt->port_filters :
|
||||
&hisi_ptt->req_filters;
|
||||
|
||||
list_for_each_entry_safe(filter, tmp, target_list, list)
|
||||
if (filter->devid == info.devid) {
|
||||
if (hisi_ptt->sysfs_inited)
|
||||
hisi_ptt_remove_filter_attr(hisi_ptt, filter);
|
||||
|
||||
hisi_ptt_del_free_filter(hisi_ptt, filter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hisi_ptt->filter_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* A PCI bus notifier is used here for dynamically updating the filter
|
||||
* list.
|
||||
*/
|
||||
static int hisi_ptt_notifier_call(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct hisi_ptt *hisi_ptt = container_of(nb, struct hisi_ptt, hisi_ptt_nb);
|
||||
struct hisi_ptt_filter_update_info info;
|
||||
struct pci_dev *pdev, *root_port;
|
||||
struct device *dev = data;
|
||||
u32 port_devid;
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
root_port = pcie_find_root_port(pdev);
|
||||
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;
|
||||
|
||||
info.is_port = pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT;
|
||||
info.devid = PCI_DEVID(pdev->bus->number, pdev->devfn);
|
||||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
info.is_add = true;
|
||||
break;
|
||||
case BUS_NOTIFY_DEL_DEVICE:
|
||||
info.is_add = false;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The FIFO size is 16 which is sufficient for almost all the cases,
|
||||
* since each PCIe core will have most 8 Root Ports (typically only
|
||||
* 1~4 Root Ports). On failure log the failed filter and let user
|
||||
* handle it.
|
||||
*/
|
||||
if (kfifo_in_spinlocked(&hisi_ptt->filter_update_kfifo, &info, 1,
|
||||
&hisi_ptt->filter_update_lock))
|
||||
schedule_delayed_work(&hisi_ptt->work, 0);
|
||||
else
|
||||
pci_warn(hisi_ptt->pdev,
|
||||
"filter update fifo overflow for target %s\n",
|
||||
pci_name(pdev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
struct pci_dev *root_port = pcie_find_root_port(pdev);
|
||||
@ -374,23 +674,10 @@ static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data)
|
||||
* should be partial initialized and users would know which filter fails
|
||||
* through the log. Other functions of PTT device are still available.
|
||||
*/
|
||||
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
|
||||
if (!filter) {
|
||||
pci_err(hisi_ptt->pdev, "failed to add filter %s\n", pci_name(pdev));
|
||||
filter = hisi_ptt_alloc_add_filter(hisi_ptt, PCI_DEVID(pdev->bus->number, pdev->devfn),
|
||||
pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT);
|
||||
if (!filter)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
filter->devid = PCI_DEVID(pdev->bus->number, pdev->devfn);
|
||||
|
||||
if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) {
|
||||
filter->is_port = true;
|
||||
list_add_tail(&filter->list, &hisi_ptt->port_filters);
|
||||
|
||||
/* Update the available port mask */
|
||||
hisi_ptt->port_mask |= hisi_ptt_get_filter_val(filter->devid, true);
|
||||
} else {
|
||||
list_add_tail(&filter->list, &hisi_ptt->req_filters);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -400,15 +687,11 @@ static void hisi_ptt_release_filters(void *data)
|
||||
struct hisi_ptt_filter_desc *filter, *tmp;
|
||||
struct hisi_ptt *hisi_ptt = data;
|
||||
|
||||
list_for_each_entry_safe(filter, tmp, &hisi_ptt->req_filters, list) {
|
||||
list_del(&filter->list);
|
||||
kfree(filter);
|
||||
}
|
||||
list_for_each_entry_safe(filter, tmp, &hisi_ptt->req_filters, list)
|
||||
hisi_ptt_del_free_filter(hisi_ptt, filter);
|
||||
|
||||
list_for_each_entry_safe(filter, tmp, &hisi_ptt->port_filters, list) {
|
||||
list_del(&filter->list);
|
||||
kfree(filter);
|
||||
}
|
||||
list_for_each_entry_safe(filter, tmp, &hisi_ptt->port_filters, list)
|
||||
hisi_ptt_del_free_filter(hisi_ptt, filter);
|
||||
}
|
||||
|
||||
static int hisi_ptt_config_trace_buf(struct hisi_ptt *hisi_ptt)
|
||||
@ -451,8 +734,13 @@ static int hisi_ptt_init_ctrls(struct hisi_ptt *hisi_ptt)
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
INIT_DELAYED_WORK(&hisi_ptt->work, hisi_ptt_update_filters);
|
||||
INIT_KFIFO(hisi_ptt->filter_update_kfifo);
|
||||
spin_lock_init(&hisi_ptt->filter_update_lock);
|
||||
|
||||
INIT_LIST_HEAD(&hisi_ptt->port_filters);
|
||||
INIT_LIST_HEAD(&hisi_ptt->req_filters);
|
||||
mutex_init(&hisi_ptt->filter_lock);
|
||||
|
||||
ret = hisi_ptt_config_trace_buf(hisi_ptt);
|
||||
if (ret)
|
||||
@ -528,10 +816,58 @@ static struct attribute_group hisi_ptt_pmu_format_group = {
|
||||
.attrs = hisi_ptt_pmu_format_attrs,
|
||||
};
|
||||
|
||||
static ssize_t hisi_ptt_filter_multiselect_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct dev_ext_attribute *ext_attr;
|
||||
|
||||
ext_attr = container_of(attr, struct dev_ext_attribute, attr);
|
||||
return sysfs_emit(buf, "%s\n", (char *)ext_attr->var);
|
||||
}
|
||||
|
||||
static struct dev_ext_attribute root_port_filters_multiselect = {
|
||||
.attr = {
|
||||
.attr = { .name = "multiselect", .mode = 0400 },
|
||||
.show = hisi_ptt_filter_multiselect_show,
|
||||
},
|
||||
.var = "1",
|
||||
};
|
||||
|
||||
static struct attribute *hisi_ptt_pmu_root_ports_attrs[] = {
|
||||
&root_port_filters_multiselect.attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group hisi_ptt_pmu_root_ports_group = {
|
||||
.name = HISI_PTT_RP_FILTERS_GRP_NAME,
|
||||
.attrs = hisi_ptt_pmu_root_ports_attrs,
|
||||
};
|
||||
|
||||
static struct dev_ext_attribute requester_filters_multiselect = {
|
||||
.attr = {
|
||||
.attr = { .name = "multiselect", .mode = 0400 },
|
||||
.show = hisi_ptt_filter_multiselect_show,
|
||||
},
|
||||
.var = "0",
|
||||
};
|
||||
|
||||
static struct attribute *hisi_ptt_pmu_requesters_attrs[] = {
|
||||
&requester_filters_multiselect.attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group hisi_ptt_pmu_requesters_group = {
|
||||
.name = HISI_PTT_REQ_FILTERS_GRP_NAME,
|
||||
.attrs = hisi_ptt_pmu_requesters_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *hisi_ptt_pmu_groups[] = {
|
||||
&hisi_ptt_cpumask_attr_group,
|
||||
&hisi_ptt_pmu_format_group,
|
||||
&hisi_ptt_tune_group,
|
||||
&hisi_ptt_pmu_root_ports_group,
|
||||
&hisi_ptt_pmu_requesters_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -605,6 +941,7 @@ static int hisi_ptt_trace_valid_filter(struct hisi_ptt *hisi_ptt, u64 config)
|
||||
{
|
||||
unsigned long val, port_mask = hisi_ptt->port_mask;
|
||||
struct hisi_ptt_filter_desc *filter;
|
||||
int ret = 0;
|
||||
|
||||
hisi_ptt->trace_ctrl.is_port = FIELD_GET(HISI_PTT_PMU_FILTER_IS_PORT, config);
|
||||
val = FIELD_GET(HISI_PTT_PMU_FILTER_VAL_MASK, config);
|
||||
@ -618,16 +955,20 @@ static int hisi_ptt_trace_valid_filter(struct hisi_ptt *hisi_ptt, u64 config)
|
||||
* For Requester ID filters, walk the available filter list to see
|
||||
* whether we have one matched.
|
||||
*/
|
||||
mutex_lock(&hisi_ptt->filter_lock);
|
||||
if (!hisi_ptt->trace_ctrl.is_port) {
|
||||
list_for_each_entry(filter, &hisi_ptt->req_filters, list) {
|
||||
if (val == hisi_ptt_get_filter_val(filter->devid, filter->is_port))
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
} else if (bitmap_subset(&val, &port_mask, BITS_PER_LONG)) {
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
mutex_unlock(&hisi_ptt->filter_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hisi_ptt_pmu_init_configs(struct hisi_ptt *hisi_ptt, struct perf_event *event)
|
||||
@ -757,8 +1098,7 @@ static void hisi_ptt_pmu_start(struct perf_event *event, int flags)
|
||||
* core in event_function_local(). If CPU passed is offline we'll fail
|
||||
* here, just log it since we can do nothing here.
|
||||
*/
|
||||
ret = irq_set_affinity(pci_irq_vector(hisi_ptt->pdev, HISI_PTT_TRACE_DMA_IRQ),
|
||||
cpumask_of(cpu));
|
||||
ret = irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(cpu));
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to set the affinity of trace interrupt\n");
|
||||
|
||||
@ -871,7 +1211,7 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt)
|
||||
|
||||
hisi_ptt->hisi_ptt_pmu = (struct pmu) {
|
||||
.module = THIS_MODULE,
|
||||
.capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE,
|
||||
.capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_NO_EXCLUDE,
|
||||
.task_ctx_nr = perf_sw_context,
|
||||
.attr_groups = hisi_ptt_pmu_groups,
|
||||
.event_init = hisi_ptt_pmu_event_init,
|
||||
@ -901,6 +1241,31 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt)
|
||||
&hisi_ptt->hisi_ptt_pmu);
|
||||
}
|
||||
|
||||
static void hisi_ptt_unregister_filter_update_notifier(void *data)
|
||||
{
|
||||
struct hisi_ptt *hisi_ptt = data;
|
||||
|
||||
bus_unregister_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb);
|
||||
|
||||
/* Cancel any work that has been queued */
|
||||
cancel_delayed_work_sync(&hisi_ptt->work);
|
||||
}
|
||||
|
||||
/* Register the bus notifier for dynamically updating the filter list */
|
||||
static int hisi_ptt_register_filter_update_notifier(struct hisi_ptt *hisi_ptt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
hisi_ptt->hisi_ptt_nb.notifier_call = hisi_ptt_notifier_call;
|
||||
ret = bus_register_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(&hisi_ptt->pdev->dev,
|
||||
hisi_ptt_unregister_filter_update_notifier,
|
||||
hisi_ptt);
|
||||
}
|
||||
|
||||
/*
|
||||
* The DMA of PTT trace can only use direct mappings due to some
|
||||
* hardware restriction. Check whether there is no IOMMU or the
|
||||
@ -972,12 +1337,22 @@ static int hisi_ptt_probe(struct pci_dev *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hisi_ptt_register_filter_update_notifier(hisi_ptt);
|
||||
if (ret)
|
||||
pci_warn(pdev, "failed to register filter update notifier, ret = %d", ret);
|
||||
|
||||
ret = hisi_ptt_register_pmu(hisi_ptt);
|
||||
if (ret) {
|
||||
pci_err(pdev, "failed to register PMU device, ret = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hisi_ptt_init_filter_attributes(hisi_ptt);
|
||||
if (ret) {
|
||||
pci_err(pdev, "failed to init sysfs filter attributes, ret = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1018,8 +1393,7 @@ static int hisi_ptt_cpu_teardown(unsigned int cpu, struct hlist_node *node)
|
||||
* Also make sure the interrupt bind to the migrated CPU as well. Warn
|
||||
* the user on failure here.
|
||||
*/
|
||||
if (irq_set_affinity(pci_irq_vector(hisi_ptt->pdev, HISI_PTT_TRACE_DMA_IRQ),
|
||||
cpumask_of(target)))
|
||||
if (irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(target)))
|
||||
dev_warn(dev, "failed to set the affinity of trace interrupt\n");
|
||||
|
||||
hisi_ptt->trace_ctrl.on_cpu = target;
|
||||
|
@ -11,12 +11,16 @@
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define DRV_NAME "hisi_ptt"
|
||||
|
||||
@ -71,6 +75,11 @@
|
||||
#define HISI_PTT_WAIT_TRACE_TIMEOUT_US 100UL
|
||||
#define HISI_PTT_WAIT_POLL_INTERVAL_US 10UL
|
||||
|
||||
/* FIFO size for dynamically updating the PTT trace filter list. */
|
||||
#define HISI_PTT_FILTER_UPDATE_FIFO_SIZE 16
|
||||
/* Delay time for filter updating work */
|
||||
#define HISI_PTT_WORK_DELAY_MS 100UL
|
||||
|
||||
#define HISI_PCIE_CORE_PORT_ID(devfn) ((PCI_SLOT(devfn) & 0x7) << 1)
|
||||
|
||||
/* Definition of the PMU configs */
|
||||
@ -131,15 +140,40 @@ struct hisi_ptt_trace_ctrl {
|
||||
u32 type:4;
|
||||
};
|
||||
|
||||
/*
|
||||
* sysfs attribute group name for root port filters and requester filters:
|
||||
* /sys/devices/hisi_ptt<sicl_id>_<core_id>/root_port_filters
|
||||
* and
|
||||
* /sys/devices/hisi_ptt<sicl_id>_<core_id>/requester_filters
|
||||
*/
|
||||
#define HISI_PTT_RP_FILTERS_GRP_NAME "root_port_filters"
|
||||
#define HISI_PTT_REQ_FILTERS_GRP_NAME "requester_filters"
|
||||
|
||||
/**
|
||||
* struct hisi_ptt_filter_desc - Descriptor of the PTT trace filter
|
||||
* @attr: sysfs attribute of this filter
|
||||
* @list: entry of this descriptor in the filter list
|
||||
* @is_port: the PCI device of the filter is a Root Port or not
|
||||
* @name: name of this filter, same as the name of the related PCI device
|
||||
* @devid: the PCI device's devid of the filter
|
||||
*/
|
||||
struct hisi_ptt_filter_desc {
|
||||
struct device_attribute attr;
|
||||
struct list_head list;
|
||||
bool is_port;
|
||||
char *name;
|
||||
u16 devid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hisi_ptt_filter_update_info - Information for PTT filter updating
|
||||
* @is_port: the PCI device to update is a Root Port or not
|
||||
* @is_add: adding to the filter or not
|
||||
* @devid: the PCI device's devid of the filter
|
||||
*/
|
||||
struct hisi_ptt_filter_update_info {
|
||||
bool is_port;
|
||||
bool is_add;
|
||||
u16 devid;
|
||||
};
|
||||
|
||||
@ -160,26 +194,35 @@ struct hisi_ptt_pmu_buf {
|
||||
/**
|
||||
* struct hisi_ptt - Per PTT device data
|
||||
* @trace_ctrl: the control information of PTT trace
|
||||
* @hisi_ptt_nb: dynamic filter update notifier
|
||||
* @hotplug_node: node for register cpu hotplug event
|
||||
* @hisi_ptt_pmu: the pum device of trace
|
||||
* @iobase: base IO address of the device
|
||||
* @pdev: pci_dev of this PTT device
|
||||
* @tune_lock: lock to serialize the tune process
|
||||
* @pmu_lock: lock to serialize the perf process
|
||||
* @trace_irq: interrupt number used by trace
|
||||
* @upper_bdf: the upper BDF range of the PCI devices managed by this PTT device
|
||||
* @lower_bdf: the lower BDF range of the PCI devices managed by this PTT device
|
||||
* @port_filters: the filter list of root ports
|
||||
* @req_filters: the filter list of requester ID
|
||||
* @filter_lock: lock to protect the filters
|
||||
* @sysfs_inited: whether the filters' sysfs entries has been initialized
|
||||
* @port_mask: port mask of the managed root ports
|
||||
* @work: delayed work for filter updating
|
||||
* @filter_update_lock: spinlock to protect the filter update fifo
|
||||
* @filter_update_fifo: fifo of the filters waiting to update the filter list
|
||||
*/
|
||||
struct hisi_ptt {
|
||||
struct hisi_ptt_trace_ctrl trace_ctrl;
|
||||
struct notifier_block hisi_ptt_nb;
|
||||
struct hlist_node hotplug_node;
|
||||
struct pmu hisi_ptt_pmu;
|
||||
void __iomem *iobase;
|
||||
struct pci_dev *pdev;
|
||||
struct mutex tune_lock;
|
||||
spinlock_t pmu_lock;
|
||||
int trace_irq;
|
||||
u32 upper_bdf;
|
||||
u32 lower_bdf;
|
||||
|
||||
@ -192,7 +235,20 @@ struct hisi_ptt {
|
||||
*/
|
||||
struct list_head port_filters;
|
||||
struct list_head req_filters;
|
||||
struct mutex filter_lock;
|
||||
bool sysfs_inited;
|
||||
u16 port_mask;
|
||||
|
||||
/*
|
||||
* We use a delayed work here to avoid indefinitely waiting for
|
||||
* the hisi_ptt->mutex which protecting the filter list. The
|
||||
* work will be delayed only if the mutex can not be held,
|
||||
* otherwise no delay will be applied.
|
||||
*/
|
||||
struct delayed_work work;
|
||||
spinlock_t filter_update_lock;
|
||||
DECLARE_KFIFO(filter_update_kfifo, struct hisi_ptt_filter_update_info,
|
||||
HISI_PTT_FILTER_UPDATE_FIFO_SIZE);
|
||||
};
|
||||
|
||||
#define to_hisi_ptt(pmu) container_of(pmu, struct hisi_ptt, hisi_ptt_pmu)
|
||||
|
@ -41,10 +41,11 @@ enum coresight_dev_type {
|
||||
CORESIGHT_DEV_TYPE_LINKSINK,
|
||||
CORESIGHT_DEV_TYPE_SOURCE,
|
||||
CORESIGHT_DEV_TYPE_HELPER,
|
||||
CORESIGHT_DEV_TYPE_ECT,
|
||||
CORESIGHT_DEV_TYPE_MAX
|
||||
};
|
||||
|
||||
enum coresight_dev_subtype_sink {
|
||||
CORESIGHT_DEV_SUBTYPE_SINK_DUMMY,
|
||||
CORESIGHT_DEV_SUBTYPE_SINK_PORT,
|
||||
CORESIGHT_DEV_SUBTYPE_SINK_BUFFER,
|
||||
CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM,
|
||||
@ -66,12 +67,7 @@ enum coresight_dev_subtype_source {
|
||||
|
||||
enum coresight_dev_subtype_helper {
|
||||
CORESIGHT_DEV_SUBTYPE_HELPER_CATU,
|
||||
};
|
||||
|
||||
/* Embedded Cross Trigger (ECT) sub-types */
|
||||
enum coresight_dev_subtype_ect {
|
||||
CORESIGHT_DEV_SUBTYPE_ECT_NONE,
|
||||
CORESIGHT_DEV_SUBTYPE_ECT_CTI,
|
||||
CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI
|
||||
};
|
||||
|
||||
/**
|
||||
@ -84,8 +80,6 @@ enum coresight_dev_subtype_ect {
|
||||
* by @coresight_dev_subtype_source.
|
||||
* @helper_subtype: type of helper this component is, as defined
|
||||
* by @coresight_dev_subtype_helper.
|
||||
* @ect_subtype: type of cross trigger this component is, as
|
||||
* defined by @coresight_dev_subtype_ect
|
||||
*/
|
||||
union coresight_dev_subtype {
|
||||
/* We have some devices which acts as LINK and SINK */
|
||||
@ -95,21 +89,25 @@ union coresight_dev_subtype {
|
||||
};
|
||||
enum coresight_dev_subtype_source source_subtype;
|
||||
enum coresight_dev_subtype_helper helper_subtype;
|
||||
enum coresight_dev_subtype_ect ect_subtype;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct coresight_platform_data - data harvested from the firmware
|
||||
* specification.
|
||||
*
|
||||
* @nr_inport: Number of elements for the input connections.
|
||||
* @nr_outport: Number of elements for the output connections.
|
||||
* @conns: Sparse array of nr_outport connections from this component.
|
||||
* @nr_inconns: Number of elements for the input connections.
|
||||
* @nr_outconns: Number of elements for the output connections.
|
||||
* @out_conns: Array of nr_outconns pointers to connections from this
|
||||
* component.
|
||||
* @in_conns: Sparse array of pointers to input connections. Sparse
|
||||
* because the source device owns the connection so when it's
|
||||
* unloaded the connection leaves an empty slot.
|
||||
*/
|
||||
struct coresight_platform_data {
|
||||
int nr_inport;
|
||||
int nr_outport;
|
||||
struct coresight_connection *conns;
|
||||
int nr_inconns;
|
||||
int nr_outconns;
|
||||
struct coresight_connection **out_conns;
|
||||
struct coresight_connection **in_conns;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -164,19 +162,42 @@ struct coresight_desc {
|
||||
|
||||
/**
|
||||
* struct coresight_connection - representation of a single connection
|
||||
* @outport: a connection's output port number.
|
||||
* @child_port: remote component's port number @output is connected to.
|
||||
* @chid_fwnode: remote component's fwnode handle.
|
||||
* @child_dev: a @coresight_device representation of the component
|
||||
connected to @outport.
|
||||
* @src_port: a connection's output port number.
|
||||
* @dest_port: destination's input port number @src_port is connected to.
|
||||
* @dest_fwnode: destination component's fwnode handle.
|
||||
* @dest_dev: a @coresight_device representation of the component
|
||||
connected to @src_port. NULL until the device is created
|
||||
* @link: Representation of the connection as a sysfs link.
|
||||
*
|
||||
* The full connection structure looks like this, where in_conns store
|
||||
* references to same connection as the source device's out_conns.
|
||||
*
|
||||
* +-----------------------------+ +-----------------------------+
|
||||
* |coresight_device | |coresight_connection |
|
||||
* |-----------------------------| |-----------------------------|
|
||||
* | | | |
|
||||
* | | | dest_dev*|<--
|
||||
* |pdata->out_conns[nr_outconns]|<->|src_dev* | |
|
||||
* | | | | |
|
||||
* +-----------------------------+ +-----------------------------+ |
|
||||
* |
|
||||
* +-----------------------------+ |
|
||||
* |coresight_device | |
|
||||
* |------------------------------ |
|
||||
* | | |
|
||||
* | pdata->in_conns[nr_inconns]|<--
|
||||
* | |
|
||||
* +-----------------------------+
|
||||
*/
|
||||
struct coresight_connection {
|
||||
int outport;
|
||||
int child_port;
|
||||
struct fwnode_handle *child_fwnode;
|
||||
struct coresight_device *child_dev;
|
||||
int src_port;
|
||||
int dest_port;
|
||||
struct fwnode_handle *dest_fwnode;
|
||||
struct coresight_device *dest_dev;
|
||||
struct coresight_sysfs_link *link;
|
||||
struct coresight_device *src_dev;
|
||||
atomic_t src_refcnt;
|
||||
atomic_t dest_refcnt;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -211,8 +232,6 @@ struct coresight_sysfs_link {
|
||||
* from source to that sink.
|
||||
* @ea: Device attribute for sink representation under PMU directory.
|
||||
* @def_sink: cached reference to default sink found for this device.
|
||||
* @ect_dev: Associated cross trigger device. Not part of the trace data
|
||||
* path or connections.
|
||||
* @nr_links: number of sysfs links created to other components from this
|
||||
* device. These will appear in the "connections" group.
|
||||
* @has_conns_grp: Have added a "connections" group for sysfs links.
|
||||
@ -228,19 +247,16 @@ struct coresight_device {
|
||||
const struct coresight_ops *ops;
|
||||
struct csdev_access access;
|
||||
struct device dev;
|
||||
atomic_t *refcnt;
|
||||
atomic_t 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 */
|
||||
struct dev_ext_attribute *ea;
|
||||
struct coresight_device *def_sink;
|
||||
/* cross trigger handling */
|
||||
struct coresight_device *ect_dev;
|
||||
/* sysfs links between components */
|
||||
int nr_links;
|
||||
bool has_conns_grp;
|
||||
bool ect_enabled; /* true only if associated ect device is enabled */
|
||||
/* system configuration and feature lists */
|
||||
struct list_head feature_csdev_list;
|
||||
struct list_head config_csdev_list;
|
||||
@ -272,6 +288,12 @@ static struct coresight_dev_list (var) = { \
|
||||
|
||||
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
|
||||
|
||||
enum cs_mode {
|
||||
CS_MODE_DISABLED,
|
||||
CS_MODE_SYSFS,
|
||||
CS_MODE_PERF,
|
||||
};
|
||||
|
||||
#define source_ops(csdev) csdev->ops->source_ops
|
||||
#define sink_ops(csdev) csdev->ops->sink_ops
|
||||
#define link_ops(csdev) csdev->ops->link_ops
|
||||
@ -288,7 +310,8 @@ static struct coresight_dev_list (var) = { \
|
||||
* @update_buffer: update buffer pointers after a trace session.
|
||||
*/
|
||||
struct coresight_ops_sink {
|
||||
int (*enable)(struct coresight_device *csdev, u32 mode, void *data);
|
||||
int (*enable)(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data);
|
||||
int (*disable)(struct coresight_device *csdev);
|
||||
void *(*alloc_buffer)(struct coresight_device *csdev,
|
||||
struct perf_event *event, void **pages,
|
||||
@ -306,8 +329,12 @@ struct coresight_ops_sink {
|
||||
* @disable: disables flow between iport and oport.
|
||||
*/
|
||||
struct coresight_ops_link {
|
||||
int (*enable)(struct coresight_device *csdev, int iport, int oport);
|
||||
void (*disable)(struct coresight_device *csdev, int iport, int oport);
|
||||
int (*enable)(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out);
|
||||
void (*disable)(struct coresight_device *csdev,
|
||||
struct coresight_connection *in,
|
||||
struct coresight_connection *out);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -320,8 +347,8 @@ struct coresight_ops_link {
|
||||
*/
|
||||
struct coresight_ops_source {
|
||||
int (*cpu_id)(struct coresight_device *csdev);
|
||||
int (*enable)(struct coresight_device *csdev,
|
||||
struct perf_event *event, u32 mode);
|
||||
int (*enable)(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode);
|
||||
void (*disable)(struct coresight_device *csdev,
|
||||
struct perf_event *event);
|
||||
};
|
||||
@ -336,27 +363,16 @@ struct coresight_ops_source {
|
||||
* @disable : Disable the device
|
||||
*/
|
||||
struct coresight_ops_helper {
|
||||
int (*enable)(struct coresight_device *csdev, void *data);
|
||||
int (*enable)(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data);
|
||||
int (*disable)(struct coresight_device *csdev, void *data);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct coresight_ops_ect - Ops for an embedded cross trigger device
|
||||
*
|
||||
* @enable : Enable the device
|
||||
* @disable : Disable the device
|
||||
*/
|
||||
struct coresight_ops_ect {
|
||||
int (*enable)(struct coresight_device *csdev);
|
||||
int (*disable)(struct coresight_device *csdev);
|
||||
};
|
||||
|
||||
struct coresight_ops {
|
||||
const struct coresight_ops_sink *sink_ops;
|
||||
const struct coresight_ops_link *link_ops;
|
||||
const struct coresight_ops_source *source_ops;
|
||||
const struct coresight_ops_helper *helper_ops;
|
||||
const struct coresight_ops_ect *ect_ops;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_CORESIGHT)
|
||||
@ -602,5 +618,18 @@ static inline void coresight_write64(struct coresight_device *csdev, u64 val, u3
|
||||
extern int coresight_get_cpu(struct device *dev);
|
||||
|
||||
struct coresight_platform_data *coresight_get_platform_data(struct device *dev);
|
||||
struct coresight_connection *
|
||||
coresight_add_out_conn(struct device *dev,
|
||||
struct coresight_platform_data *pdata,
|
||||
const struct coresight_connection *new_conn);
|
||||
int coresight_add_in_conn(struct coresight_connection *conn);
|
||||
struct coresight_device *
|
||||
coresight_find_input_type(struct coresight_platform_data *pdata,
|
||||
enum coresight_dev_type type,
|
||||
union coresight_dev_subtype subtype);
|
||||
struct coresight_device *
|
||||
coresight_find_output_type(struct coresight_platform_data *pdata,
|
||||
enum coresight_dev_type type,
|
||||
union coresight_dev_subtype subtype);
|
||||
|
||||
#endif /* _LINUX_COREISGHT_H */
|
||||
|
@ -223,6 +223,17 @@ static inline void *devm_kcalloc(struct device *dev,
|
||||
{
|
||||
return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
|
||||
}
|
||||
static inline __realloc_size(3, 4) void * __must_check
|
||||
devm_krealloc_array(struct device *dev, void *p, size_t new_n, size_t new_size, gfp_t flags)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (unlikely(check_mul_overflow(new_n, new_size, &bytes)))
|
||||
return NULL;
|
||||
|
||||
return devm_krealloc(dev, p, bytes, flags);
|
||||
}
|
||||
|
||||
void devm_kfree(struct device *dev, const void *p);
|
||||
char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) __malloc;
|
||||
const char *devm_kstrdup_const(struct device *dev, const char *s, gfp_t gfp);
|
||||
|
Loading…
x
Reference in New Issue
Block a user