blob: 235f2f22d9aa93ac8bdea0dd71837cf74eb42ebf [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2019-2021 NXP
*/
#include <asm/eth.h>
#include <net/dsa.h>
#include <net.h>
#define DSA_SANDBOX_MAGIC 0x00415344
#define DSA_SANDBOX_TAG_LEN sizeof(struct dsa_sandbox_tag)
struct dsa_sandbox_priv {
struct eth_sandbox_priv *master_priv;
int port_en_mask;
};
struct dsa_sandbox_tag {
u32 magic;
u32 port;
};
static bool sb_dsa_port_enabled(struct udevice *dev, int port)
{
struct dsa_sandbox_priv *priv = dev_get_priv(dev);
return priv->port_en_mask & BIT(port);
}
static bool sb_dsa_master_enabled(struct udevice *dev)
{
struct dsa_sandbox_priv *priv = dev_get_priv(dev);
return !priv->master_priv->disabled;
}
static int dsa_sandbox_port_enable(struct udevice *dev, int port,
struct phy_device *phy)
{
struct dsa_sandbox_priv *priv = dev_get_priv(dev);
if (!sb_dsa_master_enabled(dev))
return -EFAULT;
priv->port_en_mask |= BIT(port);
return 0;
}
static void dsa_sandbox_port_disable(struct udevice *dev, int port,
struct phy_device *phy)
{
struct dsa_sandbox_priv *priv = dev_get_priv(dev);
priv->port_en_mask &= ~BIT(port);
}
static int dsa_sandbox_xmit(struct udevice *dev, int port, void *packet,
int length)
{
struct dsa_sandbox_tag *tag = packet;
if (!sb_dsa_master_enabled(dev))
return -EFAULT;
if (!sb_dsa_port_enabled(dev, port))
return -EFAULT;
tag->magic = DSA_SANDBOX_MAGIC;
tag->port = port;
return 0;
}
static int dsa_sandbox_rcv(struct udevice *dev, int *port, void *packet,
int length)
{
struct dsa_sandbox_tag *tag = packet;
if (!sb_dsa_master_enabled(dev))
return -EFAULT;
if (tag->magic != DSA_SANDBOX_MAGIC)
return -EFAULT;
*port = tag->port;
if (!sb_dsa_port_enabled(dev, tag->port))
return -EFAULT;
return 0;
}
static const struct dsa_ops dsa_sandbox_ops = {
.port_enable = dsa_sandbox_port_enable,
.port_disable = dsa_sandbox_port_disable,
.xmit = dsa_sandbox_xmit,
.rcv = dsa_sandbox_rcv,
};
static int sb_dsa_handler(struct udevice *dev, void *packet,
unsigned int len)
{
struct eth_sandbox_priv *master_priv;
struct dsa_sandbox_tag *tag = packet;
struct udevice *dsa_dev;
u32 port_index;
void *rx_buf;
int i;
/* this emulates the switch hw and the network side */
if (tag->magic != DSA_SANDBOX_MAGIC)
return -EFAULT;
port_index = tag->port;
master_priv = dev_get_priv(dev);
dsa_dev = master_priv->priv;
if (!sb_dsa_port_enabled(dsa_dev, port_index))
return -EFAULT;
packet += DSA_SANDBOX_TAG_LEN;
len -= DSA_SANDBOX_TAG_LEN;
if (!sandbox_eth_arp_req_to_reply(dev, packet, len))
goto dsa_tagging;
if (!sandbox_eth_ping_req_to_reply(dev, packet, len))
goto dsa_tagging;
return 0;
dsa_tagging:
master_priv->recv_packets--;
i = master_priv->recv_packets;
rx_buf = master_priv->recv_packet_buffer[i];
len = master_priv->recv_packet_length[i];
memmove(rx_buf + DSA_SANDBOX_TAG_LEN, rx_buf, len);
tag = rx_buf;
tag->magic = DSA_SANDBOX_MAGIC;
tag->port = port_index;
len += DSA_SANDBOX_TAG_LEN;
master_priv->recv_packet_length[i] = len;
master_priv->recv_packets++;
return 0;
}
static int dsa_sandbox_probe(struct udevice *dev)
{
struct dsa_sandbox_priv *priv = dev_get_priv(dev);
struct udevice *master = dsa_get_master(dev);
struct eth_sandbox_priv *master_priv;
if (!master)
return -ENODEV;
dsa_set_tagging(dev, DSA_SANDBOX_TAG_LEN, 0);
master_priv = dev_get_priv(master);
master_priv->priv = dev;
master_priv->tx_handler = sb_dsa_handler;
priv->master_priv = master_priv;
return 0;
}
static const struct udevice_id dsa_sandbox_ids[] = {
{ .compatible = "sandbox,dsa" },
{ }
};
U_BOOT_DRIVER(dsa_sandbox) = {
.name = "dsa_sandbox",
.id = UCLASS_DSA,
.of_match = dsa_sandbox_ids,
.probe = dsa_sandbox_probe,
.ops = &dsa_sandbox_ops,
.priv_auto = sizeof(struct dsa_sandbox_priv),
};