[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1385688949-7101-4-git-send-email-andreas.noever@gmail.com>
Date: Fri, 29 Nov 2013 02:35:40 +0100
From: Andreas Noever <andreas.noever@...il.com>
To: linux-kernel@...r.kernel.org
Cc: Andreas Noever <andreas.noever@...il.com>
Subject: [PATCH 03/12] thunderbolt: Setup configuration channel
Add struct tb which will contain our view of the thunderbolt bus. For
now it just contains a pointer to the configuration 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@...il.com>
---
drivers/thunderbolt/Makefile | 2 +-
drivers/thunderbolt/dsl3510.c | 18 +++++-
drivers/thunderbolt/tb.c | 124 ++++++++++++++++++++++++++++++++++++++++++
drivers/thunderbolt/tb.h | 34 ++++++++++++
4 files changed, 175 insertions(+), 3 deletions(-)
create mode 100644 drivers/thunderbolt/tb.c
create mode 100644 drivers/thunderbolt/tb.h
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index f486295..7c5b811 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := dsl3510.o tb_cfg.o
+thunderbolt-objs := dsl3510.o tb_cfg.o tb.o
diff --git a/drivers/thunderbolt/dsl3510.c b/drivers/thunderbolt/dsl3510.c
index 2a326f6..434812b 100644
--- a/drivers/thunderbolt/dsl3510.c
+++ b/drivers/thunderbolt/dsl3510.c
@@ -12,6 +12,7 @@
#include "dsl3510.h"
#include "dsl3510_regs.h"
+#include "tb.h"
#define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring")
@@ -481,6 +482,7 @@ static void dsl3510_shutdown(struct tb_nhi *nhi)
static int dsl3510_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct tb_nhi *nhi;
+ struct tb *tb;
int res;
res = pcim_enable_device(pdev);
@@ -543,14 +545,26 @@ static int dsl3510_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* magic value - clock related? */
iowrite32(3906250 / 10000, nhi->iobase + 0x38c00);
- pci_set_drvdata(pdev, nhi); /* for dsl3510_remove only */
+ dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n");
+ tb = thunderbolt_alloc_and_start(nhi);
+ if (!tb) {
+ /*
+ * At this point the RX/TX rings might already have been
+ * activated. Do a proper shutdown.
+ */
+ dsl3510_shutdown(nhi);
+ return -EIO;
+ }
+ pci_set_drvdata(pdev, tb); /* for dsl3510_remove only */
return 0;
}
static void dsl3510_remove(struct pci_dev *pdev)
{
- struct tb_nhi *nhi = pci_get_drvdata(pdev);
+ struct tb *tb = pci_get_drvdata(pdev);
+ struct tb_nhi *nhi = tb->nhi;
+ thunderbolt_shutdown_and_free(tb);
dsl3510_shutdown(nhi);
}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
new file mode 100644
index 0000000..64deb7b
--- /dev/null
+++ b/drivers/thunderbolt/tb.c
@@ -0,0 +1,124 @@
+/*
+ * Device independent Thunderbolt bus logic
+ *
+ * Copyright (c) 2013 Andreas Noever <andreas.noever@...il.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include "tb.h"
+
+struct tb_hotplug_event {
+ struct work_struct work;
+ struct tb *tb;
+ u64 route;
+ u8 port;
+ bool unplug;
+};
+
+/**
+ * tb_handle_hotplug() - handle hotplug event
+ *
+ * Executes on the tb->wq.
+ */
+static void tb_handle_hotplug(struct work_struct *work)
+{
+ struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
+ struct tb *tb = ev->tb;
+ mutex_lock(&tb->lock);
+ if (tb->shutdown)
+ goto out;
+ /* do nothing for now */
+out:
+ mutex_unlock(&tb->lock);
+ kfree(ev);
+}
+
+/**
+ * tb_schedule_hotplug_handler() - callback function for the config channel
+ *
+ * Delegates to tb_handle_hotplug.
+ */
+static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port,
+ bool unplug)
+{
+ struct tb *tb = data;
+ struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL);
+ if (!ev)
+ return;
+ INIT_WORK(&ev->work, tb_handle_hotplug);
+ ev->tb = tb;
+ ev->route = route;
+ ev->port = port;
+ ev->unplug = unplug;
+ queue_work(tb->wq, &ev->work);
+}
+
+/**
+ * thunderbolt_shutdown_and_free() - shutdown everything
+ *
+ * Free the config channel.
+ */
+void thunderbolt_shutdown_and_free(struct tb *tb)
+{
+ mutex_lock(&tb->lock);
+ tb->shutdown = true; /* signal tb_handle_hotplug to quit */
+
+ if (tb->cfg)
+ tb_cfg_free(tb->cfg);
+ tb->cfg = NULL;
+
+ /* allow tb_handle_hotplug to acquire the lock */
+ mutex_unlock(&tb->lock);
+ if (tb->wq) {
+ flush_workqueue(tb->wq);
+ destroy_workqueue(tb->wq);
+ tb->wq = NULL;
+ }
+ mutex_destroy(&tb->lock);
+ kfree(tb);
+}
+
+/**
+ * thunderbolt_alloc_and_start() - setup the thunderbolt bus
+ *
+ * Allocates a tb_cfg control channel.
+ *
+ * Return: Returns NULL on error.
+ */
+struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
+{
+ struct tb *tb;
+
+ tb = kzalloc(sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ return NULL;
+
+ tb->nhi = nhi;
+ mutex_init(&tb->lock);
+ mutex_lock(&tb->lock);
+
+ tb->wq = alloc_ordered_workqueue("thunderbolt", 0);
+ if (!tb->wq)
+ goto err_locked;
+
+ /*
+ * tb_schedule_hotplug_handler may be called as soon as the config
+ * channel is allocated. Thats why we have to hold the lock here.
+ */
+ tb->cfg = tb_cfg_alloc(tb->nhi, tb_schedule_hotplug_handler, tb);
+ if (!tb->cfg)
+ goto err_locked;
+
+ mutex_unlock(&tb->lock);
+ return tb;
+
+err_locked:
+ /* In case tb_handle_hotplug is already executing. */
+ tb->shutdown = true;
+ mutex_unlock(&tb->lock);
+ thunderbolt_shutdown_and_free(tb);
+ return NULL;
+}
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
new file mode 100644
index 0000000..a3378dc
--- /dev/null
+++ b/drivers/thunderbolt/tb.h
@@ -0,0 +1,34 @@
+/*
+ * Device independent Thunderbolt bus logic
+ *
+ * Copyright (c) 2013 Andreas Noever <andreas.noever@...il.com>
+ */
+
+#ifndef TB_H_
+#define TB_H_
+
+#include <linux/pci.h>
+
+#include "tb_cfg.h"
+
+/**
+ * struct tb - main thunderbolt bus structure
+ */
+struct tb {
+ struct mutex lock; /*
+ * Big lock. Must be held when accessing cfg or
+ * any struct tb_switch / struct tb_port.
+ */
+ struct tb_nhi *nhi;
+ struct tb_cfg *cfg;
+ struct workqueue_struct *wq; /* ordered workqueue for plug events */
+ bool shutdown; /*
+ * Once this is set tb_handle_hotplug will exit (once it
+ * can aquire lock at least once). Used to drain wq.
+ */
+};
+
+struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi);
+void thunderbolt_shutdown_and_free(struct tb *tb);
+
+#endif
--
1.8.4.2
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists