diff options
author | Andreas Noever <andreas.noever@gmail.com> | 2014-06-03 16:04:00 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-06-19 17:07:07 -0400 |
commit | d6cc51cd1a4aed1d9e2dd66d643d729acb4be560 (patch) | |
tree | a193e0c2807cf18e11c770392c71e51bafbe378a /drivers/thunderbolt/tb.c | |
parent | f25bf6fcb1a83a149bc8b5285d33b48cbd47c7d7 (diff) |
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>
Diffstat (limited to 'drivers/thunderbolt/tb.c')
-rw-r--r-- | drivers/thunderbolt/tb.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c new file mode 100644 index 000000000000..6920979c5b14 --- /dev/null +++ b/drivers/thunderbolt/tb.c | |||
@@ -0,0 +1,134 @@ | |||
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 | |||