1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com |
4 | * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/device.h> |
10 | #include <linux/init.h> |
11 | #include <linux/mutex.h> |
12 | #include <linux/of.h> |
13 | #include <linux/sys_soc.h> |
14 | |
15 | #include "k3-psil-priv.h" |
16 | |
17 | static DEFINE_MUTEX(ep_map_mutex); |
18 | static const struct psil_ep_map *soc_ep_map; |
19 | |
20 | static const struct soc_device_attribute k3_soc_devices[] = { |
21 | { .family = "AM65X" , .data = &am654_ep_map }, |
22 | { .family = "J721E" , .data = &j721e_ep_map }, |
23 | { .family = "J7200" , .data = &j7200_ep_map }, |
24 | { .family = "AM64X" , .data = &am64_ep_map }, |
25 | { .family = "J721S2" , .data = &j721s2_ep_map }, |
26 | { .family = "AM62X" , .data = &am62_ep_map }, |
27 | { .family = "AM62AX" , .data = &am62a_ep_map }, |
28 | { .family = "J784S4" , .data = &j784s4_ep_map }, |
29 | { .family = "AM62PX" , .data = &am62p_ep_map }, |
30 | { .family = "J722S" , .data = &am62p_ep_map }, |
31 | { /* sentinel */ } |
32 | }; |
33 | |
34 | struct psil_endpoint_config *psil_get_ep_config(u32 thread_id) |
35 | { |
36 | int i; |
37 | |
38 | mutex_lock(&ep_map_mutex); |
39 | if (!soc_ep_map) { |
40 | const struct soc_device_attribute *soc; |
41 | |
42 | soc = soc_device_match(matches: k3_soc_devices); |
43 | if (soc) { |
44 | soc_ep_map = soc->data; |
45 | } else { |
46 | pr_err("PSIL: No compatible machine found for map\n" ); |
47 | mutex_unlock(lock: &ep_map_mutex); |
48 | return ERR_PTR(error: -ENOTSUPP); |
49 | } |
50 | pr_debug("%s: Using map for %s\n" , __func__, soc_ep_map->name); |
51 | } |
52 | mutex_unlock(lock: &ep_map_mutex); |
53 | |
54 | if (thread_id & K3_PSIL_DST_THREAD_ID_OFFSET && soc_ep_map->dst) { |
55 | /* check in destination thread map */ |
56 | for (i = 0; i < soc_ep_map->dst_count; i++) { |
57 | if (soc_ep_map->dst[i].thread_id == thread_id) |
58 | return &soc_ep_map->dst[i].ep_config; |
59 | } |
60 | } |
61 | |
62 | thread_id &= ~K3_PSIL_DST_THREAD_ID_OFFSET; |
63 | if (soc_ep_map->src) { |
64 | for (i = 0; i < soc_ep_map->src_count; i++) { |
65 | if (soc_ep_map->src[i].thread_id == thread_id) |
66 | return &soc_ep_map->src[i].ep_config; |
67 | } |
68 | } |
69 | |
70 | return ERR_PTR(error: -ENOENT); |
71 | } |
72 | EXPORT_SYMBOL_GPL(psil_get_ep_config); |
73 | |
74 | int psil_set_new_ep_config(struct device *dev, const char *name, |
75 | struct psil_endpoint_config *ep_config) |
76 | { |
77 | struct psil_endpoint_config *dst_ep_config; |
78 | struct of_phandle_args dma_spec; |
79 | u32 thread_id; |
80 | int index; |
81 | |
82 | if (!dev || !dev->of_node) |
83 | return -EINVAL; |
84 | |
85 | index = of_property_match_string(np: dev->of_node, propname: "dma-names" , string: name); |
86 | if (index < 0) |
87 | return index; |
88 | |
89 | if (of_parse_phandle_with_args(np: dev->of_node, list_name: "dmas" , cells_name: "#dma-cells" , |
90 | index, out_args: &dma_spec)) |
91 | return -ENOENT; |
92 | |
93 | thread_id = dma_spec.args[0]; |
94 | |
95 | dst_ep_config = psil_get_ep_config(thread_id); |
96 | if (IS_ERR(ptr: dst_ep_config)) { |
97 | pr_err("PSIL: thread ID 0x%04x not defined in map\n" , |
98 | thread_id); |
99 | of_node_put(node: dma_spec.np); |
100 | return PTR_ERR(ptr: dst_ep_config); |
101 | } |
102 | |
103 | memcpy(dst_ep_config, ep_config, sizeof(*dst_ep_config)); |
104 | |
105 | of_node_put(node: dma_spec.np); |
106 | return 0; |
107 | } |
108 | EXPORT_SYMBOL_GPL(psil_set_new_ep_config); |
109 | MODULE_LICENSE("GPL v2" ); |
110 | |