@@ -1349,6 +1349,17 @@ static int coresight_orphan_match(struct device *dev, void *data)
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;
@@ -1370,58 +1381,43 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
csdev, coresight_orphan_match);
}
-static int coresight_remove_match(struct device *dev, void *data)
+/* coresight_remove_conns - Remove other device's references to this device */
+static void coresight_remove_conns(struct coresight_device *csdev)
{
- int i;
- struct coresight_device *csdev, *iterator;
+ int i, j;
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.
+ * Remove the input connection references from the destination device
+ * for each output connection.
*/
- for (i = 0; i < iterator->pdata->nr_outconns; i++) {
- conn = iterator->pdata->out_conns[i];
-
- /* Child_dev being set signifies that the links were made */
- if (csdev->dev.fwnode == conn->dest_fwnode && conn->dest_dev) {
- iterator->orphan = true;
- coresight_remove_links(iterator, conn);
- conn->dest_dev = NULL;
- /* No need to continue */
- break;
- }
+ 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;
+ }
}
/*
- * Returning '0' ensures that all known component on the
- * bus will be checked.
+ * 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.
*/
- return 0;
-}
+ for (i = 0; i < csdev->pdata->nr_inconns; ++i) {
+ conn = csdev->pdata->in_conns[i];
+ /* Input conns array is sparse */
+ if (!conn)
+ continue;
-/*
- * coresight_remove_conns - Remove references to this given devices
- * from the connections of other devices.
- */
-static void coresight_remove_conns(struct coresight_device *csdev)
-{
- /*
- * 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.
- */
- if (csdev->pdata->high_inport)
- bus_for_each_dev(&coresight_bustype, NULL,
- csdev, coresight_remove_match);
+ conn->src_dev->orphan = true;
+ coresight_remove_links(conn->src_dev, conn);
+ conn->dest_dev = NULL;
+ }
}
/**
@@ -1532,6 +1528,7 @@ void coresight_release_platform_data(struct coresight_device *csdev,
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);
@@ -70,6 +70,35 @@ coresight_add_out_conn(struct device *dev,
}
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)
{
@@ -240,7 +269,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.
*
@@ -173,12 +173,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->dest_dev = target;
return 0;
} while (0);
@@ -202,5 +196,4 @@ void coresight_remove_links(struct coresight_device *orig,
devm_kfree(&orig->dev, conn->link->orig_name);
devm_kfree(&orig->dev, conn->link);
conn->link = NULL;
- conn->dest_dev = NULL;
}
@@ -106,6 +106,9 @@ union coresight_dev_subtype {
* @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 high_inport;
@@ -113,6 +116,7 @@ struct coresight_platform_data {
int nr_inconns;
int nr_outconns;
struct coresight_connection **out_conns;
+ struct coresight_connection **in_conns;
};
/**
@@ -173,6 +177,26 @@ struct coresight_desc {
* @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 src_port;
@@ -180,6 +204,7 @@ struct coresight_connection {
struct fwnode_handle *dest_fwnode;
struct coresight_device *dest_dev;
struct coresight_sysfs_link *link;
+ struct coresight_device *src_dev;
};
/**
@@ -616,5 +641,6 @@ 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);
#endif /* _LINUX_COREISGHT_H */