19ab23354SDylan Hung // SPDX-License-Identifier: GPL-2.0+
29ab23354SDylan Hung /*
39ab23354SDylan Hung * NC-SI protocol configuration
49ab23354SDylan Hung *
59ab23354SDylan Hung * Copyright (C) 2019, IBM Corporation.
69ab23354SDylan Hung */
79ab23354SDylan Hung
89ab23354SDylan Hung #include <common.h>
99ab23354SDylan Hung #include <malloc.h>
109ab23354SDylan Hung #include <phy.h>
119ab23354SDylan Hung #include <net/ncsi.h>
129ab23354SDylan Hung #include <net/ncsi-pkt.h>
139ab23354SDylan Hung #include <asm/unaligned.h>
149ab23354SDylan Hung
159ab23354SDylan Hung #define NCSI_PACKAGE_MAX 8
169ab23354SDylan Hung #define NCSI_CHANNEL_MAX 31
179ab23354SDylan Hung
189ab23354SDylan Hung #define NCSI_PACKAGE_SHIFT 5
199ab23354SDylan Hung #define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
209ab23354SDylan Hung #define NCSI_RESERVED_CHANNEL 0x1f
219ab23354SDylan Hung #define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
229ab23354SDylan Hung #define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
239ab23354SDylan Hung
249ab23354SDylan Hung #define NCSI_PKT_REVISION 0x01
259ab23354SDylan Hung
269ab23354SDylan Hung #define NCSI_CAP_GENERIC_MASK 0x7f
279ab23354SDylan Hung #define NCSI_CAP_BC_MASK 0x0f
289ab23354SDylan Hung #define NCSI_CAP_MC_MASK 0x3f
299ab23354SDylan Hung #define NCSI_CAP_AEN_MASK 0x07
309ab23354SDylan Hung #define NCSI_CAP_VLAN_MASK 0x07
319ab23354SDylan Hung
329ab23354SDylan Hung static void ncsi_send_ebf(unsigned int np, unsigned int nc);
339ab23354SDylan Hung static void ncsi_send_ae(unsigned int np, unsigned int nc);
349ab23354SDylan Hung static void ncsi_send_gls(unsigned int np, unsigned int nc);
359ab23354SDylan Hung static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
369ab23354SDylan Hung uchar *payload, int len, bool wait);
379ab23354SDylan Hung
389ab23354SDylan Hung struct ncsi_channel {
399ab23354SDylan Hung unsigned int id;
409ab23354SDylan Hung bool has_link;
419ab23354SDylan Hung
429ab23354SDylan Hung /* capabilities */
439ab23354SDylan Hung u32 cap_generic;
449ab23354SDylan Hung u32 cap_bc;
459ab23354SDylan Hung u32 cap_mc;
469ab23354SDylan Hung u32 cap_buffer;
479ab23354SDylan Hung u32 cap_aen;
489ab23354SDylan Hung u32 cap_vlan;
499ab23354SDylan Hung
509ab23354SDylan Hung /* version information */
519ab23354SDylan Hung struct {
529ab23354SDylan Hung u32 version; /* Supported BCD encoded NCSI version */
539ab23354SDylan Hung u32 alpha2; /* Supported BCD encoded NCSI version */
549ab23354SDylan Hung u8 fw_name[12]; /* Firmware name string */
559ab23354SDylan Hung u32 fw_version; /* Firmware version */
569ab23354SDylan Hung u16 pci_ids[4]; /* PCI identification */
579ab23354SDylan Hung u32 mf_id; /* Manufacture ID */
589ab23354SDylan Hung } version;
599ab23354SDylan Hung
609ab23354SDylan Hung };
619ab23354SDylan Hung
629ab23354SDylan Hung struct ncsi_package {
639ab23354SDylan Hung unsigned int id;
649ab23354SDylan Hung unsigned int n_channels;
659ab23354SDylan Hung struct ncsi_channel *channels;
669ab23354SDylan Hung };
679ab23354SDylan Hung
689ab23354SDylan Hung struct ncsi {
699ab23354SDylan Hung enum {
709ab23354SDylan Hung NCSI_PROBE_PACKAGE_SP,
719ab23354SDylan Hung NCSI_PROBE_PACKAGE_DP,
729ab23354SDylan Hung NCSI_PROBE_CHANNEL_SP,
739ab23354SDylan Hung NCSI_PROBE_CHANNEL,
749ab23354SDylan Hung NCSI_CONFIG,
759ab23354SDylan Hung } state;
769ab23354SDylan Hung
779ab23354SDylan Hung unsigned int pending_requests;
789ab23354SDylan Hung unsigned int requests[256];
799ab23354SDylan Hung unsigned int last_request;
809ab23354SDylan Hung
819ab23354SDylan Hung unsigned int current_package;
829ab23354SDylan Hung unsigned int current_channel;
839ab23354SDylan Hung
849ab23354SDylan Hung unsigned int n_packages;
859ab23354SDylan Hung struct ncsi_package *packages;
869ab23354SDylan Hung };
879ab23354SDylan Hung
889ab23354SDylan Hung struct ncsi *ncsi_priv;
899ab23354SDylan Hung
ncsi_active(void)909ab23354SDylan Hung bool ncsi_active(void)
919ab23354SDylan Hung {
929ab23354SDylan Hung unsigned int np, nc;
939ab23354SDylan Hung
949ab23354SDylan Hung if (!ncsi_priv)
959ab23354SDylan Hung return false;
969ab23354SDylan Hung
979ab23354SDylan Hung np = ncsi_priv->current_package;
989ab23354SDylan Hung nc = ncsi_priv->current_channel;
999ab23354SDylan Hung
1009ab23354SDylan Hung if (ncsi_priv->state != NCSI_CONFIG)
1019ab23354SDylan Hung return false;
1029ab23354SDylan Hung
1039ab23354SDylan Hung return np < NCSI_PACKAGE_MAX && nc < NCSI_CHANNEL_MAX &&
1049ab23354SDylan Hung ncsi_priv->packages[np].channels[nc].has_link;
1059ab23354SDylan Hung }
1069ab23354SDylan Hung
cmd_payload(int cmd)1079ab23354SDylan Hung static unsigned int cmd_payload(int cmd)
1089ab23354SDylan Hung {
1099ab23354SDylan Hung switch (cmd) {
1109ab23354SDylan Hung case NCSI_PKT_CMD_CIS:
1119ab23354SDylan Hung return 0;
1129ab23354SDylan Hung case NCSI_PKT_CMD_SP:
1139ab23354SDylan Hung return 4;
1149ab23354SDylan Hung case NCSI_PKT_CMD_DP:
1159ab23354SDylan Hung return 0;
1169ab23354SDylan Hung case NCSI_PKT_CMD_EC:
1179ab23354SDylan Hung return 0;
1189ab23354SDylan Hung case NCSI_PKT_CMD_DC:
1199ab23354SDylan Hung return 4;
1209ab23354SDylan Hung case NCSI_PKT_CMD_RC:
1219ab23354SDylan Hung return 4;
1229ab23354SDylan Hung case NCSI_PKT_CMD_ECNT:
1239ab23354SDylan Hung return 0;
1249ab23354SDylan Hung case NCSI_PKT_CMD_DCNT:
1259ab23354SDylan Hung return 0;
1269ab23354SDylan Hung case NCSI_PKT_CMD_AE:
1279ab23354SDylan Hung return 8;
1289ab23354SDylan Hung case NCSI_PKT_CMD_SL:
1299ab23354SDylan Hung return 8;
1309ab23354SDylan Hung case NCSI_PKT_CMD_GLS:
1319ab23354SDylan Hung return 0;
1329ab23354SDylan Hung case NCSI_PKT_CMD_SVF:
1339ab23354SDylan Hung return 8;
1349ab23354SDylan Hung case NCSI_PKT_CMD_EV:
1359ab23354SDylan Hung return 4;
1369ab23354SDylan Hung case NCSI_PKT_CMD_DV:
1379ab23354SDylan Hung return 0;
1389ab23354SDylan Hung case NCSI_PKT_CMD_SMA:
1399ab23354SDylan Hung return 8;
1409ab23354SDylan Hung case NCSI_PKT_CMD_EBF:
1419ab23354SDylan Hung return 4;
1429ab23354SDylan Hung case NCSI_PKT_CMD_DBF:
1439ab23354SDylan Hung return 0;
1449ab23354SDylan Hung case NCSI_PKT_CMD_EGMF:
1459ab23354SDylan Hung return 4;
1469ab23354SDylan Hung case NCSI_PKT_CMD_DGMF:
1479ab23354SDylan Hung return 0;
1489ab23354SDylan Hung case NCSI_PKT_CMD_SNFC:
1499ab23354SDylan Hung return 4;
1509ab23354SDylan Hung case NCSI_PKT_CMD_GVI:
1519ab23354SDylan Hung return 0;
1529ab23354SDylan Hung case NCSI_PKT_CMD_GC:
1539ab23354SDylan Hung return 0;
1549ab23354SDylan Hung case NCSI_PKT_CMD_GP:
1559ab23354SDylan Hung return 0;
1569ab23354SDylan Hung case NCSI_PKT_CMD_GCPS:
1579ab23354SDylan Hung return 0;
1589ab23354SDylan Hung case NCSI_PKT_CMD_GNS:
1599ab23354SDylan Hung return 0;
1609ab23354SDylan Hung case NCSI_PKT_CMD_GNPTS:
1619ab23354SDylan Hung return 0;
1629ab23354SDylan Hung case NCSI_PKT_CMD_GPS:
1639ab23354SDylan Hung return 0;
1649ab23354SDylan Hung default:
1659ab23354SDylan Hung printf("NCSI: Unknown command 0x%02x\n", cmd);
1669ab23354SDylan Hung return 0;
1679ab23354SDylan Hung }
1689ab23354SDylan Hung }
1699ab23354SDylan Hung
ncsi_calculate_checksum(unsigned char * data,int len)1709ab23354SDylan Hung static u32 ncsi_calculate_checksum(unsigned char *data, int len)
1719ab23354SDylan Hung {
1729ab23354SDylan Hung u32 checksum = 0;
1739ab23354SDylan Hung int i;
1749ab23354SDylan Hung
1759ab23354SDylan Hung for (i = 0; i < len; i += 2)
1769ab23354SDylan Hung checksum += (((u32)data[i] << 8) | data[i + 1]);
1779ab23354SDylan Hung
1789ab23354SDylan Hung checksum = (~checksum + 1);
1799ab23354SDylan Hung return checksum;
1809ab23354SDylan Hung }
1819ab23354SDylan Hung
ncsi_validate_rsp(struct ncsi_rsp_pkt * pkt,int payload)1829ab23354SDylan Hung static int ncsi_validate_rsp(struct ncsi_rsp_pkt *pkt, int payload)
1839ab23354SDylan Hung {
1849ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *hdr = &pkt->rsp;
1859ab23354SDylan Hung u32 checksum, c_offset;
1869ab23354SDylan Hung __be32 pchecksum;
1879ab23354SDylan Hung
1889ab23354SDylan Hung if (hdr->common.revision != 1) {
1899ab23354SDylan Hung printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
1909ab23354SDylan Hung hdr->common.type, hdr->common.revision);
1919ab23354SDylan Hung return -1;
1929ab23354SDylan Hung }
1939ab23354SDylan Hung
1949ab23354SDylan Hung if (hdr->code != 0) {
1959ab23354SDylan Hung printf("NCSI: 0x%02x response returns error %d\n",
1969ab23354SDylan Hung hdr->common.type, __be16_to_cpu(hdr->code));
1979ab23354SDylan Hung if (ntohs(hdr->reason) == 0x05)
1989ab23354SDylan Hung printf("(Invalid command length)\n");
1999ab23354SDylan Hung return -1;
2009ab23354SDylan Hung }
2019ab23354SDylan Hung
2029ab23354SDylan Hung if (ntohs(hdr->common.length) != payload) {
2039ab23354SDylan Hung printf("NCSI: 0x%02x response has incorrect length %d\n",
2049ab23354SDylan Hung hdr->common.type, hdr->common.length);
2059ab23354SDylan Hung return -1;
2069ab23354SDylan Hung }
2079ab23354SDylan Hung
2089ab23354SDylan Hung c_offset = sizeof(struct ncsi_rsp_pkt_hdr) + payload - sizeof(checksum);
2099ab23354SDylan Hung pchecksum = get_unaligned_be32((void *)hdr + c_offset);
2109ab23354SDylan Hung if (pchecksum != 0) {
2119ab23354SDylan Hung checksum = ncsi_calculate_checksum((unsigned char *)hdr,
2129ab23354SDylan Hung c_offset);
2139ab23354SDylan Hung if (pchecksum != checksum) {
2149ab23354SDylan Hung printf("NCSI: 0x%02x response has invalid checksum\n",
2159ab23354SDylan Hung hdr->common.type);
2169ab23354SDylan Hung return -1;
2179ab23354SDylan Hung }
2189ab23354SDylan Hung }
2199ab23354SDylan Hung
2209ab23354SDylan Hung return 0;
2219ab23354SDylan Hung }
2229ab23354SDylan Hung
ncsi_rsp_ec(struct ncsi_rsp_pkt * pkt)2239ab23354SDylan Hung static void ncsi_rsp_ec(struct ncsi_rsp_pkt *pkt)
2249ab23354SDylan Hung {
2259ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
2269ab23354SDylan Hung unsigned int np, nc;
2279ab23354SDylan Hung
2289ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
2299ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
2309ab23354SDylan Hung
2319ab23354SDylan Hung if (ncsi_priv->packages[np].channels[nc].cap_aen != 0)
2329ab23354SDylan Hung ncsi_send_ae(np, nc);
2339ab23354SDylan Hung /* else, done */
2349ab23354SDylan Hung }
2359ab23354SDylan Hung
ncsi_rsp_ecnt(struct ncsi_rsp_pkt * pkt)2369ab23354SDylan Hung static void ncsi_rsp_ecnt(struct ncsi_rsp_pkt *pkt)
2379ab23354SDylan Hung {
2389ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
2399ab23354SDylan Hung unsigned int np, nc;
2409ab23354SDylan Hung
2419ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
2429ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
2439ab23354SDylan Hung
2449ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_EC, NULL, 0, true);
2459ab23354SDylan Hung }
2469ab23354SDylan Hung
ncsi_rsp_ebf(struct ncsi_rsp_pkt * pkt)2479ab23354SDylan Hung static void ncsi_rsp_ebf(struct ncsi_rsp_pkt *pkt)
2489ab23354SDylan Hung {
2499ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
2509ab23354SDylan Hung unsigned int np, nc;
2519ab23354SDylan Hung
2529ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
2539ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
2549ab23354SDylan Hung
2559ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_ECNT, NULL, 0, true);
2569ab23354SDylan Hung }
2579ab23354SDylan Hung
ncsi_rsp_sma(struct ncsi_rsp_pkt * pkt)2589ab23354SDylan Hung static void ncsi_rsp_sma(struct ncsi_rsp_pkt *pkt)
2599ab23354SDylan Hung {
2609ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
2619ab23354SDylan Hung unsigned int np, nc;
2629ab23354SDylan Hung
2639ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
2649ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
2659ab23354SDylan Hung
2669ab23354SDylan Hung ncsi_send_ebf(np, nc);
2679ab23354SDylan Hung }
2689ab23354SDylan Hung
ncsi_rsp_gc(struct ncsi_rsp_pkt * pkt)2699ab23354SDylan Hung static void ncsi_rsp_gc(struct ncsi_rsp_pkt *pkt)
2709ab23354SDylan Hung {
2719ab23354SDylan Hung struct ncsi_rsp_gc_pkt *gc = (struct ncsi_rsp_gc_pkt *)pkt;
2729ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gc->rsp;
2739ab23354SDylan Hung struct ncsi_channel *c;
2749ab23354SDylan Hung unsigned int np, nc;
2759ab23354SDylan Hung
2769ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
2779ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
2789ab23354SDylan Hung
2799ab23354SDylan Hung if (np >= ncsi_priv->n_packages ||
2809ab23354SDylan Hung nc >= ncsi_priv->packages[np].n_channels) {
2819ab23354SDylan Hung printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
2829ab23354SDylan Hung np, nc);
2839ab23354SDylan Hung return;
2849ab23354SDylan Hung }
2859ab23354SDylan Hung
2869ab23354SDylan Hung c = &ncsi_priv->packages[np].channels[nc];
28770668ffdSDylan Hung c->cap_generic = ntohl(gc->cap) & NCSI_CAP_GENERIC_MASK;
28870668ffdSDylan Hung c->cap_bc = ntohl(gc->bc_cap) & NCSI_CAP_BC_MASK;
28970668ffdSDylan Hung c->cap_mc = ntohl(gc->mc_cap) & NCSI_CAP_MC_MASK;
29070668ffdSDylan Hung c->cap_aen = ntohl(gc->aen_cap) & NCSI_CAP_AEN_MASK;
29170668ffdSDylan Hung c->cap_vlan = ntohl(gc->vlan_mode) & NCSI_CAP_VLAN_MASK;
2929ab23354SDylan Hung
2939ab23354SDylan Hung /* End of probe for this channel */
2949ab23354SDylan Hung }
2959ab23354SDylan Hung
ncsi_rsp_gvi(struct ncsi_rsp_pkt * pkt)2969ab23354SDylan Hung static void ncsi_rsp_gvi(struct ncsi_rsp_pkt *pkt)
2979ab23354SDylan Hung {
2989ab23354SDylan Hung struct ncsi_rsp_gvi_pkt *gvi = (struct ncsi_rsp_gvi_pkt *)pkt;
2999ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gvi->rsp;
3009ab23354SDylan Hung struct ncsi_channel *c;
3019ab23354SDylan Hung unsigned int np, nc, i;
3029ab23354SDylan Hung
3039ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
3049ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
3059ab23354SDylan Hung
3069ab23354SDylan Hung if (np >= ncsi_priv->n_packages ||
3079ab23354SDylan Hung nc >= ncsi_priv->packages[np].n_channels) {
3089ab23354SDylan Hung printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
3099ab23354SDylan Hung np, nc);
3109ab23354SDylan Hung return;
3119ab23354SDylan Hung }
3129ab23354SDylan Hung
3139ab23354SDylan Hung c = &ncsi_priv->packages[np].channels[nc];
3149ab23354SDylan Hung c->version.version = get_unaligned_be32(&gvi->ncsi_version);
3159ab23354SDylan Hung c->version.alpha2 = gvi->alpha2;
3169ab23354SDylan Hung memcpy(c->version.fw_name, gvi->fw_name, sizeof(c->version.fw_name));
3179ab23354SDylan Hung c->version.fw_version = get_unaligned_be32(&gvi->fw_version);
3189ab23354SDylan Hung for (i = 0; i < ARRAY_SIZE(c->version.pci_ids); i++)
3199ab23354SDylan Hung c->version.pci_ids[i] = get_unaligned_be16(gvi->pci_ids + i);
3209ab23354SDylan Hung c->version.mf_id = get_unaligned_be32(&gvi->mf_id);
3219ab23354SDylan Hung
3229ab23354SDylan Hung if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
3239ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_GC, NULL, 0, true);
3249ab23354SDylan Hung }
3259ab23354SDylan Hung
ncsi_rsp_gls(struct ncsi_rsp_pkt * pkt)3269ab23354SDylan Hung static void ncsi_rsp_gls(struct ncsi_rsp_pkt *pkt)
3279ab23354SDylan Hung {
3289ab23354SDylan Hung struct ncsi_rsp_gls_pkt *gls = (struct ncsi_rsp_gls_pkt *)pkt;
3299ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gls->rsp;
3309ab23354SDylan Hung unsigned int np, nc;
3319ab23354SDylan Hung
3329ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
3339ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
3349ab23354SDylan Hung
3359ab23354SDylan Hung if (np >= ncsi_priv->n_packages ||
3369ab23354SDylan Hung nc >= ncsi_priv->packages[np].n_channels) {
3379ab23354SDylan Hung printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
3389ab23354SDylan Hung np, nc);
3399ab23354SDylan Hung return;
3409ab23354SDylan Hung }
3419ab23354SDylan Hung
3429ab23354SDylan Hung ncsi_priv->packages[np].channels[nc].has_link =
3439ab23354SDylan Hung !!(get_unaligned_be32(&gls->status));
3449ab23354SDylan Hung
3459ab23354SDylan Hung if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
3469ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_GVI, NULL, 0, true);
3479ab23354SDylan Hung }
3489ab23354SDylan Hung
ncsi_rsp_cis(struct ncsi_rsp_pkt * pkt)3499ab23354SDylan Hung static void ncsi_rsp_cis(struct ncsi_rsp_pkt *pkt)
3509ab23354SDylan Hung {
3519ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
3529ab23354SDylan Hung struct ncsi_package *package;
3539ab23354SDylan Hung unsigned int np, nc;
3549ab23354SDylan Hung
3559ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
3569ab23354SDylan Hung nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
3579ab23354SDylan Hung
3589ab23354SDylan Hung if (np >= ncsi_priv->n_packages) {
3599ab23354SDylan Hung printf("NCSI: Mystery package 0x%02x from CIS\n", np);
3609ab23354SDylan Hung return;
3619ab23354SDylan Hung }
3629ab23354SDylan Hung
3639ab23354SDylan Hung package = &ncsi_priv->packages[np];
3649ab23354SDylan Hung
3659ab23354SDylan Hung if (nc < package->n_channels) {
3669ab23354SDylan Hung /*
3679ab23354SDylan Hung * This is fine in general but in the current design we
3689ab23354SDylan Hung * don't send CIS commands to known channels.
3699ab23354SDylan Hung */
3709ab23354SDylan Hung debug("NCSI: Duplicate channel 0x%02x\n", nc);
3719ab23354SDylan Hung return;
3729ab23354SDylan Hung }
3739ab23354SDylan Hung
3749ab23354SDylan Hung package->channels = realloc(package->channels,
3759ab23354SDylan Hung sizeof(struct ncsi_channel) *
3769ab23354SDylan Hung (package->n_channels + 1));
3779ab23354SDylan Hung if (!package->channels) {
3789ab23354SDylan Hung printf("NCSI: Could not allocate memory for new channel\n");
3799ab23354SDylan Hung return;
3809ab23354SDylan Hung }
3819ab23354SDylan Hung
3829ab23354SDylan Hung debug("NCSI: New channel 0x%02x\n", nc);
3839ab23354SDylan Hung
3849ab23354SDylan Hung package->channels[nc].id = nc;
3859ab23354SDylan Hung package->channels[nc].has_link = false;
3869ab23354SDylan Hung package->n_channels++;
3879ab23354SDylan Hung
3889ab23354SDylan Hung ncsi_send_gls(np, nc);
3899ab23354SDylan Hung }
3909ab23354SDylan Hung
ncsi_rsp_dp(struct ncsi_rsp_pkt * pkt)3919ab23354SDylan Hung static void ncsi_rsp_dp(struct ncsi_rsp_pkt *pkt)
3929ab23354SDylan Hung {
3939ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
3949ab23354SDylan Hung unsigned int np;
3959ab23354SDylan Hung
3969ab23354SDylan Hung /* No action needed */
3979ab23354SDylan Hung
3989ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
3999ab23354SDylan Hung if (np >= ncsi_priv->n_packages)
4009ab23354SDylan Hung debug("NCSI: DP response from unknown package %d\n", np);
4019ab23354SDylan Hung }
4029ab23354SDylan Hung
ncsi_rsp_sp(struct ncsi_rsp_pkt * pkt)4039ab23354SDylan Hung static void ncsi_rsp_sp(struct ncsi_rsp_pkt *pkt)
4049ab23354SDylan Hung {
4059ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
4069ab23354SDylan Hung unsigned int np;
4079ab23354SDylan Hung
4089ab23354SDylan Hung np = NCSI_PACKAGE_INDEX(rsp->common.channel);
4099ab23354SDylan Hung
4109ab23354SDylan Hung if (np < ncsi_priv->n_packages) {
4119ab23354SDylan Hung /* Already know about this package */
4129ab23354SDylan Hung debug("NCSI: package 0x%02x selected\n", np);
4139ab23354SDylan Hung return;
4149ab23354SDylan Hung }
4159ab23354SDylan Hung
4169ab23354SDylan Hung debug("NCSI: adding new package %d\n", np);
4179ab23354SDylan Hung
4189ab23354SDylan Hung ncsi_priv->packages = realloc(ncsi_priv->packages,
4199ab23354SDylan Hung sizeof(struct ncsi_package) *
4209ab23354SDylan Hung (ncsi_priv->n_packages + 1));
4219ab23354SDylan Hung if (!ncsi_priv->packages) {
4229ab23354SDylan Hung printf("NCSI: could not allocate memory for new package\n");
4239ab23354SDylan Hung return;
4249ab23354SDylan Hung }
4259ab23354SDylan Hung
4269ab23354SDylan Hung ncsi_priv->packages[np].id = np;
4279ab23354SDylan Hung ncsi_priv->packages[np].n_channels = 0;
4289ab23354SDylan Hung ncsi_priv->packages[np].channels = NULL;
4299ab23354SDylan Hung ncsi_priv->n_packages++;
4309ab23354SDylan Hung }
4319ab23354SDylan Hung
ncsi_update_state(struct ncsi_rsp_pkt_hdr * nh)4329ab23354SDylan Hung static void ncsi_update_state(struct ncsi_rsp_pkt_hdr *nh)
4339ab23354SDylan Hung {
4349ab23354SDylan Hung bool timeout = !nh;
4359ab23354SDylan Hung int np, nc;
4369ab23354SDylan Hung
4379ab23354SDylan Hung switch (ncsi_priv->state) {
4389ab23354SDylan Hung case NCSI_PROBE_PACKAGE_SP:
4399ab23354SDylan Hung if (!timeout &&
4409ab23354SDylan Hung ncsi_priv->current_package + 1 < NCSI_PACKAGE_MAX) {
4419ab23354SDylan Hung ncsi_priv->current_package++;
4429ab23354SDylan Hung } else {
4439ab23354SDylan Hung ncsi_priv->state = NCSI_PROBE_PACKAGE_DP;
4449ab23354SDylan Hung ncsi_priv->current_package = 0;
4459ab23354SDylan Hung }
4469ab23354SDylan Hung return ncsi_probe_packages();
4479ab23354SDylan Hung case NCSI_PROBE_PACKAGE_DP:
4489ab23354SDylan Hung if (ncsi_priv->current_package + 1 < ncsi_priv->n_packages &&
4499ab23354SDylan Hung !timeout) {
4509ab23354SDylan Hung ncsi_priv->current_package++;
4519ab23354SDylan Hung } else {
4529ab23354SDylan Hung if (!ncsi_priv->n_packages) {
4539ab23354SDylan Hung printf("NCSI: no packages found\n");
4549ab23354SDylan Hung net_set_state(NETLOOP_FAIL);
4559ab23354SDylan Hung return;
4569ab23354SDylan Hung }
457*459611b1SJoel Stanley debug("NCSI: probing channels\n");
4589ab23354SDylan Hung ncsi_priv->state = NCSI_PROBE_CHANNEL_SP;
4599ab23354SDylan Hung ncsi_priv->current_package = 0;
4609ab23354SDylan Hung ncsi_priv->current_channel = 0;
4619ab23354SDylan Hung }
4629ab23354SDylan Hung return ncsi_probe_packages();
4639ab23354SDylan Hung case NCSI_PROBE_CHANNEL_SP:
4649ab23354SDylan Hung if (!timeout && nh->common.type == NCSI_PKT_RSP_SP) {
4659ab23354SDylan Hung ncsi_priv->state = NCSI_PROBE_CHANNEL;
4669ab23354SDylan Hung return ncsi_probe_packages();
4679ab23354SDylan Hung }
4689ab23354SDylan Hung printf("NCSI: failed to select package 0x%0x2 or timeout\n",
4699ab23354SDylan Hung ncsi_priv->current_package);
4709ab23354SDylan Hung net_set_state(NETLOOP_FAIL);
4719ab23354SDylan Hung break;
4729ab23354SDylan Hung case NCSI_PROBE_CHANNEL:
4739ab23354SDylan Hung // TODO only does package 0 for now
4749ab23354SDylan Hung if (ncsi_priv->pending_requests == 0) {
4759ab23354SDylan Hung np = ncsi_priv->current_package;
4769ab23354SDylan Hung nc = ncsi_priv->current_channel;
4779ab23354SDylan Hung
4789ab23354SDylan Hung /* Configure first channel that has link */
4799ab23354SDylan Hung if (ncsi_priv->packages[np].channels[nc].has_link) {
4809ab23354SDylan Hung ncsi_priv->state = NCSI_CONFIG;
4819ab23354SDylan Hung } else if (ncsi_priv->current_channel + 1 <
4829ab23354SDylan Hung NCSI_CHANNEL_MAX) {
4839ab23354SDylan Hung ncsi_priv->current_channel++;
4849ab23354SDylan Hung } else {
4859ab23354SDylan Hung // XXX As above only package 0
4869ab23354SDylan Hung printf("NCSI: no channel found with link\n");
4879ab23354SDylan Hung net_set_state(NETLOOP_FAIL);
4889ab23354SDylan Hung return;
4899ab23354SDylan Hung }
4909ab23354SDylan Hung return ncsi_probe_packages();
4919ab23354SDylan Hung }
4929ab23354SDylan Hung break;
4939ab23354SDylan Hung case NCSI_CONFIG:
4949ab23354SDylan Hung if (ncsi_priv->pending_requests == 0) {
495*459611b1SJoel Stanley debug("NCSI: configuration done!\n");
4969ab23354SDylan Hung net_set_state(NETLOOP_SUCCESS);
4979ab23354SDylan Hung } else if (timeout) {
4989ab23354SDylan Hung printf("NCSI: timeout during configure\n");
4999ab23354SDylan Hung net_set_state(NETLOOP_FAIL);
5009ab23354SDylan Hung }
5019ab23354SDylan Hung break;
5029ab23354SDylan Hung default:
5039ab23354SDylan Hung printf("NCSI: something went very wrong, nevermind\n");
5049ab23354SDylan Hung net_set_state(NETLOOP_FAIL);
5059ab23354SDylan Hung break;
5069ab23354SDylan Hung }
5079ab23354SDylan Hung }
5089ab23354SDylan Hung
ncsi_timeout_handler(void)5099ab23354SDylan Hung static void ncsi_timeout_handler(void)
5109ab23354SDylan Hung {
5119ab23354SDylan Hung if (ncsi_priv->pending_requests)
5129ab23354SDylan Hung ncsi_priv->pending_requests--;
5139ab23354SDylan Hung
5149ab23354SDylan Hung ncsi_update_state(NULL);
5159ab23354SDylan Hung }
5169ab23354SDylan Hung
ncsi_send_command(unsigned int np,unsigned int nc,unsigned int cmd,uchar * payload,int len,bool wait)5179ab23354SDylan Hung static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
5189ab23354SDylan Hung uchar *payload, int len, bool wait)
5199ab23354SDylan Hung {
5209ab23354SDylan Hung struct ncsi_pkt_hdr *hdr;
5219ab23354SDylan Hung __be32 *pchecksum;
5229ab23354SDylan Hung int eth_hdr_size;
5239ab23354SDylan Hung u32 checksum;
5249ab23354SDylan Hung uchar *pkt, *start;
5259ab23354SDylan Hung int final_len;
5269ab23354SDylan Hung
5279ab23354SDylan Hung pkt = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
5289ab23354SDylan Hung if (!pkt)
5299ab23354SDylan Hung return -ENOMEM;
5309ab23354SDylan Hung start = pkt;
5319ab23354SDylan Hung
5329ab23354SDylan Hung eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_NCSI);
5339ab23354SDylan Hung pkt += eth_hdr_size;
5349ab23354SDylan Hung
5359ab23354SDylan Hung /* Set NCSI command header fields */
5369ab23354SDylan Hung hdr = (struct ncsi_pkt_hdr *)pkt;
5379ab23354SDylan Hung hdr->mc_id = 0;
5389ab23354SDylan Hung hdr->revision = NCSI_PKT_REVISION;
5399ab23354SDylan Hung hdr->id = ++ncsi_priv->last_request;
5409ab23354SDylan Hung ncsi_priv->requests[ncsi_priv->last_request] = 1;
5419ab23354SDylan Hung hdr->type = cmd;
5429ab23354SDylan Hung hdr->channel = NCSI_TO_CHANNEL(np, nc);
5439ab23354SDylan Hung hdr->length = htons(len);
5449ab23354SDylan Hung
5459ab23354SDylan Hung if (payload && len)
5469ab23354SDylan Hung memcpy(pkt + sizeof(struct ncsi_pkt_hdr), payload, len);
5479ab23354SDylan Hung
5489ab23354SDylan Hung /* Calculate checksum */
5499ab23354SDylan Hung checksum = ncsi_calculate_checksum((unsigned char *)hdr,
5509ab23354SDylan Hung sizeof(*hdr) + len);
5519ab23354SDylan Hung pchecksum = (__be32 *)((void *)(hdr + 1) + len);
55285fe28f6SDylan Hung put_unaligned_be32(checksum, pchecksum);
5539ab23354SDylan Hung
5549ab23354SDylan Hung if (wait) {
5559ab23354SDylan Hung net_set_timeout_handler(1000UL, ncsi_timeout_handler);
5569ab23354SDylan Hung ncsi_priv->pending_requests++;
5579ab23354SDylan Hung }
5589ab23354SDylan Hung
5599ab23354SDylan Hung if (len < 26)
5609ab23354SDylan Hung len = 26;
5619ab23354SDylan Hung /* frame header, packet header, payload, checksum */
5629ab23354SDylan Hung final_len = eth_hdr_size + sizeof(struct ncsi_cmd_pkt_hdr) + len + 4;
5639ab23354SDylan Hung
5649ab23354SDylan Hung net_send_packet(start, final_len);
5659ab23354SDylan Hung free(start);
5669ab23354SDylan Hung return 0;
5679ab23354SDylan Hung }
5689ab23354SDylan Hung
ncsi_handle_aen(struct ip_udp_hdr * ip,unsigned int len)5699ab23354SDylan Hung static void ncsi_handle_aen(struct ip_udp_hdr *ip, unsigned int len)
5709ab23354SDylan Hung {
5719ab23354SDylan Hung struct ncsi_aen_pkt_hdr *hdr = (struct ncsi_aen_pkt_hdr *)ip;
5729ab23354SDylan Hung int payload, i;
5739ab23354SDylan Hung __be32 pchecksum;
5749ab23354SDylan Hung u32 checksum;
5759ab23354SDylan Hung
5769ab23354SDylan Hung switch (hdr->type) {
5779ab23354SDylan Hung case NCSI_PKT_AEN_LSC:
5789ab23354SDylan Hung printf("NCSI: link state changed\n");
5799ab23354SDylan Hung payload = 12;
5809ab23354SDylan Hung break;
5819ab23354SDylan Hung case NCSI_PKT_AEN_CR:
5829ab23354SDylan Hung printf("NCSI: re-configuration required\n");
5839ab23354SDylan Hung payload = 4;
5849ab23354SDylan Hung break;
5859ab23354SDylan Hung case NCSI_PKT_AEN_HNCDSC:
5869ab23354SDylan Hung /* Host notifcation - N/A but weird */
5879ab23354SDylan Hung debug("NCSI: HNCDSC AEN received\n");
5889ab23354SDylan Hung return;
5899ab23354SDylan Hung default:
5909ab23354SDylan Hung printf("%s: Invalid type 0x%02x\n", __func__, hdr->type);
5919ab23354SDylan Hung return;
5929ab23354SDylan Hung }
5939ab23354SDylan Hung
5949ab23354SDylan Hung /* Validate packet */
5959ab23354SDylan Hung if (hdr->common.revision != 1) {
5969ab23354SDylan Hung printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
5979ab23354SDylan Hung hdr->common.type, hdr->common.revision);
5989ab23354SDylan Hung return;
5999ab23354SDylan Hung }
6009ab23354SDylan Hung
6019ab23354SDylan Hung if (ntohs(hdr->common.length) != payload) {
6029ab23354SDylan Hung printf("NCSI: 0x%02x response has incorrect length %d\n",
6039ab23354SDylan Hung hdr->common.type, hdr->common.length);
6049ab23354SDylan Hung return;
6059ab23354SDylan Hung }
6069ab23354SDylan Hung
6079ab23354SDylan Hung pchecksum = get_unaligned_be32((void *)(hdr + 1) + payload - 4);
6089ab23354SDylan Hung if (pchecksum != 0) {
6099ab23354SDylan Hung checksum = ncsi_calculate_checksum((unsigned char *)hdr,
6109ab23354SDylan Hung sizeof(*hdr) + payload - 4);
6119ab23354SDylan Hung if (pchecksum != checksum) {
6129ab23354SDylan Hung printf("NCSI: 0x%02x response has invalid checksum\n",
6139ab23354SDylan Hung hdr->common.type);
6149ab23354SDylan Hung return;
6159ab23354SDylan Hung }
6169ab23354SDylan Hung }
6179ab23354SDylan Hung
6189ab23354SDylan Hung /* Link or configuration lost - just redo the discovery process */
6199ab23354SDylan Hung ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
6209ab23354SDylan Hung for (i = 0; i < ncsi_priv->n_packages; i++)
6219ab23354SDylan Hung free(ncsi_priv->packages[i].channels);
6229ab23354SDylan Hung free(ncsi_priv->packages);
6239ab23354SDylan Hung ncsi_priv->n_packages = 0;
6249ab23354SDylan Hung
6259ab23354SDylan Hung ncsi_priv->current_package = NCSI_PACKAGE_MAX;
6269ab23354SDylan Hung ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
6279ab23354SDylan Hung
6289ab23354SDylan Hung ncsi_probe_packages();
6299ab23354SDylan Hung }
6309ab23354SDylan Hung
ncsi_receive(struct ethernet_hdr * et,struct ip_udp_hdr * ip,unsigned int len)6319ab23354SDylan Hung void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
6329ab23354SDylan Hung unsigned int len)
6339ab23354SDylan Hung {
6349ab23354SDylan Hung struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip;
6359ab23354SDylan Hung struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
6369ab23354SDylan Hung void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL;
6379ab23354SDylan Hung unsigned short payload;
6389ab23354SDylan Hung
6399ab23354SDylan Hung if (ncsi_priv->pending_requests)
6409ab23354SDylan Hung ncsi_priv->pending_requests--;
6419ab23354SDylan Hung
6429ab23354SDylan Hung if (len < sizeof(struct ncsi_rsp_pkt_hdr)) {
6439ab23354SDylan Hung printf("NCSI: undersized packet: %u bytes\n", len);
6449ab23354SDylan Hung goto out;
6459ab23354SDylan Hung }
6469ab23354SDylan Hung
6479ab23354SDylan Hung if (nh->common.type == NCSI_PKT_AEN)
6489ab23354SDylan Hung return ncsi_handle_aen(ip, len);
6499ab23354SDylan Hung
6509ab23354SDylan Hung switch (nh->common.type) {
6519ab23354SDylan Hung case NCSI_PKT_RSP_SP:
6529ab23354SDylan Hung payload = 4;
6539ab23354SDylan Hung handler = ncsi_rsp_sp;
6549ab23354SDylan Hung break;
6559ab23354SDylan Hung case NCSI_PKT_RSP_DP:
6569ab23354SDylan Hung payload = 4;
6579ab23354SDylan Hung handler = ncsi_rsp_dp;
6589ab23354SDylan Hung break;
6599ab23354SDylan Hung case NCSI_PKT_RSP_CIS:
6609ab23354SDylan Hung payload = 4;
6619ab23354SDylan Hung handler = ncsi_rsp_cis;
6629ab23354SDylan Hung break;
6639ab23354SDylan Hung case NCSI_PKT_RSP_GLS:
6649ab23354SDylan Hung payload = 16;
6659ab23354SDylan Hung handler = ncsi_rsp_gls;
6669ab23354SDylan Hung break;
6679ab23354SDylan Hung case NCSI_PKT_RSP_GVI:
6689ab23354SDylan Hung payload = 40;
6699ab23354SDylan Hung handler = ncsi_rsp_gvi;
6709ab23354SDylan Hung break;
6719ab23354SDylan Hung case NCSI_PKT_RSP_GC:
6729ab23354SDylan Hung payload = 32;
6739ab23354SDylan Hung handler = ncsi_rsp_gc;
6749ab23354SDylan Hung break;
6759ab23354SDylan Hung case NCSI_PKT_RSP_SMA:
6769ab23354SDylan Hung payload = 4;
6779ab23354SDylan Hung handler = ncsi_rsp_sma;
6789ab23354SDylan Hung break;
6799ab23354SDylan Hung case NCSI_PKT_RSP_EBF:
6809ab23354SDylan Hung payload = 4;
6819ab23354SDylan Hung handler = ncsi_rsp_ebf;
6829ab23354SDylan Hung break;
6839ab23354SDylan Hung case NCSI_PKT_RSP_ECNT:
6849ab23354SDylan Hung payload = 4;
6859ab23354SDylan Hung handler = ncsi_rsp_ecnt;
6869ab23354SDylan Hung break;
6879ab23354SDylan Hung case NCSI_PKT_RSP_EC:
6889ab23354SDylan Hung payload = 4;
6899ab23354SDylan Hung handler = ncsi_rsp_ec;
6909ab23354SDylan Hung break;
6919ab23354SDylan Hung case NCSI_PKT_RSP_AE:
6929ab23354SDylan Hung payload = 4;
6939ab23354SDylan Hung handler = NULL;
6949ab23354SDylan Hung break;
6959ab23354SDylan Hung default:
6969ab23354SDylan Hung printf("NCSI: unsupported packet type 0x%02x\n",
6979ab23354SDylan Hung nh->common.type);
6989ab23354SDylan Hung goto out;
6999ab23354SDylan Hung }
7009ab23354SDylan Hung
7019ab23354SDylan Hung if (ncsi_validate_rsp(pkt, payload) != 0) {
7029ab23354SDylan Hung printf("NCSI: discarding invalid packet of type 0x%02x\n",
7039ab23354SDylan Hung nh->common.type);
7049ab23354SDylan Hung goto out;
7059ab23354SDylan Hung }
7069ab23354SDylan Hung
7079ab23354SDylan Hung if (handler)
7089ab23354SDylan Hung handler(pkt);
7099ab23354SDylan Hung out:
7109ab23354SDylan Hung ncsi_update_state(nh);
7119ab23354SDylan Hung }
7129ab23354SDylan Hung
ncsi_send_sp(unsigned int np)7139ab23354SDylan Hung static void ncsi_send_sp(unsigned int np)
7149ab23354SDylan Hung {
7159ab23354SDylan Hung uchar payload[4] = {0};
7169ab23354SDylan Hung
7179ab23354SDylan Hung ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP,
7189ab23354SDylan Hung (unsigned char *)&payload,
7199ab23354SDylan Hung cmd_payload(NCSI_PKT_CMD_SP), true);
7209ab23354SDylan Hung }
7219ab23354SDylan Hung
ncsi_send_dp(unsigned int np)7229ab23354SDylan Hung static void ncsi_send_dp(unsigned int np)
7239ab23354SDylan Hung {
7249ab23354SDylan Hung ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0,
7259ab23354SDylan Hung true);
7269ab23354SDylan Hung }
7279ab23354SDylan Hung
ncsi_send_gls(unsigned int np,unsigned int nc)7289ab23354SDylan Hung static void ncsi_send_gls(unsigned int np, unsigned int nc)
7299ab23354SDylan Hung {
7309ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true);
7319ab23354SDylan Hung }
7329ab23354SDylan Hung
ncsi_send_cis(unsigned int np,unsigned int nc)7339ab23354SDylan Hung static void ncsi_send_cis(unsigned int np, unsigned int nc)
7349ab23354SDylan Hung {
7359ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true);
7369ab23354SDylan Hung }
7379ab23354SDylan Hung
ncsi_send_ae(unsigned int np,unsigned int nc)7389ab23354SDylan Hung static void ncsi_send_ae(unsigned int np, unsigned int nc)
7399ab23354SDylan Hung {
7409ab23354SDylan Hung struct ncsi_cmd_ae_pkt cmd;
7419ab23354SDylan Hung
7429ab23354SDylan Hung memset(&cmd, 0, sizeof(cmd));
7439ab23354SDylan Hung cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen);
7449ab23354SDylan Hung
7459ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_AE,
7469ab23354SDylan Hung ((unsigned char *)&cmd)
7479ab23354SDylan Hung + sizeof(struct ncsi_cmd_pkt_hdr),
7489ab23354SDylan Hung cmd_payload(NCSI_PKT_CMD_AE), true);
7499ab23354SDylan Hung }
7509ab23354SDylan Hung
ncsi_send_ebf(unsigned int np,unsigned int nc)7519ab23354SDylan Hung static void ncsi_send_ebf(unsigned int np, unsigned int nc)
7529ab23354SDylan Hung {
7539ab23354SDylan Hung struct ncsi_cmd_ebf_pkt cmd;
7549ab23354SDylan Hung
7559ab23354SDylan Hung memset(&cmd, 0, sizeof(cmd));
7569ab23354SDylan Hung cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc);
7579ab23354SDylan Hung
7589ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF,
7599ab23354SDylan Hung ((unsigned char *)&cmd)
7609ab23354SDylan Hung + sizeof(struct ncsi_cmd_pkt_hdr),
7619ab23354SDylan Hung cmd_payload(NCSI_PKT_CMD_EBF), true);
7629ab23354SDylan Hung }
7639ab23354SDylan Hung
ncsi_send_sma(unsigned int np,unsigned int nc)7649ab23354SDylan Hung static void ncsi_send_sma(unsigned int np, unsigned int nc)
7659ab23354SDylan Hung {
7669ab23354SDylan Hung struct ncsi_cmd_sma_pkt cmd;
7679ab23354SDylan Hung unsigned char *addr, i;
7689ab23354SDylan Hung
7699ab23354SDylan Hung addr = eth_get_ethaddr();
7709ab23354SDylan Hung if (!addr) {
7719ab23354SDylan Hung printf("NCSI: no MAC address configured\n");
7729ab23354SDylan Hung return;
7739ab23354SDylan Hung }
7749ab23354SDylan Hung
7759ab23354SDylan Hung memset(&cmd, 0, sizeof(cmd));
7769ab23354SDylan Hung for (i = 0; i < ARP_HLEN; i++)
7779ab23354SDylan Hung cmd.mac[i] = addr[i];
7789ab23354SDylan Hung cmd.index = 1;
7799ab23354SDylan Hung cmd.at_e = 1;
7809ab23354SDylan Hung
7819ab23354SDylan Hung ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA,
7829ab23354SDylan Hung ((unsigned char *)&cmd)
7839ab23354SDylan Hung + sizeof(struct ncsi_cmd_pkt_hdr),
7849ab23354SDylan Hung cmd_payload(NCSI_PKT_CMD_SMA), true);
7859ab23354SDylan Hung }
7869ab23354SDylan Hung
ncsi_probe_packages(void)7879ab23354SDylan Hung void ncsi_probe_packages(void)
7889ab23354SDylan Hung {
7899ab23354SDylan Hung struct ncsi_package *package;
7909ab23354SDylan Hung unsigned int np, nc;
7919ab23354SDylan Hung
7929ab23354SDylan Hung switch (ncsi_priv->state) {
7939ab23354SDylan Hung case NCSI_PROBE_PACKAGE_SP:
7949ab23354SDylan Hung if (ncsi_priv->current_package == NCSI_PACKAGE_MAX)
7959ab23354SDylan Hung ncsi_priv->current_package = 0;
796*459611b1SJoel Stanley debug("%s: NCSI_PROBE_PACKAGE_SP current_package %d\n",
79754fa41afSJoel Stanley __func__, ncsi_priv->current_package);
7989ab23354SDylan Hung ncsi_send_sp(ncsi_priv->current_package);
7999ab23354SDylan Hung break;
8009ab23354SDylan Hung case NCSI_PROBE_PACKAGE_DP:
801*459611b1SJoel Stanley debug("%s: NCSI_PROBE_PACKAGE_DP current_package %d\n",
80254fa41afSJoel Stanley __func__, ncsi_priv->current_package);
8039ab23354SDylan Hung ncsi_send_dp(ncsi_priv->current_package);
8049ab23354SDylan Hung break;
8059ab23354SDylan Hung case NCSI_PROBE_CHANNEL_SP:
806*459611b1SJoel Stanley debug("%s: NCSI_PROBE_CHANNEL_SP\n", __func__);
8079ab23354SDylan Hung if (ncsi_priv->n_packages > 0)
8089ab23354SDylan Hung ncsi_send_sp(ncsi_priv->current_package);
8099ab23354SDylan Hung else
8109ab23354SDylan Hung printf("NCSI: no packages discovered, configuration not possible\n");
8119ab23354SDylan Hung break;
8129ab23354SDylan Hung case NCSI_PROBE_CHANNEL:
813*459611b1SJoel Stanley debug("%s NCSI_PROBE_CHANNEL package %d channel %d\n",
81454fa41afSJoel Stanley __func__,
81554fa41afSJoel Stanley ncsi_priv->current_package,
81654fa41afSJoel Stanley ncsi_priv->current_channel);
8179ab23354SDylan Hung /* Kicks off chain of channel discovery */
8189ab23354SDylan Hung ncsi_send_cis(ncsi_priv->current_package,
8199ab23354SDylan Hung ncsi_priv->current_channel);
8209ab23354SDylan Hung break;
8219ab23354SDylan Hung case NCSI_CONFIG:
8229ab23354SDylan Hung for (np = 0; np < ncsi_priv->n_packages; np++) {
8239ab23354SDylan Hung package = &ncsi_priv->packages[np];
8249ab23354SDylan Hung for (nc = 0; nc < package->n_channels; nc++)
8259ab23354SDylan Hung if (package->channels[nc].has_link)
8269ab23354SDylan Hung break;
8279ab23354SDylan Hung if (nc < package->n_channels)
8289ab23354SDylan Hung break;
8299ab23354SDylan Hung }
8309ab23354SDylan Hung if (np == ncsi_priv->n_packages) {
8319ab23354SDylan Hung printf("NCSI: no link available\n");
8329ab23354SDylan Hung return;
8339ab23354SDylan Hung }
8349ab23354SDylan Hung
835*459611b1SJoel Stanley debug("NCSI: configuring channel %d\n", nc);
8369ab23354SDylan Hung ncsi_priv->current_package = np;
8379ab23354SDylan Hung ncsi_priv->current_channel = nc;
8389ab23354SDylan Hung /* Kicks off rest of configure chain */
8399ab23354SDylan Hung ncsi_send_sma(np, nc);
8409ab23354SDylan Hung break;
8419ab23354SDylan Hung default:
8429ab23354SDylan Hung printf("NCSI: unknown state 0x%x\n", ncsi_priv->state);
8439ab23354SDylan Hung }
8449ab23354SDylan Hung }
8459ab23354SDylan Hung
ncsi_probe(struct phy_device * phydev)8469ab23354SDylan Hung int ncsi_probe(struct phy_device *phydev)
8479ab23354SDylan Hung {
8489ab23354SDylan Hung if (!phydev->priv) {
8499ab23354SDylan Hung phydev->priv = malloc(sizeof(struct ncsi));
8509ab23354SDylan Hung if (!phydev->priv)
8519ab23354SDylan Hung return -ENOMEM;
8529ab23354SDylan Hung memset(phydev->priv, 0, sizeof(struct ncsi));
8539ab23354SDylan Hung }
8549ab23354SDylan Hung
8559ab23354SDylan Hung ncsi_priv = phydev->priv;
8569ab23354SDylan Hung
8579ab23354SDylan Hung return 0;
8589ab23354SDylan Hung }
8599ab23354SDylan Hung
ncsi_startup(struct phy_device * phydev)8609ab23354SDylan Hung int ncsi_startup(struct phy_device *phydev)
8619ab23354SDylan Hung {
8629ab23354SDylan Hung /* Set phydev parameters */
8639ab23354SDylan Hung phydev->speed = SPEED_100;
8649ab23354SDylan Hung phydev->duplex = DUPLEX_FULL;
8659ab23354SDylan Hung /* Normal phy reset is N/A */
8669ab23354SDylan Hung phydev->flags |= PHY_FLAG_BROKEN_RESET;
8679ab23354SDylan Hung
8689ab23354SDylan Hung /* Set initial probe state */
8699ab23354SDylan Hung ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
8709ab23354SDylan Hung
8719ab23354SDylan Hung /* No active package/channel yet */
8729ab23354SDylan Hung ncsi_priv->current_package = NCSI_PACKAGE_MAX;
8739ab23354SDylan Hung ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
8749ab23354SDylan Hung
8759ab23354SDylan Hung /* Pretend link works so the MAC driver sets final bits up */
8769ab23354SDylan Hung phydev->link = true;
8779ab23354SDylan Hung
8789ab23354SDylan Hung /* Set ncsi_priv so we can use it when called from net_loop() */
8799ab23354SDylan Hung ncsi_priv = phydev->priv;
8809ab23354SDylan Hung
8819ab23354SDylan Hung return 0;
8829ab23354SDylan Hung }
8839ab23354SDylan Hung
ncsi_shutdown(struct phy_device * phydev)8849ab23354SDylan Hung int ncsi_shutdown(struct phy_device *phydev)
8859ab23354SDylan Hung {
8869ab23354SDylan Hung printf("NCSI: Disabling package %d\n", ncsi_priv->current_package);
8879ab23354SDylan Hung ncsi_send_dp(ncsi_priv->current_package);
8889ab23354SDylan Hung return 0;
8899ab23354SDylan Hung }
8909ab23354SDylan Hung
8919ab23354SDylan Hung static struct phy_driver ncsi_driver = {
8929ab23354SDylan Hung .uid = PHY_NCSI_ID,
8939ab23354SDylan Hung .mask = 0xffffffff,
8949ab23354SDylan Hung .name = "NC-SI",
8959ab23354SDylan Hung .features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES | SUPPORTED_100baseT_Full | SUPPORTED_MII,
8969ab23354SDylan Hung .probe = ncsi_probe,
8979ab23354SDylan Hung .startup = ncsi_startup,
8989ab23354SDylan Hung .shutdown = ncsi_shutdown,
8999ab23354SDylan Hung };
9009ab23354SDylan Hung
phy_ncsi_init(void)9019ab23354SDylan Hung int phy_ncsi_init(void)
9029ab23354SDylan Hung {
9039ab23354SDylan Hung phy_register(&ncsi_driver);
9049ab23354SDylan Hung return 0;
9059ab23354SDylan Hung }
906