Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thunderbolt: Setup control channel

Add struct tb which will contain our view of the thunderbolt bus. For
now it just contains a pointer to the control channel and a workqueue
for hotplug events.

Add thunderbolt_alloc_and_start() and thunderbolt_shutdown_and_free()
which are responsible for setup and teardown of struct tb.

Signed-off-by: Andreas Noever <andreas.noever@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Andreas Noever and committed by
Greg Kroah-Hartman
d6cc51cd f25bf6fc

+186 -3
+1 -1
drivers/thunderbolt/Makefile
··· 1 1 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o 2 - thunderbolt-objs := nhi.o ctl.o 2 + thunderbolt-objs := nhi.o ctl.o tb.o 3 3
+16 -2
drivers/thunderbolt/nhi.c
··· 16 16 17 17 #include "nhi.h" 18 18 #include "nhi_regs.h" 19 + #include "tb.h" 19 20 20 21 #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring") 21 22 ··· 518 517 static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) 519 518 { 520 519 struct tb_nhi *nhi; 520 + struct tb *tb; 521 521 int res; 522 522 523 523 res = pcim_enable_device(pdev); ··· 577 575 /* magic value - clock related? */ 578 576 iowrite32(3906250 / 10000, nhi->iobase + 0x38c00); 579 577 580 - pci_set_drvdata(pdev, nhi); 578 + dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n"); 579 + tb = thunderbolt_alloc_and_start(nhi); 580 + if (!tb) { 581 + /* 582 + * At this point the RX/TX rings might already have been 583 + * activated. Do a proper shutdown. 584 + */ 585 + nhi_shutdown(nhi); 586 + return -EIO; 587 + } 588 + pci_set_drvdata(pdev, tb); 581 589 582 590 return 0; 583 591 } 584 592 585 593 static void nhi_remove(struct pci_dev *pdev) 586 594 { 587 - struct tb_nhi *nhi = pci_get_drvdata(pdev); 595 + struct tb *tb = pci_get_drvdata(pdev); 596 + struct tb_nhi *nhi = tb->nhi; 597 + thunderbolt_shutdown_and_free(tb); 588 598 nhi_shutdown(nhi); 589 599 } 590 600
+134
drivers/thunderbolt/tb.c
··· 1 + /* 2 + * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) 3 + * 4 + * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> 5 + */ 6 + 7 + #include <linux/slab.h> 8 + #include <linux/errno.h> 9 + #include <linux/delay.h> 10 + 11 + #include "tb.h" 12 + 13 + /* hotplug handling */ 14 + 15 + struct tb_hotplug_event { 16 + struct work_struct work; 17 + struct tb *tb; 18 + u64 route; 19 + u8 port; 20 + bool unplug; 21 + }; 22 + 23 + /** 24 + * tb_handle_hotplug() - handle hotplug event 25 + * 26 + * Executes on tb->wq. 27 + */ 28 + static void tb_handle_hotplug(struct work_struct *work) 29 + { 30 + struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work); 31 + struct tb *tb = ev->tb; 32 + mutex_lock(&tb->lock); 33 + if (!tb->hotplug_active) 34 + goto out; /* during init, suspend or shutdown */ 35 + 36 + /* do nothing for now */ 37 + out: 38 + mutex_unlock(&tb->lock); 39 + kfree(ev); 40 + } 41 + 42 + /** 43 + * tb_schedule_hotplug_handler() - callback function for the control channel 44 + * 45 + * Delegates to tb_handle_hotplug. 46 + */ 47 + static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port, 48 + bool unplug) 49 + { 50 + struct tb *tb = data; 51 + struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL); 52 + if (!ev) 53 + return; 54 + INIT_WORK(&ev->work, tb_handle_hotplug); 55 + ev->tb = tb; 56 + ev->route = route; 57 + ev->port = port; 58 + ev->unplug = unplug; 59 + queue_work(tb->wq, &ev->work); 60 + } 61 + 62 + /** 63 + * thunderbolt_shutdown_and_free() - shutdown everything 64 + * 65 + * Free all switches and the config channel. 66 + * 67 + * Used in the error path of thunderbolt_alloc_and_start. 68 + */ 69 + void thunderbolt_shutdown_and_free(struct tb *tb) 70 + { 71 + mutex_lock(&tb->lock); 72 + 73 + if (tb->ctl) { 74 + tb_ctl_stop(tb->ctl); 75 + tb_ctl_free(tb->ctl); 76 + } 77 + tb->ctl = NULL; 78 + tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */ 79 + 80 + /* allow tb_handle_hotplug to acquire the lock */ 81 + mutex_unlock(&tb->lock); 82 + if (tb->wq) { 83 + flush_workqueue(tb->wq); 84 + destroy_workqueue(tb->wq); 85 + tb->wq = NULL; 86 + } 87 + mutex_destroy(&tb->lock); 88 + kfree(tb); 89 + } 90 + 91 + /** 92 + * thunderbolt_alloc_and_start() - setup the thunderbolt bus 93 + * 94 + * Allocates a tb_cfg control channel, initializes the root switch, enables 95 + * plug events and activates pci devices. 96 + * 97 + * Return: Returns NULL on error. 98 + */ 99 + struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi) 100 + { 101 + struct tb *tb; 102 + 103 + tb = kzalloc(sizeof(*tb), GFP_KERNEL); 104 + if (!tb) 105 + return NULL; 106 + 107 + tb->nhi = nhi; 108 + mutex_init(&tb->lock); 109 + mutex_lock(&tb->lock); 110 + 111 + tb->wq = alloc_ordered_workqueue("thunderbolt", 0); 112 + if (!tb->wq) 113 + goto err_locked; 114 + 115 + tb->ctl = tb_ctl_alloc(tb->nhi, tb_schedule_hotplug_handler, tb); 116 + if (!tb->ctl) 117 + goto err_locked; 118 + /* 119 + * tb_schedule_hotplug_handler may be called as soon as the config 120 + * channel is started. Thats why we have to hold the lock here. 121 + */ 122 + tb_ctl_start(tb->ctl); 123 + 124 + /* Allow tb_handle_hotplug to progress events */ 125 + tb->hotplug_active = true; 126 + mutex_unlock(&tb->lock); 127 + return tb; 128 + 129 + err_locked: 130 + mutex_unlock(&tb->lock); 131 + thunderbolt_shutdown_and_free(tb); 132 + return NULL; 133 + } 134 +
+35
drivers/thunderbolt/tb.h
··· 1 + /* 2 + * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) 3 + * 4 + * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> 5 + */ 6 + 7 + #ifndef TB_H_ 8 + #define TB_H_ 9 + 10 + #include "ctl.h" 11 + 12 + /** 13 + * struct tb - main thunderbolt bus structure 14 + */ 15 + struct tb { 16 + struct mutex lock; /* 17 + * Big lock. Must be held when accessing cfg or 18 + * any struct tb_switch / struct tb_port. 19 + */ 20 + struct tb_nhi *nhi; 21 + struct tb_ctl *ctl; 22 + struct workqueue_struct *wq; /* ordered workqueue for plug events */ 23 + bool hotplug_active; /* 24 + * tb_handle_hotplug will stop progressing plug 25 + * events and exit if this is not set (it needs to 26 + * acquire the lock one more time). Used to drain 27 + * wq after cfg has been paused. 28 + */ 29 + 30 + }; 31 + 32 + struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi); 33 + void thunderbolt_shutdown_and_free(struct tb *tb); 34 + 35 + #endif