Commit | Line | Data |
---|---|---|
a9be5582 MW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Thunderbolt link controller support | |
4 | * | |
5 | * Copyright (C) 2019, Intel Corporation | |
6 | * Author: Mika Westerberg <mika.westerberg@linux.intel.com> | |
7 | */ | |
8 | ||
9 | #include "tb.h" | |
10 | ||
11 | /** | |
12 | * tb_lc_read_uuid() - Read switch UUID from link controller common register | |
13 | * @sw: Switch whose UUID is read | |
14 | * @uuid: UUID is placed here | |
15 | */ | |
16 | int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid) | |
17 | { | |
18 | if (!sw->cap_lc) | |
19 | return -EINVAL; | |
20 | return tb_sw_read(sw, uuid, TB_CFG_SWITCH, sw->cap_lc + TB_LC_FUSE, 4); | |
21 | } | |
e879a709 MW |
22 | |
23 | static int read_lc_desc(struct tb_switch *sw, u32 *desc) | |
24 | { | |
25 | if (!sw->cap_lc) | |
26 | return -EINVAL; | |
27 | return tb_sw_read(sw, desc, TB_CFG_SWITCH, sw->cap_lc + TB_LC_DESC, 1); | |
28 | } | |
29 | ||
30 | static int find_port_lc_cap(struct tb_port *port) | |
31 | { | |
32 | struct tb_switch *sw = port->sw; | |
33 | int start, phys, ret, size; | |
34 | u32 desc; | |
35 | ||
36 | ret = read_lc_desc(sw, &desc); | |
37 | if (ret) | |
38 | return ret; | |
39 | ||
40 | /* Start of port LC registers */ | |
41 | start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT; | |
42 | size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT; | |
43 | phys = tb_phy_port_from_link(port->port); | |
44 | ||
45 | return sw->cap_lc + start + phys * size; | |
46 | } | |
47 | ||
48 | static int tb_lc_configure_lane(struct tb_port *port, bool configure) | |
49 | { | |
50 | bool upstream = tb_is_upstream_port(port); | |
51 | struct tb_switch *sw = port->sw; | |
52 | u32 ctrl, lane; | |
53 | int cap, ret; | |
54 | ||
55 | if (sw->generation < 2) | |
56 | return 0; | |
57 | ||
58 | cap = find_port_lc_cap(port); | |
59 | if (cap < 0) | |
60 | return cap; | |
61 | ||
62 | ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1); | |
63 | if (ret) | |
64 | return ret; | |
65 | ||
66 | /* Resolve correct lane */ | |
67 | if (port->port % 2) | |
68 | lane = TB_LC_SX_CTRL_L1C; | |
69 | else | |
70 | lane = TB_LC_SX_CTRL_L2C; | |
71 | ||
72 | if (configure) { | |
73 | ctrl |= lane; | |
74 | if (upstream) | |
75 | ctrl |= TB_LC_SX_CTRL_UPSTREAM; | |
76 | } else { | |
77 | ctrl &= ~lane; | |
78 | if (upstream) | |
79 | ctrl &= ~TB_LC_SX_CTRL_UPSTREAM; | |
80 | } | |
81 | ||
82 | return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1); | |
83 | } | |
84 | ||
85 | /** | |
86 | * tb_lc_configure_link() - Let LC know about configured link | |
87 | * @sw: Switch that is being added | |
88 | * | |
89 | * Informs LC of both parent switch and @sw that there is established | |
90 | * link between the two. | |
91 | */ | |
92 | int tb_lc_configure_link(struct tb_switch *sw) | |
93 | { | |
94 | struct tb_port *up, *down; | |
95 | int ret; | |
96 | ||
97 | if (!sw->config.enabled || !tb_route(sw)) | |
98 | return 0; | |
99 | ||
100 | up = tb_upstream_port(sw); | |
101 | down = tb_port_at(tb_route(sw), tb_to_switch(sw->dev.parent)); | |
102 | ||
103 | /* Configure parent link toward this switch */ | |
104 | ret = tb_lc_configure_lane(down, true); | |
105 | if (ret) | |
106 | return ret; | |
107 | ||
108 | /* Configure upstream link from this switch to the parent */ | |
109 | ret = tb_lc_configure_lane(up, true); | |
110 | if (ret) | |
111 | tb_lc_configure_lane(down, false); | |
112 | ||
113 | return ret; | |
114 | } | |
115 | ||
116 | /** | |
117 | * tb_lc_unconfigure_link() - Let LC know about unconfigured link | |
118 | * @sw: Switch to unconfigure | |
119 | * | |
120 | * Informs LC of both parent switch and @sw that the link between the | |
121 | * two does not exist anymore. | |
122 | */ | |
123 | void tb_lc_unconfigure_link(struct tb_switch *sw) | |
124 | { | |
125 | struct tb_port *up, *down; | |
126 | ||
127 | if (sw->is_unplugged || !sw->config.enabled || !tb_route(sw)) | |
128 | return; | |
129 | ||
130 | up = tb_upstream_port(sw); | |
131 | down = tb_port_at(tb_route(sw), tb_to_switch(sw->dev.parent)); | |
132 | ||
133 | tb_lc_configure_lane(up, false); | |
134 | tb_lc_configure_lane(down, false); | |
135 | } | |
5480dfc2 MW |
136 | |
137 | /** | |
138 | * tb_lc_set_sleep() - Inform LC that the switch is going to sleep | |
139 | * @sw: Switch to set sleep | |
140 | * | |
141 | * Let the switch link controllers know that the switch is going to | |
142 | * sleep. | |
143 | */ | |
144 | int tb_lc_set_sleep(struct tb_switch *sw) | |
145 | { | |
146 | int start, size, nlc, ret, i; | |
147 | u32 desc; | |
148 | ||
149 | if (sw->generation < 2) | |
150 | return 0; | |
151 | ||
152 | ret = read_lc_desc(sw, &desc); | |
153 | if (ret) | |
154 | return ret; | |
155 | ||
156 | /* Figure out number of link controllers */ | |
157 | nlc = desc & TB_LC_DESC_NLC_MASK; | |
158 | start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT; | |
159 | size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT; | |
160 | ||
161 | /* For each link controller set sleep bit */ | |
162 | for (i = 0; i < nlc; i++) { | |
163 | unsigned int offset = sw->cap_lc + start + i * size; | |
164 | u32 ctrl; | |
165 | ||
166 | ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, | |
167 | offset + TB_LC_SX_CTRL, 1); | |
168 | if (ret) | |
169 | return ret; | |
170 | ||
171 | ctrl |= TB_LC_SX_CTRL_SLP; | |
172 | ret = tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, | |
173 | offset + TB_LC_SX_CTRL, 1); | |
174 | if (ret) | |
175 | return ret; | |
176 | } | |
177 | ||
178 | return 0; | |
179 | } |