xref: /openbmc/linux/drivers/thunderbolt/tb.c (revision 7adf6097)
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 #include "tb_regs.h"
13 
14 /* hotplug handling */
15 
16 struct tb_hotplug_event {
17 	struct work_struct work;
18 	struct tb *tb;
19 	u64 route;
20 	u8 port;
21 	bool unplug;
22 };
23 
24 /**
25  * tb_handle_hotplug() - handle hotplug event
26  *
27  * Executes on tb->wq.
28  */
29 static void tb_handle_hotplug(struct work_struct *work)
30 {
31 	struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
32 	struct tb *tb = ev->tb;
33 	mutex_lock(&tb->lock);
34 	if (!tb->hotplug_active)
35 		goto out; /* during init, suspend or shutdown */
36 
37 	/* do nothing for now */
38 out:
39 	mutex_unlock(&tb->lock);
40 	kfree(ev);
41 }
42 
43 /**
44  * tb_schedule_hotplug_handler() - callback function for the control channel
45  *
46  * Delegates to tb_handle_hotplug.
47  */
48 static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port,
49 					bool unplug)
50 {
51 	struct tb *tb = data;
52 	struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL);
53 	if (!ev)
54 		return;
55 	INIT_WORK(&ev->work, tb_handle_hotplug);
56 	ev->tb = tb;
57 	ev->route = route;
58 	ev->port = port;
59 	ev->unplug = unplug;
60 	queue_work(tb->wq, &ev->work);
61 }
62 
63 /**
64  * thunderbolt_shutdown_and_free() - shutdown everything
65  *
66  * Free all switches and the config channel.
67  *
68  * Used in the error path of thunderbolt_alloc_and_start.
69  */
70 void thunderbolt_shutdown_and_free(struct tb *tb)
71 {
72 	mutex_lock(&tb->lock);
73 
74 	if (tb->ctl) {
75 		tb_ctl_stop(tb->ctl);
76 		tb_ctl_free(tb->ctl);
77 	}
78 	tb->ctl = NULL;
79 	tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */
80 
81 	/* allow tb_handle_hotplug to acquire the lock */
82 	mutex_unlock(&tb->lock);
83 	if (tb->wq) {
84 		flush_workqueue(tb->wq);
85 		destroy_workqueue(tb->wq);
86 		tb->wq = NULL;
87 	}
88 	mutex_destroy(&tb->lock);
89 	kfree(tb);
90 }
91 
92 /**
93  * thunderbolt_alloc_and_start() - setup the thunderbolt bus
94  *
95  * Allocates a tb_cfg control channel, initializes the root switch, enables
96  * plug events and activates pci devices.
97  *
98  * Return: Returns NULL on error.
99  */
100 struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
101 {
102 	struct tb *tb;
103 
104 	BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);
105 	BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4);
106 	BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4);
107 
108 	tb = kzalloc(sizeof(*tb), GFP_KERNEL);
109 	if (!tb)
110 		return NULL;
111 
112 	tb->nhi = nhi;
113 	mutex_init(&tb->lock);
114 	mutex_lock(&tb->lock);
115 
116 	tb->wq = alloc_ordered_workqueue("thunderbolt", 0);
117 	if (!tb->wq)
118 		goto err_locked;
119 
120 	tb->ctl = tb_ctl_alloc(tb->nhi, tb_schedule_hotplug_handler, tb);
121 	if (!tb->ctl)
122 		goto err_locked;
123 	/*
124 	 * tb_schedule_hotplug_handler may be called as soon as the config
125 	 * channel is started. Thats why we have to hold the lock here.
126 	 */
127 	tb_ctl_start(tb->ctl);
128 
129 	/* Allow tb_handle_hotplug to progress events */
130 	tb->hotplug_active = true;
131 	mutex_unlock(&tb->lock);
132 	return tb;
133 
134 err_locked:
135 	mutex_unlock(&tb->lock);
136 	thunderbolt_shutdown_and_free(tb);
137 	return NULL;
138 }
139 
140