coresight: Control path with range

CPU PM notifiers need to control only part of a path instead of always
operating on the full path.

Add internal enable and disable helpers that take an inclusive node
range [from, to], validate that the requested nodes are ordered before
using them.

Update the existed coresight_{enable|disable}_path() interfaces as
full-path wrappers by passing the first and last path nodes. The helpers
coresight_path_{first|last}_node() are provided for conveniently
fetching the first and last nodes on the path.

In coresight_enable_path_from_to(), if a failure occurs at the last node
in the range, no device is actually enabled in this case, bail out
directly.

Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-16-f88c4a3ecfe9@arm.com
This commit is contained in:
Leo Yan 2026-05-15 21:08:23 +01:00 committed by Suzuki K Poulose
parent 5cae719943
commit 3d3289c0d8

View File

@ -62,6 +62,24 @@ static LIST_HEAD(coresight_dev_idx_list);
static const struct cti_assoc_op *cti_assoc_ops;
static struct coresight_node *
coresight_path_first_node(struct coresight_path *path)
{
if (list_empty(&path->path_list))
return NULL;
return list_first_entry(&path->path_list, struct coresight_node, link);
}
static struct coresight_node *
coresight_path_last_node(struct coresight_path *path)
{
if (list_empty(&path->path_list))
return NULL;
return list_last_entry(&path->path_list, struct coresight_node, link);
}
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@ -488,19 +506,41 @@ int coresight_resume_source(struct coresight_device *csdev)
EXPORT_SYMBOL_GPL(coresight_resume_source);
/*
* coresight_disable_path_from : Disable components in the given path starting
* from @nd in the list. If @nd is NULL, all the components, except the SOURCE
* are disabled.
* Callers must fetch nodes from the path and pass @from and @to to the path
* enable/disable functions. Walk the path from @from to locate @to. If @to
* is found, it indicates @from and @to are in order. Otherwise, they are out
* of order.
*/
static void coresight_disable_path_from(struct coresight_path *path,
struct coresight_node *nd)
static bool coresight_path_nodes_in_order(struct coresight_path *path,
struct coresight_node *from,
struct coresight_node *to)
{
struct coresight_node *nd;
if (WARN_ON_ONCE(!from || !to))
return false;
nd = from;
list_for_each_entry_from(nd, &path->path_list, link) {
if (nd == to)
return true;
}
return false;
}
static void coresight_disable_path_from_to(struct coresight_path *path,
struct coresight_node *from,
struct coresight_node *to)
{
u32 type;
struct coresight_device *csdev, *parent, *child;
struct coresight_node *nd;
if (!nd)
nd = list_first_entry(&path->path_list, struct coresight_node, link);
if (!coresight_path_nodes_in_order(path, from, to))
return;
nd = from;
list_for_each_entry_from(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@ -534,12 +574,18 @@ static void coresight_disable_path_from(struct coresight_path *path,
/* Disable all helpers adjacent along the path last */
coresight_disable_helpers(csdev, path);
/* Iterate up to and including @to */
if (nd == to)
break;
}
}
void coresight_disable_path(struct coresight_path *path)
{
coresight_disable_path_from(path, NULL);
coresight_disable_path_from_to(path,
coresight_path_first_node(path),
coresight_path_last_node(path));
}
EXPORT_SYMBOL_GPL(coresight_disable_path);
@ -572,16 +618,21 @@ static int coresight_enable_helpers(struct coresight_device *csdev,
return ret;
}
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
static int coresight_enable_path_from_to(struct coresight_path *path,
enum cs_mode mode,
struct coresight_node *from,
struct coresight_node *to)
{
int ret = 0;
u32 type;
struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
struct coresight_device *source;
source = coresight_get_source(path);
list_for_each_entry_reverse(nd, &path->path_list, link) {
if (!coresight_path_nodes_in_order(path, from, to))
return -EINVAL;
nd = to;
list_for_each_entry_from_reverse(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@ -620,7 +671,8 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
case CORESIGHT_DEV_TYPE_LINK:
parent = list_prev_entry(nd, link)->csdev;
child = list_next_entry(nd, link)->csdev;
ret = coresight_enable_link(csdev, parent, child, source);
ret = coresight_enable_link(csdev, parent, child,
coresight_get_source(path));
if (ret)
goto err_disable_helpers;
break;
@ -628,6 +680,10 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
ret = -EINVAL;
goto err_disable_helpers;
}
/* Iterate down to and including @from */
if (nd == from)
break;
}
out:
@ -635,12 +691,23 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
err_disable_helpers:
coresight_disable_helpers(csdev, path);
err_disable_path:
/* No device is actually enabled */
if (nd == to)
goto out;
/* Fetch the previous node, the last successfully enabled one */
nd = list_next_entry(nd, link);
coresight_disable_path_from(path, nd);
coresight_disable_path_from_to(path, nd, to);
goto out;
}
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
{
return coresight_enable_path_from_to(path, mode,
coresight_path_first_node(path),
coresight_path_last_node(path));
}
struct coresight_device *coresight_get_sink(struct coresight_path *path)
{
struct coresight_device *csdev;