blob: eb3fd65bb47f48866ddbfd98ce945f83347303ec [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
8#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];
289 c->cap_generic = ntohl(gc->cap) & NCSI_CAP_GENERIC_MASK;
290 c->cap_bc = ntohl(gc->bc_cap) & NCSI_CAP_BC_MASK;
291 c->cap_mc = ntohl(gc->mc_cap) & NCSI_CAP_MC_MASK;
292 c->cap_aen = ntohl(gc->aen_cap) & NCSI_CAP_AEN_MASK;
293 c->cap_vlan = ntohl(gc->vlan_mode) & NCSI_CAP_VLAN_MASK;
294
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);
554 put_unaligned_be32(htonl(checksum), pchecksum);
555
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;
622 for (i = 0; i < ncsi_priv->n_packages; i++)
623 free(ncsi_priv->packages[i].channels);
624 free(ncsi_priv->packages);
625 ncsi_priv->n_packages = 0;
626
627 ncsi_priv->current_package = NCSI_PACKAGE_MAX;
628 ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
629
630 ncsi_probe_packages();
631}
632
633void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
634 unsigned int len)
635{
636 struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip;
637 struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
638 void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL;
639 unsigned short payload;
640
641 if (ncsi_priv->pending_requests)
642 ncsi_priv->pending_requests--;
643
644 if (len < sizeof(struct ncsi_rsp_pkt_hdr)) {
645 printf("NCSI: undersized packet: %u bytes\n", len);
646 goto out;
647 }
648
649 if (nh->common.type == NCSI_PKT_AEN)
650 return ncsi_handle_aen(ip, len);
651
652 switch (nh->common.type) {
653 case NCSI_PKT_RSP_SP:
654 payload = 4;
655 handler = ncsi_rsp_sp;
656 break;
657 case NCSI_PKT_RSP_DP:
658 payload = 4;
659 handler = ncsi_rsp_dp;
660 break;
661 case NCSI_PKT_RSP_CIS:
662 payload = 4;
663 handler = ncsi_rsp_cis;
664 break;
665 case NCSI_PKT_RSP_GLS:
666 payload = 16;
667 handler = ncsi_rsp_gls;
668 break;
669 case NCSI_PKT_RSP_GVI:
670 payload = 40;
671 handler = ncsi_rsp_gvi;
672 break;
673 case NCSI_PKT_RSP_GC:
674 payload = 32;
675 handler = ncsi_rsp_gc;
676 break;
677 case NCSI_PKT_RSP_SMA:
678 payload = 4;
679 handler = ncsi_rsp_sma;
680 break;
681 case NCSI_PKT_RSP_EBF:
682 payload = 4;
683 handler = ncsi_rsp_ebf;
684 break;
685 case NCSI_PKT_RSP_ECNT:
686 payload = 4;
687 handler = ncsi_rsp_ecnt;
688 break;
689 case NCSI_PKT_RSP_EC:
690 payload = 4;
691 handler = ncsi_rsp_ec;
692 break;
693 case NCSI_PKT_RSP_AE:
694 payload = 4;
695 handler = NULL;
696 break;
697 default:
698 printf("NCSI: unsupported packet type 0x%02x\n",
699 nh->common.type);
700 goto out;
701 }
702
703 if (ncsi_validate_rsp(pkt, payload) != 0) {
704 printf("NCSI: discarding invalid packet of type 0x%02x\n",
705 nh->common.type);
706 goto out;
707 }
708
709 if (handler)
710 handler(pkt);
711out:
712 ncsi_update_state(nh);
713}
714
715static void ncsi_send_sp(unsigned int np)
716{
717 uchar payload[4] = {0};
718
719 ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP,
720 (unsigned char *)&payload,
721 cmd_payload(NCSI_PKT_CMD_SP), true);
722}
723
724static void ncsi_send_dp(unsigned int np)
725{
726 ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0,
727 true);
728}
729
730static void ncsi_send_gls(unsigned int np, unsigned int nc)
731{
732 ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true);
733}
734
735static void ncsi_send_cis(unsigned int np, unsigned int nc)
736{
737 ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true);
738}
739
740static void ncsi_send_ae(unsigned int np, unsigned int nc)
741{
742 struct ncsi_cmd_ae_pkt cmd;
743
744 memset(&cmd, 0, sizeof(cmd));
745 cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen);
746
747 ncsi_send_command(np, nc, NCSI_PKT_CMD_AE,
748 ((unsigned char *)&cmd)
749 + sizeof(struct ncsi_cmd_pkt_hdr),
750 cmd_payload(NCSI_PKT_CMD_AE), true);
751}
752
753static void ncsi_send_ebf(unsigned int np, unsigned int nc)
754{
755 struct ncsi_cmd_ebf_pkt cmd;
756
757 memset(&cmd, 0, sizeof(cmd));
758 cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc);
759
760 ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF,
761 ((unsigned char *)&cmd)
762 + sizeof(struct ncsi_cmd_pkt_hdr),
763 cmd_payload(NCSI_PKT_CMD_EBF), true);
764}
765
766static void ncsi_send_sma(unsigned int np, unsigned int nc)
767{
768 struct ncsi_cmd_sma_pkt cmd;
769 unsigned char *addr, i;
770
771 addr = eth_get_ethaddr();
772 if (!addr) {
773 printf("NCSI: no MAC address configured\n");
774 return;
775 }
776
777 memset(&cmd, 0, sizeof(cmd));
778 for (i = 0; i < ARP_HLEN; i++)
779 cmd.mac[i] = addr[i];
780 cmd.index = 1;
781 cmd.at_e = 1;
782
783 ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA,
784 ((unsigned char *)&cmd)
785 + sizeof(struct ncsi_cmd_pkt_hdr),
786 cmd_payload(NCSI_PKT_CMD_SMA), true);
787}
788
789void ncsi_probe_packages(void)
790{
791 struct ncsi_package *package;
792 unsigned int np, nc;
793
794 switch (ncsi_priv->state) {
795 case NCSI_PROBE_PACKAGE_SP:
796 if (ncsi_priv->current_package == NCSI_PACKAGE_MAX)
797 ncsi_priv->current_package = 0;
798 ncsi_send_sp(ncsi_priv->current_package);
799 break;
800 case NCSI_PROBE_PACKAGE_DP:
801 ncsi_send_dp(ncsi_priv->current_package);
802 break;
803 case NCSI_PROBE_CHANNEL_SP:
804 if (ncsi_priv->n_packages > 0)
805 ncsi_send_sp(ncsi_priv->current_package);
806 else
807 printf("NCSI: no packages discovered, configuration not possible\n");
808 break;
809 case NCSI_PROBE_CHANNEL:
810 /* Kicks off chain of channel discovery */
811 ncsi_send_cis(ncsi_priv->current_package,
812 ncsi_priv->current_channel);
813 break;
814 case NCSI_CONFIG:
815 for (np = 0; np < ncsi_priv->n_packages; np++) {
816 package = &ncsi_priv->packages[np];
817 for (nc = 0; nc < package->n_channels; nc++)
818 if (package->channels[nc].has_link)
819 break;
820 if (nc < package->n_channels)
821 break;
822 }
823 if (np == ncsi_priv->n_packages) {
824 printf("NCSI: no link available\n");
825 return;
826 }
827
828 printf("NCSI: configuring channel %d\n", nc);
829 ncsi_priv->current_package = np;
830 ncsi_priv->current_channel = nc;
831 /* Kicks off rest of configure chain */
832 ncsi_send_sma(np, nc);
833 break;
834 default:
835 printf("NCSI: unknown state 0x%x\n", ncsi_priv->state);
836 }
837}
838
839int ncsi_probe(struct phy_device *phydev)
840{
841 if (!phydev->priv) {
842 phydev->priv = malloc(sizeof(struct ncsi));
843 if (!phydev->priv)
844 return -ENOMEM;
845 memset(phydev->priv, 0, sizeof(struct ncsi));
846 }
847
848 ncsi_priv = phydev->priv;
849
850 return 0;
851}
852
853int ncsi_startup(struct phy_device *phydev)
854{
855 /* Set phydev parameters */
856 phydev->speed = SPEED_100;
857 phydev->duplex = DUPLEX_FULL;
858 /* Normal phy reset is N/A */
859 phydev->flags |= PHY_FLAG_BROKEN_RESET;
860
861 /* Set initial probe state */
862 ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
863
864 /* No active package/channel yet */
865 ncsi_priv->current_package = NCSI_PACKAGE_MAX;
866 ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
867
868 /* Pretend link works so the MAC driver sets final bits up */
869 phydev->link = true;
870
871 /* Set ncsi_priv so we can use it when called from net_loop() */
872 ncsi_priv = phydev->priv;
873
874 return 0;
875}
876
877int ncsi_shutdown(struct phy_device *phydev)
878{
879 printf("NCSI: Disabling package %d\n", ncsi_priv->current_package);
880 ncsi_send_dp(ncsi_priv->current_package);
881 return 0;
882}
883
Marek Vasut43323a72023-03-19 18:03:10 +0100884U_BOOT_PHY_DRIVER(ncsi) = {
Samuel Mendoza-Jonas2325c442019-06-18 11:37:17 +1000885 .uid = PHY_NCSI_ID,
886 .mask = 0xffffffff,
887 .name = "NC-SI",
888 .features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES |
889 SUPPORTED_100baseT_Full | SUPPORTED_MII,
890 .probe = ncsi_probe,
891 .startup = ncsi_startup,
892 .shutdown = ncsi_shutdown,
893};