brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 8ecd610 Raw
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