From: Andreas Noever <andreas.noever@gmail.com>
To: linux-kernel@vger.kernel.org,
Matthew Garrett <matthew.garrett@nebula.com>,
Greg KH <greg@kroah.com>, Bjorn Helgaas <bhelgaas@google.com>,
linux-pci@vger.kernel.org
Cc: Andreas Noever <andreas.noever@gmail.com>
Subject: [PATCH v3 05/15] thunderbolt: Initialize root switch and ports
Date: Mon, 26 May 2014 17:18:02 +0200 [thread overview]
Message-ID: <1401117492-2870-6-git-send-email-andreas.noever@gmail.com> (raw)
In-Reply-To: <1401117492-2870-1-git-send-email-andreas.noever@gmail.com>
This patch adds the structures tb_switch and tb_port as well as code to
initialize the root switch.
Signed-off-by: Andreas Noever <andreas.noever@gmail.com>
---
drivers/thunderbolt/Makefile | 2 +-
drivers/thunderbolt/switch.c | 186 +++++++++++++++++++++++++++++++++++++++++++
drivers/thunderbolt/tb.c | 8 ++
drivers/thunderbolt/tb.h | 138 ++++++++++++++++++++++++++++++++
4 files changed, 333 insertions(+), 1 deletion(-)
create mode 100644 drivers/thunderbolt/switch.c
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 1f996bb..4ac18d9 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
new file mode 100644
index 0000000..e89121f
--- /dev/null
+++ b/drivers/thunderbolt/switch.c
@@ -0,0 +1,186 @@
+/*
+ * Thunderbolt Cactus Ridge driver - switch/port utility functions
+ *
+ * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
+ */
+
+#include <linux/delay.h>
+
+#include "tb.h"
+
+/* port utility functions */
+
+static const char *tb_port_type(struct tb_regs_port_header *port)
+{
+ switch (port->type >> 16) {
+ case 0:
+ switch ((u8) port->type) {
+ case 0:
+ return "Inactive";
+ case 1:
+ return "Port";
+ case 2:
+ return "NHI";
+ default:
+ return "unknown";
+ }
+ case 0x2:
+ return "Ethernet";
+ case 0x8:
+ return "SATA";
+ case 0xe:
+ return "DP/HDMI";
+ case 0x10:
+ return "PCIe";
+ case 0x20:
+ return "USB";
+ default:
+ return "unknown";
+ }
+}
+
+static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
+{
+ tb_info(tb,
+ " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n",
+ port->port_number, port->vendor_id, port->device_id,
+ port->revision, port->thunderbolt_version, tb_port_type(port),
+ port->type);
+ tb_info(tb, " Max hop id (in/out): %d/%d\n",
+ port->max_in_hop_id, port->max_out_hop_id);
+ tb_info(tb, " Max counters: %d\n", port->max_counters);
+ tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits);
+}
+
+/**
+ * tb_init_port() - initialize a port
+ *
+ * This is a helper method for tb_switch_alloc. Does not check or initialize
+ * any downstream switches.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+static int tb_init_port(struct tb_switch *sw, u8 port_nr)
+{
+ int res;
+ struct tb_port *port = &sw->ports[port_nr];
+ port->sw = sw;
+ port->port = port_nr;
+ port->remote = NULL;
+ res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
+ if (res)
+ return res;
+
+ tb_dump_port(sw->tb, &port->config);
+
+ /* TODO: Read dual link port, DP port and more from EEPROM. */
+ return 0;
+
+}
+
+/* switch utility functions */
+
+static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
+{
+ tb_info(tb,
+ " Switch: %x:%x (Revision: %d, TB Version: %d)\n",
+ sw->vendor_id, sw->device_id, sw->revision,
+ sw->thunderbolt_version);
+ tb_info(tb, " Max Port Number: %d\n", sw->max_port_number);
+ tb_info(tb, " Config:\n");
+ tb_info(tb,
+ " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n",
+ sw->upstream_port_number, sw->depth,
+ (((u64) sw->route_hi) << 32) | sw->route_lo,
+ sw->enabled, sw->plug_events_delay);
+ tb_info(tb,
+ " unknown1: %#x unknown4: %#x\n",
+ sw->__unknown1, sw->__unknown4);
+}
+
+/**
+ * tb_switch_free() - free a tb_switch and all downstream switches
+ */
+void tb_switch_free(struct tb_switch *sw)
+{
+ int i;
+ /* port 0 is the switch itself and never has a remote */
+ for (i = 1; i <= sw->config.max_port_number; i++) {
+ if (tb_is_upstream_port(&sw->ports[i]))
+ continue;
+ if (sw->ports[i].remote)
+ tb_switch_free(sw->ports[i].remote->sw);
+ sw->ports[i].remote = NULL;
+ }
+
+ kfree(sw->ports);
+ kfree(sw);
+}
+
+/**
+ * tb_switch_alloc() - allocate and initialize a switch
+ *
+ * Return: Returns a NULL on failure.
+ */
+struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
+{
+ int i;
+ struct tb_switch *sw;
+ int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route);
+ if (upstream_port < 0)
+ return NULL;
+
+ sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+ if (!sw)
+ return NULL;
+
+ sw->tb = tb;
+ if (tb_cfg_read(tb->ctl, &sw->config, route, 0, 2, 0, 5))
+ goto err;
+ tb_info(tb,
+ "initializing Switch at %#llx (depth: %d, up port: %d)\n",
+ route, tb_route_length(route), upstream_port);
+ tb_info(tb, "old switch config:\n");
+ tb_dump_switch(tb, &sw->config);
+
+ /* configure switch */
+ sw->config.upstream_port_number = upstream_port;
+ sw->config.depth = tb_route_length(route);
+ sw->config.route_lo = route;
+ sw->config.route_hi = route >> 32;
+ sw->config.enabled = 1;
+ /* from here on we may use the tb_sw_* functions & macros */
+
+ if (sw->config.vendor_id != 0x8086)
+ tb_sw_warn(sw, "unknown switch vendor id %#x\n",
+ sw->config.vendor_id);
+
+ if (sw->config.device_id != 0x1547 && sw->config.device_id != 0x1549)
+ tb_sw_warn(sw, "unsupported switch device id %#x\n",
+ sw->config.device_id);
+
+ /* upload configuration */
+ if (tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3))
+ goto err;
+
+ /* initialize ports */
+ sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports),
+ GFP_KERNEL);
+ if (!sw->ports)
+ goto err;
+
+ for (i = 0; i <= sw->config.max_port_number; i++) {
+ if (tb_init_port(sw, i))
+ goto err;
+ /* TODO: check if port is disabled (EEPROM) */
+ }
+
+ /* TODO: I2C, IECS, EEPROM, link controller */
+
+ return sw;
+err:
+ kfree(sw->ports);
+ kfree(sw);
+ return NULL;
+}
+
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 164dea0..f1b6100 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -71,6 +71,10 @@ void thunderbolt_shutdown_and_free(struct tb *tb)
{
mutex_lock(&tb->lock);
+ if (tb->root_switch)
+ tb_switch_free(tb->root_switch);
+ tb->root_switch = NULL;
+
if (tb->ctl) {
tb_ctl_stop(tb->ctl);
tb_ctl_free(tb->ctl);
@@ -126,6 +130,10 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
*/
tb_ctl_start(tb->ctl);
+ tb->root_switch = tb_switch_alloc(tb, 0);
+ if (!tb->root_switch)
+ goto err_locked;
+
/* Allow tb_handle_hotplug to progress events */
tb->hotplug_active = true;
mutex_unlock(&tb->lock);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 6055dfd..389dbb4 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -7,9 +7,31 @@
#ifndef TB_H_
#define TB_H_
+#include <linux/pci.h>
+
+#include "tb_regs.h"
#include "ctl.h"
/**
+ * struct tb_switch - a thunderbolt switch
+ */
+struct tb_switch {
+ struct tb_regs_switch_header config;
+ struct tb_port *ports;
+ struct tb *tb;
+};
+
+/**
+ * struct tb_port - a thunderbolt port, part of a tb_switch
+ */
+struct tb_port {
+ struct tb_regs_port_header config;
+ struct tb_switch *sw;
+ struct tb_port *remote; /* remote port, NULL if not connected */
+ u8 port; /* port number on switch */
+};
+
+/**
* struct tb - main thunderbolt bus structure
*/
struct tb {
@@ -20,6 +42,7 @@ struct tb {
struct tb_nhi *nhi;
struct tb_ctl *ctl;
struct workqueue_struct *wq; /* ordered workqueue for plug events */
+ struct tb_switch *root_switch;
bool hotplug_active; /*
* tb_handle_hotplug will stop progressing plug
* events and exit if this is not set (it needs to
@@ -29,7 +52,122 @@ struct tb {
};
+/* helper functions & macros */
+
+/**
+ * tb_upstream_port() - return the upstream port of a switch
+ *
+ * Every switch has an upstream port (for the root switch it is the NHI).
+ *
+ * During switch alloc/init tb_upstream_port()->remote may be NULL, even for
+ * non root switches (on the NHI port remote is always NULL).
+ *
+ * Return: Returns the upstream port of the switch.
+ */
+static inline struct tb_port *tb_upstream_port(struct tb_switch *sw)
+{
+ return &sw->ports[sw->config.upstream_port_number];
+}
+
+static inline u64 tb_route(struct tb_switch *sw)
+{
+ return ((u64) sw->config.route_hi) << 32 | sw->config.route_lo;
+}
+
+static inline int tb_sw_read(struct tb_switch *sw, void *buffer,
+ enum tb_cfg_space space, u32 offset, u32 length)
+{
+ return tb_cfg_read(sw->tb->ctl,
+ buffer,
+ tb_route(sw),
+ 0,
+ space,
+ offset,
+ length);
+}
+
+static inline int tb_sw_write(struct tb_switch *sw, void *buffer,
+ enum tb_cfg_space space, u32 offset, u32 length)
+{
+ return tb_cfg_write(sw->tb->ctl,
+ buffer,
+ tb_route(sw),
+ 0,
+ space,
+ offset,
+ length);
+}
+
+static inline int tb_port_read(struct tb_port *port, void *buffer,
+ enum tb_cfg_space space, u32 offset, u32 length)
+{
+ return tb_cfg_read(port->sw->tb->ctl,
+ buffer,
+ tb_route(port->sw),
+ port->port,
+ space,
+ offset,
+ length);
+}
+
+static inline int tb_port_write(struct tb_port *port, void *buffer,
+ enum tb_cfg_space space, u32 offset, u32 length)
+{
+ return tb_cfg_write(port->sw->tb->ctl,
+ buffer,
+ tb_route(port->sw),
+ port->port,
+ space,
+ offset,
+ length);
+}
+
+#define tb_err(tb, fmt, arg...) dev_err(&(tb)->nhi->pdev->dev, fmt, ## arg)
+#define tb_WARN(tb, fmt, arg...) dev_WARN(&(tb)->nhi->pdev->dev, fmt, ## arg)
+#define tb_warn(tb, fmt, arg...) dev_warn(&(tb)->nhi->pdev->dev, fmt, ## arg)
+#define tb_info(tb, fmt, arg...) dev_info(&(tb)->nhi->pdev->dev, fmt, ## arg)
+
+
+#define __TB_SW_PRINT(level, sw, fmt, arg...) \
+ do { \
+ struct tb_switch *__sw = (sw); \
+ level(__sw->tb, "%llx: " fmt, \
+ tb_route(__sw), ## arg); \
+ } while (0)
+#define tb_sw_WARN(sw, fmt, arg...) __TB_SW_PRINT(tb_WARN, sw, fmt, ##arg)
+#define tb_sw_warn(sw, fmt, arg...) __TB_SW_PRINT(tb_warn, sw, fmt, ##arg)
+#define tb_sw_info(sw, fmt, arg...) __TB_SW_PRINT(tb_info, sw, fmt, ##arg)
+
+
+#define __TB_PORT_PRINT(level, _port, fmt, arg...) \
+ do { \
+ struct tb_port *__port = (_port); \
+ level(__port->sw->tb, "%llx:%x: " fmt, \
+ tb_route(__port->sw), __port->port, ## arg); \
+ } while (0)
+#define tb_port_WARN(port, fmt, arg...) \
+ __TB_PORT_PRINT(tb_WARN, port, fmt, ##arg)
+#define tb_port_warn(port, fmt, arg...) \
+ __TB_PORT_PRINT(tb_warn, port, fmt, ##arg)
+#define tb_port_info(port, fmt, arg...) \
+ __TB_PORT_PRINT(tb_info, port, fmt, ##arg)
+
+
struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi);
void thunderbolt_shutdown_and_free(struct tb *tb);
+struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
+void tb_switch_free(struct tb_switch *sw);
+
+
+static inline int tb_route_length(u64 route)
+{
+ return (fls64(route) + TB_ROUTE_SHIFT - 1) / TB_ROUTE_SHIFT;
+}
+
+static inline bool tb_is_upstream_port(struct tb_port *port)
+{
+ return port == tb_upstream_port(port->sw);
+}
+
#endif
--
1.9.3
next prev parent reply other threads:[~2014-05-26 15:18 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-05-26 15:17 [PATCH v3 00/15] Thunderbolt driver for Apple MacBooks Andreas Noever
2014-05-26 15:17 ` [PATCH v3 01/15] thunderbolt: Add initial cactus ridge NHI support Andreas Noever
2014-05-26 15:17 ` [PATCH v3 02/15] thunderbolt: Add control channel interface Andreas Noever
2014-05-26 15:18 ` [PATCH v3 03/15] thunderbolt: Setup control channel Andreas Noever
2014-05-26 15:18 ` [PATCH v3 04/15] thunderbolt: Add tb_regs.h Andreas Noever
2014-05-26 15:18 ` Andreas Noever [this message]
2014-05-26 15:18 ` [PATCH v3 06/15] thunderbolt: Add thunderbolt capability handling Andreas Noever
2014-05-26 15:18 ` [PATCH v3 07/15] thunderbolt: Enable plug events Andreas Noever
2014-05-26 15:18 ` [PATCH v3 08/15] thunderbolt: Scan for downstream switches Andreas Noever
2014-05-26 15:18 ` [PATCH v3 09/15] thunderbolt: Handle hotplug events Andreas Noever
2014-05-28 22:26 ` Bjorn Helgaas
2014-05-26 15:18 ` [PATCH v3 10/15] thunderbolt: Add path setup code Andreas Noever
2014-05-28 22:30 ` Bjorn Helgaas
2014-05-26 15:18 ` [PATCH v3 11/15] thunderbolt: Add support for simple pci tunnels Andreas Noever
2014-05-26 15:18 ` [PATCH v3 12/15] pci: Add pci_fixup_suspend_late quirk pass Andreas Noever
2014-05-28 22:36 ` Bjorn Helgaas
2014-05-30 14:33 ` Andreas Noever
2014-05-30 16:09 ` Greg KH
2014-05-26 15:18 ` [PATCH v3 13/15] pci: Suspend/resume quirks for appel thunderbolt Andreas Noever
2014-05-27 14:07 ` Matthew Garrett
2014-05-27 14:07 ` Matthew Garrett
2014-05-28 22:43 ` Bjorn Helgaas
2014-05-26 15:18 ` [PATCH v3 14/15] thunderbolt: Read switch uid from EEPROM Andreas Noever
2014-05-26 15:18 ` [PATCH v3 15/15] thunderbolt: Add suspend/hibernate support Andreas Noever
2014-05-27 14:09 ` [PATCH v3 00/15] Thunderbolt driver for Apple MacBooks Matthew Garrett
2014-05-27 14:09 ` Matthew Garrett
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1401117492-2870-6-git-send-email-andreas.noever@gmail.com \
--to=andreas.noever@gmail.com \
--cc=bhelgaas@google.com \
--cc=greg@kroah.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=matthew.garrett@nebula.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.