Merge branch 'for-2023.07' of https://source.denx.de/u-boot/custodians/u-boot-mpc8xx
[platform/kernel/u-boot.git] / drivers / net / phy / ncsi.c
1 // 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>
9 #include <log.h>
10 #include <malloc.h>
11 #include <phy.h>
12 #include <net.h>
13 #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
34 static void ncsi_send_ebf(unsigned int np, unsigned int nc);
35 static void ncsi_send_ae(unsigned int np, unsigned int nc);
36 static void ncsi_send_gls(unsigned int np, unsigned int nc);
37 static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
38                              uchar *payload, int len, bool wait);
39
40 struct 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
64 struct ncsi_package {
65         unsigned int            id;
66         unsigned int            n_channels;
67         struct ncsi_channel     *channels;
68 };
69
70 struct 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
90 struct ncsi *ncsi_priv;
91
92 bool 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
109 static 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
172 static 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
184 static 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
225 static 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
238 static 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
249 static 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
260 static 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
271 static 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
298 static 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
328 static 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
351 static 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
393 static 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
405 static 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
434 static 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
511 static 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
519 static 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
571 static 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
633 void 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);
711 out:
712         ncsi_update_state(nh);
713 }
714
715 static 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
724 static 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
730 static 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
735 static 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
740 static 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
753 static 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
766 static 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
789 void 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
839 int 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
853 int 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
877 int 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
884 U_BOOT_PHY_DRIVER(ncsi) = {
885         .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 };