1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * I2C Address Translator |
4 | * |
5 | * Copyright (c) 2019,2022 Luca Ceresoli <luca@lucaceresoli.net> |
6 | * Copyright (c) 2022,2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> |
7 | * |
8 | * Originally based on i2c-mux.c |
9 | */ |
10 | |
11 | #include <linux/fwnode.h> |
12 | #include <linux/i2c-atr.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/mutex.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/spinlock.h> |
19 | |
20 | #define ATR_MAX_ADAPTERS 100 /* Just a sanity limit */ |
21 | #define ATR_MAX_SYMLINK_LEN 11 /* Longest name is 10 chars: "channel-99" */ |
22 | |
23 | /** |
24 | * struct i2c_atr_alias_pair - Holds the alias assigned to a client. |
25 | * @node: List node |
26 | * @client: Pointer to the client on the child bus |
27 | * @alias: I2C alias address assigned by the driver. |
28 | * This is the address that will be used to issue I2C transactions |
29 | * on the parent (physical) bus. |
30 | */ |
31 | struct i2c_atr_alias_pair { |
32 | struct list_head node; |
33 | const struct i2c_client *client; |
34 | u16 alias; |
35 | }; |
36 | |
37 | /** |
38 | * struct i2c_atr_chan - Data for a channel. |
39 | * @adap: The &struct i2c_adapter for the channel |
40 | * @atr: The parent I2C ATR |
41 | * @chan_id: The ID of this channel |
42 | * @alias_list: List of @struct i2c_atr_alias_pair containing the |
43 | * assigned aliases |
44 | * @orig_addrs_lock: Mutex protecting @orig_addrs |
45 | * @orig_addrs: Buffer used to store the original addresses during transmit |
46 | * @orig_addrs_size: Size of @orig_addrs |
47 | */ |
48 | struct i2c_atr_chan { |
49 | struct i2c_adapter adap; |
50 | struct i2c_atr *atr; |
51 | u32 chan_id; |
52 | |
53 | struct list_head alias_list; |
54 | |
55 | /* Lock orig_addrs during xfer */ |
56 | struct mutex orig_addrs_lock; |
57 | u16 *orig_addrs; |
58 | unsigned int orig_addrs_size; |
59 | }; |
60 | |
61 | /** |
62 | * struct i2c_atr - The I2C ATR instance |
63 | * @parent: The parent &struct i2c_adapter |
64 | * @dev: The device that owns the I2C ATR instance |
65 | * @ops: &struct i2c_atr_ops |
66 | * @priv: Private driver data, set with i2c_atr_set_driver_data() |
67 | * @algo: The &struct i2c_algorithm for adapters |
68 | * @lock: Lock for the I2C bus segment (see &struct i2c_lock_operations) |
69 | * @max_adapters: Maximum number of adapters this I2C ATR can have |
70 | * @num_aliases: Number of aliases in the aliases array |
71 | * @aliases: The aliases array |
72 | * @alias_mask_lock: Lock protecting alias_use_mask |
73 | * @alias_use_mask: Bitmask for used aliases in aliases array |
74 | * @i2c_nb: Notifier for remote client add & del events |
75 | * @adapter: Array of adapters |
76 | */ |
77 | struct i2c_atr { |
78 | struct i2c_adapter *parent; |
79 | struct device *dev; |
80 | const struct i2c_atr_ops *ops; |
81 | |
82 | void *priv; |
83 | |
84 | struct i2c_algorithm algo; |
85 | /* lock for the I2C bus segment (see struct i2c_lock_operations) */ |
86 | struct mutex lock; |
87 | int max_adapters; |
88 | |
89 | size_t num_aliases; |
90 | const u16 *aliases; |
91 | /* Protects alias_use_mask */ |
92 | spinlock_t alias_mask_lock; |
93 | unsigned long *alias_use_mask; |
94 | |
95 | struct notifier_block i2c_nb; |
96 | |
97 | struct i2c_adapter *adapter[] __counted_by(max_adapters); |
98 | }; |
99 | |
100 | static struct i2c_atr_alias_pair * |
101 | i2c_atr_find_mapping_by_client(const struct list_head *list, |
102 | const struct i2c_client *client) |
103 | { |
104 | struct i2c_atr_alias_pair *c2a; |
105 | |
106 | list_for_each_entry(c2a, list, node) { |
107 | if (c2a->client == client) |
108 | return c2a; |
109 | } |
110 | |
111 | return NULL; |
112 | } |
113 | |
114 | static struct i2c_atr_alias_pair * |
115 | i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr) |
116 | { |
117 | struct i2c_atr_alias_pair *c2a; |
118 | |
119 | list_for_each_entry(c2a, list, node) { |
120 | if (c2a->client->addr == phys_addr) |
121 | return c2a; |
122 | } |
123 | |
124 | return NULL; |
125 | } |
126 | |
127 | /* |
128 | * Replace all message addresses with their aliases, saving the original |
129 | * addresses. |
130 | * |
131 | * This function is internal for use in i2c_atr_master_xfer(). It must be |
132 | * followed by i2c_atr_unmap_msgs() to restore the original addresses. |
133 | */ |
134 | static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, |
135 | int num) |
136 | { |
137 | struct i2c_atr *atr = chan->atr; |
138 | static struct i2c_atr_alias_pair *c2a; |
139 | int i; |
140 | |
141 | /* Ensure we have enough room to save the original addresses */ |
142 | if (unlikely(chan->orig_addrs_size < num)) { |
143 | u16 *new_buf; |
144 | |
145 | /* We don't care about old data, hence no realloc() */ |
146 | new_buf = kmalloc_array(n: num, size: sizeof(*new_buf), GFP_KERNEL); |
147 | if (!new_buf) |
148 | return -ENOMEM; |
149 | |
150 | kfree(objp: chan->orig_addrs); |
151 | chan->orig_addrs = new_buf; |
152 | chan->orig_addrs_size = num; |
153 | } |
154 | |
155 | for (i = 0; i < num; i++) { |
156 | chan->orig_addrs[i] = msgs[i].addr; |
157 | |
158 | c2a = i2c_atr_find_mapping_by_addr(list: &chan->alias_list, |
159 | phys_addr: msgs[i].addr); |
160 | if (!c2a) { |
161 | dev_err(atr->dev, "client 0x%02x not mapped!\n" , |
162 | msgs[i].addr); |
163 | |
164 | while (i--) |
165 | msgs[i].addr = chan->orig_addrs[i]; |
166 | |
167 | return -ENXIO; |
168 | } |
169 | |
170 | msgs[i].addr = c2a->alias; |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | /* |
177 | * Restore all message address aliases with the original addresses. This |
178 | * function is internal for use in i2c_atr_master_xfer() and for this reason it |
179 | * needs no null and size checks on orig_addr. |
180 | * |
181 | * @see i2c_atr_map_msgs() |
182 | */ |
183 | static void i2c_atr_unmap_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, |
184 | int num) |
185 | { |
186 | int i; |
187 | |
188 | for (i = 0; i < num; i++) |
189 | msgs[i].addr = chan->orig_addrs[i]; |
190 | } |
191 | |
192 | static int i2c_atr_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, |
193 | int num) |
194 | { |
195 | struct i2c_atr_chan *chan = adap->algo_data; |
196 | struct i2c_atr *atr = chan->atr; |
197 | struct i2c_adapter *parent = atr->parent; |
198 | int ret; |
199 | |
200 | /* Translate addresses */ |
201 | mutex_lock(&chan->orig_addrs_lock); |
202 | |
203 | ret = i2c_atr_map_msgs(chan, msgs, num); |
204 | if (ret < 0) |
205 | goto err_unlock; |
206 | |
207 | /* Perform the transfer */ |
208 | ret = i2c_transfer(adap: parent, msgs, num); |
209 | |
210 | /* Restore addresses */ |
211 | i2c_atr_unmap_msgs(chan, msgs, num); |
212 | |
213 | err_unlock: |
214 | mutex_unlock(lock: &chan->orig_addrs_lock); |
215 | |
216 | return ret; |
217 | } |
218 | |
219 | static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, u16 addr, |
220 | unsigned short flags, char read_write, u8 command, |
221 | int size, union i2c_smbus_data *data) |
222 | { |
223 | struct i2c_atr_chan *chan = adap->algo_data; |
224 | struct i2c_atr *atr = chan->atr; |
225 | struct i2c_adapter *parent = atr->parent; |
226 | struct i2c_atr_alias_pair *c2a; |
227 | |
228 | c2a = i2c_atr_find_mapping_by_addr(list: &chan->alias_list, phys_addr: addr); |
229 | if (!c2a) { |
230 | dev_err(atr->dev, "client 0x%02x not mapped!\n" , addr); |
231 | return -ENXIO; |
232 | } |
233 | |
234 | return i2c_smbus_xfer(adapter: parent, addr: c2a->alias, flags, read_write, command, |
235 | protocol: size, data); |
236 | } |
237 | |
238 | static u32 i2c_atr_functionality(struct i2c_adapter *adap) |
239 | { |
240 | struct i2c_atr_chan *chan = adap->algo_data; |
241 | struct i2c_adapter *parent = chan->atr->parent; |
242 | |
243 | return parent->algo->functionality(parent); |
244 | } |
245 | |
246 | static void i2c_atr_lock_bus(struct i2c_adapter *adapter, unsigned int flags) |
247 | { |
248 | struct i2c_atr_chan *chan = adapter->algo_data; |
249 | struct i2c_atr *atr = chan->atr; |
250 | |
251 | mutex_lock(&atr->lock); |
252 | } |
253 | |
254 | static int i2c_atr_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) |
255 | { |
256 | struct i2c_atr_chan *chan = adapter->algo_data; |
257 | struct i2c_atr *atr = chan->atr; |
258 | |
259 | return mutex_trylock(lock: &atr->lock); |
260 | } |
261 | |
262 | static void i2c_atr_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) |
263 | { |
264 | struct i2c_atr_chan *chan = adapter->algo_data; |
265 | struct i2c_atr *atr = chan->atr; |
266 | |
267 | mutex_unlock(lock: &atr->lock); |
268 | } |
269 | |
270 | static const struct i2c_lock_operations i2c_atr_lock_ops = { |
271 | .lock_bus = i2c_atr_lock_bus, |
272 | .trylock_bus = i2c_atr_trylock_bus, |
273 | .unlock_bus = i2c_atr_unlock_bus, |
274 | }; |
275 | |
276 | static int i2c_atr_reserve_alias(struct i2c_atr *atr) |
277 | { |
278 | unsigned long idx; |
279 | |
280 | spin_lock(lock: &atr->alias_mask_lock); |
281 | |
282 | idx = find_first_zero_bit(addr: atr->alias_use_mask, size: atr->num_aliases); |
283 | if (idx >= atr->num_aliases) { |
284 | spin_unlock(lock: &atr->alias_mask_lock); |
285 | dev_err(atr->dev, "failed to find a free alias\n" ); |
286 | return -EBUSY; |
287 | } |
288 | |
289 | set_bit(nr: idx, addr: atr->alias_use_mask); |
290 | |
291 | spin_unlock(lock: &atr->alias_mask_lock); |
292 | |
293 | return atr->aliases[idx]; |
294 | } |
295 | |
296 | static void i2c_atr_release_alias(struct i2c_atr *atr, u16 alias) |
297 | { |
298 | unsigned int idx; |
299 | |
300 | spin_lock(lock: &atr->alias_mask_lock); |
301 | |
302 | for (idx = 0; idx < atr->num_aliases; ++idx) { |
303 | if (atr->aliases[idx] == alias) { |
304 | clear_bit(nr: idx, addr: atr->alias_use_mask); |
305 | spin_unlock(lock: &atr->alias_mask_lock); |
306 | return; |
307 | } |
308 | } |
309 | |
310 | spin_unlock(lock: &atr->alias_mask_lock); |
311 | |
312 | /* This should never happen */ |
313 | dev_warn(atr->dev, "Unable to find mapped alias\n" ); |
314 | } |
315 | |
316 | static int i2c_atr_attach_client(struct i2c_adapter *adapter, |
317 | const struct i2c_client *client) |
318 | { |
319 | struct i2c_atr_chan *chan = adapter->algo_data; |
320 | struct i2c_atr *atr = chan->atr; |
321 | struct i2c_atr_alias_pair *c2a; |
322 | u16 alias; |
323 | int ret; |
324 | |
325 | ret = i2c_atr_reserve_alias(atr); |
326 | if (ret < 0) |
327 | return ret; |
328 | |
329 | alias = ret; |
330 | |
331 | c2a = kzalloc(size: sizeof(*c2a), GFP_KERNEL); |
332 | if (!c2a) { |
333 | ret = -ENOMEM; |
334 | goto err_release_alias; |
335 | } |
336 | |
337 | ret = atr->ops->attach_client(atr, chan->chan_id, client, alias); |
338 | if (ret) |
339 | goto err_free; |
340 | |
341 | dev_dbg(atr->dev, "chan%u: client 0x%02x mapped at alias 0x%02x (%s)\n" , |
342 | chan->chan_id, client->addr, alias, client->name); |
343 | |
344 | c2a->client = client; |
345 | c2a->alias = alias; |
346 | list_add(new: &c2a->node, head: &chan->alias_list); |
347 | |
348 | return 0; |
349 | |
350 | err_free: |
351 | kfree(objp: c2a); |
352 | err_release_alias: |
353 | i2c_atr_release_alias(atr, alias); |
354 | |
355 | return ret; |
356 | } |
357 | |
358 | static void i2c_atr_detach_client(struct i2c_adapter *adapter, |
359 | const struct i2c_client *client) |
360 | { |
361 | struct i2c_atr_chan *chan = adapter->algo_data; |
362 | struct i2c_atr *atr = chan->atr; |
363 | struct i2c_atr_alias_pair *c2a; |
364 | |
365 | atr->ops->detach_client(atr, chan->chan_id, client); |
366 | |
367 | c2a = i2c_atr_find_mapping_by_client(list: &chan->alias_list, client); |
368 | if (!c2a) { |
369 | /* This should never happen */ |
370 | dev_warn(atr->dev, "Unable to find address mapping\n" ); |
371 | return; |
372 | } |
373 | |
374 | i2c_atr_release_alias(atr, alias: c2a->alias); |
375 | |
376 | dev_dbg(atr->dev, |
377 | "chan%u: client 0x%02x unmapped from alias 0x%02x (%s)\n" , |
378 | chan->chan_id, client->addr, c2a->alias, client->name); |
379 | |
380 | list_del(entry: &c2a->node); |
381 | kfree(objp: c2a); |
382 | } |
383 | |
384 | static int i2c_atr_bus_notifier_call(struct notifier_block *nb, |
385 | unsigned long event, void *device) |
386 | { |
387 | struct i2c_atr *atr = container_of(nb, struct i2c_atr, i2c_nb); |
388 | struct device *dev = device; |
389 | struct i2c_client *client; |
390 | u32 chan_id; |
391 | int ret; |
392 | |
393 | client = i2c_verify_client(dev); |
394 | if (!client) |
395 | return NOTIFY_DONE; |
396 | |
397 | /* Is the client in one of our adapters? */ |
398 | for (chan_id = 0; chan_id < atr->max_adapters; ++chan_id) { |
399 | if (client->adapter == atr->adapter[chan_id]) |
400 | break; |
401 | } |
402 | |
403 | if (chan_id == atr->max_adapters) |
404 | return NOTIFY_DONE; |
405 | |
406 | switch (event) { |
407 | case BUS_NOTIFY_ADD_DEVICE: |
408 | ret = i2c_atr_attach_client(adapter: client->adapter, client); |
409 | if (ret) |
410 | dev_err(atr->dev, |
411 | "Failed to attach remote client '%s': %d\n" , |
412 | dev_name(dev), ret); |
413 | break; |
414 | |
415 | case BUS_NOTIFY_DEL_DEVICE: |
416 | i2c_atr_detach_client(adapter: client->adapter, client); |
417 | break; |
418 | |
419 | default: |
420 | break; |
421 | } |
422 | |
423 | return NOTIFY_DONE; |
424 | } |
425 | |
426 | static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) |
427 | { |
428 | struct device *dev = atr->dev; |
429 | unsigned long *alias_use_mask; |
430 | size_t num_aliases; |
431 | unsigned int i; |
432 | u32 *aliases32; |
433 | u16 *aliases16; |
434 | int ret; |
435 | |
436 | ret = fwnode_property_count_u32(dev_fwnode(dev), propname: "i2c-alias-pool" ); |
437 | if (ret < 0) { |
438 | dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n" , |
439 | ret); |
440 | return ret; |
441 | } |
442 | |
443 | num_aliases = ret; |
444 | |
445 | if (!num_aliases) |
446 | return 0; |
447 | |
448 | aliases32 = kcalloc(n: num_aliases, size: sizeof(*aliases32), GFP_KERNEL); |
449 | if (!aliases32) |
450 | return -ENOMEM; |
451 | |
452 | ret = fwnode_property_read_u32_array(dev_fwnode(dev), propname: "i2c-alias-pool" , |
453 | val: aliases32, nval: num_aliases); |
454 | if (ret < 0) { |
455 | dev_err(dev, "Failed to read 'i2c-alias-pool' property: %d\n" , |
456 | ret); |
457 | goto err_free_aliases32; |
458 | } |
459 | |
460 | aliases16 = kcalloc(n: num_aliases, size: sizeof(*aliases16), GFP_KERNEL); |
461 | if (!aliases16) { |
462 | ret = -ENOMEM; |
463 | goto err_free_aliases32; |
464 | } |
465 | |
466 | for (i = 0; i < num_aliases; i++) { |
467 | if (!(aliases32[i] & 0xffff0000)) { |
468 | aliases16[i] = aliases32[i]; |
469 | continue; |
470 | } |
471 | |
472 | dev_err(dev, "Failed to parse 'i2c-alias-pool' property: I2C flags are not supported\n" ); |
473 | ret = -EINVAL; |
474 | goto err_free_aliases16; |
475 | } |
476 | |
477 | alias_use_mask = bitmap_zalloc(nbits: num_aliases, GFP_KERNEL); |
478 | if (!alias_use_mask) { |
479 | ret = -ENOMEM; |
480 | goto err_free_aliases16; |
481 | } |
482 | |
483 | kfree(objp: aliases32); |
484 | |
485 | atr->num_aliases = num_aliases; |
486 | atr->aliases = aliases16; |
487 | atr->alias_use_mask = alias_use_mask; |
488 | |
489 | dev_dbg(dev, "i2c-alias-pool has %zu aliases" , atr->num_aliases); |
490 | |
491 | return 0; |
492 | |
493 | err_free_aliases16: |
494 | kfree(objp: aliases16); |
495 | err_free_aliases32: |
496 | kfree(objp: aliases32); |
497 | return ret; |
498 | } |
499 | |
500 | struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, |
501 | const struct i2c_atr_ops *ops, int max_adapters) |
502 | { |
503 | struct i2c_atr *atr; |
504 | int ret; |
505 | |
506 | if (max_adapters > ATR_MAX_ADAPTERS) |
507 | return ERR_PTR(error: -EINVAL); |
508 | |
509 | if (!ops || !ops->attach_client || !ops->detach_client) |
510 | return ERR_PTR(error: -EINVAL); |
511 | |
512 | atr = kzalloc(struct_size(atr, adapter, max_adapters), GFP_KERNEL); |
513 | if (!atr) |
514 | return ERR_PTR(error: -ENOMEM); |
515 | |
516 | mutex_init(&atr->lock); |
517 | spin_lock_init(&atr->alias_mask_lock); |
518 | |
519 | atr->parent = parent; |
520 | atr->dev = dev; |
521 | atr->ops = ops; |
522 | atr->max_adapters = max_adapters; |
523 | |
524 | if (parent->algo->master_xfer) |
525 | atr->algo.master_xfer = i2c_atr_master_xfer; |
526 | if (parent->algo->smbus_xfer) |
527 | atr->algo.smbus_xfer = i2c_atr_smbus_xfer; |
528 | atr->algo.functionality = i2c_atr_functionality; |
529 | |
530 | ret = i2c_atr_parse_alias_pool(atr); |
531 | if (ret) |
532 | goto err_destroy_mutex; |
533 | |
534 | atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call; |
535 | ret = bus_register_notifier(bus: &i2c_bus_type, nb: &atr->i2c_nb); |
536 | if (ret) |
537 | goto err_free_aliases; |
538 | |
539 | return atr; |
540 | |
541 | err_free_aliases: |
542 | bitmap_free(bitmap: atr->alias_use_mask); |
543 | kfree(objp: atr->aliases); |
544 | err_destroy_mutex: |
545 | mutex_destroy(lock: &atr->lock); |
546 | kfree(objp: atr); |
547 | |
548 | return ERR_PTR(error: ret); |
549 | } |
550 | EXPORT_SYMBOL_NS_GPL(i2c_atr_new, I2C_ATR); |
551 | |
552 | void i2c_atr_delete(struct i2c_atr *atr) |
553 | { |
554 | unsigned int i; |
555 | |
556 | for (i = 0; i < atr->max_adapters; ++i) |
557 | WARN_ON(atr->adapter[i]); |
558 | |
559 | bus_unregister_notifier(bus: &i2c_bus_type, nb: &atr->i2c_nb); |
560 | bitmap_free(bitmap: atr->alias_use_mask); |
561 | kfree(objp: atr->aliases); |
562 | mutex_destroy(lock: &atr->lock); |
563 | kfree(objp: atr); |
564 | } |
565 | EXPORT_SYMBOL_NS_GPL(i2c_atr_delete, I2C_ATR); |
566 | |
567 | int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, |
568 | struct device *adapter_parent, |
569 | struct fwnode_handle *bus_handle) |
570 | { |
571 | struct i2c_adapter *parent = atr->parent; |
572 | struct device *dev = atr->dev; |
573 | struct i2c_atr_chan *chan; |
574 | char symlink_name[ATR_MAX_SYMLINK_LEN]; |
575 | int ret; |
576 | |
577 | if (chan_id >= atr->max_adapters) { |
578 | dev_err(dev, "No room for more i2c-atr adapters\n" ); |
579 | return -EINVAL; |
580 | } |
581 | |
582 | if (atr->adapter[chan_id]) { |
583 | dev_err(dev, "Adapter %d already present\n" , chan_id); |
584 | return -EEXIST; |
585 | } |
586 | |
587 | chan = kzalloc(size: sizeof(*chan), GFP_KERNEL); |
588 | if (!chan) |
589 | return -ENOMEM; |
590 | |
591 | if (!adapter_parent) |
592 | adapter_parent = dev; |
593 | |
594 | chan->atr = atr; |
595 | chan->chan_id = chan_id; |
596 | INIT_LIST_HEAD(list: &chan->alias_list); |
597 | mutex_init(&chan->orig_addrs_lock); |
598 | |
599 | snprintf(buf: chan->adap.name, size: sizeof(chan->adap.name), fmt: "i2c-%d-atr-%d" , |
600 | i2c_adapter_id(adap: parent), chan_id); |
601 | chan->adap.owner = THIS_MODULE; |
602 | chan->adap.algo = &atr->algo; |
603 | chan->adap.algo_data = chan; |
604 | chan->adap.dev.parent = adapter_parent; |
605 | chan->adap.retries = parent->retries; |
606 | chan->adap.timeout = parent->timeout; |
607 | chan->adap.quirks = parent->quirks; |
608 | chan->adap.lock_ops = &i2c_atr_lock_ops; |
609 | |
610 | if (bus_handle) { |
611 | device_set_node(dev: &chan->adap.dev, fwnode: fwnode_handle_get(fwnode: bus_handle)); |
612 | } else { |
613 | struct fwnode_handle *atr_node; |
614 | struct fwnode_handle *child; |
615 | u32 reg; |
616 | |
617 | atr_node = device_get_named_child_node(dev, childname: "i2c-atr" ); |
618 | |
619 | fwnode_for_each_child_node(atr_node, child) { |
620 | ret = fwnode_property_read_u32(fwnode: child, propname: "reg" , val: ®); |
621 | if (ret) |
622 | continue; |
623 | if (chan_id == reg) |
624 | break; |
625 | } |
626 | |
627 | device_set_node(dev: &chan->adap.dev, fwnode: child); |
628 | fwnode_handle_put(fwnode: atr_node); |
629 | } |
630 | |
631 | atr->adapter[chan_id] = &chan->adap; |
632 | |
633 | ret = i2c_add_adapter(adap: &chan->adap); |
634 | if (ret) { |
635 | dev_err(dev, "failed to add atr-adapter %u (error=%d)\n" , |
636 | chan_id, ret); |
637 | goto err_fwnode_put; |
638 | } |
639 | |
640 | snprintf(buf: symlink_name, size: sizeof(symlink_name), fmt: "channel-%u" , |
641 | chan->chan_id); |
642 | |
643 | ret = sysfs_create_link(kobj: &chan->adap.dev.kobj, target: &dev->kobj, name: "atr_device" ); |
644 | if (ret) |
645 | dev_warn(dev, "can't create symlink to atr device\n" ); |
646 | ret = sysfs_create_link(kobj: &dev->kobj, target: &chan->adap.dev.kobj, name: symlink_name); |
647 | if (ret) |
648 | dev_warn(dev, "can't create symlink for channel %u\n" , chan_id); |
649 | |
650 | dev_dbg(dev, "Added ATR child bus %d\n" , i2c_adapter_id(&chan->adap)); |
651 | |
652 | return 0; |
653 | |
654 | err_fwnode_put: |
655 | fwnode_handle_put(dev_fwnode(&chan->adap.dev)); |
656 | mutex_destroy(lock: &chan->orig_addrs_lock); |
657 | kfree(objp: chan); |
658 | return ret; |
659 | } |
660 | EXPORT_SYMBOL_NS_GPL(i2c_atr_add_adapter, I2C_ATR); |
661 | |
662 | void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id) |
663 | { |
664 | char symlink_name[ATR_MAX_SYMLINK_LEN]; |
665 | struct i2c_adapter *adap; |
666 | struct i2c_atr_chan *chan; |
667 | struct fwnode_handle *fwnode; |
668 | struct device *dev = atr->dev; |
669 | |
670 | adap = atr->adapter[chan_id]; |
671 | if (!adap) |
672 | return; |
673 | |
674 | chan = adap->algo_data; |
675 | fwnode = dev_fwnode(&adap->dev); |
676 | |
677 | dev_dbg(dev, "Removing ATR child bus %d\n" , i2c_adapter_id(adap)); |
678 | |
679 | snprintf(buf: symlink_name, size: sizeof(symlink_name), fmt: "channel-%u" , |
680 | chan->chan_id); |
681 | sysfs_remove_link(kobj: &dev->kobj, name: symlink_name); |
682 | sysfs_remove_link(kobj: &chan->adap.dev.kobj, name: "atr_device" ); |
683 | |
684 | i2c_del_adapter(adap); |
685 | |
686 | atr->adapter[chan_id] = NULL; |
687 | |
688 | fwnode_handle_put(fwnode); |
689 | mutex_destroy(lock: &chan->orig_addrs_lock); |
690 | kfree(objp: chan->orig_addrs); |
691 | kfree(objp: chan); |
692 | } |
693 | EXPORT_SYMBOL_NS_GPL(i2c_atr_del_adapter, I2C_ATR); |
694 | |
695 | void i2c_atr_set_driver_data(struct i2c_atr *atr, void *data) |
696 | { |
697 | atr->priv = data; |
698 | } |
699 | EXPORT_SYMBOL_NS_GPL(i2c_atr_set_driver_data, I2C_ATR); |
700 | |
701 | void *i2c_atr_get_driver_data(struct i2c_atr *atr) |
702 | { |
703 | return atr->priv; |
704 | } |
705 | EXPORT_SYMBOL_NS_GPL(i2c_atr_get_driver_data, I2C_ATR); |
706 | |
707 | MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>" ); |
708 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>" ); |
709 | MODULE_DESCRIPTION("I2C Address Translator" ); |
710 | MODULE_LICENSE("GPL" ); |
711 | |