From patchwork Mon May 26 15:18:03 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Noever X-Patchwork-Id: 4243721 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: X-Original-To: patchwork-linux-pci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B92DDBF90B for ; Mon, 26 May 2014 15:28:58 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C44CB20171 for ; Mon, 26 May 2014 15:28:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D2AB82012E for ; Mon, 26 May 2014 15:28:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752904AbaEZP2j (ORCPT ); Mon, 26 May 2014 11:28:39 -0400 Received: from mail-wi0-f179.google.com ([209.85.212.179]:49739 "EHLO mail-wi0-f179.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752657AbaEZPS2 (ORCPT ); Mon, 26 May 2014 11:18:28 -0400 Received: by mail-wi0-f179.google.com with SMTP id bs8so154163wib.12 for ; Mon, 26 May 2014 08:18:26 -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=rzsJZOFigD5R6z21Dg0CfgPLj2vEZ/iKPcne+zhn8EDcQtybf5ckFRiWEP6MD3NCCn 5rdYfWBwofB++3TLXRDnVn2lv5KvS5YF0quzu/0Quc5zn/XtWi/JtY7p1+XQnhAGvljV LYBEH0Sfci1IUVG4FR7jo0M589Hk9aJBSj0K5ti9xIzAJU7wW/MzjJ/w49uJJuaCokqI mG/sGtd/YHrG8XnmJU8JHQ5SPYEm0RKh0iSvwIEKk79jJSVfwgtQp8allUoKVqNtba4T JtY/246lm4aPB6fCarUfhPOhOyRM7TgxX1ESzeDn8xEMfZ/ezYueSCwrKD34KcMcz6b4 CAyw== X-Received: by 10.180.106.1 with SMTP id gq1mr29033964wib.45.1401117506892; Mon, 26 May 2014 08:18:26 -0700 (PDT) Received: from linuxbook.inf.ethz.ch (anoever.inf.ethz.ch. [129.132.153.240]) by mx.google.com with ESMTPSA id cv4sm27564479wjc.34.2014.05.26.08.18.26 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 26 May 2014 08:18:26 -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 v3 06/15] thunderbolt: Add thunderbolt capability handling Date: Mon, 26 May 2014 17:18:03 +0200 Message-Id: <1401117492-2870-7-git-send-email-andreas.noever@gmail.com> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1401117492-2870-1-git-send-email-andreas.noever@gmail.com> References: <1401117492-2870-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) {