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:
Greg Kroah-Hartman 2023-06-21 21:15:06 +02:00
commit fa50d6b8a5
39 changed files with 1657 additions and 737 deletions

View File

@ -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.

View File

@ -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>;
};
};
};
};
...

View File

@ -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>;
};
};
};
};
...

View File

@ -364,6 +364,7 @@ MEM
devm_kmalloc_array()
devm_kmemdup()
devm_krealloc()
devm_krealloc_array()
devm_kstrdup()
devm_kstrdup_const()
devm_kvasprintf()

View 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

View File

@ -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
-------

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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,32 +316,26 @@ 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;
}
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,45 +1344,57 @@ 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) {
/* Does it match this newly added device? */
if (conn->child_fwnode == csdev->dev.fwnode) {
ret = coresight_make_links(i_csdev,
conn, csdev);
if (ret)
return ret;
} else {
/* This component still has an orphan */
still_orphan = true;
}
/*
* 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 (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 {
/* This component still has an orphan */
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),

View File

@ -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,26 +584,30 @@ 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);
ctidev = &ctidrv->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev == csdev) {
cti_remove_sysfs_link(ctidrv, tc);
tc->con_dev = NULL;
break;
}
ctidrv = csdev_to_cti_drvdata(cti_csdev);
ctidev = &ctidrv->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev == csdev) {
cti_remove_sysfs_link(ctidrv, tc);
tc->con_dev = NULL;
break;
}
csdev->ect_dev = NULL;
}
mutex_unlock(&ect_mutex);
}
@ -630,8 +636,8 @@ 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,
drvdata->csdev);
coresight_add_helper(tc->con_dev,
drvdata->csdev);
else
/* otherwise remove reference from CTI */
tc->con_dev = NULL;
@ -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;

View File

@ -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);
}

View File

@ -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);

View 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");

View File

@ -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);

View File

@ -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;

View File

@ -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);
/*

View File

@ -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),
{},
};

View File

@ -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) \
&((struct dev_ext_attribute[]) { \
{ \
__ATTR(name, 0444, coresight_etm4x_reg_show, NULL), \
(void *)(unsigned long)offset \
} \
})[0].attr.attr
/*
* 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, showfn, NULL), \
(void *)(unsigned long)offset \
} \
})[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,
};

View File

@ -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 = {

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
default:
return -EINVAL;
}
/* We shouldn't be here */
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;
}

View File

@ -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

View File

@ -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 = {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -119,7 +119,7 @@ struct smb_drv_data {
struct mutex mutex;
bool reading;
pid_t pid;
u32 mode;
enum cs_mode mode;
};
#endif

View File

@ -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;

View File

@ -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)

View File

@ -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 */

View File

@ -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);