0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/slab.h>
0010 #include <linux/errno.h>
0011
0012 #include "tb.h"
0013
0014 #define CAP_OFFSET_MAX 0xff
0015 #define VSE_CAP_OFFSET_MAX 0xffff
0016 #define TMU_ACCESS_EN BIT(20)
0017
0018 static int tb_port_enable_tmu(struct tb_port *port, bool enable)
0019 {
0020 struct tb_switch *sw = port->sw;
0021 u32 value, offset;
0022 int ret;
0023
0024
0025
0026
0027
0028 if (tb_switch_is_light_ridge(sw))
0029 offset = 0x26;
0030 else if (tb_switch_is_eagle_ridge(sw))
0031 offset = 0x2a;
0032 else
0033 return 0;
0034
0035 ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);
0036 if (ret)
0037 return ret;
0038
0039 if (enable)
0040 value |= TMU_ACCESS_EN;
0041 else
0042 value &= ~TMU_ACCESS_EN;
0043
0044 return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
0045 }
0046
0047 static void tb_port_dummy_read(struct tb_port *port)
0048 {
0049
0050
0051
0052
0053
0054
0055 if (tb_switch_is_light_ridge(port->sw)) {
0056 u32 dummy;
0057
0058 tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
0059 }
0060 }
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072 int tb_port_next_cap(struct tb_port *port, unsigned int offset)
0073 {
0074 struct tb_cap_any header;
0075 int ret;
0076
0077 if (!offset)
0078 return port->config.first_cap_offset;
0079
0080 ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
0081 if (ret)
0082 return ret;
0083
0084 return header.basic.next;
0085 }
0086
0087 static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
0088 {
0089 int offset = 0;
0090
0091 do {
0092 struct tb_cap_any header;
0093 int ret;
0094
0095 offset = tb_port_next_cap(port, offset);
0096 if (offset < 0)
0097 return offset;
0098
0099 ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
0100 if (ret)
0101 return ret;
0102
0103 if (header.basic.cap == cap)
0104 return offset;
0105 } while (offset > 0);
0106
0107 return -ENOENT;
0108 }
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
0120 {
0121 int ret;
0122
0123 ret = tb_port_enable_tmu(port, true);
0124 if (ret)
0125 return ret;
0126
0127 ret = __tb_port_find_cap(port, cap);
0128
0129 tb_port_dummy_read(port);
0130 tb_port_enable_tmu(port, false);
0131
0132 return ret;
0133 }
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145 int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset)
0146 {
0147 struct tb_cap_any header;
0148 int ret;
0149
0150 if (!offset)
0151 return sw->config.first_cap_offset;
0152
0153 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
0154 if (ret)
0155 return ret;
0156
0157 switch (header.basic.cap) {
0158 case TB_SWITCH_CAP_TMU:
0159 ret = header.basic.next;
0160 break;
0161
0162 case TB_SWITCH_CAP_VSE:
0163 if (!header.extended_short.length)
0164 ret = header.extended_long.next;
0165 else
0166 ret = header.extended_short.next;
0167 break;
0168
0169 default:
0170 tb_sw_dbg(sw, "unknown capability %#x at %#x\n",
0171 header.basic.cap, offset);
0172 ret = -EINVAL;
0173 break;
0174 }
0175
0176 return ret >= VSE_CAP_OFFSET_MAX ? 0 : ret;
0177 }
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188 int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
0189 {
0190 int offset = 0;
0191
0192 do {
0193 struct tb_cap_any header;
0194 int ret;
0195
0196 offset = tb_switch_next_cap(sw, offset);
0197 if (offset < 0)
0198 return offset;
0199
0200 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
0201 if (ret)
0202 return ret;
0203
0204 if (header.basic.cap == cap)
0205 return offset;
0206 } while (offset);
0207
0208 return -ENOENT;
0209 }
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
0222 {
0223 int offset = 0;
0224
0225 do {
0226 struct tb_cap_any header;
0227 int ret;
0228
0229 offset = tb_switch_next_cap(sw, offset);
0230 if (offset < 0)
0231 return offset;
0232
0233 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
0234 if (ret)
0235 return ret;
0236
0237 if (header.extended_short.cap == TB_SWITCH_CAP_VSE &&
0238 header.extended_short.vsec_id == vsec)
0239 return offset;
0240 } while (offset);
0241
0242 return -ENOENT;
0243 }