]>
Commit | Line | Data |
---|---|---|
7b79d708 GKH |
1 | From 01da6b99d49f60b1edead44e33569b1a2e9f49b7 Mon Sep 17 00:00:00 2001 |
2 | From: Sanath S <Sanath.S@amd.com> | |
3 | Date: Sat, 13 Jan 2024 11:39:57 +0200 | |
4 | Subject: thunderbolt: Introduce tb_port_reset() | |
5 | ||
6 | From: Sanath S <Sanath.S@amd.com> | |
7 | ||
8 | commit 01da6b99d49f60b1edead44e33569b1a2e9f49b7 upstream. | |
9 | ||
10 | Introduce a function that issues Downstream Port Reset to a USB4 port. | |
11 | This supports Thunderbolt 2, 3 and USB4 routers. | |
12 | ||
13 | Signed-off-by: Sanath S <Sanath.S@amd.com> | |
14 | Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> | |
15 | Cc: Mario Limonciello <mario.limonciello@amd.com> | |
16 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
17 | --- | |
18 | drivers/thunderbolt/lc.c | 45 ++++++++++++++++++++++++++++++++++++++++++ | |
19 | drivers/thunderbolt/switch.c | 7 ++++++ | |
20 | drivers/thunderbolt/tb.h | 2 + | |
21 | drivers/thunderbolt/tb_regs.h | 4 +++ | |
22 | drivers/thunderbolt/usb4.c | 39 ++++++++++++++++++++++++++++++++++++ | |
23 | 5 files changed, 97 insertions(+) | |
24 | ||
25 | --- a/drivers/thunderbolt/lc.c | |
26 | +++ b/drivers/thunderbolt/lc.c | |
27 | @@ -6,6 +6,8 @@ | |
28 | * Author: Mika Westerberg <mika.westerberg@linux.intel.com> | |
29 | */ | |
30 | ||
31 | +#include <linux/delay.h> | |
32 | + | |
33 | #include "tb.h" | |
34 | ||
35 | /** | |
36 | @@ -45,6 +47,49 @@ static int find_port_lc_cap(struct tb_po | |
37 | return sw->cap_lc + start + phys * size; | |
38 | } | |
39 | ||
40 | +/** | |
41 | + * tb_lc_reset_port() - Trigger downstream port reset through LC | |
42 | + * @port: Port that is reset | |
43 | + * | |
44 | + * Triggers downstream port reset through link controller registers. | |
45 | + * Returns %0 in case of success negative errno otherwise. Only supports | |
46 | + * non-USB4 routers with link controller (that's Thunderbolt 2 and | |
47 | + * Thunderbolt 3). | |
48 | + */ | |
49 | +int tb_lc_reset_port(struct tb_port *port) | |
50 | +{ | |
51 | + struct tb_switch *sw = port->sw; | |
52 | + int cap, ret; | |
53 | + u32 mode; | |
54 | + | |
55 | + if (sw->generation < 2) | |
56 | + return -EINVAL; | |
57 | + | |
58 | + cap = find_port_lc_cap(port); | |
59 | + if (cap < 0) | |
60 | + return cap; | |
61 | + | |
62 | + ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); | |
63 | + if (ret) | |
64 | + return ret; | |
65 | + | |
66 | + mode |= TB_LC_PORT_MODE_DPR; | |
67 | + | |
68 | + ret = tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); | |
69 | + if (ret) | |
70 | + return ret; | |
71 | + | |
72 | + fsleep(10000); | |
73 | + | |
74 | + ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); | |
75 | + if (ret) | |
76 | + return ret; | |
77 | + | |
78 | + mode &= ~TB_LC_PORT_MODE_DPR; | |
79 | + | |
80 | + return tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); | |
81 | +} | |
82 | + | |
83 | static int tb_lc_set_port_configured(struct tb_port *port, bool configured) | |
84 | { | |
85 | bool upstream = tb_is_upstream_port(port); | |
86 | --- a/drivers/thunderbolt/switch.c | |
87 | +++ b/drivers/thunderbolt/switch.c | |
88 | @@ -675,6 +675,13 @@ int tb_port_disable(struct tb_port *port | |
89 | return __tb_port_enable(port, false); | |
90 | } | |
91 | ||
92 | +static int tb_port_reset(struct tb_port *port) | |
93 | +{ | |
94 | + if (tb_switch_is_usb4(port->sw)) | |
95 | + return port->cap_usb4 ? usb4_port_reset(port) : 0; | |
96 | + return tb_lc_reset_port(port); | |
97 | +} | |
98 | + | |
99 | /* | |
100 | * tb_init_port() - initialize a port | |
101 | * | |
102 | --- a/drivers/thunderbolt/tb.h | |
103 | +++ b/drivers/thunderbolt/tb.h | |
104 | @@ -1119,6 +1119,7 @@ int tb_drom_read(struct tb_switch *sw); | |
105 | int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); | |
106 | ||
107 | int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid); | |
108 | +int tb_lc_reset_port(struct tb_port *port); | |
109 | int tb_lc_configure_port(struct tb_port *port); | |
110 | void tb_lc_unconfigure_port(struct tb_port *port); | |
111 | int tb_lc_configure_xdomain(struct tb_port *port); | |
112 | @@ -1251,6 +1252,7 @@ void usb4_switch_remove_ports(struct tb_ | |
113 | ||
114 | int usb4_port_unlock(struct tb_port *port); | |
115 | int usb4_port_hotplug_enable(struct tb_port *port); | |
116 | +int usb4_port_reset(struct tb_port *port); | |
117 | int usb4_port_configure(struct tb_port *port); | |
118 | void usb4_port_unconfigure(struct tb_port *port); | |
119 | int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd); | |
120 | --- a/drivers/thunderbolt/tb_regs.h | |
121 | +++ b/drivers/thunderbolt/tb_regs.h | |
122 | @@ -383,6 +383,7 @@ struct tb_regs_port_header { | |
123 | #define PORT_CS_18_WODS BIT(17) | |
124 | #define PORT_CS_18_WOU4S BIT(18) | |
125 | #define PORT_CS_19 0x13 | |
126 | +#define PORT_CS_19_DPR BIT(0) | |
127 | #define PORT_CS_19_PC BIT(3) | |
128 | #define PORT_CS_19_PID BIT(4) | |
129 | #define PORT_CS_19_WOC BIT(16) | |
130 | @@ -579,6 +580,9 @@ struct tb_regs_hop { | |
131 | #define TB_LC_POWER 0x740 | |
132 | ||
133 | /* Link controller registers */ | |
134 | +#define TB_LC_PORT_MODE 0x26 | |
135 | +#define TB_LC_PORT_MODE_DPR BIT(0) | |
136 | + | |
137 | #define TB_LC_CS_42 0x2a | |
138 | #define TB_LC_CS_42_USB_PLUGGED BIT(31) | |
139 | ||
140 | --- a/drivers/thunderbolt/usb4.c | |
141 | +++ b/drivers/thunderbolt/usb4.c | |
142 | @@ -1113,6 +1113,45 @@ int usb4_port_hotplug_enable(struct tb_p | |
143 | return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_5, 1); | |
144 | } | |
145 | ||
146 | +/** | |
147 | + * usb4_port_reset() - Issue downstream port reset | |
148 | + * @port: USB4 port to reset | |
149 | + * | |
150 | + * Issues downstream port reset to @port. | |
151 | + */ | |
152 | +int usb4_port_reset(struct tb_port *port) | |
153 | +{ | |
154 | + int ret; | |
155 | + u32 val; | |
156 | + | |
157 | + if (!port->cap_usb4) | |
158 | + return -EINVAL; | |
159 | + | |
160 | + ret = tb_port_read(port, &val, TB_CFG_PORT, | |
161 | + port->cap_usb4 + PORT_CS_19, 1); | |
162 | + if (ret) | |
163 | + return ret; | |
164 | + | |
165 | + val |= PORT_CS_19_DPR; | |
166 | + | |
167 | + ret = tb_port_write(port, &val, TB_CFG_PORT, | |
168 | + port->cap_usb4 + PORT_CS_19, 1); | |
169 | + if (ret) | |
170 | + return ret; | |
171 | + | |
172 | + fsleep(10000); | |
173 | + | |
174 | + ret = tb_port_read(port, &val, TB_CFG_PORT, | |
175 | + port->cap_usb4 + PORT_CS_19, 1); | |
176 | + if (ret) | |
177 | + return ret; | |
178 | + | |
179 | + val &= ~PORT_CS_19_DPR; | |
180 | + | |
181 | + return tb_port_write(port, &val, TB_CFG_PORT, | |
182 | + port->cap_usb4 + PORT_CS_19, 1); | |
183 | +} | |
184 | + | |
185 | static int usb4_port_set_configured(struct tb_port *port, bool configured) | |
186 | { | |
187 | int ret; |