|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | #include <linux/acpi.h> |
10 | | -#include <linux/usb.h> |
11 | | -#include <linux/usb/typec.h> |
| 10 | +#include <linux/component.h> |
12 | 11 |
|
13 | 12 | #include "class.h" |
14 | 13 |
|
15 | | -struct port_node { |
16 | | - struct list_head list; |
17 | | - struct device *dev; |
18 | | - void *pld; |
19 | | -}; |
20 | | - |
21 | | -static int acpi_pld_match(const struct acpi_pld_info *pld1, |
22 | | - const struct acpi_pld_info *pld2) |
23 | | -{ |
24 | | - if (!pld1 || !pld2) |
25 | | - return 0; |
26 | | - |
27 | | - /* |
28 | | - * To speed things up, first checking only the group_position. It seems |
29 | | - * to often have the first unique value in the _PLD. |
30 | | - */ |
31 | | - if (pld1->group_position == pld2->group_position) |
32 | | - return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info)); |
33 | | - |
34 | | - return 0; |
35 | | -} |
36 | | - |
37 | | -static void *get_pld(struct device *dev) |
| 14 | +static int typec_aggregate_bind(struct device *dev) |
38 | 15 | { |
39 | | -#ifdef CONFIG_ACPI |
40 | | - struct acpi_pld_info *pld; |
41 | | - acpi_status status; |
42 | | - |
43 | | - if (!has_acpi_companion(dev)) |
44 | | - return NULL; |
45 | | - |
46 | | - status = acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld); |
47 | | - if (ACPI_FAILURE(status)) |
48 | | - return NULL; |
49 | | - |
50 | | - return pld; |
51 | | -#else |
52 | | - return NULL; |
53 | | -#endif |
54 | | -} |
55 | | - |
56 | | -static void free_pld(void *pld) |
57 | | -{ |
58 | | -#ifdef CONFIG_ACPI |
59 | | - ACPI_FREE(pld); |
60 | | -#endif |
| 16 | + return component_bind_all(dev, NULL); |
61 | 17 | } |
62 | 18 |
|
63 | | -static int __link_port(struct typec_port *con, struct port_node *node) |
| 19 | +static void typec_aggregate_unbind(struct device *dev) |
64 | 20 | { |
65 | | - int ret; |
66 | | - |
67 | | - ret = sysfs_create_link(&node->dev->kobj, &con->dev.kobj, "connector"); |
68 | | - if (ret) |
69 | | - return ret; |
70 | | - |
71 | | - ret = sysfs_create_link(&con->dev.kobj, &node->dev->kobj, |
72 | | - dev_name(node->dev)); |
73 | | - if (ret) { |
74 | | - sysfs_remove_link(&node->dev->kobj, "connector"); |
75 | | - return ret; |
76 | | - } |
77 | | - |
78 | | - list_add_tail(&node->list, &con->port_list); |
79 | | - |
80 | | - return 0; |
| 21 | + component_unbind_all(dev, NULL); |
81 | 22 | } |
82 | 23 |
|
83 | | -static int link_port(struct typec_port *con, struct port_node *node) |
84 | | -{ |
85 | | - int ret; |
86 | | - |
87 | | - mutex_lock(&con->port_list_lock); |
88 | | - ret = __link_port(con, node); |
89 | | - mutex_unlock(&con->port_list_lock); |
90 | | - |
91 | | - return ret; |
92 | | -} |
93 | | - |
94 | | -static void __unlink_port(struct typec_port *con, struct port_node *node) |
95 | | -{ |
96 | | - sysfs_remove_link(&con->dev.kobj, dev_name(node->dev)); |
97 | | - sysfs_remove_link(&node->dev->kobj, "connector"); |
98 | | - list_del(&node->list); |
99 | | -} |
100 | | - |
101 | | -static void unlink_port(struct typec_port *con, struct port_node *node) |
102 | | -{ |
103 | | - mutex_lock(&con->port_list_lock); |
104 | | - __unlink_port(con, node); |
105 | | - mutex_unlock(&con->port_list_lock); |
106 | | -} |
107 | | - |
108 | | -static struct port_node *create_port_node(struct device *port) |
109 | | -{ |
110 | | - struct port_node *node; |
111 | | - |
112 | | - node = kzalloc(sizeof(*node), GFP_KERNEL); |
113 | | - if (!node) |
114 | | - return ERR_PTR(-ENOMEM); |
115 | | - |
116 | | - node->dev = get_device(port); |
117 | | - node->pld = get_pld(port); |
118 | | - |
119 | | - return node; |
120 | | -} |
121 | | - |
122 | | -static void remove_port_node(struct port_node *node) |
123 | | -{ |
124 | | - put_device(node->dev); |
125 | | - free_pld(node->pld); |
126 | | - kfree(node); |
127 | | -} |
128 | | - |
129 | | -static int connector_match(struct device *dev, const void *data) |
130 | | -{ |
131 | | - const struct port_node *node = data; |
132 | | - |
133 | | - if (!is_typec_port(dev)) |
134 | | - return 0; |
135 | | - |
136 | | - return acpi_pld_match(to_typec_port(dev)->pld, node->pld); |
137 | | -} |
138 | | - |
139 | | -static struct device *find_connector(struct port_node *node) |
140 | | -{ |
141 | | - if (!node->pld) |
142 | | - return NULL; |
143 | | - |
144 | | - return class_find_device(&typec_class, NULL, node, connector_match); |
145 | | -} |
146 | | - |
147 | | -/** |
148 | | - * typec_link_port - Link a port to its connector |
149 | | - * @port: The port device |
150 | | - * |
151 | | - * Find the connector of @port and create symlink named "connector" for it. |
152 | | - * Returns 0 on success, or errno in case of a failure. |
153 | | - * |
154 | | - * NOTE. The function increments the reference count of @port on success. |
155 | | - */ |
156 | | -int typec_link_port(struct device *port) |
157 | | -{ |
158 | | - struct device *connector; |
159 | | - struct port_node *node; |
160 | | - int ret; |
161 | | - |
162 | | - node = create_port_node(port); |
163 | | - if (IS_ERR(node)) |
164 | | - return PTR_ERR(node); |
165 | | - |
166 | | - connector = find_connector(node); |
167 | | - if (!connector) { |
168 | | - ret = 0; |
169 | | - goto remove_node; |
170 | | - } |
171 | | - |
172 | | - ret = link_port(to_typec_port(connector), node); |
173 | | - if (ret) |
174 | | - goto put_connector; |
175 | | - |
176 | | - return 0; |
177 | | - |
178 | | -put_connector: |
179 | | - put_device(connector); |
180 | | -remove_node: |
181 | | - remove_port_node(node); |
182 | | - |
183 | | - return ret; |
184 | | -} |
185 | | -EXPORT_SYMBOL_GPL(typec_link_port); |
186 | | - |
187 | | -static int port_match_and_unlink(struct device *connector, void *port) |
188 | | -{ |
189 | | - struct port_node *node; |
190 | | - struct port_node *tmp; |
191 | | - int ret = 0; |
192 | | - |
193 | | - if (!is_typec_port(connector)) |
194 | | - return 0; |
195 | | - |
196 | | - mutex_lock(&to_typec_port(connector)->port_list_lock); |
197 | | - list_for_each_entry_safe(node, tmp, &to_typec_port(connector)->port_list, list) { |
198 | | - ret = node->dev == port; |
199 | | - if (ret) { |
200 | | - unlink_port(to_typec_port(connector), node); |
201 | | - remove_port_node(node); |
202 | | - put_device(connector); |
203 | | - break; |
204 | | - } |
205 | | - } |
206 | | - mutex_unlock(&to_typec_port(connector)->port_list_lock); |
| 24 | +static const struct component_master_ops typec_aggregate_ops = { |
| 25 | + .bind = typec_aggregate_bind, |
| 26 | + .unbind = typec_aggregate_unbind, |
| 27 | +}; |
207 | 28 |
|
208 | | - return ret; |
209 | | -} |
| 29 | +struct each_port_arg { |
| 30 | + struct typec_port *port; |
| 31 | + struct component_match *match; |
| 32 | +}; |
210 | 33 |
|
211 | | -/** |
212 | | - * typec_unlink_port - Unlink port from its connector |
213 | | - * @port: The port device |
214 | | - * |
215 | | - * Removes the symlink "connector" and decrements the reference count of @port. |
216 | | - */ |
217 | | -void typec_unlink_port(struct device *port) |
| 34 | +static int typec_port_compare(struct device *dev, void *fwnode) |
218 | 35 | { |
219 | | - class_for_each_device(&typec_class, NULL, port, port_match_and_unlink); |
| 36 | + return device_match_fwnode(dev, fwnode); |
220 | 37 | } |
221 | | -EXPORT_SYMBOL_GPL(typec_unlink_port); |
222 | 38 |
|
223 | | -static int each_port(struct device *port, void *connector) |
| 39 | +static int typec_port_match(struct device *dev, void *data) |
224 | 40 | { |
225 | | - struct port_node *node; |
226 | | - int ret; |
227 | | - |
228 | | - node = create_port_node(port); |
229 | | - if (IS_ERR(node)) |
230 | | - return PTR_ERR(node); |
| 41 | + struct acpi_device *adev = to_acpi_device(dev); |
| 42 | + struct each_port_arg *arg = data; |
| 43 | + struct acpi_device *con_adev; |
231 | 44 |
|
232 | | - if (!connector_match(connector, node)) { |
233 | | - remove_port_node(node); |
| 45 | + con_adev = ACPI_COMPANION(&arg->port->dev); |
| 46 | + if (con_adev == adev) |
234 | 47 | return 0; |
235 | | - } |
236 | | - |
237 | | - ret = link_port(to_typec_port(connector), node); |
238 | | - if (ret) { |
239 | | - remove_port_node(node->pld); |
240 | | - return ret; |
241 | | - } |
242 | | - |
243 | | - get_device(connector); |
244 | 48 |
|
| 49 | + if (con_adev->pld_crc == adev->pld_crc) |
| 50 | + component_match_add(&arg->port->dev, &arg->match, typec_port_compare, |
| 51 | + acpi_fwnode_handle(adev)); |
245 | 52 | return 0; |
246 | 53 | } |
247 | 54 |
|
248 | 55 | int typec_link_ports(struct typec_port *con) |
249 | 56 | { |
250 | | - int ret = 0; |
| 57 | + struct each_port_arg arg = { .port = con, .match = NULL }; |
251 | 58 |
|
252 | | - con->pld = get_pld(&con->dev); |
253 | | - if (!con->pld) |
254 | | - return 0; |
| 59 | + bus_for_each_dev(&acpi_bus_type, NULL, &arg, typec_port_match); |
255 | 60 |
|
256 | | - ret = usb_for_each_port(&con->dev, each_port); |
257 | | - if (ret) |
258 | | - typec_unlink_ports(con); |
259 | | - |
260 | | - return ret; |
| 61 | + /* |
| 62 | + * REVISIT: Now each connector can have only a single component master. |
| 63 | + * So far only the USB ports connected to the USB Type-C connector share |
| 64 | + * the _PLD with it, but if there one day is something else (like maybe |
| 65 | + * the DisplayPort ACPI device object) that also shares the _PLD with |
| 66 | + * the connector, every one of those needs to have its own component |
| 67 | + * master, because each different type of component needs to be bind to |
| 68 | + * the connector independently of the other components. That requires |
| 69 | + * improvements to the component framework. Right now you can only have |
| 70 | + * one master per device. |
| 71 | + */ |
| 72 | + return component_master_add_with_match(&con->dev, &typec_aggregate_ops, arg.match); |
261 | 73 | } |
262 | 74 |
|
263 | 75 | void typec_unlink_ports(struct typec_port *con) |
264 | 76 | { |
265 | | - struct port_node *node; |
266 | | - struct port_node *tmp; |
267 | | - |
268 | | - mutex_lock(&con->port_list_lock); |
269 | | - |
270 | | - list_for_each_entry_safe(node, tmp, &con->port_list, list) { |
271 | | - __unlink_port(con, node); |
272 | | - remove_port_node(node); |
273 | | - put_device(&con->dev); |
274 | | - } |
275 | | - |
276 | | - mutex_unlock(&con->port_list_lock); |
277 | | - |
278 | | - free_pld(con->pld); |
| 77 | + component_master_del(&con->dev, &typec_aggregate_ops); |
279 | 78 | } |
0 commit comments