Switchtec Userspace PROJECT_NUMBER = 4.4
Loading...
Searching...
No Matches
cap.c
Go to the documentation of this file.
1/*
2 * Microsemi Switchtec(tm) PCIe Management Library
3 * Copyright (c) 2017, Microsemi Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
29
30#define SWITCHTEC_LIB_CORE
31
32#include "switchtec/cap.h"
33#include "switchtec/switchtec.h"
34#include "switchtec/mrpc.h"
35#include "switchtec_priv.h"
36
37#include <stddef.h>
38#include <errno.h>
39#include <string.h>
40
51
53 uint32_t offset;
54 uint32_t len;
55};
56
58 uint32_t offset;
59 uint32_t len;
60 uint8_t data[];
61};
62
70int switchtec_multicast_cap_get(struct switchtec_dev *dev, uint32_t gas_base,
71 struct switchtec_multicast_cap *cap)
72{
73 struct gas_cap_read_cmd cmd;
74 int ret;
75
76 if (!dev || !cap)
77 return -EINVAL;
78
79 if (switchtec_boot_phase(dev) != SWITCHTEC_BOOT_PHASE_FW) {
80 errno = ENODEV;
81 return -1;
82 }
83
84 cmd.offset = htole32(gas_base + SWITCHTEC_CAP_MULTICAST_OFFSET);
85 cmd.len = htole32(sizeof(*cap));
86
87 ret = switchtec_cmd(dev, MRPC_GAS_READ, &cmd, sizeof(cmd),
88 cap, sizeof(*cap));
89 if (ret)
90 return ret;
91
92 cap->header = le32toh(cap->header);
93 cap->cap_ctrl = le32toh(cap->cap_ctrl);
94 cap->mc_base_addr = le64toh(cap->mc_base_addr);
95 cap->mc_receive = le64toh(cap->mc_receive);
96 cap->mc_block_all = le64toh(cap->mc_block_all);
97 cap->mc_block_untranslated = le64toh(cap->mc_block_untranslated);
98 cap->mc_overlay_bar = le64toh(cap->mc_overlay_bar);
99
100 return 0;
101}
102
118int switchtec_multicast_cap_set(struct switchtec_dev *dev, uint32_t gas_base,
119 struct switchtec_multicast_set *set)
120{
121 struct switchtec_multicast_cap cap;
122 uint16_t new_ctrl;
123 uint32_t addr;
124 int ret;
125 struct {
126 uint32_t offset;
127 uint32_t len;
128 uint8_t data[8];
129 } cmd;
130
131 if (!dev || !set)
132 return -EINVAL;
133
134 if (switchtec_boot_phase(dev) != SWITCHTEC_BOOT_PHASE_FW) {
135 errno = ENODEV;
136 return -1;
137 }
138
139 ret = switchtec_multicast_cap_get(dev, gas_base, &cap);
140 if (ret)
141 return ret;
142
143 addr = gas_base + SWITCHTEC_CAP_MULTICAST_OFFSET;
144
145 /* Control register is in upper 16 bits of cap_ctrl (DW1) */
146 new_ctrl = SWITCHTEC_MC_CTRL(cap.cap_ctrl);
147
148 if (set->enable && !set->disable)
149 new_ctrl |= (1 << 15);
150 else if (set->disable)
151 new_ctrl &= ~(1 << 15);
152
153 if (set->num_group >= 0 && set->num_group <= 63)
154 new_ctrl = (new_ctrl & ~0x3F) | (set->num_group & 0x3F);
155
156 if (new_ctrl != SWITCHTEC_MC_CTRL(cap.cap_ctrl)) {
157 /* Write to upper 16 bits of DW1 (offset +4 bytes, then +2 for upper half) */
158 cmd.offset = htole32(addr +
159 offsetof(struct switchtec_multicast_cap, cap_ctrl) + 2);
160 cmd.len = htole32(2);
161 *(uint16_t *)cmd.data = htole16(new_ctrl);
162 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
163 sizeof(cmd.offset) + sizeof(cmd.len) + 2,
164 NULL, 0);
165 if (ret)
166 return ret;
167 }
168
169 if (set->set_base_addr || set->index_pos >= 0) {
170 uint64_t new_base;
171 uint8_t idx;
172
173 if (set->set_base_addr)
174 new_base = set->base_addr & ~0xFFFULL;
175 else
176 new_base = SWITCHTEC_MC_BASE_ADDR(cap.mc_base_addr);
177
178 if (set->index_pos >= 0 && set->index_pos <= 63)
179 idx = set->index_pos & 0x3F;
180 else
181 idx = SWITCHTEC_MC_BASE_INDEX_POS(cap.mc_base_addr);
182
183 new_base |= idx;
184
185 cmd.offset = htole32(addr +
186 offsetof(struct switchtec_multicast_cap, mc_base_addr));
187 cmd.len = htole32(8);
188 *(uint64_t *)cmd.data = htole64(new_base);
189 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
190 sizeof(cmd.offset) + sizeof(cmd.len) + 8,
191 NULL, 0);
192 if (ret)
193 return ret;
194 }
195
196 if (set->set_receive) {
197 cmd.offset = htole32(addr +
198 offsetof(struct switchtec_multicast_cap, mc_receive));
199 cmd.len = htole32(8);
200 *(uint64_t *)cmd.data = htole64(set->receive);
201 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
202 sizeof(cmd.offset) + sizeof(cmd.len) + 8,
203 NULL, 0);
204 if (ret)
205 return ret;
206 }
207
208 if (set->set_block_all) {
209 cmd.offset = htole32(addr +
210 offsetof(struct switchtec_multicast_cap, mc_block_all));
211 cmd.len = htole32(8);
212 *(uint64_t *)cmd.data = htole64(set->block_all);
213 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
214 sizeof(cmd.offset) + sizeof(cmd.len) + 8,
215 NULL, 0);
216 if (ret)
217 return ret;
218 }
219
220 if (set->set_block_untranslated) {
221 cmd.offset = htole32(addr +
222 offsetof(struct switchtec_multicast_cap, mc_block_untranslated));
223 cmd.len = htole32(8);
224 *(uint64_t *)cmd.data = htole64(set->block_untranslated);
225 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
226 sizeof(cmd.offset) + sizeof(cmd.len) + 8,
227 NULL, 0);
228 if (ret)
229 return ret;
230 }
231
232 if (set->set_overlay_bar) {
233 uint64_t new_overlay;
234 uint8_t size;
235
236 if (set->overlay_size >= 0 && set->overlay_size <= 63)
237 size = set->overlay_size & 0x3F;
238 else
239 size = SWITCHTEC_MC_OVERLAY_SIZE(cap.mc_overlay_bar);
240
241 new_overlay = (set->overlay_bar & ~0x3FULL) | size;
242
243 cmd.offset = htole32(addr +
244 offsetof(struct switchtec_multicast_cap, mc_overlay_bar));
245 cmd.len = htole32(8);
246 *(uint64_t *)cmd.data = htole64(new_overlay);
247 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
248 sizeof(cmd.offset) + sizeof(cmd.len) + 8,
249 NULL, 0);
250 if (ret)
251 return ret;
252 } else if (set->overlay_size >= 0) {
253 uint64_t new_overlay = cap.mc_overlay_bar;
254
255 new_overlay = (new_overlay & ~0x3FULL) |
256 (set->overlay_size & 0x3F);
257
258 cmd.offset = htole32(addr +
259 offsetof(struct switchtec_multicast_cap, mc_overlay_bar));
260 cmd.len = htole32(8);
261 *(uint64_t *)cmd.data = htole64(new_overlay);
262 ret = switchtec_cmd(dev, MRPC_GAS_WRITE, &cmd,
263 sizeof(cmd.offset) + sizeof(cmd.len) + 8,
264 NULL, 0);
265 if (ret)
266 return ret;
267 }
268
269 return 0;
270}
271
272static int bdf_match(const char *bdf1, const char *bdf2)
273{
274 unsigned int d1, b1, dv1, f1;
275 unsigned int d2, b2, dv2, f2;
276 int has_domain1, has_domain2;
277
278 has_domain1 = (sscanf(bdf1, "%x:%x:%x.%x", &d1, &b1, &dv1, &f1) == 4);
279 if (!has_domain1) {
280 d1 = 0;
281 if (sscanf(bdf1, "%x:%x.%x", &b1, &dv1, &f1) != 3)
282 return 0;
283 }
284
285 has_domain2 = (sscanf(bdf2, "%x:%x:%x.%x", &d2, &b2, &dv2, &f2) == 4);
286 if (!has_domain2) {
287 d2 = 0;
288 if (sscanf(bdf2, "%x:%x.%x", &b2, &dv2, &f2) != 3)
289 return 0;
290 }
291
292 return (d1 == d2 && b1 == b2 && dv1 == dv2 && f1 == f2);
293}
294
295int switchtec_find_port_by_bdf(struct switchtec_dev *dev, const char *target_bdf,
296 struct switchtec_port_info *info)
297{
298 int p, usp_idx = 0, dsp_idx = 0;
299 int num_usps = 0;
300 uint32_t dsp_gas_base;
301 int nr_ports;
302 struct switchtec_status *status;
303 int ret = 0;
304
305 nr_ports = switchtec_status(dev, &status);
306 if (nr_ports < 0) {
307 switchtec_perror("status");
308 return -1;
309 }
310
311 ret = switchtec_get_devices(dev, status, nr_ports);
312 if (ret < 0) {
313 switchtec_perror("get_devices");
314 goto free_status;
315 }
316
317 for (p = 0; p < nr_ports; p++) {
318 if (status[p].port.upstream)
319 num_usps++;
320 }
321
322 /* DSP region starts after USPs + 1 for MGMT */
323 dsp_gas_base = SWITCHTEC_CAP_GAS_BASE +
324 ((num_usps + 1) * SWITCHTEC_CAP_DEV_STRIDE);
325
326 for (p = 0; p < nr_ports; p++) {
327 struct switchtec_status *s = &status[p];
328
329 if (s->port.partition == SWITCHTEC_UNBOUND_PORT)
330 continue;
331
332 if (!s->pci_bdf)
333 continue;
334
335 if (bdf_match(s->pci_bdf, target_bdf)) {
336 info->bdf = s->pci_bdf;
337 if (s->port.upstream) {
338 info->port_type = SWITCHTEC_CAP_PORT_USP;
339 info->gas_base = SWITCHTEC_CAP_GAS_BASE +
340 (usp_idx * SWITCHTEC_CAP_DEV_STRIDE);
341 } else {
342 info->port_type = SWITCHTEC_CAP_PORT_DSP;
343 info->gas_base = dsp_gas_base +
344 (dsp_idx * SWITCHTEC_CAP_DEV_STRIDE);
345 }
346 switchtec_status_free(status, nr_ports);
347 return 0;
348 }
349
350 if (s->port.upstream)
351 usp_idx++;
352 else
353 dsp_idx++;
354 }
355
356 fprintf(stderr, "BDF %s not found in switch ports\n", target_bdf);
357free_status:
358 switchtec_status_free(status, nr_ports);
359 return -1;
360}
361
PCI Capability access functions.
int switchtec_multicast_cap_set(struct switchtec_dev *dev, uint32_t gas_base, struct switchtec_multicast_set *set)
Set multicast capability registers.
Definition cap.c:118
int switchtec_multicast_cap_get(struct switchtec_dev *dev, uint32_t gas_base, struct switchtec_multicast_cap *cap)
Read the multicast capability registers.
Definition cap.c:70
int switchtec_cmd(struct switchtec_dev *dev, uint32_t cmd, const void *payload, size_t payload_len, void *resp, size_t resp_len)
Execute an MRPC command.
Definition platform.c:178
void switchtec_perror(const char *str)
Print an error string to stdout.
Definition switchtec.c:871
void switchtec_status_free(struct switchtec_status *status, int ports)
Free a list of status structures allocated by switchtec_status().
Definition switchtec.c:709
int switchtec_status(struct switchtec_dev *dev, struct switchtec_status **status)
Get the status of all the ports on a switchtec device.
Definition switchtec.c:694
int switchtec_get_devices(struct switchtec_dev *dev, struct switchtec_status *status, int ports)
Populate an already retrieved switchtec_status structure list with information about the devices plug...
Definition platform.c:209
Multicast Extended Capability register structure (PCIe 3.1).
Definition cap.h:56
Multicast capability set parameters.
Definition cap.h:91
unsigned char upstream
1 if this is an upstream port
Definition switchtec.h:169
unsigned char partition
Partition the port is in.
Definition switchtec.h:166
Port status structure.
Definition switchtec.h:181
struct switchtec_port_id port
Port ID.
Definition switchtec.h:182
char * pci_bdf
PCI BDF of the port.
Definition switchtec.h:194
Main Switchtec header.
switchtec_boot_phase
Device boot phase.
Definition switchtec.h:116