244 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Thunderbolt driver - capabilities lookup4 *5 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>6 * Copyright (C) 2018, Intel Corporation7 */8 9#include <linux/slab.h>10#include <linux/errno.h>11 12#include "tb.h"13 14#define CAP_OFFSET_MAX 0xff15#define VSE_CAP_OFFSET_MAX 0xffff16#define TMU_ACCESS_EN BIT(20)17 18static int tb_port_enable_tmu(struct tb_port *port, bool enable)19{20 struct tb_switch *sw = port->sw;21 u32 value, offset;22 int ret;23 24 /*25 * Legacy devices need to have TMU access enabled before port26 * space can be fully accessed.27 */28 if (tb_switch_is_light_ridge(sw))29 offset = 0x26;30 else if (tb_switch_is_eagle_ridge(sw))31 offset = 0x2a;32 else33 return 0;34 35 ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);36 if (ret)37 return ret;38 39 if (enable)40 value |= TMU_ACCESS_EN;41 else42 value &= ~TMU_ACCESS_EN;43 44 return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);45}46 47static void tb_port_dummy_read(struct tb_port *port)48{49 /*50 * When reading from next capability pointer location in port51 * config space the read data is not cleared on LR. To avoid52 * reading stale data on next read perform one dummy read after53 * port capabilities are walked.54 */55 if (tb_switch_is_light_ridge(port->sw)) {56 u32 dummy;57 58 tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);59 }60}61 62/**63 * tb_port_next_cap() - Return next capability in the linked list64 * @port: Port to find the capability for65 * @offset: Previous capability offset (%0 for start)66 *67 * Returns dword offset of the next capability in port config space68 * capability list and returns it. Passing %0 returns the first entry in69 * the capability list. If no next capability is found returns %0. In case70 * of failure returns negative errno.71 */72int tb_port_next_cap(struct tb_port *port, unsigned int offset)73{74 struct tb_cap_any header;75 int ret;76 77 if (!offset)78 return port->config.first_cap_offset;79 80 ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);81 if (ret)82 return ret;83 84 return header.basic.next;85}86 87static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)88{89 int offset = 0;90 91 do {92 struct tb_cap_any header;93 int ret;94 95 offset = tb_port_next_cap(port, offset);96 if (offset < 0)97 return offset;98 99 ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);100 if (ret)101 return ret;102 103 if (header.basic.cap == cap)104 return offset;105 } while (offset > 0);106 107 return -ENOENT;108}109 110/**111 * tb_port_find_cap() - Find port capability112 * @port: Port to find the capability for113 * @cap: Capability to look114 *115 * Returns offset to start of capability or %-ENOENT if no such116 * capability was found. Negative errno is returned if there was an117 * error.118 */119int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)120{121 int ret;122 123 ret = tb_port_enable_tmu(port, true);124 if (ret)125 return ret;126 127 ret = __tb_port_find_cap(port, cap);128 129 tb_port_dummy_read(port);130 tb_port_enable_tmu(port, false);131 132 return ret;133}134 135/**136 * tb_switch_next_cap() - Return next capability in the linked list137 * @sw: Switch to find the capability for138 * @offset: Previous capability offset (%0 for start)139 *140 * Finds dword offset of the next capability in router config space141 * capability list and returns it. Passing %0 returns the first entry in142 * the capability list. If no next capability is found returns %0. In case143 * of failure returns negative errno.144 */145int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset)146{147 struct tb_cap_any header;148 int ret;149 150 if (!offset)151 return sw->config.first_cap_offset;152 153 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);154 if (ret)155 return ret;156 157 switch (header.basic.cap) {158 case TB_SWITCH_CAP_TMU:159 ret = header.basic.next;160 break;161 162 case TB_SWITCH_CAP_VSE:163 if (!header.extended_short.length)164 ret = header.extended_long.next;165 else166 ret = header.extended_short.next;167 break;168 169 default:170 tb_sw_dbg(sw, "unknown capability %#x at %#x\n",171 header.basic.cap, offset);172 ret = -EINVAL;173 break;174 }175 176 return ret >= VSE_CAP_OFFSET_MAX ? 0 : ret;177}178 179/**180 * tb_switch_find_cap() - Find switch capability181 * @sw: Switch to find the capability for182 * @cap: Capability to look183 *184 * Returns offset to start of capability or %-ENOENT if no such185 * capability was found. Negative errno is returned if there was an186 * error.187 */188int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)189{190 int offset = 0;191 192 do {193 struct tb_cap_any header;194 int ret;195 196 offset = tb_switch_next_cap(sw, offset);197 if (offset < 0)198 return offset;199 200 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);201 if (ret)202 return ret;203 204 if (header.basic.cap == cap)205 return offset;206 } while (offset);207 208 return -ENOENT;209}210 211/**212 * tb_switch_find_vse_cap() - Find switch vendor specific capability213 * @sw: Switch to find the capability for214 * @vsec: Vendor specific capability to look215 *216 * Functions enumerates vendor specific capabilities (VSEC) of a switch217 * and returns offset when capability matching @vsec is found. If no218 * such capability is found returns %-ENOENT. In case of error returns219 * negative errno.220 */221int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)222{223 int offset = 0;224 225 do {226 struct tb_cap_any header;227 int ret;228 229 offset = tb_switch_next_cap(sw, offset);230 if (offset < 0)231 return offset;232 233 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);234 if (ret)235 return ret;236 237 if (header.extended_short.cap == TB_SWITCH_CAP_VSE &&238 header.extended_short.vsec_id == vsec)239 return offset;240 } while (offset);241 242 return -ENOENT;243}244