From patchwork Fri Apr 11 00:24:53 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Noever X-Patchwork-Id: 3965791 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 D4054BFF02 for ; Fri, 11 Apr 2014 00:31:32 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 028FE2081B for ; Fri, 11 Apr 2014 00:31:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0BC6B207F3 for ; Fri, 11 Apr 2014 00:31:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759438AbaDKAbI (ORCPT ); Thu, 10 Apr 2014 20:31:08 -0400 Received: from mail-ee0-f48.google.com ([74.125.83.48]:58787 "EHLO mail-ee0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1422751AbaDKAZs (ORCPT ); Thu, 10 Apr 2014 20:25:48 -0400 Received: by mail-ee0-f48.google.com with SMTP id b57so3500315eek.21 for ; Thu, 10 Apr 2014 17:25:47 -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=OOuMY3SASCJmtjNDp2rtqruWQtR+8+0dJU2X89fxcnk=; b=dGZPiP+hQdDUVZkkS8CdJJTpZxBDoIc4A+KKgEuBEZSGfb/wVamaCptE4Q29hpMTdE dnDNc4gUUvj9IvjdE4kT0l2FQaH/9Rb+v/0JGooSQ8sGr8HHO7UW052KGNofvv9drLxf DQBg9yvbkYzi+n3Oyx6lULlNABeb33x3BxLRpFDP72nqQLo4O6ctEb2E7oXgpgzc1dOH z+3GM9Vxr1cx3Mg3/AnGKWdlra2GbWHUn/OOQjaPZpRNHb3jk0+KmqmJoZt+HZJzX0tG cqcmCF9q9H1snlH2o0qBus6ffqyxoDFllAAVlf8If9I/a3hrGio2SSXLc1AANEUlHkw/ t8Iw== X-Received: by 10.14.210.65 with SMTP id t41mr24099129eeo.35.1397175947462; Thu, 10 Apr 2014 17:25:47 -0700 (PDT) Received: from localhost.localdomain (77-58-151-250.dclient.hispeed.ch. [77.58.151.250]) by mx.google.com with ESMTPSA id g3sm13535401eet.35.2014.04.10.17.25.46 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 10 Apr 2014 17:25:46 -0700 (PDT) From: Andreas Noever To: linux-kernel@vger.kernel.org, Matthew Garrett Cc: Daniel J Blueman , Bjorn Helgaas , linux-pci@vger.kernel.org, Andreas Noever Subject: [PATCH v2 06/14] thunderbolt: Add thunderbolt capability handling Date: Fri, 11 Apr 2014 02:24:53 +0200 Message-Id: <1397175901-4023-7-git-send-email-andreas.noever@gmail.com> X-Mailer: git-send-email 1.9.2 In-Reply-To: <1397175901-4023-1-git-send-email-andreas.noever@gmail.com> References: <1397175901-4023-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.3 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 9f01800..e620f1b 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) {