aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/thunderbolt/tb_debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/thunderbolt/tb_debug.c')
-rw-r--r--sys/dev/thunderbolt/tb_debug.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/sys/dev/thunderbolt/tb_debug.c b/sys/dev/thunderbolt/tb_debug.c
new file mode 100644
index 000000000000..f455ee72e9f6
--- /dev/null
+++ b/sys/dev/thunderbolt/tb_debug.c
@@ -0,0 +1,334 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_thunderbolt.h"
+
+/* PCIe bridge for Thunderbolt */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/gsb_crc32.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/stdarg.h>
+
+#include <dev/thunderbolt/nhi_reg.h>
+#include <dev/thunderbolt/nhi_var.h>
+#include <dev/thunderbolt/tb_reg.h>
+#include <dev/thunderbolt/tb_var.h>
+#include <dev/thunderbolt/tbcfg_reg.h>
+#include <dev/thunderbolt/tb_debug.h>
+
+tb_string_t nhi_outmailcmd_opmode[] = {
+ { 0x000, "Safe Mode" },
+ { 0x100, "Authentication Mode" },
+ { 0x200, "Endpoint Mode" },
+ { 0x300, "Connection Manager Fully Functional" },
+ { 0, NULL }
+};
+
+tb_string_t nhi_frame_pdf[] = {
+ { 0x01, "PDF_READ" },
+ { 0x02, "PDF_WRITE" },
+ { 0x03, "PDF_NOTIFY" },
+ { 0x04, "PDF_NOTIFY_ACK" },
+ { 0x05, "PDF_HOTPLUG" },
+ { 0x06, "PDF_XDOMAIN_REQ" },
+ { 0x07, "PDF_XDOMAIN_RESP" },
+ { 0x0a, "PDF_CM_EVENT" },
+ { 0x0b, "PDF_CM_REQ" },
+ { 0x0c, "PDF_CM_RESP" },
+ { 0, NULL }
+};
+
+tb_string_t tb_security_level[] = {
+ { TBSEC_NONE, "None" },
+ { TBSEC_USER, "User" },
+ { TBSEC_SECURE, "Secure Authorization" },
+ { TBSEC_DP, "Display Port" },
+ { TBSEC_UNKNOWN,"Unknown" },
+ { 0, NULL }
+};
+
+tb_string_t tb_mbox_connmode[] = {
+ { INMAILCMD_SETMODE_CERT_TB_1ST_DEPTH, "Certified/1st" },
+ { INMAILCMD_SETMODE_ANY_TB_1ST_DEPTH, "Any/1st" },
+ { INMAILCMD_SETMODE_CERT_TB_ANY_DEPTH, "Certified/Any" },
+ { INMAILCMD_SETMODE_ANY_TB_ANY_DEPTH, "Any/Any" },
+ { 0, NULL }
+};
+
+tb_string_t tb_device_power[] = {
+ { 0x0, "Self-powered" },
+ { 0x1, "Normal power" },
+ { 0x2, "High power" },
+ { 0x3, "Unknown power draw" },
+ { 0, NULL }
+};
+
+tb_string_t tb_notify_code[] = {
+ { 0x03, "DEVCONN" },
+ { 0x04, "DISCONN" },
+ { 0x05, "DPCONN" },
+ { 0x06, "DOMCONN" },
+ { 0x07, "DOMDISCONN" },
+ { 0x08, "DPCHANGE" },
+ { 0x09, "I2C" },
+ { 0x0a, "RTD3" },
+ { 0, NULL }
+};
+
+tb_string_t tb_adapter_type[] = {
+ { ADP_CS2_UNSUPPORTED, "Unsupported Adapter" },
+ { ADP_CS2_LANE, "Lane Adapter" },
+ { ADP_CS2_HOSTIF, "Host Interface Adapter" },
+ { ADP_CS2_PCIE_DFP, "Downstream PCIe Adapter" },
+ { ADP_CS2_PCIE_UFP, "Upstream PCIe Adapter" },
+ { ADP_CS2_DP_OUT, "DP OUT Adapter" },
+ { ADP_CS2_DP_IN, "DP IN Adapter" },
+ { ADP_CS2_USB3_DFP, "Downstream USB3 Adapter" },
+ { ADP_CS2_USB3_UFP, "Upstream USB3 Adapter" },
+ { 0, NULL }
+};
+
+tb_string_t tb_adapter_state[] = {
+ { CAP_LANE_STATE_DISABLE, "Disabled" },
+ { CAP_LANE_STATE_TRAINING, "Training" },
+ { CAP_LANE_STATE_CL0, "CL0" },
+ { CAP_LANE_STATE_TXCL0, "TX CL0s" },
+ { CAP_LANE_STATE_RXCL0, "RX CL0s" },
+ { CAP_LANE_STATE_CL1, "CL1" },
+ { CAP_LANE_STATE_CL2, "CL2" },
+ { CAP_LANE_STATE_CLD, "CLd" },
+ { 0, NULL }
+};
+
+tb_string_t tb_notify_event[] = {
+ { TB_CFG_ERR_CONN, "Connection error" },
+ { TB_CFG_ERR_LINK, "Link error" },
+ { TB_CFG_ERR_ADDR, "Addressing error" },
+ { TB_CFG_ERR_ADP, "Invalid adapter" },
+ { TB_CFG_ERR_ENUM, "Enumeration error" },
+ { TB_CFG_ERR_NUA, "Adapter not enumerated" },
+ { TB_CFG_ERR_LEN, "Invalid request length" },
+ { TB_CFG_ERR_HEC, "Invalid packet header" },
+ { TB_CFG_ERR_FC, "Flow control error" },
+ { TB_CFG_ERR_PLUG, "Hot plug error" },
+ { TB_CFG_ERR_LOCK, "Adapter locked" },
+ { TB_CFG_HP_ACK, "Hotplug acknowledgement" },
+ { TB_CFG_DP_BW, "Display port bandwidth change" },
+ { 0, NULL }
+};
+
+const char *
+tb_get_string(uintmax_t key, tb_string_t *table)
+{
+
+ if (table == NULL)
+ return ("<null>");
+
+ while (table->value != NULL) {
+ if (table->key == key)
+ return (table->value);
+ table++;
+ }
+
+ return ("<unknown>");
+}
+
+static struct tb_debug_string {
+ char *name;
+ int flag;
+} tb_debug_strings[] = {
+ {"info", DBG_INFO},
+ {"init", DBG_INIT},
+ {"info", DBG_INFO},
+ {"rxq", DBG_RXQ},
+ {"txq", DBG_TXQ},
+ {"intr", DBG_INTR},
+ {"tb", DBG_TB},
+ {"mbox", DBG_MBOX},
+ {"bridge", DBG_BRIDGE},
+ {"cfg", DBG_CFG},
+ {"router", DBG_ROUTER},
+ {"port", DBG_PORT},
+ {"hcm", DBG_HCM},
+ {"extra", DBG_EXTRA},
+ {"noisy", DBG_NOISY},
+ {"full", DBG_FULL}
+};
+
+enum tb_debug_level_combiner {
+ COMB_NONE,
+ COMB_ADD,
+ COMB_SUB
+};
+
+int
+tb_debug_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct sbuf *sbuf;
+#if defined (THUNDERBOLT_DEBUG) && (THUNDERBOLT_DEBUG > 0)
+ struct tb_debug_string *string;
+ char *buffer;
+ size_t sz;
+ u_int *debug;
+ int i, len;
+#endif
+ int error;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error != 0)
+ return (error);
+
+ sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
+
+#if defined (THUNDERBOLT_DEBUG) && (THUNDERBOLT_DEBUG > 0)
+ debug = (u_int *)arg1;
+
+ sbuf_printf(sbuf, "%#x", *debug);
+
+ sz = sizeof(tb_debug_strings) / sizeof(tb_debug_strings[0]);
+ for (i = 0; i < sz; i++) {
+ string = &tb_debug_strings[i];
+ if (*debug & string->flag)
+ sbuf_printf(sbuf, ",%s", string->name);
+ }
+
+ error = sbuf_finish(sbuf);
+ sbuf_delete(sbuf);
+
+ if (error || req->newptr == NULL)
+ return (error);
+
+ len = req->newlen - req->newidx;
+ if (len == 0)
+ return (0);
+
+ buffer = malloc(len, M_THUNDERBOLT, M_ZERO|M_WAITOK);
+ error = SYSCTL_IN(req, buffer, len);
+
+ tb_parse_debug(debug, buffer);
+
+ free(buffer, M_THUNDERBOLT);
+#else
+ sbuf_printf(sbuf, "debugging unavailable");
+ error = sbuf_finish(sbuf);
+ sbuf_delete(sbuf);
+#endif
+
+ return (error);
+}
+
+void
+tb_parse_debug(u_int *debug, char *list)
+{
+ struct tb_debug_string *string;
+ enum tb_debug_level_combiner op;
+ char *token, *endtoken;
+ size_t sz;
+ int flags, i;
+
+ if (list == NULL || *list == '\0')
+ return;
+
+ if (*list == '+') {
+ op = COMB_ADD;
+ list++;
+ } else if (*list == '-') {
+ op = COMB_SUB;
+ list++;
+ } else
+ op = COMB_NONE;
+ if (*list == '\0')
+ return;
+
+ flags = 0;
+ sz = sizeof(tb_debug_strings) / sizeof(tb_debug_strings[0]);
+ while ((token = strsep(&list, ":,")) != NULL) {
+
+ /* Handle integer flags */
+ flags |= strtol(token, &endtoken, 0);
+ if (token != endtoken)
+ continue;
+
+ /* Handle text flags */
+ for (i = 0; i < sz; i++) {
+ string = &tb_debug_strings[i];
+ if (strcasecmp(token, string->name) == 0) {
+ flags |= string->flag;
+ break;
+ }
+ }
+ }
+
+ switch (op) {
+ case COMB_NONE:
+ *debug = flags;
+ break;
+ case COMB_ADD:
+ *debug |= flags;
+ break;
+ case COMB_SUB:
+ *debug &= (~flags);
+ break;
+ }
+ return;
+}
+
+void
+tbdbg_dprintf(device_t dev, u_int debug, u_int val, const char *fmt, ...)
+{
+#if defined(THUNDERBOLT_DEBUG) && (THUNDERBOLT_DEBUG > 0)
+ va_list ap;
+ u_int lvl, dbg;
+
+ lvl = debug & 0xc0000000;
+ dbg = debug & 0x3fffffff;
+ va_start(ap, fmt);
+ if ((lvl >= (val & 0xc0000000)) &&
+ ((dbg & (val & 0x3fffffff)) != 0)) {
+ device_printf(dev, "");
+ vprintf(fmt, ap);
+ }
+ va_end(ap);
+#endif
+}