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 | |
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')
-rw-r--r-- | drivers/thunderbolt/Makefile | 2 | ||||
-rw-r--r-- | drivers/thunderbolt/nhi.c | 18 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.c | 134 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.h | 35 |
4 files changed, 186 insertions, 3 deletions
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 4b21c9926ea8..1f996bb96ac9 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | obj-${CONFIG_THUNDERBOLT} := thunderbolt.o | 1 | obj-${CONFIG_THUNDERBOLT} := thunderbolt.o |
2 | thunderbolt-objs := nhi.o ctl.o | 2 | thunderbolt-objs := nhi.o ctl.o tb.o |
3 | 3 | ||
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 11070ff2cec7..d2b9ce857818 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | #include "nhi.h" | 17 | #include "nhi.h" |
18 | #include "nhi_regs.h" | 18 | #include "nhi_regs.h" |
19 | #include "tb.h" | ||
19 | 20 | ||
20 | #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring") | 21 | #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring") |
21 | 22 | ||
@@ -517,6 +518,7 @@ static void nhi_shutdown(struct tb_nhi *nhi) | |||
517 | static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 518 | static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
518 | { | 519 | { |
519 | struct tb_nhi *nhi; | 520 | struct tb_nhi *nhi; |
521 | struct tb *tb; | ||
520 | int res; | 522 | int res; |
521 | 523 | ||
522 | res = pcim_enable_device(pdev); | 524 | res = pcim_enable_device(pdev); |
@@ -575,14 +577,26 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
575 | /* magic value - clock related? */ | 577 | /* magic value - clock related? */ |
576 | iowrite32(3906250 / 10000, nhi->iobase + 0x38c00); | 578 | iowrite32(3906250 / 10000, nhi->iobase + 0x38c00); |
577 | 579 | ||
578 | pci_set_drvdata(pdev, nhi); | 580 | dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n"); |
581 | tb = thunderbolt_alloc_and_start(nhi); | ||
582 | if (!tb) { | ||
583 | /* | ||
584 | * At this point the RX/TX rings might already have been | ||
585 | * activated. Do a proper shutdown. | ||
586 | */ | ||
587 | nhi_shutdown(nhi); | ||
588 | return -EIO; | ||
589 | } | ||
590 | pci_set_drvdata(pdev, tb); | ||
579 | 591 | ||
580 | return 0; | 592 | return 0; |
581 | } | 593 | } |
582 | 594 | ||
583 | static void nhi_remove(struct pci_dev *pdev) | 595 | static void nhi_remove(struct pci_dev *pdev) |
584 | { | 596 | { |
585 | struct tb_nhi *nhi = pci_get_drvdata(pdev); | 597 | struct tb *tb = pci_get_drvdata(pdev); |
598 | struct tb_nhi *nhi = tb->nhi; | ||
599 | thunderbolt_shutdown_and_free(tb); | ||
586 | nhi_shutdown(nhi); | 600 | nhi_shutdown(nhi); |
587 | } | 601 | } |
588 | 602 | ||
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 | |||
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h new file mode 100644 index 000000000000..6055dfd713fc --- /dev/null +++ b/drivers/thunderbolt/tb.h | |||
@@ -0,0 +1,35 @@ | |||
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 | ||