From cf9610f9116d55c5a66ef9f1faecacabd93a9322 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 24 Aug 2026 08:46:03 -0700 Subject: Merge tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl Pull pin control updates from Linus Walleij: "Core changes: - Use the non-blocking mux_state_try_select() in the generic MUX pin control back-end - Free pin maps on pinctrl_generic_to_map() failure in the core helpers New hardware pin control: - Qualcomm Maili TLMM SoC, PMG1110 PMIC, and Eliza LPASS LPI TLMM SoC (this is for the low power audio, LPASS = Low Power Audio SubSystem portions) - Upboard support on top of the Intel pin controller - Apple T6030 and T6031 support (just compatible strings) - Samsung Exynos 8855 SoC - Mediatek MT6858 SoC - Rockchip RK3308B and RV1106 SoCs - Airoha EN7523 and AN7563 SoCs Improvements: - Qualcomm TLMM GPIOs are unconditionally marked as wakeup capable - Qualcomm improvements on top of the Qualcomm PDC (power domain controller) irqchip improvements from tglx:s tree - Qualcomm IPQ806x and IPQ9650 non-urgent pin grop fixups - The NPCM8xx driver has been put into shape fixing many pin and group definitions - Some non-urgent Tegra 264 pin assignments are fixed up - Some non-urgent fixups to Airoha AN7581 and AN7583 registers and pin assignments, missing features etc" * tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (130 commits) dt-bindings: pinctrl: Convert TI DA850 pupd to DT schema pinctrl: meson: sync some modify from A4 pinctrl: meson: a4: Add input enable pin configuration pinctrl: realtek: rtd1625: remove unused group name spdif_sel pinctrl: realtek: rtd1625: fix base_bit for VE4 GPIO 13 dt-bindings: pinctrl: microchip,pic32mzda-pinctrl: Convert to DT schema Revert "Merge branch 'ib-rsk7204' into devel" pinctrl: rockchip: Add RV1106 pinctrl support dt-bindings: pinctrl: rockchip: Add RV1106 compatible pinctrl: rockchip: Decode drive strength in the get function pinctrl: fix PINCTRL_GENERIC_MUX not always being selectable pinctrl: airoha: add support of an7563 SoC dt-bindings: pinctrl: airoha: add support of an7563 pin controller pinctrl: airoha: try to find chip scu node by phandle first pinctrl: airoha: add support of en7523 SoC dt-bindings: pinctrl: airoha: add support of en7523 pin controller pinctrl: airoha: an7583: add support for olt pinmux pinctrl: airoha: an7583: add support for pon_alt pinmux pinctrl: airoha: an7583: add support for npu_uart pinmux dt-bindings: pinctrl: airoha: an7583: add missed features ... --- drivers/thunderbolt/cap.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 drivers/thunderbolt/cap.c (limited to 'drivers/thunderbolt/cap.c') diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c new file mode 100644 index 000000000..4ab22d529 --- /dev/null +++ b/drivers/thunderbolt/cap.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thunderbolt driver - capabilities lookup + * + * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation + */ + +#include +#include + +#include "tb.h" + +#define CAP_OFFSET_MAX 0xff +#define VSE_CAP_OFFSET_MAX 0xffff +#define TMU_ACCESS_EN BIT(20) + +static int tb_port_enable_tmu(struct tb_port *port, bool enable) +{ + struct tb_switch *sw = port->sw; + u32 value, offset; + int ret; + + /* + * Legacy devices need to have TMU access enabled before port + * space can be fully accessed. + */ + if (tb_switch_is_light_ridge(sw)) + offset = 0x26; + else if (tb_switch_is_eagle_ridge(sw)) + offset = 0x2a; + else + return 0; + + ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1); + if (ret) + return ret; + + if (enable) + value |= TMU_ACCESS_EN; + else + value &= ~TMU_ACCESS_EN; + + return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1); +} + +static void tb_port_dummy_read(struct tb_port *port) +{ + /* + * When reading from next capability pointer location in port + * config space the read data is not cleared on LR. To avoid + * reading stale data on next read perform one dummy read after + * port capabilities are walked. + */ + if (tb_switch_is_light_ridge(port->sw)) { + u32 dummy; + + tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1); + } +} + +/** + * tb_port_next_cap() - Return next capability in the linked list + * @port: Port to find the capability for + * @offset: Previous capability offset (%0 for start) + * + * Finds dword offset of the next capability in port config space + * capability list. When passed %0 in @offset parameter, first entry + * will be returned, if it exists. + * + * Return: + * * Double word offset of the first or next capability - On success. + * * %0 - If no next capability is found. + * * Negative errno - Another error occurred. + */ +int tb_port_next_cap(struct tb_port *port, unsigned int offset) +{ + struct tb_cap_any header; + int ret; + + if (!offset) + return port->config.first_cap_offset; + + ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1); + if (ret) + return ret; + + return header.basic.next; +} + +static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap) +{ + int offset = 0; + + do { + struct tb_cap_any header; + int ret; + + offset = tb_port_next_cap(port, offset); + if (offset < 0) + return offset; + + ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1); + if (ret) + return ret; + + if (header.basic.cap == cap) + return offset; + } while (offset > 0); + + return -ENOENT; +} + +/** + * tb_port_find_cap() - Find port capability + * @port: Port to find the capability for + * @cap: Capability to look + * + * Return: + * * Offset to the start of capability - On success. + * * %-ENOENT - If no such capability was found. + * * Negative errno - Another error occurred. + */ +int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap) +{ + int ret; + + ret = tb_port_enable_tmu(port, true); + if (ret) + return ret; + + ret = __tb_port_find_cap(port, cap); + + tb_port_dummy_read(port); + tb_port_enable_tmu(port, false); + + return ret; +} + +/** + * tb_switch_next_cap() - Return next capability in the linked list + * @sw: Switch to find the capability for + * @offset: Previous capability offset (%0 for start) + * + * Finds dword offset of the next capability in port config space + * capability list. When passed %0 in @offset parameter, first entry + * will be returned, if it exists. + * + * Return: + * * Double word offset of the first or next capability - On success. + * * %0 - If no next capability is found. + * * Negative errno - Another error occurred. + */ +int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset) +{ + struct tb_cap_any header; + int ret; + + if (!offset) + return sw->config.first_cap_offset; + + ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2); + if (ret) + return ret; + + switch (header.basic.cap) { + case TB_SWITCH_CAP_TMU: + ret = header.basic.next; + break; + + case TB_SWITCH_CAP_VSE: + if (!header.extended_short.length) + ret = header.extended_long.next; + else + ret = header.extended_short.next; + break; + + default: + tb_sw_dbg(sw, "unknown capability %#x at %#x\n", + header.basic.cap, offset); + ret = -EINVAL; + break; + } + + return ret >= VSE_CAP_OFFSET_MAX ? 0 : ret; +} + +/** + * tb_switch_find_cap() - Find switch capability + * @sw: Switch to find the capability for + * @cap: Capability to look + * + * Return: + * * Offset to the start of capability - On success. + * * %-ENOENT - If no such capability was found. + * * Negative errno - Another error occurred. + */ +int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap) +{ + int offset = 0; + + do { + struct tb_cap_any header; + int ret; + + offset = tb_switch_next_cap(sw, offset); + if (offset < 0) + return offset; + + ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1); + if (ret) + return ret; + + if (header.basic.cap == cap) + return offset; + } while (offset); + + return -ENOENT; +} + +/** + * tb_switch_find_vse_cap() - Find switch vendor specific capability + * @sw: Switch to find the capability for + * @vsec: Vendor specific capability to look + * + * This function enumerates vendor specific capabilities (VSEC) of a + * switch and returns offset when capability matching @vsec is found. + * + * Return: + * * Offset of capability - On success. + * * %-ENOENT - If capability was not found. + * * Negative errno - Another error occurred. + */ +int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec) +{ + int offset = 0; + + do { + struct tb_cap_any header; + int ret; + + offset = tb_switch_next_cap(sw, offset); + if (offset < 0) + return offset; + + ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1); + if (ret) + return ret; + + if (header.extended_short.cap == TB_SWITCH_CAP_VSE && + header.extended_short.vsec_id == vsec) + return offset; + } while (offset); + + return -ENOENT; +} -- cgit v1.3.1