xref: /openbmc/linux/drivers/thunderbolt/cap.c (revision da2da04b)
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