| // 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), |
| }; |