summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2017-06-06 08:25:09 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-06-09 05:42:42 -0400
commit81a54b5e1986d02da33c59133556ce9fe2032049 (patch)
treece49795db099240d89d573cc8a6cd49b0e06578f /drivers/thunderbolt
parent05c242e9e47d210ed6cbef31f2c441fa6ee325c6 (diff)
thunderbolt: Let the connection manager handle all notifications
Currently the control channel (ctl.c) handles the one supported notification (PLUG_EVENT) and sends back ACK accordingly. However, we are going to add support for the internal connection manager (ICM) that needs to handle a different notifications. So instead of dealing everything in the control channel, we change the callback to take an arbitrary thunderbolt packet and convert the native connection manager to handle the event itself. In addition we only push replies we know of to the response FIFO. Everything else is treated as notification (or request) and is expected to be dealt by the connection manager implementation. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com> Reviewed-by: Michael Jamet <michael.jamet@intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 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/ctl.c86
-rw-r--r--drivers/thunderbolt/ctl.h5
-rw-r--r--drivers/thunderbolt/domain.c15
-rw-r--r--drivers/thunderbolt/tb.c30
-rw-r--r--drivers/thunderbolt/tb.h5
5 files changed, 103 insertions, 38 deletions
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c
index c6633da582b8..5417ed244edc 100644
--- a/drivers/thunderbolt/ctl.c
+++ b/drivers/thunderbolt/ctl.c
@@ -35,7 +35,7 @@ struct tb_ctl {
35 DECLARE_KFIFO(response_fifo, struct ctl_pkg*, 16); 35 DECLARE_KFIFO(response_fifo, struct ctl_pkg*, 16);
36 struct completion response_ready; 36 struct completion response_ready;
37 37
38 hotplug_cb callback; 38 event_cb callback;
39 void *callback_data; 39 void *callback_data;
40}; 40};
41 41
@@ -52,6 +52,9 @@ struct tb_ctl {
52#define tb_ctl_info(ctl, format, arg...) \ 52#define tb_ctl_info(ctl, format, arg...) \
53 dev_info(&(ctl)->nhi->pdev->dev, format, ## arg) 53 dev_info(&(ctl)->nhi->pdev->dev, format, ## arg)
54 54
55#define tb_ctl_dbg(ctl, format, arg...) \
56 dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg)
57
55/* utility functions */ 58/* utility functions */
56 59
57static int check_header(struct ctl_pkg *pkg, u32 len, enum tb_cfg_pkg_type type, 60static int check_header(struct ctl_pkg *pkg, u32 len, enum tb_cfg_pkg_type type,
@@ -272,24 +275,12 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len,
272} 275}
273 276
274/** 277/**
275 * tb_ctl_handle_plug_event() - acknowledge a plug event, invoke ctl->callback 278 * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback
276 */ 279 */
277static void tb_ctl_handle_plug_event(struct tb_ctl *ctl, 280static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
278 struct ctl_pkg *response) 281 struct ctl_pkg *pkg, size_t size)
279{ 282{
280 struct cfg_event_pkg *pkg = response->buffer; 283 ctl->callback(ctl->callback_data, type, pkg->buffer, size);
281 u64 route = tb_cfg_get_route(&pkg->header);
282
283 if (check_header(response, sizeof(*pkg), TB_CFG_PKG_EVENT, route)) {
284 tb_ctl_warn(ctl, "malformed TB_CFG_PKG_EVENT\n");
285 return;
286 }
287
288 if (tb_cfg_error(ctl, route, pkg->port, TB_CFG_ERROR_ACK_PLUG_EVENT))
289 tb_ctl_warn(ctl, "could not ack plug event on %llx:%x\n",
290 route, pkg->port);
291 WARN(pkg->zero, "pkg->zero is %#x\n", pkg->zero);
292 ctl->callback(ctl->callback_data, route, pkg->port, pkg->unplug);
293} 284}
294 285
295static void tb_ctl_rx_submit(struct ctl_pkg *pkg) 286static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
@@ -302,10 +293,29 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
302 */ 293 */
303} 294}
304 295
296static int tb_async_error(const struct ctl_pkg *pkg)
297{
298 const struct cfg_error_pkg *error = (const struct cfg_error_pkg *)pkg;
299
300 if (pkg->frame.eof != TB_CFG_PKG_ERROR)
301 return false;
302
303 switch (error->error) {
304 case TB_CFG_ERROR_LINK_ERROR:
305 case TB_CFG_ERROR_HEC_ERROR_DETECTED:
306 case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
307 return true;
308
309 default:
310 return false;
311 }
312}
313
305static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, 314static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
306 bool canceled) 315 bool canceled)
307{ 316{
308 struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame); 317 struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame);
318 __be32 crc32;
309 319
310 if (canceled) 320 if (canceled)
311 return; /* 321 return; /*
@@ -320,18 +330,42 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
320 } 330 }
321 331
322 frame->size -= 4; /* remove checksum */ 332 frame->size -= 4; /* remove checksum */
323 if (*(__be32 *) (pkg->buffer + frame->size) 333 crc32 = tb_crc(pkg->buffer, frame->size);
324 != tb_crc(pkg->buffer, frame->size)) {
325 tb_ctl_err(pkg->ctl,
326 "RX: checksum mismatch, dropping packet\n");
327 goto rx;
328 }
329 be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4); 334 be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4);
330 335
331 if (frame->eof == TB_CFG_PKG_EVENT) { 336 switch (frame->eof) {
332 tb_ctl_handle_plug_event(pkg->ctl, pkg); 337 case TB_CFG_PKG_READ:
338 case TB_CFG_PKG_WRITE:
339 case TB_CFG_PKG_ERROR:
340 case TB_CFG_PKG_OVERRIDE:
341 case TB_CFG_PKG_RESET:
342 if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
343 tb_ctl_err(pkg->ctl,
344 "RX: checksum mismatch, dropping packet\n");
345 goto rx;
346 }
347 if (tb_async_error(pkg)) {
348 tb_ctl_handle_event(pkg->ctl, frame->eof,
349 pkg, frame->size);
350 goto rx;
351 }
352 break;
353
354 case TB_CFG_PKG_EVENT:
355 if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
356 tb_ctl_err(pkg->ctl,
357 "RX: checksum mismatch, dropping packet\n");
358 goto rx;
359 }
360 tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size);
361 goto rx;
362
363 default:
364 tb_ctl_dbg(pkg->ctl, "RX: unknown package %#x, dropping\n",
365 frame->eof);
333 goto rx; 366 goto rx;
334 } 367 }
368
335 if (!kfifo_put(&pkg->ctl->response_fifo, pkg)) { 369 if (!kfifo_put(&pkg->ctl->response_fifo, pkg)) {
336 tb_ctl_err(pkg->ctl, "RX: fifo is full\n"); 370 tb_ctl_err(pkg->ctl, "RX: fifo is full\n");
337 goto rx; 371 goto rx;
@@ -379,7 +413,7 @@ static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer,
379 * 413 *
380 * Return: Returns a pointer on success or NULL on failure. 414 * Return: Returns a pointer on success or NULL on failure.
381 */ 415 */
382struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data) 416struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
383{ 417{
384 int i; 418 int i;
385 struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 419 struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h
index 914da86ec77d..2b23e030a85b 100644
--- a/drivers/thunderbolt/ctl.h
+++ b/drivers/thunderbolt/ctl.h
@@ -13,9 +13,10 @@
13/* control channel */ 13/* control channel */
14struct tb_ctl; 14struct tb_ctl;
15 15
16typedef void (*hotplug_cb)(void *data, u64 route, u8 port, bool unplug); 16typedef void (*event_cb)(void *data, enum tb_cfg_pkg_type type,
17 const void *buf, size_t size);
17 18
18struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data); 19struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data);
19void tb_ctl_start(struct tb_ctl *ctl); 20void tb_ctl_start(struct tb_ctl *ctl);
20void tb_ctl_stop(struct tb_ctl *ctl); 21void tb_ctl_stop(struct tb_ctl *ctl);
21void tb_ctl_free(struct tb_ctl *ctl); 22void tb_ctl_free(struct tb_ctl *ctl);
diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 3302f4c59638..54bc15f9bf6b 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -95,6 +95,19 @@ err_free:
95 return NULL; 95 return NULL;
96} 96}
97 97
98static void tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type,
99 const void *buf, size_t size)
100{
101 struct tb *tb = data;
102
103 if (!tb->cm_ops->handle_event) {
104 tb_warn(tb, "domain does not have event handler\n");
105 return;
106 }
107
108 tb->cm_ops->handle_event(tb, type, buf, size);
109}
110
98/** 111/**
99 * tb_domain_add() - Add domain to the system 112 * tb_domain_add() - Add domain to the system
100 * @tb: Domain to add 113 * @tb: Domain to add
@@ -115,7 +128,7 @@ int tb_domain_add(struct tb *tb)
115 128
116 mutex_lock(&tb->lock); 129 mutex_lock(&tb->lock);
117 130
118 tb->ctl = tb_ctl_alloc(tb->nhi, tb->cm_ops->hotplug, tb); 131 tb->ctl = tb_ctl_alloc(tb->nhi, tb_domain_event_cb, tb);
119 if (!tb->ctl) { 132 if (!tb->ctl) {
120 ret = -ENOMEM; 133 ret = -ENOMEM;
121 goto err_unlock; 134 goto err_unlock;
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 94ecac012428..ea9de49b5e10 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -311,18 +311,34 @@ out:
311 * 311 *
312 * Delegates to tb_handle_hotplug. 312 * Delegates to tb_handle_hotplug.
313 */ 313 */
314static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port, 314static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
315 bool unplug) 315 const void *buf, size_t size)
316{ 316{
317 struct tb *tb = data; 317 const struct cfg_event_pkg *pkg = buf;
318 struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL); 318 struct tb_hotplug_event *ev;
319 u64 route;
320
321 if (type != TB_CFG_PKG_EVENT) {
322 tb_warn(tb, "unexpected event %#x, ignoring\n", type);
323 return;
324 }
325
326 route = tb_cfg_get_route(&pkg->header);
327
328 if (tb_cfg_error(tb->ctl, route, pkg->port,
329 TB_CFG_ERROR_ACK_PLUG_EVENT)) {
330 tb_warn(tb, "could not ack plug event on %llx:%x\n", route,
331 pkg->port);
332 }
333
334 ev = kmalloc(sizeof(*ev), GFP_KERNEL);
319 if (!ev) 335 if (!ev)
320 return; 336 return;
321 INIT_WORK(&ev->work, tb_handle_hotplug); 337 INIT_WORK(&ev->work, tb_handle_hotplug);
322 ev->tb = tb; 338 ev->tb = tb;
323 ev->route = route; 339 ev->route = route;
324 ev->port = port; 340 ev->port = pkg->port;
325 ev->unplug = unplug; 341 ev->unplug = pkg->unplug;
326 queue_work(tb->wq, &ev->work); 342 queue_work(tb->wq, &ev->work);
327} 343}
328 344
@@ -419,7 +435,7 @@ static const struct tb_cm_ops tb_cm_ops = {
419 .stop = tb_stop, 435 .stop = tb_stop,
420 .suspend_noirq = tb_suspend_noirq, 436 .suspend_noirq = tb_suspend_noirq,
421 .resume_noirq = tb_resume_noirq, 437 .resume_noirq = tb_resume_noirq,
422 .hotplug = tb_schedule_hotplug_handler, 438 .handle_event = tb_handle_event,
423}; 439};
424 440
425struct tb *tb_probe(struct tb_nhi *nhi) 441struct tb *tb_probe(struct tb_nhi *nhi)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 6d4910ef2eb9..5bb9a5d60d2c 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -118,14 +118,15 @@ struct tb_path {
118 * @stop: Stops the domain 118 * @stop: Stops the domain
119 * @suspend_noirq: Connection manager specific suspend_noirq 119 * @suspend_noirq: Connection manager specific suspend_noirq
120 * @resume_noirq: Connection manager specific resume_noirq 120 * @resume_noirq: Connection manager specific resume_noirq
121 * @hotplug: Handle hotplug event 121 * @handle_event: Handle thunderbolt event
122 */ 122 */
123struct tb_cm_ops { 123struct tb_cm_ops {
124 int (*start)(struct tb *tb); 124 int (*start)(struct tb *tb);
125 void (*stop)(struct tb *tb); 125 void (*stop)(struct tb *tb);
126 int (*suspend_noirq)(struct tb *tb); 126 int (*suspend_noirq)(struct tb *tb);
127 int (*resume_noirq)(struct tb *tb); 127 int (*resume_noirq)(struct tb *tb);
128 hotplug_cb hotplug; 128 void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type,
129 const void *buf, size_t size);
129}; 130};
130 131
131/** 132/**