summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/ctl.c
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2017-06-06 14:25:09 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-06-09 11:42:42 +0200
commit81a54b5e1986d02da33c59133556ce9fe2032049 (patch)
treece49795db099240d89d573cc8a6cd49b0e06578f /drivers/thunderbolt/ctl.c
parentthunderbolt: Expose make_header() to other files (diff)
downloadlinux-81a54b5e1986d02da33c59133556ce9fe2032049.tar.xz
linux-81a54b5e1986d02da33c59133556ce9fe2032049.zip
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/ctl.c')
-rw-r--r--drivers/thunderbolt/ctl.c86
1 files changed, 60 insertions, 26 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 {
DECLARE_KFIFO(response_fifo, struct ctl_pkg*, 16);
struct completion response_ready;
- hotplug_cb callback;
+ event_cb callback;
void *callback_data;
};
@@ -52,6 +52,9 @@ struct tb_ctl {
#define tb_ctl_info(ctl, format, arg...) \
dev_info(&(ctl)->nhi->pdev->dev, format, ## arg)
+#define tb_ctl_dbg(ctl, format, arg...) \
+ dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg)
+
/* utility functions */
static 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,
}
/**
- * tb_ctl_handle_plug_event() - acknowledge a plug event, invoke ctl->callback
+ * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback
*/
-static void tb_ctl_handle_plug_event(struct tb_ctl *ctl,
- struct ctl_pkg *response)
+static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
+ struct ctl_pkg *pkg, size_t size)
{
- struct cfg_event_pkg *pkg = response->buffer;
- u64 route = tb_cfg_get_route(&pkg->header);
-
- if (check_header(response, sizeof(*pkg), TB_CFG_PKG_EVENT, route)) {
- tb_ctl_warn(ctl, "malformed TB_CFG_PKG_EVENT\n");
- return;
- }
-
- if (tb_cfg_error(ctl, route, pkg->port, TB_CFG_ERROR_ACK_PLUG_EVENT))
- tb_ctl_warn(ctl, "could not ack plug event on %llx:%x\n",
- route, pkg->port);
- WARN(pkg->zero, "pkg->zero is %#x\n", pkg->zero);
- ctl->callback(ctl->callback_data, route, pkg->port, pkg->unplug);
+ ctl->callback(ctl->callback_data, type, pkg->buffer, size);
}
static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
@@ -302,10 +293,29 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
*/
}
+static int tb_async_error(const struct ctl_pkg *pkg)
+{
+ const struct cfg_error_pkg *error = (const struct cfg_error_pkg *)pkg;
+
+ if (pkg->frame.eof != TB_CFG_PKG_ERROR)
+ return false;
+
+ switch (error->error) {
+ case TB_CFG_ERROR_LINK_ERROR:
+ case TB_CFG_ERROR_HEC_ERROR_DETECTED:
+ case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
bool canceled)
{
struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame);
+ __be32 crc32;
if (canceled)
return; /*
@@ -320,18 +330,42 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
}
frame->size -= 4; /* remove checksum */
- if (*(__be32 *) (pkg->buffer + frame->size)
- != tb_crc(pkg->buffer, frame->size)) {
- tb_ctl_err(pkg->ctl,
- "RX: checksum mismatch, dropping packet\n");
- goto rx;
- }
+ crc32 = tb_crc(pkg->buffer, frame->size);
be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4);
- if (frame->eof == TB_CFG_PKG_EVENT) {
- tb_ctl_handle_plug_event(pkg->ctl, pkg);
+ switch (frame->eof) {
+ case TB_CFG_PKG_READ:
+ case TB_CFG_PKG_WRITE:
+ case TB_CFG_PKG_ERROR:
+ case TB_CFG_PKG_OVERRIDE:
+ case TB_CFG_PKG_RESET:
+ if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
+ tb_ctl_err(pkg->ctl,
+ "RX: checksum mismatch, dropping packet\n");
+ goto rx;
+ }
+ if (tb_async_error(pkg)) {
+ tb_ctl_handle_event(pkg->ctl, frame->eof,
+ pkg, frame->size);
+ goto rx;
+ }
+ break;
+
+ case TB_CFG_PKG_EVENT:
+ if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
+ tb_ctl_err(pkg->ctl,
+ "RX: checksum mismatch, dropping packet\n");
+ goto rx;
+ }
+ tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size);
+ goto rx;
+
+ default:
+ tb_ctl_dbg(pkg->ctl, "RX: unknown package %#x, dropping\n",
+ frame->eof);
goto rx;
}
+
if (!kfifo_put(&pkg->ctl->response_fifo, pkg)) {
tb_ctl_err(pkg->ctl, "RX: fifo is full\n");
goto rx;
@@ -379,7 +413,7 @@ static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer,
*
* Return: Returns a pointer on success or NULL on failure.
*/
-struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data)
+struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
{
int i;
struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);