| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| */ |
| |
| #include <linux/acpi.h> |
| #include <linux/types.h> |
| #include <linux/err.h> |
| #include <linux/slab.h> |
| #include <linux/clk.h> |
| #include <linux/of.h> |
| #include <linux/of_graph.h> |
| #include <linux/platform_device.h> |
| #include <linux/amba/bus.h> |
| #include <linux/coresight.h> |
| #include <linux/cpumask.h> |
| #include <asm/smp_plat.h> |
| |
| #include "coresight-priv.h" |
| |
| /* |
| * 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 |
| */ |
| struct coresight_connection * |
| coresight_add_out_conn(struct device *dev, |
| struct coresight_platform_data *pdata, |
| const struct coresight_connection *new_conn) |
| { |
| 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) |
| { |
| struct device *dev = NULL; |
| |
| /* |
| * If we have a non-configurable replicator, it will be found on the |
| * platform bus. |
| */ |
| dev = bus_find_device_by_fwnode(&platform_bus_type, fwnode); |
| if (dev) |
| return dev; |
| |
| /* |
| * We have a configurable component - circle through the AMBA bus |
| * looking for the device that matches the endpoint node. |
| */ |
| return bus_find_device_by_fwnode(&amba_bustype, fwnode); |
| } |
| |
| /* |
| * Find a registered coresight device from a device fwnode. |
| * The node info is associated with the AMBA parent, but the |
| * csdev keeps a copy so iterate round the coresight bus to |
| * find the device. |
| */ |
| struct coresight_device * |
| coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode) |
| { |
| struct device *dev; |
| struct coresight_device *csdev = NULL; |
| |
| dev = bus_find_device_by_fwnode(&coresight_bustype, r_fwnode); |
| if (dev) { |
| csdev = to_coresight_device(dev); |
| put_device(dev); |
| } |
| return csdev; |
| } |
| EXPORT_SYMBOL_GPL(coresight_find_csdev_by_fwnode); |
| |
| #ifdef CONFIG_OF |
| static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep) |
| { |
| return of_property_read_bool(ep, "slave-mode"); |
| } |
| |
| static struct device_node *of_coresight_get_port_parent(struct device_node *ep) |
| { |
| struct device_node *parent = of_graph_get_port_parent(ep); |
| |
| /* |
| * Skip one-level up to the real device node, if we |
| * are using the new bindings. |
| */ |
| if (of_node_name_eq(parent, "in-ports") || |
| of_node_name_eq(parent, "out-ports")) |
| parent = of_get_next_parent(parent); |
| |
| return parent; |
| } |
| |
| 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 int of_coresight_get_cpu(struct device *dev) |
| { |
| int cpu; |
| struct device_node *dn; |
| |
| if (!dev->of_node) |
| return -ENODEV; |
| |
| dn = of_parse_phandle(dev->of_node, "cpu", 0); |
| if (!dn) |
| return -ENODEV; |
| |
| cpu = of_cpu_node_to_id(dn); |
| of_node_put(dn); |
| |
| return cpu; |
| } |
| |
| /* |
| * of_coresight_parse_endpoint : Parse the given output endpoint @ep |
| * and fill the connection information in @pdata->out_conns |
| * |
| * Parses the local port, remote device name and the remote port. |
| * |
| * Returns : |
| * 0 - If the parsing completed without any fatal errors. |
| * -Errno - Fatal error, abort the scanning. |
| */ |
| static int of_coresight_parse_endpoint(struct device *dev, |
| struct device_node *ep, |
| struct coresight_platform_data *pdata) |
| { |
| int ret = 0; |
| struct of_endpoint endpoint, rendpoint; |
| struct device_node *rparent = NULL; |
| struct device_node *rep = NULL; |
| struct device *rdev = NULL; |
| struct fwnode_handle *rdev_fwnode; |
| struct coresight_connection conn = {}; |
| struct coresight_connection *new_conn; |
| |
| do { |
| /* Parse the local port details */ |
| if (of_graph_parse_endpoint(ep, &endpoint)) |
| break; |
| /* |
| * Get a handle on the remote endpoint and the device it is |
| * attached to. |
| */ |
| rep = of_graph_get_remote_endpoint(ep); |
| if (!rep) |
| break; |
| rparent = of_coresight_get_port_parent(rep); |
| if (!rparent) |
| break; |
| if (of_graph_parse_endpoint(rep, &rendpoint)) |
| break; |
| |
| rdev_fwnode = of_fwnode_handle(rparent); |
| /* If the remote device is not available, defer probing */ |
| rdev = coresight_find_device_by_fwnode(rdev_fwnode); |
| if (!rdev) { |
| ret = -EPROBE_DEFER; |
| break; |
| } |
| |
| conn.src_port = endpoint.port; |
| /* |
| * Hold the refcount to the target device. This could be |
| * released via: |
| * 1) coresight_release_platform_data() if the probe fails or |
| * this device is unregistered. |
| * 2) While removing the target device via |
| * coresight_remove_match() |
| */ |
| 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); |
| |
| of_node_put(rparent); |
| of_node_put(rep); |
| put_device(rdev); |
| |
| return ret; |
| } |
| |
| static int of_get_coresight_platform_data(struct device *dev, |
| struct coresight_platform_data *pdata) |
| { |
| int ret = 0; |
| struct device_node *ep = NULL; |
| const struct device_node *parent = NULL; |
| bool legacy_binding = false; |
| struct device_node *node = dev->of_node; |
| |
| parent = of_coresight_get_output_ports_node(node); |
| /* |
| * If the DT uses obsoleted bindings, the ports are listed |
| * under the device and we need to filter out the input |
| * 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 */ |
| 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 |
| * with legacy binding, as they processed with their |
| * connected output ports. |
| */ |
| if (legacy_binding && of_coresight_legacy_ep_is_input(ep)) |
| continue; |
| |
| ret = of_coresight_parse_endpoint(dev, ep, pdata); |
| if (ret) { |
| of_node_put(ep); |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| #else |
| static inline 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) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| #ifdef CONFIG_ACPI |
| |
| #include <acpi/actypes.h> |
| #include <acpi/processor.h> |
| |
| /* ACPI Graph _DSD UUID : "ab02a46b-74c7-45a2-bd68-f7d344ef2153" */ |
| static const guid_t acpi_graph_uuid = GUID_INIT(0xab02a46b, 0x74c7, 0x45a2, |
| 0xbd, 0x68, 0xf7, 0xd3, |
| 0x44, 0xef, 0x21, 0x53); |
| /* Coresight ACPI Graph UUID : "3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd" */ |
| static const guid_t coresight_graph_uuid = GUID_INIT(0x3ecbc8b6, 0x1d0e, 0x4fb3, |
| 0x81, 0x07, 0xe6, 0x27, |
| 0xf8, 0x05, 0xc6, 0xcd); |
| #define ACPI_CORESIGHT_LINK_SLAVE 0 |
| #define ACPI_CORESIGHT_LINK_MASTER 1 |
| |
| static inline bool is_acpi_guid(const union acpi_object *obj) |
| { |
| return (obj->type == ACPI_TYPE_BUFFER) && (obj->buffer.length == 16); |
| } |
| |
| /* |
| * 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, |
| 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) |
| { |
| return acpi_guid_matches(obj, &acpi_graph_uuid); |
| } |
| |
| static inline 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) |
| { |
| const union acpi_object *graphid, *guid, *links; |
| |
| if (obj->type != ACPI_TYPE_PACKAGE || |
| obj->package.count < 3) |
| return false; |
| |
| graphid = &obj->package.elements[0]; |
| guid = &obj->package.elements[1]; |
| links = &obj->package.elements[2]; |
| |
| if (graphid->type != ACPI_TYPE_INTEGER || |
| links->type != ACPI_TYPE_INTEGER) |
| return false; |
| |
| return is_acpi_coresight_graph_guid(guid); |
| } |
| |
| /* |
| * acpi_validate_dsd_graph - Make sure the given _DSD graph conforms |
| * to the ACPI _DSD Graph specification. |
| * |
| * ACPI Devices Graph property has the following format: |
| * { |
| * Revision - Integer, must be 0 |
| * NumberOfGraphs - Integer, N indicating the following list. |
| * Graph[1], |
| * ... |
| * Graph[N] |
| * } |
| * |
| * And each Graph entry has the following format: |
| * { |
| * GraphID - Integer, identifying a graph the device belongs to. |
| * UUID - UUID identifying the specification that governs |
| * this graph. (e.g, see is_acpi_coresight_graph()) |
| * NumberOfLinks - Number "N" of connections on this node of the graph. |
| * Links[1] |
| * ... |
| * Links[N] |
| * } |
| * |
| * Where each "Links" entry has the following format: |
| * |
| * { |
| * SourcePortAddress - Integer |
| * DestinationPortAddress - Integer |
| * DestinationDeviceName - Reference to another device |
| * ( --- CoreSight specific extensions below ---) |
| * DirectionOfFlow - Integer 1 for output(master) |
| * 0 for input(slave) |
| * } |
| * |
| * e.g: |
| * For a Funnel device |
| * |
| * Device(MFUN) { |
| * ... |
| * |
| * Name (_DSD, Package() { |
| * // DSD Package contains tuples of { Proeprty_Type_UUID, Package() } |
| * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), //Std. Property UUID |
| * Package() { |
| * Package(2) { "property-name", <property-value> } |
| * }, |
| * |
| * ToUUID("ab02a46b-74c7-45a2-bd68-f7d344ef2153"), // ACPI Graph UUID |
| * Package() { |
| * 0, // Revision |
| * 1, // NumberOfGraphs. |
| * Package() { // Graph[0] Package |
| * 1, // GraphID |
| * // Coresight Graph UUID |
| * ToUUID("3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd"), |
| * 3, // NumberOfLinks aka ports |
| * // Link[0]: Output_0 -> Replicator:Input_0 |
| * Package () { 0, 0, \_SB_.RPL0, 1 }, |
| * // Link[1]: Input_0 <- Cluster0_Funnel0:Output_0 |
| * Package () { 0, 0, \_SB_.CLU0.FUN0, 0 }, |
| * // Link[2]: Input_1 <- Cluster1_Funnel0:Output_0 |
| * Package () { 1, 0, \_SB_.CLU1.FUN0, 0 }, |
| * } // End of Graph[0] Package |
| * |
| * }, // End of ACPI Graph Property |
| * }) |
| */ |
| static inline bool acpi_validate_dsd_graph(const union acpi_object *graph) |
| { |
| int i, n; |
| const union acpi_object *rev, *nr_graphs; |
| |
| /* The graph must contain at least the Revision and Number of Graphs */ |
| if (graph->package.count < 2) |
| return false; |
| |
| rev = &graph->package.elements[0]; |
| nr_graphs = &graph->package.elements[1]; |
| |
| if (rev->type != ACPI_TYPE_INTEGER || |
| nr_graphs->type != ACPI_TYPE_INTEGER) |
| return false; |
| |
| /* We only support revision 0 */ |
| if (rev->integer.value != 0) |
| return false; |
| |
| n = nr_graphs->integer.value; |
| /* CoreSight devices are only part of a single Graph */ |
| if (n != 1) |
| return false; |
| |
| /* Make sure the ACPI graph package has right number of elements */ |
| if (graph->package.count != (n + 2)) |
| return false; |
| |
| /* |
| * Each entry must be a graph package with at least 3 members : |
| * { GraphID, UUID, NumberOfLinks(n), Links[.],... } |
| */ |
| for (i = 2; i < n + 2; i++) { |
| const union acpi_object *obj = &graph->package.elements[i]; |
| |
| if (obj->type != ACPI_TYPE_PACKAGE || |
| obj->package.count < 3) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* 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, struct acpi_buffer *buf) |
| { |
| int i; |
| acpi_status status; |
| const union acpi_object *dsd; |
| |
| status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, |
| buf, ACPI_TYPE_PACKAGE); |
| if (ACPI_FAILURE(status)) |
| return NULL; |
| |
| dsd = buf->pointer; |
| |
| /* |
| * _DSD property consists tuples { Prop_UUID, Package() } |
| * Iterate through all the packages and find the Graph. |
| */ |
| for (i = 0; i + 1 < dsd->package.count; i += 2) { |
| const union acpi_object *guid, *package; |
| |
| guid = &dsd->package.elements[i]; |
| package = &dsd->package.elements[i + 1]; |
| |
| /* All _DSD elements must have a UUID and a Package */ |
| if (!is_acpi_guid(guid) || package->type != ACPI_TYPE_PACKAGE) |
| break; |
| /* Skip the non-Graph _DSD packages */ |
| if (!is_acpi_dsd_graph_guid(guid)) |
| continue; |
| if (acpi_validate_dsd_graph(package)) |
| return package; |
| /* Invalid graph format, continue */ |
| dev_warn(&adev->dev, "Invalid Graph _DSD property\n"); |
| } |
| |
| return NULL; |
| } |
| |
| static inline bool |
| acpi_validate_coresight_graph(const union acpi_object *cs_graph) |
| { |
| int nlinks; |
| |
| nlinks = cs_graph->package.elements[2].integer.value; |
| /* |
| * Graph must have the following fields : |
| * { GraphID, GraphUUID, NumberOfLinks, Links... } |
| */ |
| if (cs_graph->package.count != (nlinks + 3)) |
| return false; |
| /* The links are validated in acpi_coresight_parse_link() */ |
| return true; |
| } |
| |
| /* |
| * acpi_get_coresight_graph - Parse the device _DSD tables and find |
| * the Graph property matching the CoreSight Graphs. |
| * |
| * Returns the pointer to the CoreSight Graph Package when found. Otherwise |
| * returns NULL. |
| */ |
| static const union acpi_object * |
| 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, buf); |
| if (!graph_list) |
| return graph_list; |
| |
| nr_graphs = graph_list->package.elements[1].integer.value; |
| |
| for (i = 2; i < nr_graphs + 2; i++) { |
| graph = &graph_list->package.elements[i]; |
| if (!is_acpi_coresight_graph(graph)) |
| continue; |
| if (acpi_validate_coresight_graph(graph)) |
| return graph; |
| /* Invalid graph format */ |
| break; |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * acpi_coresight_parse_link - Parse the given Graph connection |
| * of the device and populate the coresight_connection for an output |
| * connection. |
| * |
| * CoreSight Graph specification mandates that the direction of the data |
| * flow must be specified in the link. i.e, |
| * |
| * SourcePortAddress, // Integer |
| * DestinationPortAddress, // Integer |
| * DestinationDeviceName, // Reference to another device |
| * DirectionOfFlow, // 1 for output(master), 0 for input(slave) |
| * |
| * Returns the direction of the data flow [ Input(slave) or Output(master) ] |
| * upon success. |
| * Returns an negative error number otherwise. |
| */ |
| static int acpi_coresight_parse_link(struct acpi_device *adev, |
| const union acpi_object *link, |
| struct coresight_connection *conn) |
| { |
| int dir; |
| const union acpi_object *fields; |
| struct acpi_device *r_adev; |
| struct device *rdev; |
| |
| if (link->type != ACPI_TYPE_PACKAGE || |
| link->package.count != 4) |
| return -EINVAL; |
| |
| fields = link->package.elements; |
| |
| if (fields[0].type != ACPI_TYPE_INTEGER || |
| fields[1].type != ACPI_TYPE_INTEGER || |
| fields[2].type != ACPI_TYPE_LOCAL_REFERENCE || |
| fields[3].type != ACPI_TYPE_INTEGER) |
| return -EINVAL; |
| |
| r_adev = acpi_fetch_acpi_dev(fields[2].reference.handle); |
| if (!r_adev) |
| return -ENODEV; |
| |
| dir = fields[3].integer.value; |
| if (dir == ACPI_CORESIGHT_LINK_MASTER) { |
| 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; |
| /* |
| * Hold the refcount to the target device. This could be |
| * released via: |
| * 1) coresight_release_platform_data() if the probe fails or |
| * this device is unregistered. |
| * 2) While removing the target device via |
| * coresight_remove_match(). |
| */ |
| 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->dest_port = fields[0].integer.value; |
| } else { |
| /* Invalid direction */ |
| return -EINVAL; |
| } |
| |
| return dir; |
| } |
| |
| /* |
| * acpi_coresight_parse_graph - Parse the _DSD CoreSight graph |
| * connection information and populate the supplied coresight_platform_data |
| * instance. |
| */ |
| static int acpi_coresight_parse_graph(struct device *dev, |
| struct acpi_device *adev, |
| struct coresight_platform_data *pdata) |
| { |
| int ret = 0; |
| int i, nlinks; |
| const union acpi_object *graph; |
| struct coresight_connection conn, zero_conn = {}; |
| struct coresight_connection *new_conn; |
| struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; |
| |
| graph = acpi_get_coresight_graph(adev, &buf); |
| /* |
| * There are no graph connections, which is fine for some components. |
| * e.g., ETE |
| */ |
| if (!graph) |
| goto free; |
| |
| nlinks = graph->package.elements[2].integer.value; |
| if (!nlinks) |
| goto free; |
| |
| for (i = 0; i < nlinks; i++) { |
| const union acpi_object *link = &graph->package.elements[3 + i]; |
| int 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) { |
| new_conn = coresight_add_out_conn(dev, pdata, &conn); |
| if (IS_ERR(new_conn)) { |
| ret = PTR_ERR(new_conn); |
| goto free; |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| /* |
| * acpi_handle_to_logical_cpuid - Map a given acpi_handle to the |
| * logical CPU id of the corresponding CPU device. |
| * |
| * Returns the logical CPU id when found. Otherwise returns >= nr_cpus_id. |
| */ |
| static int |
| acpi_handle_to_logical_cpuid(acpi_handle handle) |
| { |
| int i; |
| struct acpi_processor *pr; |
| |
| for_each_possible_cpu(i) { |
| pr = per_cpu(processors, i); |
| if (pr && pr->handle == handle) |
| break; |
| } |
| |
| return i; |
| } |
| |
| /* |
| * acpi_coresigh_get_cpu - Find the logical CPU id of the CPU associated |
| * with this coresight device. With ACPI bindings, the CoreSight components |
| * are listed as child device of the associated CPU. |
| * |
| * Returns the logical CPU id when found. Otherwise returns 0. |
| */ |
| static int acpi_coresight_get_cpu(struct device *dev) |
| { |
| int cpu; |
| acpi_handle cpu_handle; |
| acpi_status status; |
| struct acpi_device *adev = ACPI_COMPANION(dev); |
| |
| if (!adev) |
| return -ENODEV; |
| status = acpi_get_parent(adev->handle, &cpu_handle); |
| if (ACPI_FAILURE(status)) |
| return -ENODEV; |
| |
| cpu = acpi_handle_to_logical_cpuid(cpu_handle); |
| if (cpu >= nr_cpu_ids) |
| return -ENODEV; |
| return cpu; |
| } |
| |
| static int |
| acpi_get_coresight_platform_data(struct device *dev, |
| struct coresight_platform_data *pdata) |
| { |
| struct acpi_device *adev; |
| |
| adev = ACPI_COMPANION(dev); |
| if (!adev) |
| return -EINVAL; |
| |
| return acpi_coresight_parse_graph(dev, adev, pdata); |
| } |
| |
| #else |
| |
| static inline 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) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| int coresight_get_cpu(struct device *dev) |
| { |
| if (is_of_node(dev->fwnode)) |
| return of_coresight_get_cpu(dev); |
| else if (is_acpi_device_node(dev->fwnode)) |
| return acpi_coresight_get_cpu(dev); |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(coresight_get_cpu); |
| |
| struct coresight_platform_data * |
| coresight_get_platform_data(struct device *dev) |
| { |
| int ret = -ENOENT; |
| struct coresight_platform_data *pdata = NULL; |
| struct fwnode_handle *fwnode = dev_fwnode(dev); |
| |
| if (IS_ERR_OR_NULL(fwnode)) |
| goto error; |
| |
| pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| if (is_of_node(fwnode)) |
| ret = of_get_coresight_platform_data(dev, pdata); |
| else if (is_acpi_device_node(fwnode)) |
| ret = acpi_get_coresight_platform_data(dev, pdata); |
| |
| if (!ret) |
| return pdata; |
| error: |
| if (!IS_ERR_OR_NULL(pdata)) |
| /* Cleanup the connection information */ |
| coresight_release_platform_data(NULL, dev, pdata); |
| return ERR_PTR(ret); |
| } |
| EXPORT_SYMBOL_GPL(coresight_get_platform_data); |