xref: /openbmc/u-boot/drivers/net/phy/ncsi.c (revision 1bc678330e3cfea0062d347cafc2726daf49f221)
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