summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/coresight/coresight-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-platform.c')
-rw-r--r--drivers/hwtracing/coresight/coresight-platform.c370
1 files changed, 178 insertions, 192 deletions
diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
index 475899714104..0db64c5f4995 100644
--- a/drivers/hwtracing/coresight/coresight-platform.c
+++ b/drivers/hwtracing/coresight/coresight-platform.c
@@ -9,9 +9,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/of_graph.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
#include <linux/coresight.h>
@@ -19,22 +17,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)
@@ -78,46 +139,11 @@ coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode)
EXPORT_SYMBOL_GPL(coresight_find_csdev_by_fwnode);
#ifdef CONFIG_OF
-static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep)
+static 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 +159,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 *
+static 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 +185,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 +203,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 +231,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 +240,35 @@ 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;
+
+ /*
+ * Get the firmware node of the filter source through the
+ * reference. This could be used to filter the source in
+ * building path.
+ */
+ conn.filter_src_fwnode =
+ fwnode_find_reference(&ep->fwnode, "filter-source", 0);
+ if (IS_ERR(conn.filter_src_fwnode)) {
+ conn.filter_src_fwnode = NULL;
+ } else {
+ conn.filter_src_dev =
+ coresight_find_csdev_by_fwnode(conn.filter_src_fwnode);
+ if (conn.filter_src_dev &&
+ !coresight_is_device_source(conn.filter_src_dev)) {
+ dev_warn(dev, "port %d: Filter handle is not a trace source : %s\n",
+ conn.src_port, dev_name(&conn.filter_src_dev->dev));
+ conn.filter_src_dev = NULL;
+ conn.filter_src_fwnode = NULL;
+ }
+ }
+
+ 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 +288,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,13 +295,19 @@ static int of_get_coresight_platform_data(struct device *dev,
* ports.
*/
if (!parent) {
+ /*
+ * Avoid warnings in for_each_endpoint_of_node()
+ * 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");
}
/* Iterate through each output port to discover topology */
- while ((ep = of_graph_get_next_endpoint(parent, ep))) {
+ for_each_endpoint_of_node(parent, ep) {
/*
* Legacy binding mixes input/output ports under the
* same parent. So, skip the input ports if we are dealing
@@ -323,21 +318,23 @@ static int of_get_coresight_platform_data(struct device *dev,
continue;
ret = of_coresight_parse_endpoint(dev, ep, pdata);
- if (ret)
+ if (ret) {
+ of_node_put(ep);
return ret;
+ }
}
return 0;
}
#else
-static inline int
+static int
of_get_coresight_platform_data(struct device *dev,
struct coresight_platform_data *pdata)
{
return -ENOENT;
}
-static inline int of_coresight_get_cpu(struct device *dev)
+static int of_coresight_get_cpu(struct device *dev)
{
return -ENODEV;
}
@@ -359,7 +356,7 @@ static const guid_t coresight_graph_uuid = GUID_INIT(0x3ecbc8b6, 0x1d0e, 0x4fb3,
#define ACPI_CORESIGHT_LINK_SLAVE 0
#define ACPI_CORESIGHT_LINK_MASTER 1
-static inline bool is_acpi_guid(const union acpi_object *obj)
+static bool is_acpi_guid(const union acpi_object *obj)
{
return (obj->type == ACPI_TYPE_BUFFER) && (obj->buffer.length == 16);
}
@@ -368,24 +365,24 @@ static inline bool is_acpi_guid(const union acpi_object *obj)
* acpi_guid_matches - Checks if the given object is a GUID object and
* that it matches the supplied the GUID.
*/
-static inline bool acpi_guid_matches(const union acpi_object *obj,
+static bool acpi_guid_matches(const union acpi_object *obj,
const guid_t *guid)
{
return is_acpi_guid(obj) &&
guid_equal((guid_t *)obj->buffer.pointer, guid);
}
-static inline bool is_acpi_dsd_graph_guid(const union acpi_object *obj)
+static bool is_acpi_dsd_graph_guid(const union acpi_object *obj)
{
return acpi_guid_matches(obj, &acpi_graph_uuid);
}
-static inline bool is_acpi_coresight_graph_guid(const union acpi_object *obj)
+static bool is_acpi_coresight_graph_guid(const union acpi_object *obj)
{
return acpi_guid_matches(obj, &coresight_graph_uuid);
}
-static inline bool is_acpi_coresight_graph(const union acpi_object *obj)
+static bool is_acpi_coresight_graph(const union acpi_object *obj)
{
const union acpi_object *graphid, *guid, *links;
@@ -472,7 +469,7 @@ static inline bool is_acpi_coresight_graph(const union acpi_object *obj)
* }, // End of ACPI Graph Property
* })
*/
-static inline bool acpi_validate_dsd_graph(const union acpi_object *graph)
+static bool acpi_validate_dsd_graph(const union acpi_object *graph)
{
int i, n;
const union acpi_object *rev, *nr_graphs;
@@ -518,19 +515,18 @@ static inline bool acpi_validate_dsd_graph(const union acpi_object *graph)
/* acpi_get_dsd_graph - Find the _DSD Graph property for the given device. */
static const union acpi_object *
-acpi_get_dsd_graph(struct acpi_device *adev)
+acpi_get_dsd_graph(struct acpi_device *adev, struct acpi_buffer *buf)
{
int i;
- struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
acpi_status status;
const union acpi_object *dsd;
status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL,
- &buf, ACPI_TYPE_PACKAGE);
+ buf, ACPI_TYPE_PACKAGE);
if (ACPI_FAILURE(status))
return NULL;
- dsd = buf.pointer;
+ dsd = buf->pointer;
/*
* _DSD property consists tuples { Prop_UUID, Package() }
@@ -557,7 +553,7 @@ acpi_get_dsd_graph(struct acpi_device *adev)
return NULL;
}
-static inline bool
+static bool
acpi_validate_coresight_graph(const union acpi_object *cs_graph)
{
int nlinks;
@@ -581,12 +577,12 @@ acpi_validate_coresight_graph(const union acpi_object *cs_graph)
* returns NULL.
*/
static const union acpi_object *
-acpi_get_coresight_graph(struct acpi_device *adev)
+acpi_get_coresight_graph(struct acpi_device *adev, struct acpi_buffer *buf)
{
const union acpi_object *graph_list, *graph;
int i, nr_graphs;
- graph_list = acpi_get_dsd_graph(adev);
+ graph_list = acpi_get_dsd_graph(adev, buf);
if (!graph_list)
return graph_list;
@@ -649,8 +645,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 +658,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,73 +679,57 @@ 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 ret = 0;
+ int i, nlinks;
const union acpi_object *graph;
- struct coresight_connection *conns, *ptr;
+ struct coresight_connection conn, zero_conn = {};
+ struct coresight_connection *new_conn;
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
- pdata->nr_inport = pdata->nr_outport = 0;
- graph = acpi_get_coresight_graph(adev);
+ graph = acpi_get_coresight_graph(adev, &buf);
+ /*
+ * There are no graph connections, which is fine for some components.
+ * e.g., ETE
+ */
if (!graph)
- return -ENOENT;
+ goto free;
nlinks = graph->package.elements[2].integer.value;
if (!nlinks)
- return 0;
+ goto free;
- /*
- * 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);
- if (dir < 0)
- return dir;
+ conn = zero_conn;
+ dir = acpi_coresight_parse_link(adev, link, &conn);
+ if (dir < 0) {
+ ret = dir;
+ goto free;
+ }
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)) {
+ ret = PTR_ERR(new_conn);
+ goto free;
+ }
}
}
- 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;
+free:
+ /*
+ * When ACPI fails to alloc a buffer, it will free the buffer
+ * created via ACPI_ALLOCATE_BUFFER and set to NULL.
+ * ACPI_FREE can handle NULL pointers, so free it directly.
+ */
+ ACPI_FREE(buf.pointer);
+ return ret;
}
/*
@@ -809,19 +789,19 @@ 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
-static inline int
+static int
acpi_get_coresight_platform_data(struct device *dev,
struct coresight_platform_data *pdata)
{
return -ENOENT;
}
-static inline int acpi_coresight_get_cpu(struct device *dev)
+static int acpi_coresight_get_cpu(struct device *dev)
{
return -ENODEV;
}
@@ -837,6 +817,12 @@ int coresight_get_cpu(struct device *dev)
}
EXPORT_SYMBOL_GPL(coresight_get_cpu);
+int coresight_get_static_trace_id(struct device *dev, u32 *id)
+{
+ return fwnode_property_read_u32(dev_fwnode(dev), "arm,static-trace-id", id);
+}
+EXPORT_SYMBOL_GPL(coresight_get_static_trace_id);
+
struct coresight_platform_data *
coresight_get_platform_data(struct device *dev)
{
@@ -863,7 +849,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);