blob: 2bca116a9d81808755ff1bf7c3071a9e0a04b3c1 [file] [log] [blame]
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +10001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * NC-SI protocol configuration
4 *
5 * Copyright (C) 2019, IBM Corporation.
6 */
7
Tom Riniabb9a042024-05-18 20:20:43 -06008#include <common.h>
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +100010#include <malloc.h>
11#include <phy.h>
Samuel Mendoza-Jonasc8f4ab02022-08-08 21:46:03 +093012#include <net.h>
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +100013#include <net/ncsi.h>
14#include <net/ncsi-pkt.h>
15#include <asm/unaligned.h>
16
17#define NCSI_PACKAGE_MAX 8
18#define NCSI_CHANNEL_MAX 31
19
20#define NCSI_PACKAGE_SHIFT 5
21#define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
22#define NCSI_RESERVED_CHANNEL 0x1f
23#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
24#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
25
26#define NCSI_PKT_REVISION 0x01
27
28#define NCSI_CAP_GENERIC_MASK 0x7f
29#define NCSI_CAP_BC_MASK 0x0f
30#define NCSI_CAP_MC_MASK 0x3f
31#define NCSI_CAP_AEN_MASK 0x07
32#define NCSI_CAP_VLAN_MASK 0x07
33
34static void ncsi_send_ebf(unsigned int np, unsigned int nc);
35static void ncsi_send_ae(unsigned int np, unsigned int nc);
36static void ncsi_send_gls(unsigned int np, unsigned int nc);
37static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
38 uchar *payload, int len, bool wait);
39
40struct ncsi_channel {
41 unsigned int id;
42 bool has_link;
43
44 /* capabilities */
45 u32 cap_generic;
46 u32 cap_bc;
47 u32 cap_mc;
48 u32 cap_buffer;
49 u32 cap_aen;
50 u32 cap_vlan;
51
52 /* version information */
53 struct {
54 u32 version; /* Supported BCD encoded NCSI version */
55 u32 alpha2; /* Supported BCD encoded NCSI version */
56 u8 fw_name[12]; /* Firmware name string */
57 u32 fw_version; /* Firmware version */
58 u16 pci_ids[4]; /* PCI identification */
59 u32 mf_id; /* Manufacture ID */
60 } version;
61
62};
63
64struct ncsi_package {
65 unsigned int id;
66 unsigned int n_channels;
67 struct ncsi_channel *channels;
68};
69
70struct ncsi {
71 enum {
72 NCSI_PROBE_PACKAGE_SP,
73 NCSI_PROBE_PACKAGE_DP,
74 NCSI_PROBE_CHANNEL_SP,
75 NCSI_PROBE_CHANNEL,
76 NCSI_CONFIG,
77 } state;
78
79 unsigned int pending_requests;
80 unsigned int requests[256];
81 unsigned int last_request;
82
83 unsigned int current_package;
84 unsigned int current_channel;
85
86 unsigned int n_packages;
87 struct ncsi_package *packages;
88};
89
90struct ncsi *ncsi_priv;
91
92bool ncsi_active(void)
93{
94 unsigned int np, nc;
95
96 if (!ncsi_priv)
97 return false;
98
99 np = ncsi_priv->current_package;
100 nc = ncsi_priv->current_channel;
101
102 if (ncsi_priv->state != NCSI_CONFIG)
103 return false;
104
105 return np < NCSI_PACKAGE_MAX && nc < NCSI_CHANNEL_MAX &&
106 ncsi_priv->packages[np].channels[nc].has_link;
107}
108
109static unsigned int cmd_payload(int cmd)
110{
111 switch (cmd) {
112 case NCSI_PKT_CMD_CIS:
113 return 0;
114 case NCSI_PKT_CMD_SP:
115 return 4;
116 case NCSI_PKT_CMD_DP:
117 return 0;
118 case NCSI_PKT_CMD_EC:
119 return 0;
120 case NCSI_PKT_CMD_DC:
121 return 4;
122 case NCSI_PKT_CMD_RC:
123 return 4;
124 case NCSI_PKT_CMD_ECNT:
125 return 0;
126 case NCSI_PKT_CMD_DCNT:
127 return 0;
128 case NCSI_PKT_CMD_AE:
129 return 8;
130 case NCSI_PKT_CMD_SL:
131 return 8;
132 case NCSI_PKT_CMD_GLS:
133 return 0;
134 case NCSI_PKT_CMD_SVF:
135 return 8;
136 case NCSI_PKT_CMD_EV:
137 return 4;
138 case NCSI_PKT_CMD_DV:
139 return 0;
140 case NCSI_PKT_CMD_SMA:
141 return 8;
142 case NCSI_PKT_CMD_EBF:
143 return 4;
144 case NCSI_PKT_CMD_DBF:
145 return 0;
146 case NCSI_PKT_CMD_EGMF:
147 return 4;
148 case NCSI_PKT_CMD_DGMF:
149 return 0;
150 case NCSI_PKT_CMD_SNFC:
151 return 4;
152 case NCSI_PKT_CMD_GVI:
153 return 0;
154 case NCSI_PKT_CMD_GC:
155 return 0;
156 case NCSI_PKT_CMD_GP:
157 return 0;
158 case NCSI_PKT_CMD_GCPS:
159 return 0;
160 case NCSI_PKT_CMD_GNS:
161 return 0;
162 case NCSI_PKT_CMD_GNPTS:
163 return 0;
164 case NCSI_PKT_CMD_GPS:
165 return 0;
166 default:
167 printf("NCSI: Unknown command 0x%02x\n", cmd);
168 return 0;
169 }
170}
171
172static u32 ncsi_calculate_checksum(unsigned char *data, int len)
173{
174 u32 checksum = 0;
175 int i;
176
177 for (i = 0; i < len; i += 2)
178 checksum += (((u32)data[i] << 8) | data[i + 1]);
179
180 checksum = (~checksum + 1);
181 return checksum;
182}
183
184static int ncsi_validate_rsp(struct ncsi_rsp_pkt *pkt, int payload)
185{
186 struct ncsi_rsp_pkt_hdr *hdr = &pkt->rsp;
187 u32 checksum, c_offset;
188 __be32 pchecksum;
189
190 if (hdr->common.revision != 1) {
191 printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
192 hdr->common.type, hdr->common.revision);
193 return -1;
194 }
195
196 if (hdr->code != 0) {
197 printf("NCSI: 0x%02x response returns error %d\n",
198 hdr->common.type, __be16_to_cpu(hdr->code));
199 if (ntohs(hdr->reason) == 0x05)
200 printf("(Invalid command length)\n");
201 return -1;
202 }
203
204 if (ntohs(hdr->common.length) != payload) {
205 printf("NCSI: 0x%02x response has incorrect length %d\n",
206 hdr->common.type, hdr->common.length);
207 return -1;
208 }
209
210 c_offset = sizeof(struct ncsi_rsp_pkt_hdr) + payload - sizeof(checksum);
211 pchecksum = get_unaligned_be32((void *)hdr + c_offset);
212 if (pchecksum != 0) {
213 checksum = ncsi_calculate_checksum((unsigned char *)hdr,
214 c_offset);
215 if (pchecksum != checksum) {
216 printf("NCSI: 0x%02x response has invalid checksum\n",
217 hdr->common.type);
218 return -1;
219 }
220 }
221
222 return 0;
223}
224
225static void ncsi_rsp_ec(struct ncsi_rsp_pkt *pkt)
226{
227 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
228 unsigned int np, nc;
229
230 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
231 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
232
233 if (ncsi_priv->packages[np].channels[nc].cap_aen != 0)
234 ncsi_send_ae(np, nc);
235 /* else, done */
236}
237
238static void ncsi_rsp_ecnt(struct ncsi_rsp_pkt *pkt)
239{
240 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
241 unsigned int np, nc;
242
243 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
244 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
245
246 ncsi_send_command(np, nc, NCSI_PKT_CMD_EC, NULL, 0, true);
247}
248
249static void ncsi_rsp_ebf(struct ncsi_rsp_pkt *pkt)
250{
251 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
252 unsigned int np, nc;
253
254 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
255 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
256
257 ncsi_send_command(np, nc, NCSI_PKT_CMD_ECNT, NULL, 0, true);
258}
259
260static void ncsi_rsp_sma(struct ncsi_rsp_pkt *pkt)
261{
262 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
263 unsigned int np, nc;
264
265 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
266 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
267
268 ncsi_send_ebf(np, nc);
269}
270
271static void ncsi_rsp_gc(struct ncsi_rsp_pkt *pkt)
272{
273 struct ncsi_rsp_gc_pkt *gc = (struct ncsi_rsp_gc_pkt *)pkt;
274 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gc->rsp;
275 struct ncsi_channel *c;
276 unsigned int np, nc;
277
278 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
279 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
280
281 if (np >= ncsi_priv->n_packages ||
282 nc >= ncsi_priv->packages[np].n_channels) {
283 printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
284 np, nc);
285 return;
286 }
287
288 c = &ncsi_priv->packages[np].channels[nc];
Jacky Chou13fddd52024-02-05 16:13:23 +0800289 c->cap_generic = get_unaligned_be32(&gc->cap) & NCSI_CAP_GENERIC_MASK;
290 c->cap_bc = get_unaligned_be32(&gc->bc_cap) & NCSI_CAP_BC_MASK;
291 c->cap_mc = get_unaligned_be32(&gc->mc_cap) & NCSI_CAP_MC_MASK;
292 c->cap_aen = get_unaligned_be32(&gc->aen_cap) & NCSI_CAP_AEN_MASK;
293 c->cap_vlan = gc->vlan_mode & NCSI_CAP_VLAN_MASK;
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000294
295 /* End of probe for this channel */
296}
297
298static void ncsi_rsp_gvi(struct ncsi_rsp_pkt *pkt)
299{
300 struct ncsi_rsp_gvi_pkt *gvi = (struct ncsi_rsp_gvi_pkt *)pkt;
301 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gvi->rsp;
302 struct ncsi_channel *c;
303 unsigned int np, nc, i;
304
305 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
306 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
307
308 if (np >= ncsi_priv->n_packages ||
309 nc >= ncsi_priv->packages[np].n_channels) {
310 printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
311 np, nc);
312 return;
313 }
314
315 c = &ncsi_priv->packages[np].channels[nc];
316 c->version.version = get_unaligned_be32(&gvi->ncsi_version);
317 c->version.alpha2 = gvi->alpha2;
318 memcpy(c->version.fw_name, gvi->fw_name, sizeof(c->version.fw_name));
319 c->version.fw_version = get_unaligned_be32(&gvi->fw_version);
320 for (i = 0; i < ARRAY_SIZE(c->version.pci_ids); i++)
321 c->version.pci_ids[i] = get_unaligned_be16(gvi->pci_ids + i);
322 c->version.mf_id = get_unaligned_be32(&gvi->mf_id);
323
324 if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
325 ncsi_send_command(np, nc, NCSI_PKT_CMD_GC, NULL, 0, true);
326}
327
328static void ncsi_rsp_gls(struct ncsi_rsp_pkt *pkt)
329{
330 struct ncsi_rsp_gls_pkt *gls = (struct ncsi_rsp_gls_pkt *)pkt;
331 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gls->rsp;
332 unsigned int np, nc;
333
334 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
335 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
336
337 if (np >= ncsi_priv->n_packages ||
338 nc >= ncsi_priv->packages[np].n_channels) {
339 printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
340 np, nc);
341 return;
342 }
343
344 ncsi_priv->packages[np].channels[nc].has_link =
345 !!(get_unaligned_be32(&gls->status));
346
347 if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
348 ncsi_send_command(np, nc, NCSI_PKT_CMD_GVI, NULL, 0, true);
349}
350
351static void ncsi_rsp_cis(struct ncsi_rsp_pkt *pkt)
352{
353 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
354 struct ncsi_package *package;
355 unsigned int np, nc;
356
357 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
358 nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
359
360 if (np >= ncsi_priv->n_packages) {
361 printf("NCSI: Mystery package 0x%02x from CIS\n", np);
362 return;
363 }
364
365 package = &ncsi_priv->packages[np];
366
367 if (nc < package->n_channels) {
368 /*
369 * This is fine in general but in the current design we
370 * don't send CIS commands to known channels.
371 */
372 debug("NCSI: Duplicate channel 0x%02x\n", nc);
373 return;
374 }
375
376 package->channels = realloc(package->channels,
377 sizeof(struct ncsi_channel) *
378 (package->n_channels + 1));
379 if (!package->channels) {
380 printf("NCSI: Could not allocate memory for new channel\n");
381 return;
382 }
383
384 debug("NCSI: New channel 0x%02x\n", nc);
385
386 package->channels[nc].id = nc;
387 package->channels[nc].has_link = false;
388 package->n_channels++;
389
390 ncsi_send_gls(np, nc);
391}
392
393static void ncsi_rsp_dp(struct ncsi_rsp_pkt *pkt)
394{
395 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
396 unsigned int np;
397
398 /* No action needed */
399
400 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
401 if (np >= ncsi_priv->n_packages)
402 debug("NCSI: DP response from unknown package %d\n", np);
403}
404
405static void ncsi_rsp_sp(struct ncsi_rsp_pkt *pkt)
406{
407 struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
408 unsigned int np;
409
410 np = NCSI_PACKAGE_INDEX(rsp->common.channel);
411
412 if (np < ncsi_priv->n_packages) {
413 /* Already know about this package */
414 debug("NCSI: package 0x%02x selected\n", np);
415 return;
416 }
417
418 debug("NCSI: adding new package %d\n", np);
419
420 ncsi_priv->packages = realloc(ncsi_priv->packages,
421 sizeof(struct ncsi_package) *
422 (ncsi_priv->n_packages + 1));
423 if (!ncsi_priv->packages) {
424 printf("NCSI: could not allocate memory for new package\n");
425 return;
426 }
427
428 ncsi_priv->packages[np].id = np;
429 ncsi_priv->packages[np].n_channels = 0;
430 ncsi_priv->packages[np].channels = NULL;
431 ncsi_priv->n_packages++;
432}
433
434static void ncsi_update_state(struct ncsi_rsp_pkt_hdr *nh)
435{
436 bool timeout = !nh;
437 int np, nc;
438
439 switch (ncsi_priv->state) {
440 case NCSI_PROBE_PACKAGE_SP:
441 if (!timeout &&
442 ncsi_priv->current_package + 1 < NCSI_PACKAGE_MAX) {
443 ncsi_priv->current_package++;
444 } else {
445 ncsi_priv->state = NCSI_PROBE_PACKAGE_DP;
446 ncsi_priv->current_package = 0;
447 }
448 return ncsi_probe_packages();
449 case NCSI_PROBE_PACKAGE_DP:
450 if (ncsi_priv->current_package + 1 < ncsi_priv->n_packages &&
451 !timeout) {
452 ncsi_priv->current_package++;
453 } else {
454 if (!ncsi_priv->n_packages) {
455 printf("NCSI: no packages found\n");
456 net_set_state(NETLOOP_FAIL);
457 return;
458 }
459 printf("NCSI: probing channels\n");
460 ncsi_priv->state = NCSI_PROBE_CHANNEL_SP;
461 ncsi_priv->current_package = 0;
462 ncsi_priv->current_channel = 0;
463 }
464 return ncsi_probe_packages();
465 case NCSI_PROBE_CHANNEL_SP:
466 if (!timeout && nh->common.type == NCSI_PKT_RSP_SP) {
467 ncsi_priv->state = NCSI_PROBE_CHANNEL;
468 return ncsi_probe_packages();
469 }
470 printf("NCSI: failed to select package 0x%0x2 or timeout\n",
471 ncsi_priv->current_package);
472 net_set_state(NETLOOP_FAIL);
473 break;
474 case NCSI_PROBE_CHANNEL:
475 // TODO only does package 0 for now
476 if (ncsi_priv->pending_requests == 0) {
477 np = ncsi_priv->current_package;
478 nc = ncsi_priv->current_channel;
479
480 /* Configure first channel that has link */
481 if (ncsi_priv->packages[np].channels[nc].has_link) {
482 ncsi_priv->state = NCSI_CONFIG;
483 } else if (ncsi_priv->current_channel + 1 <
484 NCSI_CHANNEL_MAX) {
485 ncsi_priv->current_channel++;
486 } else {
487 // XXX As above only package 0
488 printf("NCSI: no channel found with link\n");
489 net_set_state(NETLOOP_FAIL);
490 return;
491 }
492 return ncsi_probe_packages();
493 }
494 break;
495 case NCSI_CONFIG:
496 if (ncsi_priv->pending_requests == 0) {
497 printf("NCSI: configuration done!\n");
498 net_set_state(NETLOOP_SUCCESS);
499 } else if (timeout) {
500 printf("NCSI: timeout during configure\n");
501 net_set_state(NETLOOP_FAIL);
502 }
503 break;
504 default:
505 printf("NCSI: something went very wrong, nevermind\n");
506 net_set_state(NETLOOP_FAIL);
507 break;
508 }
509}
510
511static void ncsi_timeout_handler(void)
512{
513 if (ncsi_priv->pending_requests)
514 ncsi_priv->pending_requests--;
515
516 ncsi_update_state(NULL);
517}
518
519static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
520 uchar *payload, int len, bool wait)
521{
522 struct ncsi_pkt_hdr *hdr;
523 __be32 *pchecksum;
524 int eth_hdr_size;
525 u32 checksum;
526 uchar *pkt, *start;
527 int final_len;
528
529 pkt = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
530 if (!pkt)
531 return -ENOMEM;
532 start = pkt;
533
534 eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_NCSI);
535 pkt += eth_hdr_size;
536
537 /* Set NCSI command header fields */
538 hdr = (struct ncsi_pkt_hdr *)pkt;
539 hdr->mc_id = 0;
540 hdr->revision = NCSI_PKT_REVISION;
541 hdr->id = ++ncsi_priv->last_request;
542 ncsi_priv->requests[ncsi_priv->last_request] = 1;
543 hdr->type = cmd;
544 hdr->channel = NCSI_TO_CHANNEL(np, nc);
545 hdr->length = htons(len);
546
547 if (payload && len)
548 memcpy(pkt + sizeof(struct ncsi_pkt_hdr), payload, len);
549
550 /* Calculate checksum */
551 checksum = ncsi_calculate_checksum((unsigned char *)hdr,
552 sizeof(*hdr) + len);
553 pchecksum = (__be32 *)((void *)(hdr + 1) + len);
Jacky Chou32d7b192024-02-05 16:02:28 +0800554 put_unaligned_be32(checksum, pchecksum);
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000555
556 if (wait) {
557 net_set_timeout_handler(1000UL, ncsi_timeout_handler);
558 ncsi_priv->pending_requests++;
559 }
560
561 if (len < 26)
562 len = 26;
563 /* frame header, packet header, payload, checksum */
564 final_len = eth_hdr_size + sizeof(struct ncsi_cmd_pkt_hdr) + len + 4;
565
566 net_send_packet(start, final_len);
567 free(start);
568 return 0;
569}
570
571static void ncsi_handle_aen(struct ip_udp_hdr *ip, unsigned int len)
572{
573 struct ncsi_aen_pkt_hdr *hdr = (struct ncsi_aen_pkt_hdr *)ip;
574 int payload, i;
575 __be32 pchecksum;
576 u32 checksum;
577
578 switch (hdr->type) {
579 case NCSI_PKT_AEN_LSC:
580 printf("NCSI: link state changed\n");
581 payload = 12;
582 break;
583 case NCSI_PKT_AEN_CR:
584 printf("NCSI: re-configuration required\n");
585 payload = 4;
586 break;
587 case NCSI_PKT_AEN_HNCDSC:
588 /* Host notifcation - N/A but weird */
589 debug("NCSI: HNCDSC AEN received\n");
590 return;
591 default:
592 printf("%s: Invalid type 0x%02x\n", __func__, hdr->type);
593 return;
594 }
595
596 /* Validate packet */
597 if (hdr->common.revision != 1) {
598 printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
599 hdr->common.type, hdr->common.revision);
600 return;
601 }
602
603 if (ntohs(hdr->common.length) != payload) {
604 printf("NCSI: 0x%02x response has incorrect length %d\n",
605 hdr->common.type, hdr->common.length);
606 return;
607 }
608
609 pchecksum = get_unaligned_be32((void *)(hdr + 1) + payload - 4);
610 if (pchecksum != 0) {
611 checksum = ncsi_calculate_checksum((unsigned char *)hdr,
612 sizeof(*hdr) + payload - 4);
613 if (pchecksum != checksum) {
614 printf("NCSI: 0x%02x response has invalid checksum\n",
615 hdr->common.type);
616 return;
617 }
618 }
619
620 /* Link or configuration lost - just redo the discovery process */
621 ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
Jacky Choue4f07602023-12-29 09:45:55 +0800622 for (i = 0; i < ncsi_priv->n_packages; i++) {
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000623 free(ncsi_priv->packages[i].channels);
Jacky Choue4f07602023-12-29 09:45:55 +0800624 ncsi_priv->packages[i].channels = NULL;
625 }
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000626 free(ncsi_priv->packages);
Jacky Choue4f07602023-12-29 09:45:55 +0800627 ncsi_priv->packages = NULL;
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000628 ncsi_priv->n_packages = 0;
629
630 ncsi_priv->current_package = NCSI_PACKAGE_MAX;
631 ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
632
633 ncsi_probe_packages();
634}
635
636void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
637 unsigned int len)
638{
639 struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip;
640 struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
641 void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL;
642 unsigned short payload;
643
644 if (ncsi_priv->pending_requests)
645 ncsi_priv->pending_requests--;
646
647 if (len < sizeof(struct ncsi_rsp_pkt_hdr)) {
648 printf("NCSI: undersized packet: %u bytes\n", len);
649 goto out;
650 }
651
652 if (nh->common.type == NCSI_PKT_AEN)
653 return ncsi_handle_aen(ip, len);
654
655 switch (nh->common.type) {
656 case NCSI_PKT_RSP_SP:
657 payload = 4;
658 handler = ncsi_rsp_sp;
659 break;
660 case NCSI_PKT_RSP_DP:
661 payload = 4;
662 handler = ncsi_rsp_dp;
663 break;
664 case NCSI_PKT_RSP_CIS:
665 payload = 4;
666 handler = ncsi_rsp_cis;
667 break;
668 case NCSI_PKT_RSP_GLS:
669 payload = 16;
670 handler = ncsi_rsp_gls;
671 break;
672 case NCSI_PKT_RSP_GVI:
673 payload = 40;
674 handler = ncsi_rsp_gvi;
675 break;
676 case NCSI_PKT_RSP_GC:
677 payload = 32;
678 handler = ncsi_rsp_gc;
679 break;
680 case NCSI_PKT_RSP_SMA:
681 payload = 4;
682 handler = ncsi_rsp_sma;
683 break;
684 case NCSI_PKT_RSP_EBF:
685 payload = 4;
686 handler = ncsi_rsp_ebf;
687 break;
688 case NCSI_PKT_RSP_ECNT:
689 payload = 4;
690 handler = ncsi_rsp_ecnt;
691 break;
692 case NCSI_PKT_RSP_EC:
693 payload = 4;
694 handler = ncsi_rsp_ec;
695 break;
696 case NCSI_PKT_RSP_AE:
697 payload = 4;
698 handler = NULL;
699 break;
700 default:
701 printf("NCSI: unsupported packet type 0x%02x\n",
702 nh->common.type);
703 goto out;
704 }
705
706 if (ncsi_validate_rsp(pkt, payload) != 0) {
707 printf("NCSI: discarding invalid packet of type 0x%02x\n",
708 nh->common.type);
709 goto out;
710 }
711
712 if (handler)
713 handler(pkt);
714out:
715 ncsi_update_state(nh);
716}
717
718static void ncsi_send_sp(unsigned int np)
719{
720 uchar payload[4] = {0};
721
722 ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP,
723 (unsigned char *)&payload,
724 cmd_payload(NCSI_PKT_CMD_SP), true);
725}
726
727static void ncsi_send_dp(unsigned int np)
728{
729 ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0,
730 true);
731}
732
733static void ncsi_send_gls(unsigned int np, unsigned int nc)
734{
735 ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true);
736}
737
738static void ncsi_send_cis(unsigned int np, unsigned int nc)
739{
740 ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true);
741}
742
743static void ncsi_send_ae(unsigned int np, unsigned int nc)
744{
745 struct ncsi_cmd_ae_pkt cmd;
746
747 memset(&cmd, 0, sizeof(cmd));
748 cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen);
749
750 ncsi_send_command(np, nc, NCSI_PKT_CMD_AE,
751 ((unsigned char *)&cmd)
752 + sizeof(struct ncsi_cmd_pkt_hdr),
753 cmd_payload(NCSI_PKT_CMD_AE), true);
754}
755
756static void ncsi_send_ebf(unsigned int np, unsigned int nc)
757{
758 struct ncsi_cmd_ebf_pkt cmd;
759
760 memset(&cmd, 0, sizeof(cmd));
761 cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc);
762
763 ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF,
764 ((unsigned char *)&cmd)
765 + sizeof(struct ncsi_cmd_pkt_hdr),
766 cmd_payload(NCSI_PKT_CMD_EBF), true);
767}
768
769static void ncsi_send_sma(unsigned int np, unsigned int nc)
770{
771 struct ncsi_cmd_sma_pkt cmd;
772 unsigned char *addr, i;
773
774 addr = eth_get_ethaddr();
775 if (!addr) {
776 printf("NCSI: no MAC address configured\n");
777 return;
778 }
779
780 memset(&cmd, 0, sizeof(cmd));
781 for (i = 0; i < ARP_HLEN; i++)
782 cmd.mac[i] = addr[i];
783 cmd.index = 1;
784 cmd.at_e = 1;
785
786 ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA,
787 ((unsigned char *)&cmd)
788 + sizeof(struct ncsi_cmd_pkt_hdr),
789 cmd_payload(NCSI_PKT_CMD_SMA), true);
790}
791
792void ncsi_probe_packages(void)
793{
794 struct ncsi_package *package;
795 unsigned int np, nc;
796
797 switch (ncsi_priv->state) {
798 case NCSI_PROBE_PACKAGE_SP:
799 if (ncsi_priv->current_package == NCSI_PACKAGE_MAX)
800 ncsi_priv->current_package = 0;
801 ncsi_send_sp(ncsi_priv->current_package);
802 break;
803 case NCSI_PROBE_PACKAGE_DP:
804 ncsi_send_dp(ncsi_priv->current_package);
805 break;
806 case NCSI_PROBE_CHANNEL_SP:
807 if (ncsi_priv->n_packages > 0)
808 ncsi_send_sp(ncsi_priv->current_package);
809 else
810 printf("NCSI: no packages discovered, configuration not possible\n");
811 break;
812 case NCSI_PROBE_CHANNEL:
813 /* Kicks off chain of channel discovery */
814 ncsi_send_cis(ncsi_priv->current_package,
815 ncsi_priv->current_channel);
816 break;
817 case NCSI_CONFIG:
818 for (np = 0; np < ncsi_priv->n_packages; np++) {
819 package = &ncsi_priv->packages[np];
820 for (nc = 0; nc < package->n_channels; nc++)
821 if (package->channels[nc].has_link)
822 break;
823 if (nc < package->n_channels)
824 break;
825 }
826 if (np == ncsi_priv->n_packages) {
827 printf("NCSI: no link available\n");
828 return;
829 }
830
831 printf("NCSI: configuring channel %d\n", nc);
832 ncsi_priv->current_package = np;
833 ncsi_priv->current_channel = nc;
834 /* Kicks off rest of configure chain */
835 ncsi_send_sma(np, nc);
836 break;
837 default:
838 printf("NCSI: unknown state 0x%x\n", ncsi_priv->state);
839 }
840}
841
842int ncsi_probe(struct phy_device *phydev)
843{
844 if (!phydev->priv) {
845 phydev->priv = malloc(sizeof(struct ncsi));
846 if (!phydev->priv)
847 return -ENOMEM;
848 memset(phydev->priv, 0, sizeof(struct ncsi));
849 }
850
851 ncsi_priv = phydev->priv;
852
853 return 0;
854}
855
856int ncsi_startup(struct phy_device *phydev)
857{
858 /* Set phydev parameters */
859 phydev->speed = SPEED_100;
860 phydev->duplex = DUPLEX_FULL;
861 /* Normal phy reset is N/A */
862 phydev->flags |= PHY_FLAG_BROKEN_RESET;
863
864 /* Set initial probe state */
865 ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
866
867 /* No active package/channel yet */
868 ncsi_priv->current_package = NCSI_PACKAGE_MAX;
869 ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
870
871 /* Pretend link works so the MAC driver sets final bits up */
872 phydev->link = true;
873
874 /* Set ncsi_priv so we can use it when called from net_loop() */
875 ncsi_priv = phydev->priv;
876
877 return 0;
878}
879
880int ncsi_shutdown(struct phy_device *phydev)
881{
882 printf("NCSI: Disabling package %d\n", ncsi_priv->current_package);
883 ncsi_send_dp(ncsi_priv->current_package);
884 return 0;
885}
886
Marek Vasut43323a72023-03-19 18:03:10 +0100887U_BOOT_PHY_DRIVER(ncsi) = {
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000888 .uid = PHY_NCSI_ID,
889 .mask = 0xffffffff,
890 .name = "NC-SI",
891 .features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES |
892 SUPPORTED_100baseT_Full | SUPPORTED_MII,
893 .probe = ncsi_probe,
894 .startup = ncsi_startup,
895 .shutdown = ncsi_shutdown,
896};