u-boot/drivers/net/phy/ncsi.c
Jacky Chou cc09160f30 net: phy: ncsi: reslove the unaligned access issue
From the ethernet header is not on aligned, because the length
of the ethernet header is 14 bytes.
Therefore, unaligned access must be done here.

Signed-off-by: Jacky Chou <jacky_chou@aspeedtech.com>
2024-03-26 19:58:26 -04:00

896 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* NC-SI protocol configuration
*
* Copyright (C) 2019, IBM Corporation.
*/
#include <common.h>
#include <log.h>
#include <malloc.h>
#include <phy.h>
#include <net.h>
#include <net/ncsi.h>
#include <net/ncsi-pkt.h>
#include <asm/unaligned.h>
#define NCSI_PACKAGE_MAX 8
#define NCSI_CHANNEL_MAX 31
#define NCSI_PACKAGE_SHIFT 5
#define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
#define NCSI_RESERVED_CHANNEL 0x1f
#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
#define NCSI_PKT_REVISION 0x01
#define NCSI_CAP_GENERIC_MASK 0x7f
#define NCSI_CAP_BC_MASK 0x0f
#define NCSI_CAP_MC_MASK 0x3f
#define NCSI_CAP_AEN_MASK 0x07
#define NCSI_CAP_VLAN_MASK 0x07
static void ncsi_send_ebf(unsigned int np, unsigned int nc);
static void ncsi_send_ae(unsigned int np, unsigned int nc);
static void ncsi_send_gls(unsigned int np, unsigned int nc);
static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
uchar *payload, int len, bool wait);
struct ncsi_channel {
unsigned int id;
bool has_link;
/* capabilities */
u32 cap_generic;
u32 cap_bc;
u32 cap_mc;
u32 cap_buffer;
u32 cap_aen;
u32 cap_vlan;
/* version information */
struct {
u32 version; /* Supported BCD encoded NCSI version */
u32 alpha2; /* Supported BCD encoded NCSI version */
u8 fw_name[12]; /* Firmware name string */
u32 fw_version; /* Firmware version */
u16 pci_ids[4]; /* PCI identification */
u32 mf_id; /* Manufacture ID */
} version;
};
struct ncsi_package {
unsigned int id;
unsigned int n_channels;
struct ncsi_channel *channels;
};
struct ncsi {
enum {
NCSI_PROBE_PACKAGE_SP,
NCSI_PROBE_PACKAGE_DP,
NCSI_PROBE_CHANNEL_SP,
NCSI_PROBE_CHANNEL,
NCSI_CONFIG,
} state;
unsigned int pending_requests;
unsigned int requests[256];
unsigned int last_request;
unsigned int current_package;
unsigned int current_channel;
unsigned int n_packages;
struct ncsi_package *packages;
};
struct ncsi *ncsi_priv;
bool ncsi_active(void)
{
unsigned int np, nc;
if (!ncsi_priv)
return false;
np = ncsi_priv->current_package;
nc = ncsi_priv->current_channel;
if (ncsi_priv->state != NCSI_CONFIG)
return false;
return np < NCSI_PACKAGE_MAX && nc < NCSI_CHANNEL_MAX &&
ncsi_priv->packages[np].channels[nc].has_link;
}
static unsigned int cmd_payload(int cmd)
{
switch (cmd) {
case NCSI_PKT_CMD_CIS:
return 0;
case NCSI_PKT_CMD_SP:
return 4;
case NCSI_PKT_CMD_DP:
return 0;
case NCSI_PKT_CMD_EC:
return 0;
case NCSI_PKT_CMD_DC:
return 4;
case NCSI_PKT_CMD_RC:
return 4;
case NCSI_PKT_CMD_ECNT:
return 0;
case NCSI_PKT_CMD_DCNT:
return 0;
case NCSI_PKT_CMD_AE:
return 8;
case NCSI_PKT_CMD_SL:
return 8;
case NCSI_PKT_CMD_GLS:
return 0;
case NCSI_PKT_CMD_SVF:
return 8;
case NCSI_PKT_CMD_EV:
return 4;
case NCSI_PKT_CMD_DV:
return 0;
case NCSI_PKT_CMD_SMA:
return 8;
case NCSI_PKT_CMD_EBF:
return 4;
case NCSI_PKT_CMD_DBF:
return 0;
case NCSI_PKT_CMD_EGMF:
return 4;
case NCSI_PKT_CMD_DGMF:
return 0;
case NCSI_PKT_CMD_SNFC:
return 4;
case NCSI_PKT_CMD_GVI:
return 0;
case NCSI_PKT_CMD_GC:
return 0;
case NCSI_PKT_CMD_GP:
return 0;
case NCSI_PKT_CMD_GCPS:
return 0;
case NCSI_PKT_CMD_GNS:
return 0;
case NCSI_PKT_CMD_GNPTS:
return 0;
case NCSI_PKT_CMD_GPS:
return 0;
default:
printf("NCSI: Unknown command 0x%02x\n", cmd);
return 0;
}
}
static u32 ncsi_calculate_checksum(unsigned char *data, int len)
{
u32 checksum = 0;
int i;
for (i = 0; i < len; i += 2)
checksum += (((u32)data[i] << 8) | data[i + 1]);
checksum = (~checksum + 1);
return checksum;
}
static int ncsi_validate_rsp(struct ncsi_rsp_pkt *pkt, int payload)
{
struct ncsi_rsp_pkt_hdr *hdr = &pkt->rsp;
u32 checksum, c_offset;
__be32 pchecksum;
if (hdr->common.revision != 1) {
printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
hdr->common.type, hdr->common.revision);
return -1;
}
if (hdr->code != 0) {
printf("NCSI: 0x%02x response returns error %d\n",
hdr->common.type, __be16_to_cpu(hdr->code));
if (ntohs(hdr->reason) == 0x05)
printf("(Invalid command length)\n");
return -1;
}
if (ntohs(hdr->common.length) != payload) {
printf("NCSI: 0x%02x response has incorrect length %d\n",
hdr->common.type, hdr->common.length);
return -1;
}
c_offset = sizeof(struct ncsi_rsp_pkt_hdr) + payload - sizeof(checksum);
pchecksum = get_unaligned_be32((void *)hdr + c_offset);
if (pchecksum != 0) {
checksum = ncsi_calculate_checksum((unsigned char *)hdr,
c_offset);
if (pchecksum != checksum) {
printf("NCSI: 0x%02x response has invalid checksum\n",
hdr->common.type);
return -1;
}
}
return 0;
}
static void ncsi_rsp_ec(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
if (ncsi_priv->packages[np].channels[nc].cap_aen != 0)
ncsi_send_ae(np, nc);
/* else, done */
}
static void ncsi_rsp_ecnt(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
ncsi_send_command(np, nc, NCSI_PKT_CMD_EC, NULL, 0, true);
}
static void ncsi_rsp_ebf(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
ncsi_send_command(np, nc, NCSI_PKT_CMD_ECNT, NULL, 0, true);
}
static void ncsi_rsp_sma(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
ncsi_send_ebf(np, nc);
}
static void ncsi_rsp_gc(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_gc_pkt *gc = (struct ncsi_rsp_gc_pkt *)pkt;
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gc->rsp;
struct ncsi_channel *c;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
if (np >= ncsi_priv->n_packages ||
nc >= ncsi_priv->packages[np].n_channels) {
printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
np, nc);
return;
}
c = &ncsi_priv->packages[np].channels[nc];
c->cap_generic = get_unaligned_be32(&gc->cap) & NCSI_CAP_GENERIC_MASK;
c->cap_bc = get_unaligned_be32(&gc->bc_cap) & NCSI_CAP_BC_MASK;
c->cap_mc = get_unaligned_be32(&gc->mc_cap) & NCSI_CAP_MC_MASK;
c->cap_aen = get_unaligned_be32(&gc->aen_cap) & NCSI_CAP_AEN_MASK;
c->cap_vlan = gc->vlan_mode & NCSI_CAP_VLAN_MASK;
/* End of probe for this channel */
}
static void ncsi_rsp_gvi(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_gvi_pkt *gvi = (struct ncsi_rsp_gvi_pkt *)pkt;
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gvi->rsp;
struct ncsi_channel *c;
unsigned int np, nc, i;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
if (np >= ncsi_priv->n_packages ||
nc >= ncsi_priv->packages[np].n_channels) {
printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
np, nc);
return;
}
c = &ncsi_priv->packages[np].channels[nc];
c->version.version = get_unaligned_be32(&gvi->ncsi_version);
c->version.alpha2 = gvi->alpha2;
memcpy(c->version.fw_name, gvi->fw_name, sizeof(c->version.fw_name));
c->version.fw_version = get_unaligned_be32(&gvi->fw_version);
for (i = 0; i < ARRAY_SIZE(c->version.pci_ids); i++)
c->version.pci_ids[i] = get_unaligned_be16(gvi->pci_ids + i);
c->version.mf_id = get_unaligned_be32(&gvi->mf_id);
if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
ncsi_send_command(np, nc, NCSI_PKT_CMD_GC, NULL, 0, true);
}
static void ncsi_rsp_gls(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_gls_pkt *gls = (struct ncsi_rsp_gls_pkt *)pkt;
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gls->rsp;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
if (np >= ncsi_priv->n_packages ||
nc >= ncsi_priv->packages[np].n_channels) {
printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
np, nc);
return;
}
ncsi_priv->packages[np].channels[nc].has_link =
!!(get_unaligned_be32(&gls->status));
if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
ncsi_send_command(np, nc, NCSI_PKT_CMD_GVI, NULL, 0, true);
}
static void ncsi_rsp_cis(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
struct ncsi_package *package;
unsigned int np, nc;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
if (np >= ncsi_priv->n_packages) {
printf("NCSI: Mystery package 0x%02x from CIS\n", np);
return;
}
package = &ncsi_priv->packages[np];
if (nc < package->n_channels) {
/*
* This is fine in general but in the current design we
* don't send CIS commands to known channels.
*/
debug("NCSI: Duplicate channel 0x%02x\n", nc);
return;
}
package->channels = realloc(package->channels,
sizeof(struct ncsi_channel) *
(package->n_channels + 1));
if (!package->channels) {
printf("NCSI: Could not allocate memory for new channel\n");
return;
}
debug("NCSI: New channel 0x%02x\n", nc);
package->channels[nc].id = nc;
package->channels[nc].has_link = false;
package->n_channels++;
ncsi_send_gls(np, nc);
}
static void ncsi_rsp_dp(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
unsigned int np;
/* No action needed */
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
if (np >= ncsi_priv->n_packages)
debug("NCSI: DP response from unknown package %d\n", np);
}
static void ncsi_rsp_sp(struct ncsi_rsp_pkt *pkt)
{
struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
unsigned int np;
np = NCSI_PACKAGE_INDEX(rsp->common.channel);
if (np < ncsi_priv->n_packages) {
/* Already know about this package */
debug("NCSI: package 0x%02x selected\n", np);
return;
}
debug("NCSI: adding new package %d\n", np);
ncsi_priv->packages = realloc(ncsi_priv->packages,
sizeof(struct ncsi_package) *
(ncsi_priv->n_packages + 1));
if (!ncsi_priv->packages) {
printf("NCSI: could not allocate memory for new package\n");
return;
}
ncsi_priv->packages[np].id = np;
ncsi_priv->packages[np].n_channels = 0;
ncsi_priv->packages[np].channels = NULL;
ncsi_priv->n_packages++;
}
static void ncsi_update_state(struct ncsi_rsp_pkt_hdr *nh)
{
bool timeout = !nh;
int np, nc;
switch (ncsi_priv->state) {
case NCSI_PROBE_PACKAGE_SP:
if (!timeout &&
ncsi_priv->current_package + 1 < NCSI_PACKAGE_MAX) {
ncsi_priv->current_package++;
} else {
ncsi_priv->state = NCSI_PROBE_PACKAGE_DP;
ncsi_priv->current_package = 0;
}
return ncsi_probe_packages();
case NCSI_PROBE_PACKAGE_DP:
if (ncsi_priv->current_package + 1 < ncsi_priv->n_packages &&
!timeout) {
ncsi_priv->current_package++;
} else {
if (!ncsi_priv->n_packages) {
printf("NCSI: no packages found\n");
net_set_state(NETLOOP_FAIL);
return;
}
printf("NCSI: probing channels\n");
ncsi_priv->state = NCSI_PROBE_CHANNEL_SP;
ncsi_priv->current_package = 0;
ncsi_priv->current_channel = 0;
}
return ncsi_probe_packages();
case NCSI_PROBE_CHANNEL_SP:
if (!timeout && nh->common.type == NCSI_PKT_RSP_SP) {
ncsi_priv->state = NCSI_PROBE_CHANNEL;
return ncsi_probe_packages();
}
printf("NCSI: failed to select package 0x%0x2 or timeout\n",
ncsi_priv->current_package);
net_set_state(NETLOOP_FAIL);
break;
case NCSI_PROBE_CHANNEL:
// TODO only does package 0 for now
if (ncsi_priv->pending_requests == 0) {
np = ncsi_priv->current_package;
nc = ncsi_priv->current_channel;
/* Configure first channel that has link */
if (ncsi_priv->packages[np].channels[nc].has_link) {
ncsi_priv->state = NCSI_CONFIG;
} else if (ncsi_priv->current_channel + 1 <
NCSI_CHANNEL_MAX) {
ncsi_priv->current_channel++;
} else {
// XXX As above only package 0
printf("NCSI: no channel found with link\n");
net_set_state(NETLOOP_FAIL);
return;
}
return ncsi_probe_packages();
}
break;
case NCSI_CONFIG:
if (ncsi_priv->pending_requests == 0) {
printf("NCSI: configuration done!\n");
net_set_state(NETLOOP_SUCCESS);
} else if (timeout) {
printf("NCSI: timeout during configure\n");
net_set_state(NETLOOP_FAIL);
}
break;
default:
printf("NCSI: something went very wrong, nevermind\n");
net_set_state(NETLOOP_FAIL);
break;
}
}
static void ncsi_timeout_handler(void)
{
if (ncsi_priv->pending_requests)
ncsi_priv->pending_requests--;
ncsi_update_state(NULL);
}
static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
uchar *payload, int len, bool wait)
{
struct ncsi_pkt_hdr *hdr;
__be32 *pchecksum;
int eth_hdr_size;
u32 checksum;
uchar *pkt, *start;
int final_len;
pkt = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
if (!pkt)
return -ENOMEM;
start = pkt;
eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_NCSI);
pkt += eth_hdr_size;
/* Set NCSI command header fields */
hdr = (struct ncsi_pkt_hdr *)pkt;
hdr->mc_id = 0;
hdr->revision = NCSI_PKT_REVISION;
hdr->id = ++ncsi_priv->last_request;
ncsi_priv->requests[ncsi_priv->last_request] = 1;
hdr->type = cmd;
hdr->channel = NCSI_TO_CHANNEL(np, nc);
hdr->length = htons(len);
if (payload && len)
memcpy(pkt + sizeof(struct ncsi_pkt_hdr), payload, len);
/* Calculate checksum */
checksum = ncsi_calculate_checksum((unsigned char *)hdr,
sizeof(*hdr) + len);
pchecksum = (__be32 *)((void *)(hdr + 1) + len);
put_unaligned_be32(checksum, pchecksum);
if (wait) {
net_set_timeout_handler(1000UL, ncsi_timeout_handler);
ncsi_priv->pending_requests++;
}
if (len < 26)
len = 26;
/* frame header, packet header, payload, checksum */
final_len = eth_hdr_size + sizeof(struct ncsi_cmd_pkt_hdr) + len + 4;
net_send_packet(start, final_len);
free(start);
return 0;
}
static void ncsi_handle_aen(struct ip_udp_hdr *ip, unsigned int len)
{
struct ncsi_aen_pkt_hdr *hdr = (struct ncsi_aen_pkt_hdr *)ip;
int payload, i;
__be32 pchecksum;
u32 checksum;
switch (hdr->type) {
case NCSI_PKT_AEN_LSC:
printf("NCSI: link state changed\n");
payload = 12;
break;
case NCSI_PKT_AEN_CR:
printf("NCSI: re-configuration required\n");
payload = 4;
break;
case NCSI_PKT_AEN_HNCDSC:
/* Host notifcation - N/A but weird */
debug("NCSI: HNCDSC AEN received\n");
return;
default:
printf("%s: Invalid type 0x%02x\n", __func__, hdr->type);
return;
}
/* Validate packet */
if (hdr->common.revision != 1) {
printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
hdr->common.type, hdr->common.revision);
return;
}
if (ntohs(hdr->common.length) != payload) {
printf("NCSI: 0x%02x response has incorrect length %d\n",
hdr->common.type, hdr->common.length);
return;
}
pchecksum = get_unaligned_be32((void *)(hdr + 1) + payload - 4);
if (pchecksum != 0) {
checksum = ncsi_calculate_checksum((unsigned char *)hdr,
sizeof(*hdr) + payload - 4);
if (pchecksum != checksum) {
printf("NCSI: 0x%02x response has invalid checksum\n",
hdr->common.type);
return;
}
}
/* Link or configuration lost - just redo the discovery process */
ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
for (i = 0; i < ncsi_priv->n_packages; i++) {
free(ncsi_priv->packages[i].channels);
ncsi_priv->packages[i].channels = NULL;
}
free(ncsi_priv->packages);
ncsi_priv->packages = NULL;
ncsi_priv->n_packages = 0;
ncsi_priv->current_package = NCSI_PACKAGE_MAX;
ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
ncsi_probe_packages();
}
void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
unsigned int len)
{
struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip;
struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL;
unsigned short payload;
if (ncsi_priv->pending_requests)
ncsi_priv->pending_requests--;
if (len < sizeof(struct ncsi_rsp_pkt_hdr)) {
printf("NCSI: undersized packet: %u bytes\n", len);
goto out;
}
if (nh->common.type == NCSI_PKT_AEN)
return ncsi_handle_aen(ip, len);
switch (nh->common.type) {
case NCSI_PKT_RSP_SP:
payload = 4;
handler = ncsi_rsp_sp;
break;
case NCSI_PKT_RSP_DP:
payload = 4;
handler = ncsi_rsp_dp;
break;
case NCSI_PKT_RSP_CIS:
payload = 4;
handler = ncsi_rsp_cis;
break;
case NCSI_PKT_RSP_GLS:
payload = 16;
handler = ncsi_rsp_gls;
break;
case NCSI_PKT_RSP_GVI:
payload = 40;
handler = ncsi_rsp_gvi;
break;
case NCSI_PKT_RSP_GC:
payload = 32;
handler = ncsi_rsp_gc;
break;
case NCSI_PKT_RSP_SMA:
payload = 4;
handler = ncsi_rsp_sma;
break;
case NCSI_PKT_RSP_EBF:
payload = 4;
handler = ncsi_rsp_ebf;
break;
case NCSI_PKT_RSP_ECNT:
payload = 4;
handler = ncsi_rsp_ecnt;
break;
case NCSI_PKT_RSP_EC:
payload = 4;
handler = ncsi_rsp_ec;
break;
case NCSI_PKT_RSP_AE:
payload = 4;
handler = NULL;
break;
default:
printf("NCSI: unsupported packet type 0x%02x\n",
nh->common.type);
goto out;
}
if (ncsi_validate_rsp(pkt, payload) != 0) {
printf("NCSI: discarding invalid packet of type 0x%02x\n",
nh->common.type);
goto out;
}
if (handler)
handler(pkt);
out:
ncsi_update_state(nh);
}
static void ncsi_send_sp(unsigned int np)
{
uchar payload[4] = {0};
ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP,
(unsigned char *)&payload,
cmd_payload(NCSI_PKT_CMD_SP), true);
}
static void ncsi_send_dp(unsigned int np)
{
ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0,
true);
}
static void ncsi_send_gls(unsigned int np, unsigned int nc)
{
ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true);
}
static void ncsi_send_cis(unsigned int np, unsigned int nc)
{
ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true);
}
static void ncsi_send_ae(unsigned int np, unsigned int nc)
{
struct ncsi_cmd_ae_pkt cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen);
ncsi_send_command(np, nc, NCSI_PKT_CMD_AE,
((unsigned char *)&cmd)
+ sizeof(struct ncsi_cmd_pkt_hdr),
cmd_payload(NCSI_PKT_CMD_AE), true);
}
static void ncsi_send_ebf(unsigned int np, unsigned int nc)
{
struct ncsi_cmd_ebf_pkt cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc);
ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF,
((unsigned char *)&cmd)
+ sizeof(struct ncsi_cmd_pkt_hdr),
cmd_payload(NCSI_PKT_CMD_EBF), true);
}
static void ncsi_send_sma(unsigned int np, unsigned int nc)
{
struct ncsi_cmd_sma_pkt cmd;
unsigned char *addr, i;
addr = eth_get_ethaddr();
if (!addr) {
printf("NCSI: no MAC address configured\n");
return;
}
memset(&cmd, 0, sizeof(cmd));
for (i = 0; i < ARP_HLEN; i++)
cmd.mac[i] = addr[i];
cmd.index = 1;
cmd.at_e = 1;
ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA,
((unsigned char *)&cmd)
+ sizeof(struct ncsi_cmd_pkt_hdr),
cmd_payload(NCSI_PKT_CMD_SMA), true);
}
void ncsi_probe_packages(void)
{
struct ncsi_package *package;
unsigned int np, nc;
switch (ncsi_priv->state) {
case NCSI_PROBE_PACKAGE_SP:
if (ncsi_priv->current_package == NCSI_PACKAGE_MAX)
ncsi_priv->current_package = 0;
ncsi_send_sp(ncsi_priv->current_package);
break;
case NCSI_PROBE_PACKAGE_DP:
ncsi_send_dp(ncsi_priv->current_package);
break;
case NCSI_PROBE_CHANNEL_SP:
if (ncsi_priv->n_packages > 0)
ncsi_send_sp(ncsi_priv->current_package);
else
printf("NCSI: no packages discovered, configuration not possible\n");
break;
case NCSI_PROBE_CHANNEL:
/* Kicks off chain of channel discovery */
ncsi_send_cis(ncsi_priv->current_package,
ncsi_priv->current_channel);
break;
case NCSI_CONFIG:
for (np = 0; np < ncsi_priv->n_packages; np++) {
package = &ncsi_priv->packages[np];
for (nc = 0; nc < package->n_channels; nc++)
if (package->channels[nc].has_link)
break;
if (nc < package->n_channels)
break;
}
if (np == ncsi_priv->n_packages) {
printf("NCSI: no link available\n");
return;
}
printf("NCSI: configuring channel %d\n", nc);
ncsi_priv->current_package = np;
ncsi_priv->current_channel = nc;
/* Kicks off rest of configure chain */
ncsi_send_sma(np, nc);
break;
default:
printf("NCSI: unknown state 0x%x\n", ncsi_priv->state);
}
}
int ncsi_probe(struct phy_device *phydev)
{
if (!phydev->priv) {
phydev->priv = malloc(sizeof(struct ncsi));
if (!phydev->priv)
return -ENOMEM;
memset(phydev->priv, 0, sizeof(struct ncsi));
}
ncsi_priv = phydev->priv;
return 0;
}
int ncsi_startup(struct phy_device *phydev)
{
/* Set phydev parameters */
phydev->speed = SPEED_100;
phydev->duplex = DUPLEX_FULL;
/* Normal phy reset is N/A */
phydev->flags |= PHY_FLAG_BROKEN_RESET;
/* Set initial probe state */
ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
/* No active package/channel yet */
ncsi_priv->current_package = NCSI_PACKAGE_MAX;
ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
/* Pretend link works so the MAC driver sets final bits up */
phydev->link = true;
/* Set ncsi_priv so we can use it when called from net_loop() */
ncsi_priv = phydev->priv;
return 0;
}
int ncsi_shutdown(struct phy_device *phydev)
{
printf("NCSI: Disabling package %d\n", ncsi_priv->current_package);
ncsi_send_dp(ncsi_priv->current_package);
return 0;
}
U_BOOT_PHY_DRIVER(ncsi) = {
.uid = PHY_NCSI_ID,
.mask = 0xffffffff,
.name = "NC-SI",
.features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES |
SUPPORTED_100baseT_Full | SUPPORTED_MII,
.probe = ncsi_probe,
.startup = ncsi_startup,
.shutdown = ncsi_shutdown,
};