1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Implements DMTF specification |
4 | * "DSP0233 Management Component Transport Protocol (MCTP) I3C Transport |
5 | * Binding" |
6 | * https://www.dmtf.org/sites/default/files/standards/documents/DSP0233_1.0.0.pdf |
7 | * |
8 | * Copyright (c) 2023 Code Construct |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/netdevice.h> |
13 | #include <linux/i3c/device.h> |
14 | #include <linux/i3c/master.h> |
15 | #include <linux/if_arp.h> |
16 | #include <linux/unaligned.h> |
17 | #include <net/mctp.h> |
18 | #include <net/mctpdevice.h> |
19 | |
20 | #define MCTP_I3C_MAXBUF 65536 |
21 | /* 48 bit Provisioned Id */ |
22 | #define PID_SIZE 6 |
23 | |
24 | /* 64 byte payload, 4 byte MCTP header */ |
25 | static const int MCTP_I3C_MINMTU = 64 + 4; |
26 | /* One byte less to allow for the PEC */ |
27 | static const int MCTP_I3C_MAXMTU = MCTP_I3C_MAXBUF - 1; |
28 | /* 4 byte MCTP header, no data, 1 byte PEC */ |
29 | static const int MCTP_I3C_MINLEN = 4 + 1; |
30 | |
31 | /* Sufficient for 64kB at min mtu */ |
32 | static const int MCTP_I3C_TX_QUEUE_LEN = 1100; |
33 | |
34 | /* Somewhat arbitrary */ |
35 | static const int MCTP_I3C_IBI_SLOTS = 8; |
36 | |
37 | /* Mandatory Data Byte in an IBI, from DSP0233 */ |
38 | #define I3C_MDB_MCTP 0xAE |
39 | /* From MIPI Device Characteristics Register (DCR) Assignments */ |
40 | #define I3C_DCR_MCTP 0xCC |
41 | |
42 | static const char *MCTP_I3C_OF_PROP = "mctp-controller"; |
43 | |
44 | /* List of mctp_i3c_busdev */ |
45 | static LIST_HEAD(busdevs); |
46 | /* Protects busdevs, as well as mctp_i3c_bus.devs lists */ |
47 | static DEFINE_MUTEX(busdevs_lock); |
48 | |
49 | struct mctp_i3c_bus { |
50 | struct net_device *ndev; |
51 | |
52 | struct task_struct *tx_thread; |
53 | wait_queue_head_t tx_wq; |
54 | /* tx_lock protects tx_skb and devs */ |
55 | spinlock_t tx_lock; |
56 | /* Next skb to transmit */ |
57 | struct sk_buff *tx_skb; |
58 | /* Scratch buffer for xmit */ |
59 | u8 tx_scratch[MCTP_I3C_MAXBUF]; |
60 | |
61 | /* Element of busdevs */ |
62 | struct list_head list; |
63 | |
64 | /* Provisioned ID of our controller */ |
65 | u64 pid; |
66 | |
67 | struct i3c_bus *bus; |
68 | /* Head of mctp_i3c_device.list. Protected by busdevs_lock */ |
69 | struct list_head devs; |
70 | }; |
71 | |
72 | struct mctp_i3c_device { |
73 | struct i3c_device *i3c; |
74 | struct mctp_i3c_bus *mbus; |
75 | struct list_head list; /* Element of mctp_i3c_bus.devs */ |
76 | |
77 | /* Held while tx_thread is using this device */ |
78 | struct mutex lock; |
79 | |
80 | /* Whether BCR indicates MDB is present in IBI */ |
81 | bool have_mdb; |
82 | /* I3C dynamic address */ |
83 | u8 addr; |
84 | /* Maximum read length */ |
85 | u16 mrl; |
86 | /* Maximum write length */ |
87 | u16 mwl; |
88 | /* Provisioned ID */ |
89 | u64 pid; |
90 | }; |
91 | |
92 | /* We synthesise a mac header using the Provisioned ID. |
93 | * Used to pass dest to mctp_i3c_start_xmit. |
94 | */ |
95 | struct mctp_i3c_internal_hdr { |
96 | u8 dest[PID_SIZE]; |
97 | u8 source[PID_SIZE]; |
98 | } __packed; |
99 | |
100 | static int mctp_i3c_read(struct mctp_i3c_device *mi) |
101 | { |
102 | struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl }; |
103 | struct net_device_stats *stats = &mi->mbus->ndev->stats; |
104 | struct mctp_i3c_internal_hdr *ihdr = NULL; |
105 | struct sk_buff *skb = NULL; |
106 | struct mctp_skb_cb *cb; |
107 | int net_status, rc; |
108 | u8 pec, addr; |
109 | |
110 | skb = netdev_alloc_skb(dev: mi->mbus->ndev, |
111 | length: mi->mrl + sizeof(struct mctp_i3c_internal_hdr)); |
112 | if (!skb) { |
113 | stats->rx_dropped++; |
114 | rc = -ENOMEM; |
115 | goto err; |
116 | } |
117 | |
118 | skb->protocol = htons(ETH_P_MCTP); |
119 | /* Create a header for internal use */ |
120 | skb_reset_mac_header(skb); |
121 | ihdr = skb_put(skb, len: sizeof(struct mctp_i3c_internal_hdr)); |
122 | put_unaligned_be48(val: mi->pid, p: ihdr->source); |
123 | put_unaligned_be48(val: mi->mbus->pid, p: ihdr->dest); |
124 | skb_pull(skb, len: sizeof(struct mctp_i3c_internal_hdr)); |
125 | |
126 | xfer.data.in = skb_put(skb, len: mi->mrl); |
127 | |
128 | /* Make sure netif_rx() is read in the same order as i3c. */ |
129 | mutex_lock(&mi->lock); |
130 | rc = i3c_device_do_priv_xfers(dev: mi->i3c, xfers: &xfer, nxfers: 1); |
131 | if (rc < 0) |
132 | goto err; |
133 | |
134 | if (WARN_ON_ONCE(xfer.len > mi->mrl)) { |
135 | /* Bad i3c bus driver */ |
136 | rc = -EIO; |
137 | goto err; |
138 | } |
139 | if (xfer.len < MCTP_I3C_MINLEN) { |
140 | stats->rx_length_errors++; |
141 | rc = -EIO; |
142 | goto err; |
143 | } |
144 | |
145 | /* check PEC, including address byte */ |
146 | addr = mi->addr << 1 | 1; |
147 | pec = i2c_smbus_pec(crc: 0, p: &addr, count: 1); |
148 | pec = i2c_smbus_pec(crc: pec, p: xfer.data.in, count: xfer.len - 1); |
149 | if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) { |
150 | stats->rx_crc_errors++; |
151 | rc = -EINVAL; |
152 | goto err; |
153 | } |
154 | |
155 | /* Remove PEC */ |
156 | skb_trim(skb, len: xfer.len - 1); |
157 | |
158 | cb = __mctp_cb(skb); |
159 | cb->halen = PID_SIZE; |
160 | put_unaligned_be48(val: mi->pid, p: cb->haddr); |
161 | |
162 | net_status = netif_rx(skb); |
163 | |
164 | if (net_status == NET_RX_SUCCESS) { |
165 | stats->rx_packets++; |
166 | stats->rx_bytes += xfer.len - 1; |
167 | } else { |
168 | stats->rx_dropped++; |
169 | } |
170 | |
171 | mutex_unlock(lock: &mi->lock); |
172 | return 0; |
173 | err: |
174 | mutex_unlock(lock: &mi->lock); |
175 | kfree_skb(skb); |
176 | return rc; |
177 | } |
178 | |
179 | static void mctp_i3c_ibi_handler(struct i3c_device *i3c, |
180 | const struct i3c_ibi_payload *payload) |
181 | { |
182 | struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3cdev: i3c); |
183 | |
184 | if (WARN_ON_ONCE(!mi)) |
185 | return; |
186 | |
187 | if (mi->have_mdb) { |
188 | if (payload->len > 0) { |
189 | if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) { |
190 | /* Not a mctp-i3c interrupt, ignore it */ |
191 | return; |
192 | } |
193 | } else { |
194 | /* The BCR advertised a Mandatory Data Byte but the |
195 | * device didn't send one. |
196 | */ |
197 | dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB"); |
198 | } |
199 | } |
200 | |
201 | mctp_i3c_read(mi); |
202 | } |
203 | |
204 | static int mctp_i3c_setup(struct mctp_i3c_device *mi) |
205 | { |
206 | const struct i3c_ibi_setup ibi = { |
207 | .max_payload_len = 1, |
208 | .num_slots = MCTP_I3C_IBI_SLOTS, |
209 | .handler = mctp_i3c_ibi_handler, |
210 | }; |
211 | struct i3c_device_info info; |
212 | int rc; |
213 | |
214 | i3c_device_get_info(dev: mi->i3c, info: &info); |
215 | mi->have_mdb = info.bcr & BIT(2); |
216 | mi->addr = info.dyn_addr; |
217 | mi->mwl = info.max_write_len; |
218 | mi->mrl = info.max_read_len; |
219 | mi->pid = info.pid; |
220 | |
221 | rc = i3c_device_request_ibi(dev: mi->i3c, setup: &ibi); |
222 | if (rc == -ENOTSUPP) { |
223 | /* This driver only supports In-Band Interrupt mode. |
224 | * Support for Polling Mode could be added if required. |
225 | * (ENOTSUPP is from the i3c layer, not EOPNOTSUPP). |
226 | */ |
227 | dev_warn(i3cdev_to_dev(mi->i3c), |
228 | "Failed, bus driver doesn't support In-Band Interrupts"); |
229 | goto err; |
230 | } else if (rc < 0) { |
231 | dev_err(i3cdev_to_dev(mi->i3c), |
232 | "Failed requesting IBI (%d)\n", rc); |
233 | goto err; |
234 | } |
235 | |
236 | rc = i3c_device_enable_ibi(dev: mi->i3c); |
237 | if (rc < 0) { |
238 | /* Assume a driver supporting request_ibi also |
239 | * supports enable_ibi. |
240 | */ |
241 | dev_err(i3cdev_to_dev(mi->i3c), "Failed enabling IBI (%d)\n", rc); |
242 | goto err_free_ibi; |
243 | } |
244 | |
245 | return 0; |
246 | |
247 | err_free_ibi: |
248 | i3c_device_free_ibi(dev: mi->i3c); |
249 | |
250 | err: |
251 | return rc; |
252 | } |
253 | |
254 | /* Adds a new MCTP i3c_device to a bus */ |
255 | static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus, |
256 | struct i3c_device *i3c) |
257 | __must_hold(&busdevs_lock) |
258 | { |
259 | struct mctp_i3c_device *mi = NULL; |
260 | int rc; |
261 | |
262 | mi = kzalloc(sizeof(*mi), GFP_KERNEL); |
263 | if (!mi) { |
264 | rc = -ENOMEM; |
265 | goto err; |
266 | } |
267 | mi->mbus = mbus; |
268 | mi->i3c = i3c; |
269 | mutex_init(&mi->lock); |
270 | list_add(new: &mi->list, head: &mbus->devs); |
271 | |
272 | i3cdev_set_drvdata(i3cdev: i3c, data: mi); |
273 | rc = mctp_i3c_setup(mi); |
274 | if (rc < 0) |
275 | goto err_free; |
276 | |
277 | return 0; |
278 | |
279 | err_free: |
280 | list_del(entry: &mi->list); |
281 | kfree(objp: mi); |
282 | |
283 | err: |
284 | dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc); |
285 | return rc; |
286 | } |
287 | |
288 | static int mctp_i3c_probe(struct i3c_device *i3c) |
289 | { |
290 | struct mctp_i3c_bus *b = NULL, *mbus = NULL; |
291 | |
292 | /* Look for a known bus */ |
293 | mutex_lock(&busdevs_lock); |
294 | list_for_each_entry(b, &busdevs, list) |
295 | if (b->bus == i3c->bus) { |
296 | mbus = b; |
297 | break; |
298 | } |
299 | mutex_unlock(lock: &busdevs_lock); |
300 | |
301 | if (!mbus) { |
302 | /* probably no "mctp-controller" property on the i3c bus */ |
303 | return -ENODEV; |
304 | } |
305 | |
306 | return mctp_i3c_add_device(mbus, i3c); |
307 | } |
308 | |
309 | static void mctp_i3c_remove_device(struct mctp_i3c_device *mi) |
310 | __must_hold(&busdevs_lock) |
311 | { |
312 | /* Ensure the tx thread isn't using the device */ |
313 | mutex_lock(&mi->lock); |
314 | |
315 | /* Counterpart of mctp_i3c_setup */ |
316 | i3c_device_disable_ibi(dev: mi->i3c); |
317 | i3c_device_free_ibi(dev: mi->i3c); |
318 | |
319 | /* Counterpart of mctp_i3c_add_device */ |
320 | i3cdev_set_drvdata(i3cdev: mi->i3c, NULL); |
321 | list_del(entry: &mi->list); |
322 | |
323 | /* Safe to unlock after removing from the list */ |
324 | mutex_unlock(lock: &mi->lock); |
325 | kfree(objp: mi); |
326 | } |
327 | |
328 | static void mctp_i3c_remove(struct i3c_device *i3c) |
329 | { |
330 | struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3cdev: i3c); |
331 | |
332 | /* We my have received a Bus Remove notify prior to device remove, |
333 | * so mi will already be removed. |
334 | */ |
335 | if (!mi) |
336 | return; |
337 | |
338 | mutex_lock(&busdevs_lock); |
339 | mctp_i3c_remove_device(mi); |
340 | mutex_unlock(lock: &busdevs_lock); |
341 | } |
342 | |
343 | /* Returns the device for an address, with mi->lock held */ |
344 | static struct mctp_i3c_device * |
345 | mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid) |
346 | { |
347 | struct mctp_i3c_device *mi = NULL, *ret = NULL; |
348 | |
349 | mutex_lock(&busdevs_lock); |
350 | list_for_each_entry(mi, &mbus->devs, list) |
351 | if (mi->pid == pid) { |
352 | ret = mi; |
353 | mutex_lock(&mi->lock); |
354 | break; |
355 | } |
356 | mutex_unlock(lock: &busdevs_lock); |
357 | return ret; |
358 | } |
359 | |
360 | static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb) |
361 | { |
362 | struct net_device_stats *stats = &mbus->ndev->stats; |
363 | struct i3c_priv_xfer xfer = { .rnw = false }; |
364 | struct mctp_i3c_internal_hdr *ihdr = NULL; |
365 | struct mctp_i3c_device *mi = NULL; |
366 | unsigned int data_len; |
367 | u8 *data = NULL; |
368 | u8 addr, pec; |
369 | int rc = 0; |
370 | u64 pid; |
371 | |
372 | skb_pull(skb, len: sizeof(struct mctp_i3c_internal_hdr)); |
373 | data_len = skb->len; |
374 | |
375 | ihdr = (void *)skb_mac_header(skb); |
376 | |
377 | pid = get_unaligned_be48(p: ihdr->dest); |
378 | mi = mctp_i3c_lookup(mbus, pid); |
379 | if (!mi) { |
380 | /* I3C endpoint went away after the packet was enqueued? */ |
381 | stats->tx_dropped++; |
382 | goto out; |
383 | } |
384 | |
385 | if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF)) |
386 | goto out; |
387 | |
388 | if (data_len + 1 > (unsigned int)mi->mwl) { |
389 | /* Route MTU was larger than supported by the endpoint */ |
390 | stats->tx_dropped++; |
391 | goto out; |
392 | } |
393 | |
394 | /* Need a linear buffer with space for the PEC */ |
395 | xfer.len = data_len + 1; |
396 | if (skb_tailroom(skb) >= 1) { |
397 | skb_put(skb, len: 1); |
398 | data = skb->data; |
399 | } else { |
400 | /* Otherwise need to copy the buffer */ |
401 | skb_copy_bits(skb, offset: 0, to: mbus->tx_scratch, len: skb->len); |
402 | data = mbus->tx_scratch; |
403 | } |
404 | |
405 | /* PEC calculation */ |
406 | addr = mi->addr << 1; |
407 | pec = i2c_smbus_pec(crc: 0, p: &addr, count: 1); |
408 | pec = i2c_smbus_pec(crc: pec, p: data, count: data_len); |
409 | data[data_len] = pec; |
410 | |
411 | xfer.data.out = data; |
412 | rc = i3c_device_do_priv_xfers(dev: mi->i3c, xfers: &xfer, nxfers: 1); |
413 | if (rc == 0) { |
414 | stats->tx_bytes += data_len; |
415 | stats->tx_packets++; |
416 | } else { |
417 | stats->tx_errors++; |
418 | } |
419 | |
420 | out: |
421 | if (mi) |
422 | mutex_unlock(lock: &mi->lock); |
423 | } |
424 | |
425 | static int mctp_i3c_tx_thread(void *data) |
426 | { |
427 | struct mctp_i3c_bus *mbus = data; |
428 | struct sk_buff *skb; |
429 | |
430 | for (;;) { |
431 | if (kthread_should_stop()) |
432 | break; |
433 | |
434 | spin_lock_bh(lock: &mbus->tx_lock); |
435 | skb = mbus->tx_skb; |
436 | mbus->tx_skb = NULL; |
437 | spin_unlock_bh(lock: &mbus->tx_lock); |
438 | |
439 | if (netif_queue_stopped(dev: mbus->ndev)) |
440 | netif_wake_queue(dev: mbus->ndev); |
441 | |
442 | if (skb) { |
443 | mctp_i3c_xmit(mbus, skb); |
444 | kfree_skb(skb); |
445 | } else { |
446 | wait_event_idle(mbus->tx_wq, |
447 | mbus->tx_skb || kthread_should_stop()); |
448 | } |
449 | } |
450 | |
451 | return 0; |
452 | } |
453 | |
454 | static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb, |
455 | struct net_device *ndev) |
456 | { |
457 | struct mctp_i3c_bus *mbus = netdev_priv(dev: ndev); |
458 | netdev_tx_t ret; |
459 | |
460 | spin_lock(lock: &mbus->tx_lock); |
461 | netif_stop_queue(dev: ndev); |
462 | if (mbus->tx_skb) { |
463 | dev_warn_ratelimited(&ndev->dev, "TX with queue stopped"); |
464 | ret = NETDEV_TX_BUSY; |
465 | } else { |
466 | mbus->tx_skb = skb; |
467 | ret = NETDEV_TX_OK; |
468 | } |
469 | spin_unlock(lock: &mbus->tx_lock); |
470 | |
471 | if (ret == NETDEV_TX_OK) |
472 | wake_up(&mbus->tx_wq); |
473 | |
474 | return ret; |
475 | } |
476 | |
477 | static void mctp_i3c_bus_free(struct mctp_i3c_bus *mbus) |
478 | __must_hold(&busdevs_lock) |
479 | { |
480 | struct mctp_i3c_device *mi = NULL, *tmp = NULL; |
481 | |
482 | if (mbus->tx_thread) { |
483 | kthread_stop(k: mbus->tx_thread); |
484 | mbus->tx_thread = NULL; |
485 | } |
486 | |
487 | /* Remove any child devices */ |
488 | list_for_each_entry_safe(mi, tmp, &mbus->devs, list) { |
489 | mctp_i3c_remove_device(mi); |
490 | } |
491 | |
492 | kfree_skb(skb: mbus->tx_skb); |
493 | list_del(entry: &mbus->list); |
494 | } |
495 | |
496 | static void mctp_i3c_ndo_uninit(struct net_device *ndev) |
497 | { |
498 | struct mctp_i3c_bus *mbus = netdev_priv(dev: ndev); |
499 | |
500 | /* Perform cleanup here to ensure there are no remaining references */ |
501 | mctp_i3c_bus_free(mbus); |
502 | } |
503 | |
504 | static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev, |
505 | unsigned short type, const void *daddr, |
506 | const void *saddr, unsigned int len) |
507 | { |
508 | struct mctp_i3c_internal_hdr *ihdr; |
509 | int rc; |
510 | |
511 | if (!daddr || !saddr) |
512 | return -EINVAL; |
513 | |
514 | rc = skb_cow_head(skb, headroom: sizeof(struct mctp_i3c_internal_hdr)); |
515 | if (rc) |
516 | return rc; |
517 | |
518 | skb_push(skb, len: sizeof(struct mctp_i3c_internal_hdr)); |
519 | skb_reset_mac_header(skb); |
520 | ihdr = (void *)skb_mac_header(skb); |
521 | memcpy(ihdr->dest, daddr, PID_SIZE); |
522 | memcpy(ihdr->source, saddr, PID_SIZE); |
523 | return 0; |
524 | } |
525 | |
526 | static const struct net_device_ops mctp_i3c_ops = { |
527 | .ndo_start_xmit = mctp_i3c_start_xmit, |
528 | .ndo_uninit = mctp_i3c_ndo_uninit, |
529 | }; |
530 | |
531 | static const struct header_ops mctp_i3c_headops = { |
532 | .create = mctp_i3c_header_create, |
533 | }; |
534 | |
535 | static void mctp_i3c_net_setup(struct net_device *dev) |
536 | { |
537 | dev->type = ARPHRD_MCTP; |
538 | |
539 | dev->mtu = MCTP_I3C_MAXMTU; |
540 | dev->min_mtu = MCTP_I3C_MINMTU; |
541 | dev->max_mtu = MCTP_I3C_MAXMTU; |
542 | dev->tx_queue_len = MCTP_I3C_TX_QUEUE_LEN; |
543 | |
544 | dev->hard_header_len = sizeof(struct mctp_i3c_internal_hdr); |
545 | dev->addr_len = PID_SIZE; |
546 | |
547 | dev->netdev_ops = &mctp_i3c_ops; |
548 | dev->header_ops = &mctp_i3c_headops; |
549 | } |
550 | |
551 | static bool mctp_i3c_is_mctp_controller(struct i3c_bus *bus) |
552 | { |
553 | struct i3c_dev_desc *master = bus->cur_master; |
554 | |
555 | if (!master) |
556 | return false; |
557 | |
558 | return of_property_read_bool(np: master->common.master->dev.of_node, |
559 | propname: MCTP_I3C_OF_PROP); |
560 | } |
561 | |
562 | /* Returns the Provisioned Id of a local bus master */ |
563 | static int mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid) |
564 | { |
565 | struct i3c_dev_desc *master; |
566 | |
567 | master = bus->cur_master; |
568 | if (WARN_ON_ONCE(!master)) |
569 | return -ENOENT; |
570 | *ret_pid = master->info.pid; |
571 | |
572 | return 0; |
573 | } |
574 | |
575 | /* Returns an ERR_PTR on failure */ |
576 | static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus) |
577 | __must_hold(&busdevs_lock) |
578 | { |
579 | struct mctp_i3c_bus *mbus = NULL; |
580 | struct net_device *ndev = NULL; |
581 | char namebuf[IFNAMSIZ]; |
582 | u8 addr[PID_SIZE]; |
583 | int rc; |
584 | |
585 | if (!mctp_i3c_is_mctp_controller(bus)) |
586 | return ERR_PTR(error: -ENOENT); |
587 | |
588 | snprintf(buf: namebuf, size: sizeof(namebuf), fmt: "mctpi3c%d", bus->id); |
589 | ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM, |
590 | mctp_i3c_net_setup); |
591 | if (!ndev) { |
592 | rc = -ENOMEM; |
593 | goto err; |
594 | } |
595 | |
596 | mbus = netdev_priv(dev: ndev); |
597 | mbus->ndev = ndev; |
598 | mbus->bus = bus; |
599 | INIT_LIST_HEAD(list: &mbus->devs); |
600 | list_add(new: &mbus->list, head: &busdevs); |
601 | |
602 | rc = mctp_i3c_bus_local_pid(bus, ret_pid: &mbus->pid); |
603 | if (rc < 0) { |
604 | dev_err(&ndev->dev, "No I3C PID available\n"); |
605 | goto err_free_uninit; |
606 | } |
607 | put_unaligned_be48(val: mbus->pid, p: addr); |
608 | dev_addr_set(dev: ndev, addr); |
609 | |
610 | init_waitqueue_head(&mbus->tx_wq); |
611 | spin_lock_init(&mbus->tx_lock); |
612 | mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus, |
613 | "%s/tx", ndev->name); |
614 | if (IS_ERR(ptr: mbus->tx_thread)) { |
615 | dev_warn(&ndev->dev, "Error creating thread: %pe\n", |
616 | mbus->tx_thread); |
617 | rc = PTR_ERR(ptr: mbus->tx_thread); |
618 | mbus->tx_thread = NULL; |
619 | goto err_free_uninit; |
620 | } |
621 | |
622 | rc = mctp_register_netdev(dev: ndev, NULL, binding: MCTP_PHYS_BINDING_I3C); |
623 | if (rc < 0) { |
624 | dev_warn(&ndev->dev, "netdev register failed: %d\n", rc); |
625 | goto err_free_netdev; |
626 | } |
627 | return mbus; |
628 | |
629 | err_free_uninit: |
630 | /* uninit will not get called if a netdev has not been registered, |
631 | * so we perform the same mbus cleanup manually. |
632 | */ |
633 | mctp_i3c_bus_free(mbus); |
634 | |
635 | err_free_netdev: |
636 | free_netdev(dev: ndev); |
637 | |
638 | err: |
639 | return ERR_PTR(error: rc); |
640 | } |
641 | |
642 | static void mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus) |
643 | __must_hold(&busdevs_lock) |
644 | { |
645 | /* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */ |
646 | mctp_unregister_netdev(dev: mbus->ndev); |
647 | |
648 | free_netdev(dev: mbus->ndev); |
649 | /* mbus is deallocated */ |
650 | } |
651 | |
652 | /* Removes all mctp-i3c busses */ |
653 | static void mctp_i3c_bus_remove_all(void) |
654 | { |
655 | struct mctp_i3c_bus *mbus = NULL, *tmp = NULL; |
656 | |
657 | mutex_lock(&busdevs_lock); |
658 | list_for_each_entry_safe(mbus, tmp, &busdevs, list) { |
659 | mctp_i3c_bus_remove(mbus); |
660 | } |
661 | mutex_unlock(lock: &busdevs_lock); |
662 | } |
663 | |
664 | /* Adds a i3c_bus if it isn't already in the busdevs list. |
665 | * Suitable as an i3c_for_each_bus_locked callback. |
666 | */ |
667 | static int mctp_i3c_bus_add_new(struct i3c_bus *bus, void *data) |
668 | { |
669 | struct mctp_i3c_bus *mbus = NULL, *tmp = NULL; |
670 | bool exists = false; |
671 | |
672 | mutex_lock(&busdevs_lock); |
673 | list_for_each_entry_safe(mbus, tmp, &busdevs, list) |
674 | if (mbus->bus == bus) |
675 | exists = true; |
676 | |
677 | /* It is OK for a bus to already exist. That can occur due to |
678 | * the race in mod_init between notifier and for_each_bus |
679 | */ |
680 | if (!exists) |
681 | mctp_i3c_bus_add(bus); |
682 | mutex_unlock(lock: &busdevs_lock); |
683 | return 0; |
684 | } |
685 | |
686 | static void mctp_i3c_notify_bus_remove(struct i3c_bus *bus) |
687 | { |
688 | struct mctp_i3c_bus *mbus = NULL, *tmp; |
689 | |
690 | mutex_lock(&busdevs_lock); |
691 | list_for_each_entry_safe(mbus, tmp, &busdevs, list) |
692 | if (mbus->bus == bus) |
693 | mctp_i3c_bus_remove(mbus); |
694 | mutex_unlock(lock: &busdevs_lock); |
695 | } |
696 | |
697 | static int mctp_i3c_notifier_call(struct notifier_block *nb, |
698 | unsigned long action, void *data) |
699 | { |
700 | switch (action) { |
701 | case I3C_NOTIFY_BUS_ADD: |
702 | mctp_i3c_bus_add_new(bus: (struct i3c_bus *)data, NULL); |
703 | break; |
704 | case I3C_NOTIFY_BUS_REMOVE: |
705 | mctp_i3c_notify_bus_remove(bus: (struct i3c_bus *)data); |
706 | break; |
707 | } |
708 | return NOTIFY_DONE; |
709 | } |
710 | |
711 | static struct notifier_block mctp_i3c_notifier = { |
712 | .notifier_call = mctp_i3c_notifier_call, |
713 | }; |
714 | |
715 | static const struct i3c_device_id mctp_i3c_ids[] = { |
716 | I3C_CLASS(I3C_DCR_MCTP, NULL), |
717 | { 0 }, |
718 | }; |
719 | |
720 | static struct i3c_driver mctp_i3c_driver = { |
721 | .driver = { |
722 | .name = "mctp-i3c", |
723 | }, |
724 | .probe = mctp_i3c_probe, |
725 | .remove = mctp_i3c_remove, |
726 | .id_table = mctp_i3c_ids, |
727 | }; |
728 | |
729 | static __init int mctp_i3c_mod_init(void) |
730 | { |
731 | int rc; |
732 | |
733 | rc = i3c_register_notifier(nb: &mctp_i3c_notifier); |
734 | if (rc < 0) { |
735 | i3c_driver_unregister(drv: &mctp_i3c_driver); |
736 | return rc; |
737 | } |
738 | |
739 | i3c_for_each_bus_locked(fn: mctp_i3c_bus_add_new, NULL); |
740 | |
741 | rc = i3c_driver_register(&mctp_i3c_driver); |
742 | if (rc < 0) |
743 | return rc; |
744 | |
745 | return 0; |
746 | } |
747 | |
748 | static __exit void mctp_i3c_mod_exit(void) |
749 | { |
750 | int rc; |
751 | |
752 | i3c_driver_unregister(drv: &mctp_i3c_driver); |
753 | |
754 | rc = i3c_unregister_notifier(nb: &mctp_i3c_notifier); |
755 | if (rc < 0) |
756 | pr_warn("MCTP I3C could not unregister notifier, %d\n", rc); |
757 | |
758 | mctp_i3c_bus_remove_all(); |
759 | } |
760 | |
761 | module_init(mctp_i3c_mod_init); |
762 | module_exit(mctp_i3c_mod_exit); |
763 | |
764 | MODULE_DEVICE_TABLE(i3c, mctp_i3c_ids); |
765 | MODULE_DESCRIPTION("MCTP I3C device"); |
766 | MODULE_LICENSE("GPL"); |
767 | MODULE_AUTHOR("Matt Johnston <matt@codeconstruct.com.au>"); |
768 |
Definitions
- MCTP_I3C_MINMTU
- MCTP_I3C_MAXMTU
- MCTP_I3C_MINLEN
- MCTP_I3C_TX_QUEUE_LEN
- MCTP_I3C_IBI_SLOTS
- MCTP_I3C_OF_PROP
- busdevs
- busdevs_lock
- mctp_i3c_bus
- mctp_i3c_device
- mctp_i3c_internal_hdr
- mctp_i3c_read
- mctp_i3c_ibi_handler
- mctp_i3c_setup
- mctp_i3c_add_device
- mctp_i3c_probe
- mctp_i3c_remove_device
- mctp_i3c_remove
- mctp_i3c_lookup
- mctp_i3c_xmit
- mctp_i3c_tx_thread
- mctp_i3c_start_xmit
- mctp_i3c_bus_free
- mctp_i3c_ndo_uninit
- mctp_i3c_header_create
- mctp_i3c_ops
- mctp_i3c_headops
- mctp_i3c_net_setup
- mctp_i3c_is_mctp_controller
- mctp_i3c_bus_local_pid
- mctp_i3c_bus_add
- mctp_i3c_bus_remove
- mctp_i3c_bus_remove_all
- mctp_i3c_bus_add_new
- mctp_i3c_notify_bus_remove
- mctp_i3c_notifier_call
- mctp_i3c_notifier
- mctp_i3c_ids
- mctp_i3c_driver
- mctp_i3c_mod_init
Improve your Profiling and Debugging skills
Find out more