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