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