From patchwork Sat May 31 14:27:51 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Noever X-Patchwork-Id: 4275891 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: X-Original-To: patchwork-linux-pci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 31F469F1D6 for ; Sat, 31 May 2014 14:32:28 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4890F20295 for ; Sat, 31 May 2014 14:32:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 538B0200CA for ; Sat, 31 May 2014 14:32:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756669AbaEaO24 (ORCPT ); Sat, 31 May 2014 10:28:56 -0400 Received: from mail-qg0-f43.google.com ([209.85.192.43]:41042 "EHLO mail-qg0-f43.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756662AbaEaO2x (ORCPT ); Sat, 31 May 2014 10:28:53 -0400 Received: by mail-qg0-f43.google.com with SMTP id 63so8403319qgz.30 for ; Sat, 31 May 2014 07:28:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=oSoM4/iEGgNODCtN78muBKnJ3whDvfa+IBHSSLL8HZU=; b=i0tmXF9S0dtmtdIfqJ+Pf+zxA+EOVqKSMaapEZp4qNKZgwCUU0T38B0Bgd9BGtwunB 5He+MrhUYHOL6eEPl9vav0cqW4tgB8zgnxJYQxQXe/y8AUH4oF6cgMnx7jM5jjeK+mJQ m6LZwkJQADNJVqEDlLXC15V4Q2RJCxKn5jt+cic4+VavGlTF8gW8/bD9sMPvYFYPTMdU 7dWlreUmqy128b/8UcT6pptbbcDSg+GTptJM47J3TB0t0m9J3LCXvkRPDGsSqZ8iQCnv cVk+r6BgEUlhwx25D2G9vINLL2jgxlrw8BnnLmxqNq2XSiIlkpIhydVr04E55Iay3Xu4 iG7A== X-Received: by 10.224.69.130 with SMTP id z2mr32417286qai.87.1401546532121; Sat, 31 May 2014 07:28:52 -0700 (PDT) Received: from linuxbook.fritz.box (HSI-KBW-109-193-140-057.hsi7.kabel-badenwuerttemberg.de. [109.193.140.57]) by mx.google.com with ESMTPSA id q7sm11542251qad.12.2014.05.31.07.28.50 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 31 May 2014 07:28:51 -0700 (PDT) From: Andreas Noever To: linux-kernel@vger.kernel.org, Matthew Garrett , Greg KH , Bjorn Helgaas , linux-pci@vger.kernel.org Cc: Andreas Noever Subject: [PATCH v4 06/15] thunderbolt: Add thunderbolt capability handling Date: Sat, 31 May 2014 16:27:51 +0200 Message-Id: <1401546480-2071-7-git-send-email-andreas.noever@gmail.com> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1401546480-2071-1-git-send-email-andreas.noever@gmail.com> References: <1401546480-2071-1-git-send-email-andreas.noever@gmail.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Thunderbolt config areas contain capability lists similar to those found on pci devices. This patch introduces a tb_find_cap utility method to search for capabilities. Signed-off-by: Andreas Noever --- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/cap.c | 116 +++++++++++++++++++++++++++++++++++++++++++ drivers/thunderbolt/tb.h | 2 + 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 drivers/thunderbolt/cap.c diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 4ac18d9..617b314 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 switch.o +thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c new file mode 100644 index 0000000..a7b47e7 --- /dev/null +++ b/drivers/thunderbolt/cap.c @@ -0,0 +1,116 @@ +/* + * Thunderbolt Cactus Ridge driver - capabilities lookup + * + * Copyright (c) 2014 Andreas Noever + */ + +#include +#include + +#include "tb.h" + + +struct tb_cap_any { + union { + struct tb_cap_basic basic; + struct tb_cap_extended_short extended_short; + struct tb_cap_extended_long extended_long; + }; +} __packed; + +static bool tb_cap_is_basic(struct tb_cap_any *cap) +{ + /* basic.cap is u8. This checks only the lower 8 bit of cap. */ + return cap->basic.cap != 5; +} + +static bool tb_cap_is_long(struct tb_cap_any *cap) +{ + return !tb_cap_is_basic(cap) + && cap->extended_short.next == 0 + && cap->extended_short.length == 0; +} + +static enum tb_cap tb_cap(struct tb_cap_any *cap) +{ + if (tb_cap_is_basic(cap)) + return cap->basic.cap; + else + /* extended_short/long have cap at the same offset. */ + return cap->extended_short.cap; +} + +static u32 tb_cap_next(struct tb_cap_any *cap, u32 offset) +{ + int next; + if (offset == 1) { + /* + * The first pointer is part of the switch header and always + * a simple pointer. + */ + next = cap->basic.next; + } else { + /* + * Somehow Intel decided to use 3 different types of capability + * headers. It is not like anyone could have predicted that + * single byte offsets are not enough... + */ + if (tb_cap_is_basic(cap)) + next = cap->basic.next; + else if (!tb_cap_is_long(cap)) + next = cap->extended_short.next; + else + next = cap->extended_long.next; + } + /* + * "Hey, we could terminate some capability lists with a null offset + * and others with a pointer to the last element." - "Great idea!" + */ + if (next == offset) + return 0; + return next; +} + +/** + * tb_find_cap() - find a capability + * + * Return: Returns a positive offset if the capability was found and 0 if not. + * Returns an error code on failure. + */ +int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap) +{ + u32 offset = 1; + struct tb_cap_any header; + int res; + int retries = 10; + while (retries--) { + res = tb_port_read(port, &header, space, offset, 1); + if (res) { + /* Intel needs some help with linked lists. */ + if (space == TB_CFG_PORT && offset == 0xa + && port->config.type == TB_TYPE_DP_HDMI_OUT) { + offset = 0x39; + continue; + } + return res; + } + if (offset != 1) { + if (tb_cap(&header) == cap) + return offset; + if (tb_cap_is_long(&header)) { + /* tb_cap_extended_long is 2 dwords */ + res = tb_port_read(port, &header, space, + offset, 2); + if (res) + return res; + } + } + offset = tb_cap_next(&header, offset); + if (!offset) + return 0; + } + tb_port_WARN(port, + "run out of retries while looking for cap %#x in config space %d, last offset: %#x\n", + cap, space, offset); + return -EIO; +} diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 389dbb4..38e4c23 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -159,6 +159,8 @@ 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); +int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, u32 value); + static inline int tb_route_length(u64 route) {