thunderbolt: Enable plug events
[deliverable/linux.git] / drivers / thunderbolt / tb.c
CommitLineData
d6cc51cd
AN
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"
7adf6097 12#include "tb_regs.h"
d6cc51cd
AN
13
14/* hotplug handling */
15
16struct 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 */
29static 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 */
38out:
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 */
48static 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 */
70void thunderbolt_shutdown_and_free(struct tb *tb)
71{
72 mutex_lock(&tb->lock);
73
a25c8b2f
AN
74 if (tb->root_switch)
75 tb_switch_free(tb->root_switch);
76 tb->root_switch = NULL;
77
d6cc51cd
AN
78 if (tb->ctl) {
79 tb_ctl_stop(tb->ctl);
80 tb_ctl_free(tb->ctl);
81 }
82 tb->ctl = NULL;
83 tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */
84
85 /* allow tb_handle_hotplug to acquire the lock */
86 mutex_unlock(&tb->lock);
87 if (tb->wq) {
88 flush_workqueue(tb->wq);
89 destroy_workqueue(tb->wq);
90 tb->wq = NULL;
91 }
92 mutex_destroy(&tb->lock);
93 kfree(tb);
94}
95
96/**
97 * thunderbolt_alloc_and_start() - setup the thunderbolt bus
98 *
99 * Allocates a tb_cfg control channel, initializes the root switch, enables
100 * plug events and activates pci devices.
101 *
102 * Return: Returns NULL on error.
103 */
104struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
105{
106 struct tb *tb;
107
7adf6097
AN
108 BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);
109 BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4);
110 BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4);
111
d6cc51cd
AN
112 tb = kzalloc(sizeof(*tb), GFP_KERNEL);
113 if (!tb)
114 return NULL;
115
116 tb->nhi = nhi;
117 mutex_init(&tb->lock);
118 mutex_lock(&tb->lock);
119
120 tb->wq = alloc_ordered_workqueue("thunderbolt", 0);
121 if (!tb->wq)
122 goto err_locked;
123
124 tb->ctl = tb_ctl_alloc(tb->nhi, tb_schedule_hotplug_handler, tb);
125 if (!tb->ctl)
126 goto err_locked;
127 /*
128 * tb_schedule_hotplug_handler may be called as soon as the config
129 * channel is started. Thats why we have to hold the lock here.
130 */
131 tb_ctl_start(tb->ctl);
132
a25c8b2f
AN
133 tb->root_switch = tb_switch_alloc(tb, 0);
134 if (!tb->root_switch)
135 goto err_locked;
136
d6cc51cd
AN
137 /* Allow tb_handle_hotplug to progress events */
138 tb->hotplug_active = true;
139 mutex_unlock(&tb->lock);
140 return tb;
141
142err_locked:
143 mutex_unlock(&tb->lock);
144 thunderbolt_shutdown_and_free(tb);
145 return NULL;
146}
147
This page took 0.02984 seconds and 5 git commands to generate.