1e2b8785eSAndreas Noever /* 2e2b8785eSAndreas Noever * Thunderbolt Cactus Ridge driver - capabilities lookup 3e2b8785eSAndreas Noever * 4e2b8785eSAndreas Noever * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> 5e2b8785eSAndreas Noever */ 6e2b8785eSAndreas Noever 7e2b8785eSAndreas Noever #include <linux/slab.h> 8e2b8785eSAndreas Noever #include <linux/errno.h> 9e2b8785eSAndreas Noever 10e2b8785eSAndreas Noever #include "tb.h" 11e2b8785eSAndreas Noever 12da2da04bSMika Westerberg #define CAP_OFFSET_MAX 0xff 13da2da04bSMika Westerberg #define VSE_CAP_OFFSET_MAX 0xffff 14e2b8785eSAndreas Noever 15e2b8785eSAndreas Noever struct tb_cap_any { 16e2b8785eSAndreas Noever union { 17e2b8785eSAndreas Noever struct tb_cap_basic basic; 18e2b8785eSAndreas Noever struct tb_cap_extended_short extended_short; 19e2b8785eSAndreas Noever struct tb_cap_extended_long extended_long; 20e2b8785eSAndreas Noever }; 21e2b8785eSAndreas Noever } __packed; 22e2b8785eSAndreas Noever 23da2da04bSMika Westerberg /** 24da2da04bSMika Westerberg * tb_port_find_cap() - Find port capability 25da2da04bSMika Westerberg * @port: Port to find the capability for 26da2da04bSMika Westerberg * @cap: Capability to look 27da2da04bSMika Westerberg * 28da2da04bSMika Westerberg * Returns offset to start of capability or %-ENOENT if no such 29da2da04bSMika Westerberg * capability was found. Negative errno is returned if there was an 30da2da04bSMika Westerberg * error. 31da2da04bSMika Westerberg */ 32da2da04bSMika Westerberg int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap) 33e2b8785eSAndreas Noever { 34da2da04bSMika Westerberg u32 offset; 35e2b8785eSAndreas Noever 36da2da04bSMika Westerberg /* 37da2da04bSMika Westerberg * DP out adapters claim to implement TMU capability but in 38da2da04bSMika Westerberg * reality they do not so we hard code the adapter specific 39da2da04bSMika Westerberg * capability offset here. 40da2da04bSMika Westerberg */ 41da2da04bSMika Westerberg if (port->config.type == TB_TYPE_DP_HDMI_OUT) 42da2da04bSMika Westerberg offset = 0x39; 43e2b8785eSAndreas Noever else 44da2da04bSMika Westerberg offset = 0x1; 45da2da04bSMika Westerberg 46da2da04bSMika Westerberg do { 47da2da04bSMika Westerberg struct tb_cap_any header; 48da2da04bSMika Westerberg int ret; 49da2da04bSMika Westerberg 50da2da04bSMika Westerberg ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1); 51da2da04bSMika Westerberg if (ret) 52da2da04bSMika Westerberg return ret; 53da2da04bSMika Westerberg 54da2da04bSMika Westerberg if (header.basic.cap == cap) 55da2da04bSMika Westerberg return offset; 56da2da04bSMika Westerberg 57da2da04bSMika Westerberg offset = header.basic.next; 58da2da04bSMika Westerberg } while (offset); 59da2da04bSMika Westerberg 60da2da04bSMika Westerberg return -ENOENT; 61e2b8785eSAndreas Noever } 62e2b8785eSAndreas Noever 63da2da04bSMika Westerberg static int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap) 64e2b8785eSAndreas Noever { 65da2da04bSMika Westerberg int offset = sw->config.first_cap_offset; 66da2da04bSMika Westerberg 67da2da04bSMika Westerberg while (offset > 0 && offset < CAP_OFFSET_MAX) { 68da2da04bSMika Westerberg struct tb_cap_any header; 69da2da04bSMika Westerberg int ret; 70da2da04bSMika Westerberg 71da2da04bSMika Westerberg ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1); 72da2da04bSMika Westerberg if (ret) 73da2da04bSMika Westerberg return ret; 74da2da04bSMika Westerberg 75da2da04bSMika Westerberg if (header.basic.cap == cap) 76da2da04bSMika Westerberg return offset; 77da2da04bSMika Westerberg 78da2da04bSMika Westerberg offset = header.basic.next; 79e2b8785eSAndreas Noever } 80da2da04bSMika Westerberg 81da2da04bSMika Westerberg return -ENOENT; 82e2b8785eSAndreas Noever } 83e2b8785eSAndreas Noever 84e2b8785eSAndreas Noever /** 85da2da04bSMika Westerberg * tb_switch_find_vse_cap() - Find switch vendor specific capability 86da2da04bSMika Westerberg * @sw: Switch to find the capability for 87da2da04bSMika Westerberg * @vsec: Vendor specific capability to look 88e2b8785eSAndreas Noever * 89da2da04bSMika Westerberg * Functions enumerates vendor specific capabilities (VSEC) of a switch 90da2da04bSMika Westerberg * and returns offset when capability matching @vsec is found. If no 91da2da04bSMika Westerberg * such capability is found returns %-ENOENT. In case of error returns 92da2da04bSMika Westerberg * negative errno. 93e2b8785eSAndreas Noever */ 94da2da04bSMika Westerberg int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec) 95e2b8785eSAndreas Noever { 96e2b8785eSAndreas Noever struct tb_cap_any header; 97da2da04bSMika Westerberg int offset; 98da2da04bSMika Westerberg 99da2da04bSMika Westerberg offset = tb_switch_find_cap(sw, TB_SWITCH_CAP_VSE); 100da2da04bSMika Westerberg if (offset < 0) 101e2b8785eSAndreas Noever return offset; 102da2da04bSMika Westerberg 103da2da04bSMika Westerberg while (offset > 0 && offset < VSE_CAP_OFFSET_MAX) { 104da2da04bSMika Westerberg int ret; 105da2da04bSMika Westerberg 106da2da04bSMika Westerberg ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2); 107da2da04bSMika Westerberg if (ret) 108da2da04bSMika Westerberg return ret; 109da2da04bSMika Westerberg 110da2da04bSMika Westerberg /* 111da2da04bSMika Westerberg * Extended vendor specific capabilities come in two 112da2da04bSMika Westerberg * flavors: short and long. The latter is used when 113da2da04bSMika Westerberg * offset is over 0xff. 114da2da04bSMika Westerberg */ 115da2da04bSMika Westerberg if (offset >= CAP_OFFSET_MAX) { 116da2da04bSMika Westerberg if (header.extended_long.vsec_id == vsec) 117da2da04bSMika Westerberg return offset; 118da2da04bSMika Westerberg offset = header.extended_long.next; 119da2da04bSMika Westerberg } else { 120da2da04bSMika Westerberg if (header.extended_short.vsec_id == vsec) 121da2da04bSMika Westerberg return offset; 122da2da04bSMika Westerberg if (!header.extended_short.length) 123da2da04bSMika Westerberg return -ENOENT; 124da2da04bSMika Westerberg offset = header.extended_short.next; 125e2b8785eSAndreas Noever } 126e2b8785eSAndreas Noever } 127da2da04bSMika Westerberg 128da2da04bSMika Westerberg return -ENOENT; 129e2b8785eSAndreas Noever } 130