Эх сурвалжийг харах

rpmsg-kdrv: add support for remote eth device

In J721E, the remote R5F can run firmware which controls 9-port CPSW9G NUSS
module - Eth switch fw. Only the Eth Switch FW has full control over J721E
CPSW9G and allows segregate Ethernet traffic for each attached Remote CPU
by using programable ALE classifiers and exclusive UDMA TX channels and RX
flows. The Eth switch FW supports rpmsg-kdrv protocol and presents itself
as rpmsg-kdrv device, which provides CPSW9g resource management and
debugging functions for each attached Remote CPU:
- DMA resources allocation: TX UDMA channel and RX Flow
- MAC addresses assignment
- ARP helper for ARP requests processing from remote Net Hosts
- dbg: IPC ping
- dbg: CPSW9G register access
- dbg: statistic print request on R5F console (ioctl)

Reviewed-by: Roger Quadros <rogerq@ti.com>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Grygorii Strashko 6 жил өмнө
parent
commit
f70203fc84

+ 10 - 0
drivers/rpmsg-kdrv/Kconfig

@@ -22,4 +22,14 @@ config RPMSG_KDRV_DISPLAY
 	  by a remoteproc and DRM driver will be able to use display features
 	  using remote_device framework
 
+config RPMSG_KDRV_ETH_SWITCH
+	tristate "RPMSG virtual eth switch device support"
+	select RPMSG_KDRV
+	default m
+	help
+	  Say Y here to enable support for remote device based Eth switch
+	  virtualization. This setup expects that the Eth switch will be driven
+	  by a remoteproc and virtual Network device will be able to use
+	  Eth switch features using remote_device framework
+
 endmenu

+ 1 - 0
drivers/rpmsg-kdrv/Makefile

@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_RPMSG_KDRV)		+= rpmsg_kdrv.o
 obj-$(CONFIG_RPMSG_KDRV_DISPLAY)	+= rpmsg_kdrv_display.o
+obj-$(CONFIG_RPMSG_KDRV_ETH_SWITCH)	+= rpmsg_kdrv_switch.o

+ 828 - 0
drivers/rpmsg-kdrv/rpmsg_kdrv_switch.c

@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Grygorii Strashko <grygorii.strashko@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+#include <linux/rpmsg-remotedev/rpmsg-remotedev.h>
+
+#include "rpmsg_kdrv_internal.h"
+#include "shared/rpmsg-kdrv-transport-switch.h"
+
+struct rpmsg_kdrv_switch_private {
+	struct rpmsg_kdrv_device *kddev;
+	struct rpmsg_remotedev rdev;
+	u64	session_id;
+	u32	core_key;
+	u32	permissions;
+	u32	uart_id;
+	u32	attached:1;
+	u32	uart_connected:1;
+};
+
+static bool
+rpmsg_kdrv_switch_check_perm(struct rpmsg_kdrv_switch_private *priv,
+			     enum rpmsg_kdrv_ethswitch_message_type msg_type)
+{
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+
+	if (priv->permissions & BIT(msg_type))
+		return true;
+
+	dev_err_ratelimited(&kddev->dev, "permission denied msg: 0x%02X\n",
+			    msg_type);
+	return false;
+}
+
+static int
+rpmsg_kdrv_switch_check_resp_status(struct rpmsg_kdrv_ethswitch_common_resp_info *info)
+{
+	if (info->status == RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EAGAIN)
+		return -EAGAIN;
+	if (info->status == RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EFAIL)
+		return -EIO;
+	return 0;
+}
+
+static int rpmsg_kdrv_switch_ping(struct rpmsg_remotedev *rdev,
+				  const u8 *data, int size)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_ping_req *req;
+	struct rpmsg_kdrv_ethswitch_ping_resp *resp;
+	int ret;
+
+	if (!rpmsg_kdrv_switch_check_perm(priv,
+					  RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST))
+		return -EPERM;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	if (size > RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN)
+		size = RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN;
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST;
+	memcpy(req->data, data, size);
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	if (memcmp(req->data, resp->data, size)) {
+		dev_dbg(&kddev->dev, "%s: ping fail - data\n", __func__);
+		ret = -EINVAL;
+	}
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int
+rpmsg_kdrv_switch_attach(struct rpmsg_remotedev *rdev,
+			 struct rpmsg_rdev_eth_switch_attach_info *attach_info)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_attach_req *req;
+	struct rpmsg_kdrv_ethswitch_attach_resp *resp;
+	int ret;
+
+	if (priv->attached)
+		return -EBUSY;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ATTACH;
+	req->cpsw_type = RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_9G;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+	if (ret)
+		goto out;
+
+	priv->session_id = resp->id;
+	priv->core_key = resp->core_key;
+	priv->attached = true;
+
+	dev_dbg(&kddev->dev, "%s: done id:%llX core_key:%08X\n",
+		__func__, priv->session_id, priv->core_key);
+
+	attach_info->rx_mtu = resp->rx_mtu;
+	memcpy(attach_info->tx_mtu, resp->tx_mtu, sizeof(attach_info->tx_mtu));
+	attach_info->features = resp->features;
+	if (priv->uart_connected)
+		attach_info->features |=
+				RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS;
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int
+rpmsg_kdrv_switch_attach_ext(struct rpmsg_remotedev *rdev,
+			     struct rpmsg_rdev_eth_switch_attach_ext_info *attach_ext_info)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_attach_extended_req *req;
+	struct rpmsg_kdrv_ethswitch_attach_extended_resp *resp;
+	int ret;
+
+	if (priv->attached)
+		return -EBUSY;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT;
+	req->cpsw_type = RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_9G;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+	if (ret)
+		goto out;
+
+	priv->session_id = resp->id;
+	priv->core_key = resp->core_key;
+	priv->attached = true;
+
+	dev_dbg(&kddev->dev, "%s: done id:%llX core_key:%08X\n",
+		__func__, priv->session_id, priv->core_key);
+
+	attach_ext_info->rx_mtu = resp->rx_mtu;
+	memcpy(attach_ext_info->tx_mtu, resp->tx_mtu,
+	       sizeof(attach_ext_info->tx_mtu));
+	attach_ext_info->features = resp->features;
+	if (priv->uart_connected)
+		attach_ext_info->features |=
+				RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS;
+	attach_ext_info->flow_idx = resp->alloc_flow_idx;
+	attach_ext_info->tx_cpsw_psil_dst_id = resp->tx_cpsw_psil_dst_id;
+	ether_addr_copy(attach_ext_info->mac_addr, resp->mac_address);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_detach(struct rpmsg_remotedev *rdev)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_detach_req *req;
+	struct rpmsg_kdrv_ethswitch_detach_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_DETACH;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+
+	priv->attached = false;
+
+	dev_dbg(&kddev->dev, "%s: done ret:%d\n", __func__, ret);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int
+rpmsg_kdrv_switch_get_tx_info(struct rpmsg_remotedev *rdev,
+			      struct rpmsg_rdev_eth_switch_tx_info *info)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_alloc_req *req;
+	struct rpmsg_kdrv_ethswitch_alloc_tx_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+	if (ret)
+		goto out;
+
+	info->tx_cpsw_psil_dst_id = resp->tx_cpsw_psil_dst_id;
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int
+rpmsg_kdrv_switch_get_rx_info(struct rpmsg_remotedev *rdev,
+			      struct rpmsg_rdev_eth_switch_rx_info *info)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_alloc_req *req;
+	struct rpmsg_kdrv_ethswitch_alloc_rx_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+	if (ret)
+		goto out;
+
+	info->flow_idx = resp->alloc_flow_idx;
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_get_mac(struct rpmsg_remotedev *rdev,
+				     void *mac_addr)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_alloc_req *req;
+	struct rpmsg_kdrv_ethswitch_alloc_mac_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+	if (ret)
+		goto out;
+
+	ether_addr_copy(mac_addr, resp->mac_address);
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_register_mac(struct rpmsg_remotedev *rdev,
+					  void *mac_addr, u32 flow_idx_offset)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_register_mac_req *req;
+	struct rpmsg_kdrv_ethswitch_register_mac_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+	ether_addr_copy(req->mac_address, mac_addr);
+	req->flow_idx = flow_idx_offset;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int
+rpmsg_kdrv_switch_unregister_mac(struct rpmsg_remotedev *rdev,
+				 void *mac_addr, u32 flow_idx_offset)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_unregister_mac_req *req;
+	struct rpmsg_kdrv_ethswitch_unregister_mac_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+	ether_addr_copy(req->mac_address, mac_addr);
+	req->flow_idx = flow_idx_offset;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_reg_ipv4(struct rpmsg_remotedev *rdev,
+				      void *mac_addr, __be32 ipv4)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_ipv4_register_mac_req *req;
+	struct rpmsg_kdrv_ethswitch_ipv4_register_mac_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+	ether_addr_copy(req->mac_address, mac_addr);
+	memcpy(req->ipv4_addr, &ipv4, RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN);
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_unreg_ipv4(struct rpmsg_remotedev *rdev,
+					__be32 ipv4)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_req *req;
+	struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_resp *resp;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+	memcpy(req->ipv4_addr, &ipv4, RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN);
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_reg_read(struct rpmsg_remotedev *rdev,
+				      u32 reg_addr, u32 *val)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_regrd_req *req;
+	struct rpmsg_kdrv_ethswitch_regrd_resp *resp;
+	int ret;
+
+	if (!rpmsg_kdrv_switch_check_perm(priv, RPMSG_KDRV_TP_ETHSWITCH_REGRD))
+		return -EPERM;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_REGRD;
+	req->regaddr = reg_addr;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id,
+						    req, sizeof(*req),
+						    resp, sizeof(*resp));
+	if (ret) {
+		dev_dbg(&kddev->dev, "%s: send: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_switch_check_resp_status(&resp->info);
+	if (ret)
+		goto out;
+
+	*val = resp->regval;
+	dev_dbg(&kddev->dev, "%s: done\n", __func__);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_switch_c2s_dbg_dump_stats(struct rpmsg_remotedev *rdev)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_ethswitch_c2s_notify *req;
+	int ret;
+
+	if (!priv->attached)
+		return -EINVAL;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->header.message_type = RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY;
+	req->info.id = priv->session_id;
+	req->info.core_key = priv->core_key;
+	req->notifyid = RPMSG_KDRV_TP_ETHSWITCH_CLIENTNOTIFY_DUMPSTATS;
+
+	ret = rpmsg_kdrv_send_message(rpdev, kddev->device_id,
+				      req, sizeof(*req));
+
+	dev_dbg(&kddev->dev, "%s: done ret:%d\n", __func__, ret);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static void rpmsg_kdrv_switch_get_fw_ver(struct rpmsg_remotedev *rdev,
+				         char *buf, size_t size)
+{
+	struct rpmsg_kdrv_switch_private *priv =
+		container_of(rdev, struct rpmsg_kdrv_switch_private, rdev);
+	struct rpmsg_kdrv_ethswitch_fw_version_info *fw_info;
+	struct rpmsg_kdrv_ethswitch_device_data *kddev_data;
+
+	kddev_data = priv->kddev->device_data;
+	fw_info = &kddev_data->fw_ver;
+
+	snprintf(buf, size, "%u.%u.%u %.*s/%.*s/%.*s SHA:%.*s",
+		 fw_info->major, fw_info->minor, fw_info->rev,
+		 RPMSG_KDRV_TP_ETHSWITCH_DATELEN, fw_info->date,
+		 RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN, fw_info->month,
+		 RPMSG_KDRV_TP_ETHSWITCH_YEARLEN, fw_info->year,
+		 RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN, fw_info->commit_hash);
+}
+
+struct rpmsg_remotedev_eth_switch_ops switch_ops = {
+	.get_fw_ver = rpmsg_kdrv_switch_get_fw_ver,
+	.attach = rpmsg_kdrv_switch_attach,
+	.attach_ext = rpmsg_kdrv_switch_attach_ext,
+	.detach = rpmsg_kdrv_switch_detach,
+	.get_tx_info = rpmsg_kdrv_switch_get_tx_info,
+	.get_rx_info = rpmsg_kdrv_switch_get_rx_info,
+	.get_mac = rpmsg_kdrv_switch_get_mac,
+	.register_mac = rpmsg_kdrv_switch_register_mac,
+	.unregister_mac = rpmsg_kdrv_switch_unregister_mac,
+	.register_ipv4 = rpmsg_kdrv_switch_reg_ipv4,
+	.unregister_ipv4 = rpmsg_kdrv_switch_unreg_ipv4,
+	.ping = rpmsg_kdrv_switch_ping,
+	.read_reg = rpmsg_kdrv_switch_reg_read,
+	.dbg_dump_stats = rpmsg_kdrv_switch_c2s_dbg_dump_stats,
+};
+
+static int rpmsg_kdrv_switch_callback(struct rpmsg_kdrv_device *dev,
+				      void *msg, int len)
+{
+	return 0;
+}
+
+static int
+rpmsg_kdrv_switch_dev_data_parse(struct rpmsg_kdrv_device *kddev,
+				 void *data, int len,
+				 struct rpmsg_kdrv_switch_private *priv)
+{
+	struct rpmsg_kdrv_ethswitch_device_data *kddev_data = data;
+	struct rpmsg_kdrv_ethswitch_fw_version_info *fw_info;
+
+	if (sizeof(*kddev_data) != len)
+		return -EINVAL;
+
+	dev_info(&kddev->dev, "Device info: permissions: %08X uart_id: %d\n",
+		 kddev_data->permission_flags,
+		 kddev_data->uart_connected ? kddev_data->uart_id : -1);
+
+	fw_info = &kddev_data->fw_ver;
+
+	dev_info(&kddev->dev, "FW ver %u.%u (rev %u) %.*s/%.*s/%.*s SHA:%.*s\n",
+		 fw_info->major, fw_info->minor, fw_info->rev,
+		 RPMSG_KDRV_TP_ETHSWITCH_DATELEN, fw_info->date,
+		 RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN, fw_info->month,
+		 RPMSG_KDRV_TP_ETHSWITCH_YEARLEN, fw_info->year,
+		 RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN, fw_info->commit_hash);
+
+	if (fw_info->major != RPMSG_KDRV_TP_ETHSWITCH_VERSION_MAJOR &&
+	    fw_info->minor != RPMSG_KDRV_TP_ETHSWITCH_VERSION_MINOR) {
+		dev_err(&kddev->dev, "Unsupported EthSwitch FW version\n");
+		return -EOPNOTSUPP;
+	}
+
+	priv->uart_connected = kddev_data->uart_connected;
+	priv->uart_id = kddev_data->uart_connected ? kddev_data->uart_id : -1;
+	priv->permissions = kddev_data->permission_flags;
+
+	return 0;
+}
+
+static int rpmsg_kdrv_switch_probe(struct rpmsg_kdrv_device *dev)
+{
+	struct rpmsg_kdrv_switch_private *priv;
+	int ret;
+
+	dev_dbg(&dev->dev, "%s\n", __func__);
+
+	priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->rdev.type = RPMSG_REMOTEDEV_ETH_SWITCH_DEVICE;
+	priv->rdev.device.eth_switch.ops = &switch_ops;
+
+	priv->kddev = dev;
+
+	ret = rpmsg_kdrv_switch_dev_data_parse(dev, dev->device_data,
+					       dev->device_data_len, priv);
+	if (ret)
+		return ret;
+
+	dev->driver_private = priv;
+	dev->remotedev = &priv->rdev;
+
+	return 0;
+}
+
+static void rpmsg_kdrv_switch_remove(struct rpmsg_kdrv_device *dev)
+{
+	dev->driver_private = NULL;
+	dev->remotedev = NULL;
+
+	dev_dbg(&dev->dev, "%s\n", __func__);
+}
+
+struct rpmsg_kdrv_driver rpmsg_kdrv_switch = {
+	.drv = {
+		.name = "rpmsg-kdrv-eth-switch",
+	},
+	.device_type = RPMSG_KDRV_TP_DEVICE_TYPE_ETHSWITCH,
+	.probe = rpmsg_kdrv_switch_probe,
+	.remove = rpmsg_kdrv_switch_remove,
+	.callback = rpmsg_kdrv_switch_callback,
+};
+
+static int __init rpmsg_kdrv_display_driver_init(void)
+{
+	return rpmsg_kdrv_register_driver(&rpmsg_kdrv_switch);
+}
+module_init(rpmsg_kdrv_display_driver_init);
+
+static void rpmsg_kdrv_display_driver_fini(void)
+{
+}
+module_exit(rpmsg_kdrv_display_driver_fini);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");
+MODULE_DESCRIPTION("TI J721E RPMSG KDRV Ethernet switch driver");

+ 3 - 1
drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-common.h

@@ -15,8 +15,10 @@
 #define RPMSG_KDRV_TP_DEVICE_TYPE_INIT		(0x0)
 #define RPMSG_KDRV_TP_DEVICE_TYPE_DISPLAY	(0x1)
 #define RPMSG_KDRV_TP_DEVICE_TYPE_DEMO		(0x2)
+#define RPMSG_KDRV_TP_DEVICE_TYPE_ETHSWITCH	(0x3)
+
 /* More device types here*/
-#define RPMSG_KDRV_TP_DEVICE_TYPE_MAX		(0x3)
+#define RPMSG_KDRV_TP_DEVICE_TYPE_MAX		(0x4)
 
 /*
  * Maximum number of proxy devices per remotecore

+ 563 - 0
drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-switch.h

@@ -0,0 +1,563 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Grygorii Strashko <grygorii.strashko@ti.com>
+ */
+
+#ifndef DRIVERS_RPMSG_KDRV_SHARED_RPMSG_KDRV_TRANSPORT_SWITCH_H_
+#define DRIVERS_RPMSG_KDRV_SHARED_RPMSG_KDRV_TRANSPORT_SWITCH_H_
+
+#include <linux/etherdevice.h>
+#include "rpmsg-kdrv-transport-common.h"
+
+#define RPMSG_KDRV_TP_ETHSWITCH_VERSION_MAJOR             (0)
+#define RPMSG_KDRV_TP_ETHSWITCH_VERSION_MINOR             (1)
+#define RPMSG_KDRV_TP_ETHSWITCH_VERSION_REVISION          (1)
+
+/**
+ * enum rpmsg_kdrv_ethswitch_message_type - Eth switch rpmsg protocol messages
+ */
+enum rpmsg_kdrv_ethswitch_message_type {
+	RPMSG_KDRV_TP_ETHSWITCH_ATTACH = 0x00,
+	RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT = 0x01,
+	RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX = 0x02,
+	RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX = 0x03,
+	RPMSG_KDRV_TP_ETHSWITCH_REGISTER_DEFAULTFLOW = 0x04,
+	RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC = 0x05,
+	RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC = 0x06,
+	RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC = 0x07,
+	RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_DEFAULTFLOW = 0x08,
+	RPMSG_KDRV_TP_ETHSWITCH_FREE_MAC = 0x09,
+	RPMSG_KDRV_TP_ETHSWITCH_FREE_TX = 0x0A,
+	RPMSG_KDRV_TP_ETHSWITCH_FREE_RX = 0x0B,
+	RPMSG_KDRV_TP_ETHSWITCH_DETACH = 0x0C,
+	RPMSG_KDRV_TP_ETHSWITCH_IOCTL = 0x0D,
+	RPMSG_KDRV_TP_ETHSWITCH_REGWR = 0x0E,
+	RPMSG_KDRV_TP_ETHSWITCH_REGRD = 0x0F,
+	RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER = 0x10,
+	RPMSG_KDRV_TP_ETHSWITCH_IPV6_MAC_REGISTER = 0x11,
+	RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER = 0x12,
+	RPMSG_KDRV_TP_ETHSWITCH_IPV6_MAC_UNREGISTER = 0x13,
+	RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST = 0x14,
+	RPMSG_KDRV_TP_ETHSWITCH_S2C_NOTIFY = 0x15,
+	RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY = 0x16,
+	RPMSG_KDRV_TP_ETHSWITCH_MAX = 0x17,
+};
+
+/**
+ * Client to Eth switch notification events @RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY
+ */
+enum rpmsg_kdrv_ethswitch_c2s_notify_type {
+	RPMSG_KDRV_TP_ETHSWITCH_CLIENTNOTIFY_DUMPSTATS = 0x00,
+	RPMSG_KDRV_TP_ETHSWITCH_CLIENTNOTIFY_MAX,
+};
+
+/**
+ * Eth switch HW ID
+ */
+enum rpmsg_kdrv_ethswitch_cpsw_type {
+	RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_2G,
+	RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_9G,
+	RPMSG_KDRV_TP_ETHSWITCH_CPSWTYPE_MAX,
+};
+
+/**
+ * Response status codes returned by Eth switch FW in
+ * struct @rpmsg_kdrv_ethswitch_common_resp_info
+ */
+#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK		(0)
+#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EAGAIN	(-1)
+#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EFAIL		(-2)
+#define RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_EACCESS	(-3)
+
+/* Maximum length of message data */
+#define RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN	(128)
+
+/* Number of priorities supported by CPSW */
+#define RPMSG_KDRV_TP_ETHSWITCH_PRIORITY_NUM   (8)
+
+/* IPv4 Address length in octets */
+#define RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN         (4)
+
+/**
+ * struct rpmsg_kdrv_ethswitch_msg_header - Message Header for outgoing messages
+ *
+ * @message_type: Type of messages: One of
+ *	enum @rpmsg_kdrv_ethswitch_message_type values
+ */
+struct rpmsg_kdrv_ethswitch_msg_header {
+	u8 message_type;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_common_req_info - common request msgs data
+ *
+ * @id: unique handle
+ * @core_key: core specific key to indicate attached core
+ *
+ * Common structure used for all Eth switch FW request msgs except
+ * @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. It has to be filled with values returned
+ * by @RPMSG_KDRV_TP_ETHSWITCH_ATTACH.
+ */
+struct rpmsg_kdrv_ethswitch_common_req_info {
+	u64 id;
+	u32 core_key;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_common_resp_info - common response data
+ *
+ * @status: status of request
+ *
+ * Common data returned by Eth switch FW in all response messages to identify
+ * status of request message processing.
+ */
+struct rpmsg_kdrv_ethswitch_common_resp_info {
+	s32 status;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_attach_req - attach cmd client request msg
+ *
+ * @header: msg header
+ * @cpsw_type: CPSW HW type enum @rpmsg_kdrv_ethswitch_cpsw_type
+ *
+ * Client attach message @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. it should be always
+ * sent first before other requests to Eth switch FW.
+ */
+struct rpmsg_kdrv_ethswitch_attach_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	u8 cpsw_type;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_attach_resp - attach client response msg
+ *
+ * @info: common response data. Status of the request processing
+ * @id: unique handle used by all further CMDs
+ * @core_key: core specific key to indicate attached core
+ * @rx_mtu: MTU of rx packets
+ * @tx_mtu: MTU of tx packet per priority
+ * @features: supported features mask
+ *
+ * Attach client response msg received as response to client attach request
+ * @RPMSG_KDRV_TP_ETHSWITCH_ATTACH. The @id and @core_key should be used to
+ * fill struct @rpmsg_kdrv_ethswitch_common_req_info in all further request
+ * messages.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_attach_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+	u64 id;
+	u32 core_key;
+	u32 rx_mtu;
+	u32 tx_mtu[RPMSG_KDRV_TP_ETHSWITCH_PRIORITY_NUM];
+	u32 features;
+#define RPMSG_KDRV_TP_ETHSWITCH_FEATURE_TXCSUM BIT(0)
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_attach_extended_req - extended attach request msg
+ *
+ * @header: msg header
+ * @cpsw_type: CPSW HW type enum @rpmsg_kdrv_ethswitch_cpsw_type
+ *
+ * Client extended attach request @RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT. It can
+ * be used instead of @RPMSG_KDRV_TP_ETHSWITCH_ATTACH and has to sent first
+ * before other requests to Eth switch FW.
+ */
+struct rpmsg_kdrv_ethswitch_attach_extended_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	u8 cpsw_type;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_common_response_info - extended attach resp msg
+ *
+ * @info: common response data. Status of the request processing
+ * @id: unique handle used by all further CMDs
+ * @core_key: core specific key to indicate attached core
+ * @rx_mtu: MTU of rx packets
+ * @tx_mtu: MTU of tx packet per priority
+ * @features: supported features mask
+ * @alloc_flow_idx: RX UDMA flow ID
+ * @tx_cpsw_psil_dst_id: PSI-L dest thread id
+ * @mac_address: default eth MAC address assigned to this client
+ *
+ * Extended attach response msg received as response to client extended attach
+ * request @RPMSG_KDRV_TP_ETHSWITCH_ATTACH_EXT. The @id and @core_key should be
+ * used to fill struct @rpmsg_kdrv_ethswitch_common_req_info in all further
+ * request messages. In addition, it provides allocated DMA resources and
+ * MAC address.
+ *
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_attach_extended_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+	u64 id;
+	u32 core_key;
+	u32 rx_mtu;
+	u32 tx_mtu[RPMSG_KDRV_TP_ETHSWITCH_PRIORITY_NUM];
+	u32 features;
+	u32 alloc_flow_idx;
+	u32 tx_cpsw_psil_dst_id;
+	u8 mac_address[ETH_ALEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_detach_req  - detach client request msg
+ *
+ * @header: msg header
+ * @info: common request msgs data
+ *
+ * Client detach request message @RPMSG_KDRV_TP_ETHSWITCH_DETACH.
+ * it should be always sent as the last message to Eth switch FW.
+ */
+struct rpmsg_kdrv_ethswitch_detach_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_detach_resp - detach client response msg
+ *
+ * @info: common response data. Status of the request processing
+ *
+ * Client detach response msg received as response to client detach request
+ * @RPMSG_KDRV_TP_ETHSWITCH_DETACH.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_detach_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_alloc_req  - alloc resources request msg
+ *
+ * @header: msg header
+ * @info: common request msgs data
+ *
+ * Client resources allocation request messages
+ * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX: get RX DMA resources
+ * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX: get TX DMA resources
+ * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC: get MAC address
+ */
+struct rpmsg_kdrv_ethswitch_alloc_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_alloc_rx_resp - alloc rx resources response msg
+ *
+ * @info: common response data. Status of the request processing
+ * @alloc_flow_idx: RX UDMA flow ID
+ *
+ * Client alloc rx resources response msg received as response to request
+ * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_RX. The @alloc_flow_idx is RX UDMA flow ID
+ * to be used for ingress packets reception.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_alloc_rx_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+	u32 alloc_flow_idx;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_alloc_tx_resp - alloc tx resources response msg
+ *
+ * @info: common response data. Status of the request processing
+ * @tx_cpsw_psil_dst_id: PSI-L dest thread id
+ *
+ * Client alloc tx resources response msg received as response to request
+ * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_TX. The @tx_cpsw_psil_dst_id is TX PSI-L dest
+ * thread ID to be used for TX UDMA channel setup.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_alloc_tx_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+	u32 tx_cpsw_psil_dst_id;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_alloc_mac_resp - alloc MAC resources response msg
+ *
+ * @info: common response data. Status of the request processing
+ * @mac_address: default eth MAC address assigned to this client
+ *
+ * Client alloc MAC resources response msg received as response to request
+ * @RPMSG_KDRV_TP_ETHSWITCH_ALLOC_MAC.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_alloc_mac_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+	u8 mac_address[ETH_ALEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_register_mac_req - register MAC addr
+ *
+ * @header: msg header
+ * @info: common request msgs data
+ * @mac_address: eth MAC address used by client
+ * @flow_idx: RX UDMA flow ID
+ *
+ * Client register MAC addr message @RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC.
+ * it should be sent to Eth switch FW to configure HW network traffic
+ * classifiers so all network traffic directed to @mac_address will be
+ * redirected to this client and can be received through allocated RX UDMA flow
+ * @flow_idx.
+ *
+ * This message has to be sent by client when it's ready to receive network
+ * traffic.
+ */
+struct rpmsg_kdrv_ethswitch_register_mac_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+	u8 mac_address[ETH_ALEN];
+	u32 flow_idx;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_register_mac_resp - register MAC addr response
+ *
+ * @info: common response data. Status of the request processing
+ *
+ * Client register MAC addr response response msg received as response to
+ * request @RPMSG_KDRV_TP_ETHSWITCH_REGISTER_MAC.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_register_mac_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_unregister_mac_req - unregister MAC addr
+ *
+ * @header: msg header
+ * @info: common request msgs data
+ * @mac_address: eth MAC address used by client
+ * @flow_idx: RX UDMA flow ID
+ *
+ * Client unregister MAC addr message @RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC.
+ * it should be sent to Eth switch FW to disable HW network traffic
+ * classifiers so all network traffic directed to @mac_address will be dropped.
+ *
+ * This message has to be sent by client when it does not want to receive any
+ * more network traffic.
+ */
+struct rpmsg_kdrv_ethswitch_unregister_mac_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+	u8 mac_address[ETH_ALEN];
+	u32 flow_idx;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_unregister_mac_resp - unregister MAC addr resp
+ *
+ * @info: common response data. Status of the request processing
+ *
+ * Client unregister MAC addr response msg received as response to
+ * request @RPMSG_KDRV_TP_ETHSWITCH_UNREGISTER_MAC.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_unregister_mac_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_ipv4_register_mac_req - register IPv4:MAC pair
+ *
+ * @header: msg header
+ * @info: common request msgs data
+ * @mac_address: eth MAC address used by client
+ * @ipv4_addr: IPv4 addr
+ *
+ * Client register IPv4:MAC addr pair message
+ * @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER registers pair of IPv4 @ipv4_addr
+ * and Eth MAC @mac_address addresses in Eth switch FW ARP database.
+ *
+ * This message has to be sent by client when there is new IPv4 addr assigned.
+ */
+struct rpmsg_kdrv_ethswitch_ipv4_register_mac_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+	u8 mac_address[ETH_ALEN];
+	u8 ipv4_addr[RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_ipv4_register_mac_resp - register IPv4:MAC pair
+ *	response
+ *
+ * @info: common response data. Status of the request processing
+ *
+ * Client register IPv4:MAC addr pair response msg received as response to
+ * request @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_REGISTER.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_ipv4_register_mac_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_req - unregister IPv4 addr
+ *
+ * @header: msg header
+ * @info: common request msgs data
+ * @ipv4_addr: IPv4 addr
+ *
+ * Client unregister IPv4 addr message
+ * @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER. It removes IPv4 @ipv4_addr
+ * address from Eth switch FW ARP database.
+ *
+ * This message has to be sent by client when there is IPv4 addr unassigned.
+ */
+struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+	u8 ipv4_addr[RPMSG_KDRV_TP_ETHSWITCH_IPV4ADDRLEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_resp - unregister IPv4 addr
+ *
+ * @info: common response data. Status of the request processing
+ *
+ * Client unregister IPv4 addr response msg received as response to
+ * request @RPMSG_KDRV_TP_ETHSWITCH_IPV4_MAC_UNREGISTER.
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_ipv4_unregister_mac_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_ping_req - ping request
+ *
+ * @header: msg header
+ * @data: custom data
+ *
+ * Client ping request @RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST. The Eth switch FW
+ * should return the same @data in struct @rpmsg_kdrv_ethswitch_ping_resp.
+ * Can be used any time - no attach required.
+ */
+struct rpmsg_kdrv_ethswitch_ping_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	u8 data[RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_ping_resp - ping response
+ *
+ * @data: custom data
+ *
+ * The ping response msg received as response to request
+ * @RPMSG_KDRV_TP_ETHSWITCH_PING_REQUEST. The Eth switch FW should return
+ * the same @data as was provided in struct @rpmsg_kdrv_ethswitch_ping_req.
+ */
+struct rpmsg_kdrv_ethswitch_ping_resp {
+	u8 data[RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_regrd_req - read hw register request
+ *
+ * @header: msg header
+ * @regaddr: phys register address
+ *
+ * The read hw register request @RPMSG_KDRV_TP_ETHSWITCH_REGRD.
+ * The Eth switch FW should return the @regaddr register value.
+ * Can be used any time - no attach required.
+ */
+struct rpmsg_kdrv_ethswitch_regrd_req {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	u32 regaddr;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_regrd_resp - read hw register response
+ *
+ * @info: common response data. Status of the request processing
+ * @regval: register value
+ *
+ * The read hw register response received as response to request
+ * @RPMSG_KDRV_TP_ETHSWITCH_REGRD. The @regval is hw register value from
+ * @regaddr phys register address provided in
+ * struct @rpmsg_kdrv_ethswitch_regrd_req
+ *
+ * The @info.status field is @RPMSG_KDRV_TP_ETHSWITCH_CMDSTATUS_OK on success.
+ */
+struct rpmsg_kdrv_ethswitch_regrd_resp {
+	struct rpmsg_kdrv_ethswitch_common_resp_info info;
+	u32 regval;
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_c2s_notify - notification request
+ *
+ * @header: msg header
+ * @info: common request msg data
+ * @notifyid: enum @rpmsg_kdrv_ethswitch_c2s_notify_type
+ * @notify_info_len: length of @notify_info
+ * @notify_info: notification message data
+ *
+ * The notification request message @RPMSG_KDRV_TP_ETHSWITCH_C2S_NOTIFY is one
+ * way message to Eth switch FW without response.
+ */
+struct rpmsg_kdrv_ethswitch_c2s_notify {
+	struct rpmsg_kdrv_ethswitch_msg_header header;
+	struct rpmsg_kdrv_ethswitch_common_req_info info;
+	u8 notifyid;
+	u32 notify_info_len;
+	u8 notify_info[RPMSG_KDRV_TP_ETHSWITCH_MESSAGE_DATA_LEN];
+} __packed;
+
+/**
+ * struct rpmsg_kdrv_ethswitch_fw_version_info - fw version info
+ *
+ * @major: major
+ * @minor: minor
+ * @rev: revision
+ * @year:
+ * @month:
+ * @date: build date
+ * @commit_hash: commit hash
+ */
+struct rpmsg_kdrv_ethswitch_fw_version_info {
+#define RPMSG_KDRV_TP_ETHSWITCH_YEARLEN		(4)
+#define RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN	(3)
+#define RPMSG_KDRV_TP_ETHSWITCH_DATELEN		(2)
+#define RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN	(8)
+	u32 major;
+	u32 minor;
+	u32 rev;
+	char year[RPMSG_KDRV_TP_ETHSWITCH_YEARLEN];
+	char month[RPMSG_KDRV_TP_ETHSWITCH_MONTHLEN];
+	char date[RPMSG_KDRV_TP_ETHSWITCH_DATELEN];
+	char commit_hash[RPMSG_KDRV_TP_ETHSWITCH_COMMITSHALEN];
+} __packed;
+
+/*
+ * per-device data for ethswitch device
+ */
+/**
+ * struct rpmsg_kdrv_ethswitch_device_data - rpmsg_kdrv_device data
+ *
+ * @fw_ver: fw version info
+ * @permission_flags: permission enabled for each
+ *	enum @rpmsg_kdrv_ethswitch_message_type command
+ * @uart_connected: flag indicating if UART is connected
+ * @uart_id: UART ID used by firmware for log prints
+ *
+ * Provided as part of RPMSG KDRV device discovery protocol
+ */
+struct rpmsg_kdrv_ethswitch_device_data {
+	struct rpmsg_kdrv_ethswitch_fw_version_info fw_ver;
+	u32 permission_flags;
+	u32 uart_connected;
+	u32 uart_id;
+} __packed;
+
+#endif /* DRIVERS_RPMSG_KDRV_SHARED_RPMSG_KDRV_TRANSPORT_SWITCH_H_ */

+ 69 - 1
include/linux/rpmsg-remotedev/rpmsg-remotedev.h

@@ -7,6 +7,8 @@
 #ifndef __RPMSG_REMOTEDEV_H__
 #define __RPMSG_REMOTEDEV_H__
 
+#include <linux/etherdevice.h>
+
 #define RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES		(3)
 #define RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS		(8)
 #define RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES		(8)
@@ -84,8 +86,71 @@ struct rpmsg_remotedev_display_ops {
 	int (*commit)(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_commit *commit);
 };
 
+#define RPMSG_RDEV_ETHSWITCH_CPSW_PRIORITY_NUM   (8)
+
+struct rpmsg_rdev_eth_switch_attach_info {
+	/* MTU of rx packet */
+	u32 rx_mtu;
+	/* MTU of tx packet per priority */
+	u32 tx_mtu[RPMSG_RDEV_ETHSWITCH_CPSW_PRIORITY_NUM];
+	/* Supported Features mask */
+	u32 features;
+#define RPMSG_KDRV_ETHSWITCH_FEATURE_TXCSUM BIT(0)
+#define RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS BIT(1)
+};
+
+struct rpmsg_rdev_eth_switch_attach_ext_info {
+	/* MTU of rx packet */
+	u32 rx_mtu;
+	/* MTU of tx packet per priority */
+	u32 tx_mtu[RPMSG_RDEV_ETHSWITCH_CPSW_PRIORITY_NUM];
+	/* Supported Features mask */
+	u32 features;
+#define RPMSG_KDRV_ETHSWITCH_FEATURE_TXCSUM BIT(0)
+#define RPMSG_KDRV_ETHSWITCH_FEATURE_DUMP_STATS BIT(1)
+	u32 flow_idx;
+	u32 tx_cpsw_psil_dst_id;
+	u8 mac_addr[ETH_ALEN];
+};
+
+struct rpmsg_rdev_eth_switch_tx_info {
+	/* Tx PSIL Peer destination thread id */
+	u32 tx_cpsw_psil_dst_id;
+};
+
+struct rpmsg_rdev_eth_switch_rx_info {
+	/* Allocated flow's index */
+	u32 flow_idx;
+};
+
+struct rpmsg_remotedev_eth_switch_ops {
+	void (*get_fw_ver)(struct rpmsg_remotedev *rdev,
+			   char *buf, size_t size);
+	int (*attach)(struct rpmsg_remotedev *rdev,
+		      struct rpmsg_rdev_eth_switch_attach_info *attach_info);
+	int (*attach_ext)(struct rpmsg_remotedev *rdev,
+			  struct rpmsg_rdev_eth_switch_attach_ext_info *attach_ext_info);
+	int (*detach)(struct rpmsg_remotedev *rdev);
+	int (*get_tx_info)(struct rpmsg_remotedev *rdev,
+			   struct rpmsg_rdev_eth_switch_tx_info *info);
+	int (*get_rx_info)(struct rpmsg_remotedev *rdev,
+			   struct rpmsg_rdev_eth_switch_rx_info *info);
+	int (*get_mac)(struct rpmsg_remotedev *rdev, void *mac_addr);
+	int (*register_mac)(struct rpmsg_remotedev *rdev,
+			    void *mac_addr, u32 flow_idx_offset);
+	int (*unregister_mac)(struct rpmsg_remotedev *rdev,
+			      void *mac_addr, u32 flow_idx_offset);
+	int (*register_ipv4)(struct rpmsg_remotedev *rdev,
+			     void *mac_addr, __be32 ipv4);
+	int (*unregister_ipv4)(struct rpmsg_remotedev *rdev, __be32 ipv4);
+	int (*ping)(struct rpmsg_remotedev *rdev, const u8 *data, int size);
+	int (*read_reg)(struct rpmsg_remotedev *rdev, u32 reg_addr, u32 *val);
+	int (*dbg_dump_stats)(struct rpmsg_remotedev *rdev);
+};
+
 enum rpmsg_remotedev_type {
 	RPMSG_REMOTEDEV_DISPLAY_DEVICE,
+	RPMSG_REMOTEDEV_ETH_SWITCH_DEVICE,
 };
 
 struct rpmsg_remotedev {
@@ -95,9 +160,12 @@ struct rpmsg_remotedev {
 			struct rpmsg_remotedev_display_ops *ops;
 			struct rpmsg_remotedev_display_cb *cb_ops;
 		} display;
+
+		struct {
+			struct rpmsg_remotedev_eth_switch_ops *ops;
+		} eth_switch;
 	} device;
 	void *cb_data;
-
 };
 
 #if IS_REACHABLE(CONFIG_RPMSG_KDRV)