1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* Copyright (C) 2021-2022 Linaro Ltd. */ |
4 | |
5 | #include <linux/kernel.h> |
6 | #include <linux/types.h> |
7 | #include <linux/device.h> |
8 | #include <linux/sysfs.h> |
9 | |
10 | #include "ipa.h" |
11 | #include "ipa_version.h" |
12 | #include "ipa_sysfs.h" |
13 | |
14 | static const char *ipa_version_string(struct ipa *ipa) |
15 | { |
16 | switch (ipa->version) { |
17 | case IPA_VERSION_3_0: |
18 | return "3.0" ; |
19 | case IPA_VERSION_3_1: |
20 | return "3.1" ; |
21 | case IPA_VERSION_3_5: |
22 | return "3.5" ; |
23 | case IPA_VERSION_3_5_1: |
24 | return "3.5.1" ; |
25 | case IPA_VERSION_4_0: |
26 | return "4.0" ; |
27 | case IPA_VERSION_4_1: |
28 | return "4.1" ; |
29 | case IPA_VERSION_4_2: |
30 | return "4.2" ; |
31 | case IPA_VERSION_4_5: |
32 | return "4.5" ; |
33 | case IPA_VERSION_4_7: |
34 | return "4.7" ; |
35 | case IPA_VERSION_4_9: |
36 | return "4.9" ; |
37 | case IPA_VERSION_4_11: |
38 | return "4.11" ; |
39 | case IPA_VERSION_5_0: |
40 | return "5.0" ; |
41 | default: |
42 | return "0.0" ; /* Won't happen (checked at probe time) */ |
43 | } |
44 | } |
45 | |
46 | static ssize_t |
47 | version_show(struct device *dev, struct device_attribute *attr, char *buf) |
48 | { |
49 | struct ipa *ipa = dev_get_drvdata(dev); |
50 | |
51 | return sysfs_emit(buf, fmt: "%s\n" , ipa_version_string(ipa)); |
52 | } |
53 | |
54 | static DEVICE_ATTR_RO(version); |
55 | |
56 | static struct attribute *ipa_attrs[] = { |
57 | &dev_attr_version.attr, |
58 | NULL |
59 | }; |
60 | |
61 | const struct attribute_group ipa_attribute_group = { |
62 | .attrs = ipa_attrs, |
63 | }; |
64 | |
65 | static const char *ipa_offload_string(struct ipa *ipa) |
66 | { |
67 | return ipa->version < IPA_VERSION_4_5 ? "MAPv4" : "MAPv5" ; |
68 | } |
69 | |
70 | static ssize_t rx_offload_show(struct device *dev, |
71 | struct device_attribute *attr, char *buf) |
72 | { |
73 | struct ipa *ipa = dev_get_drvdata(dev); |
74 | |
75 | return sysfs_emit(buf, fmt: "%s\n" , ipa_offload_string(ipa)); |
76 | } |
77 | |
78 | static DEVICE_ATTR_RO(rx_offload); |
79 | |
80 | static ssize_t tx_offload_show(struct device *dev, |
81 | struct device_attribute *attr, char *buf) |
82 | { |
83 | struct ipa *ipa = dev_get_drvdata(dev); |
84 | |
85 | return sysfs_emit(buf, fmt: "%s\n" , ipa_offload_string(ipa)); |
86 | } |
87 | |
88 | static DEVICE_ATTR_RO(tx_offload); |
89 | |
90 | static struct attribute *ipa_feature_attrs[] = { |
91 | &dev_attr_rx_offload.attr, |
92 | &dev_attr_tx_offload.attr, |
93 | NULL |
94 | }; |
95 | |
96 | const struct attribute_group ipa_feature_attribute_group = { |
97 | .name = "feature" , |
98 | .attrs = ipa_feature_attrs, |
99 | }; |
100 | |
101 | static umode_t ipa_endpoint_id_is_visible(struct kobject *kobj, |
102 | struct attribute *attr, int n) |
103 | { |
104 | struct ipa *ipa = dev_get_drvdata(kobj_to_dev(kobj)); |
105 | struct device_attribute *dev_attr; |
106 | struct dev_ext_attribute *ea; |
107 | bool visible; |
108 | |
109 | /* An endpoint id attribute is only visible if it's defined */ |
110 | dev_attr = container_of(attr, struct device_attribute, attr); |
111 | ea = container_of(dev_attr, struct dev_ext_attribute, attr); |
112 | |
113 | visible = !!ipa->name_map[(enum ipa_endpoint_name)(uintptr_t)ea->var]; |
114 | |
115 | return visible ? attr->mode : 0; |
116 | } |
117 | |
118 | static ssize_t endpoint_id_attr_show(struct device *dev, |
119 | struct device_attribute *attr, char *buf) |
120 | { |
121 | struct ipa *ipa = dev_get_drvdata(dev); |
122 | struct ipa_endpoint *endpoint; |
123 | struct dev_ext_attribute *ea; |
124 | |
125 | ea = container_of(attr, struct dev_ext_attribute, attr); |
126 | endpoint = ipa->name_map[(enum ipa_endpoint_name)(uintptr_t)ea->var]; |
127 | |
128 | return sysfs_emit(buf, fmt: "%u\n" , endpoint->endpoint_id); |
129 | } |
130 | |
131 | #define ENDPOINT_ID_ATTR(_n, _endpoint_name) \ |
132 | static struct dev_ext_attribute dev_attr_endpoint_id_ ## _n = { \ |
133 | .attr = __ATTR(_n, 0444, endpoint_id_attr_show, NULL), \ |
134 | .var = (void *)(_endpoint_name), \ |
135 | } |
136 | |
137 | ENDPOINT_ID_ATTR(modem_rx, IPA_ENDPOINT_AP_MODEM_RX); |
138 | ENDPOINT_ID_ATTR(modem_tx, IPA_ENDPOINT_AP_MODEM_TX); |
139 | |
140 | static struct attribute *ipa_endpoint_id_attrs[] = { |
141 | &dev_attr_endpoint_id_modem_rx.attr.attr, |
142 | &dev_attr_endpoint_id_modem_tx.attr.attr, |
143 | NULL |
144 | }; |
145 | |
146 | const struct attribute_group ipa_endpoint_id_attribute_group = { |
147 | .name = "endpoint_id" , |
148 | .is_visible = ipa_endpoint_id_is_visible, |
149 | .attrs = ipa_endpoint_id_attrs, |
150 | }; |
151 | |
152 | /* Reuse endpoint ID attributes for the legacy modem endpoint IDs */ |
153 | #define MODEM_ATTR(_n, _endpoint_name) \ |
154 | static struct dev_ext_attribute dev_attr_modem_ ## _n = { \ |
155 | .attr = __ATTR(_n, 0444, endpoint_id_attr_show, NULL), \ |
156 | .var = (void *)(_endpoint_name), \ |
157 | } |
158 | |
159 | MODEM_ATTR(rx_endpoint_id, IPA_ENDPOINT_AP_MODEM_RX); |
160 | MODEM_ATTR(tx_endpoint_id, IPA_ENDPOINT_AP_MODEM_TX); |
161 | |
162 | static struct attribute *ipa_modem_attrs[] = { |
163 | &dev_attr_modem_rx_endpoint_id.attr.attr, |
164 | &dev_attr_modem_tx_endpoint_id.attr.attr, |
165 | NULL, |
166 | }; |
167 | |
168 | const struct attribute_group ipa_modem_attribute_group = { |
169 | .name = "modem" , |
170 | .attrs = ipa_modem_attrs, |
171 | }; |
172 | |