180 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
// 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),
 | 
						|
};
 |