Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb741ea9 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mhi: dev: net_dev: add support for reserve side coalesced (rsc) channel"

parents 67e96864 84ed56cd
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -313,6 +313,13 @@ MHI netdev properties
  Definition: mhi net_device should have numbered alias in the alias node,
	in the form of mhi_netdevN, N = 0, 1..n for each network interface.

- mhi,disable-chain-skb
  Usage: optional
  Value type: <bool>
  Definition: If true, netdev will not chain skbs. Set this flag to true
	if netdev used as a standalone device. For example, for software IP
	path, chaining should be disabled.

========
Example:
========
+146 −32
Original line number Diff line number Diff line
@@ -72,20 +72,28 @@
			       __func__, ##__VA_ARGS__); \
} while (0)

struct mhi_net_chain {
	struct sk_buff *head, *tail; /* chained skb */
};

struct mhi_netdev {
	int alias;
	struct mhi_device *mhi_dev;
	struct mhi_netdev *rsc_dev; /* rsc linked node */
	bool is_rsc_dev;
	int wake;

	u32 mru;
	u32 order;
	const char *interface_name;
	struct napi_struct napi;
	struct napi_struct *napi;
	struct net_device *ndev;

	struct mhi_netbuf **netbuf_pool;
	int pool_size; /* must be power of 2 */
	int current_index;
	bool chain_skb;
	struct mhi_net_chain *chain;

	struct dentry *dentry;
	enum MHI_DEBUG_LEVEL msg_lvl;
@@ -332,27 +340,36 @@ static int mhi_netdev_poll(struct napi_struct *napi, int budget)
	struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev);
	struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev;
	struct mhi_device *mhi_dev = mhi_netdev->mhi_dev;
	struct mhi_netdev *rsc_dev = mhi_netdev->rsc_dev;
	struct mhi_net_chain *chain = mhi_netdev->chain;
	int rx_work = 0;

	MSG_VERB("Entered\n");

	rx_work = mhi_poll(mhi_dev, budget);

	/* chained skb, push it to stack */
	if (chain && chain->head) {
		netif_receive_skb(chain->head);
		chain->head = NULL;
	}

	if (rx_work < 0) {
		MSG_ERR("Error polling ret:%d\n", rx_work);
		rx_work = 0;
		napi_complete(napi);
		goto exit_poll;
		return 0;
	}

	/* queue new buffers */
	mhi_netdev_queue(mhi_netdev);

	if (rsc_dev)
		mhi_netdev_queue(rsc_dev);

	/* complete work if # of packet processed less than allocated budget */
	if (rx_work < budget)
		napi_complete(napi);

exit_poll:

	MSG_VERB("polled %d pkts\n", rx_work);

	return rx_work;
@@ -564,7 +581,14 @@ static int mhi_netdev_enable_iface(struct mhi_netdev *mhi_netdev)
	mhi_netdev_priv->mhi_netdev = mhi_netdev;
	rtnl_unlock();

	netif_napi_add(mhi_netdev->ndev, &mhi_netdev->napi,
	mhi_netdev->napi = devm_kzalloc(&mhi_dev->dev,
					sizeof(*mhi_netdev->napi), GFP_KERNEL);
	if (!mhi_netdev->napi) {
		ret = -ENOMEM;
		goto napi_alloc_fail;
	}

	netif_napi_add(mhi_netdev->ndev, mhi_netdev->napi,
		       mhi_netdev_poll, NAPI_POLL_WEIGHT);
	ret = register_netdev(mhi_netdev->ndev);
	if (ret) {
@@ -572,14 +596,16 @@ static int mhi_netdev_enable_iface(struct mhi_netdev *mhi_netdev)
		goto net_dev_reg_fail;
	}

	napi_enable(&mhi_netdev->napi);
	napi_enable(mhi_netdev->napi);

	MSG_LOG("Exited.\n");

	return 0;

net_dev_reg_fail:
	netif_napi_del(&mhi_netdev->napi);
	netif_napi_del(mhi_netdev->napi);

napi_alloc_fail:
	free_netdev(mhi_netdev->ndev);
	mhi_netdev->ndev = NULL;

@@ -626,8 +652,10 @@ static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev,
	struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev);
	struct mhi_netbuf *netbuf = mhi_result->buf_addr;
	struct mhi_buf *mhi_buf = &netbuf->mhi_buf;
	struct sk_buff *skb;
	struct net_device *ndev = mhi_netdev->ndev;
	struct device *dev = mhi_dev->dev.parent;
	struct mhi_net_chain *chain = mhi_netdev->chain;

	netbuf->unmap(dev, mhi_buf->dma_addr, mhi_buf->len, DMA_FROM_DEVICE);

@@ -640,7 +668,30 @@ static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev,
	ndev->stats.rx_packets++;
	ndev->stats.rx_bytes += mhi_result->bytes_xferd;

	if (unlikely(!chain)) {
		mhi_netdev_push_skb(mhi_netdev, mhi_buf, mhi_result);
		return;
	}

	/* we support chaining */
	skb = alloc_skb(0, GFP_ATOMIC);
	if (likely(skb)) {
		skb_add_rx_frag(skb, 0, mhi_buf->page, 0,
				mhi_result->bytes_xferd, mhi_netdev->mru);
		/* this is first on list */
		if (!chain->head) {
			skb->dev = ndev;
			skb->protocol =
				mhi_netdev_ip_type_trans(*(u8 *)mhi_buf->buf);
			chain->head = skb;
		} else {
			skb_shinfo(chain->tail)->frag_list = skb;
		}

		chain->tail = skb;
	} else {
		__free_pages(mhi_buf->page, mhi_netdev->order);
	}
}

static void mhi_netdev_status_cb(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb)
@@ -650,11 +701,7 @@ static void mhi_netdev_status_cb(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb)
	if (mhi_cb != MHI_CB_PENDING_DATA)
		return;

	if (napi_schedule_prep(&mhi_netdev->napi)) {
		__napi_schedule(&mhi_netdev->napi);
		return;
	}

	napi_schedule(mhi_netdev->napi);
}

#ifdef CONFIG_DEBUG_FS
@@ -702,10 +749,16 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev)

	MSG_LOG("Remove notification received\n");

	/* rsc parent takes cares of the cleanup */
	if (mhi_netdev->is_rsc_dev) {
		mhi_netdev_free_pool(mhi_netdev);
		return;
	}

	netif_stop_queue(mhi_netdev->ndev);
	napi_disable(&mhi_netdev->napi);
	napi_disable(mhi_netdev->napi);
	unregister_netdev(mhi_netdev->ndev);
	netif_napi_del(&mhi_netdev->napi);
	netif_napi_del(mhi_netdev->napi);
	free_netdev(mhi_netdev->ndev);
	mhi_netdev_free_pool(mhi_netdev);

@@ -713,14 +766,34 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev)
		debugfs_remove_recursive(mhi_netdev->dentry);
}

static int mhi_netdev_match(struct device *dev, void *data)
{
	/* if phandle dt == device dt, we found a match */
	return (dev->of_node == data);
}

static void mhi_netdev_clone_dev(struct mhi_netdev *mhi_netdev,
				 struct mhi_netdev *parent)
{
	mhi_netdev->ndev = parent->ndev;
	mhi_netdev->napi = parent->napi;
	mhi_netdev->ipc_log = parent->ipc_log;
	mhi_netdev->msg_lvl = parent->msg_lvl;
	mhi_netdev->ipc_log_lvl = parent->ipc_log_lvl;
	mhi_netdev->is_rsc_dev = true;
	mhi_netdev->chain = parent->chain;
}

static int mhi_netdev_probe(struct mhi_device *mhi_dev,
			    const struct mhi_device_id *id)
{
	int ret;
	struct mhi_netdev *mhi_netdev;
	struct mhi_netdev *mhi_netdev, *p_netdev = NULL;
	struct device_node *of_node = mhi_dev->dev.of_node;
	int nr_tre;
	char node_name[32];
	struct device_node *phandle;
	bool no_chain;

	if (!of_node)
		return -ENODEV;
@@ -742,20 +815,53 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
	if ((PAGE_SIZE << mhi_netdev->order) < mhi_netdev->mru)
		return -EINVAL;

	/* check if this device shared by a parent device */
	phandle = of_parse_phandle(of_node, "mhi,rsc-parent", 0);
	if (phandle) {
		struct device *dev;
		struct mhi_device *pdev;
		/* find the parent device */
		dev = driver_find_device(mhi_dev->dev.driver, NULL, phandle,
					 mhi_netdev_match);
		if (!dev)
			return -ENODEV;

		/* this device is shared with parent device. so we won't be
		 * creating a new network interface. Clone parent
		 * information to child node
		 */
		pdev = to_mhi_device(dev);
		p_netdev = mhi_device_get_devdata(pdev);
		mhi_netdev_clone_dev(mhi_netdev, p_netdev);
		put_device(dev);
	} else {
		mhi_netdev->msg_lvl = MHI_MSG_LVL_ERROR;
		no_chain = of_property_read_bool(of_node,
						 "mhi,disable-chain-skb");
		if (!no_chain) {
			mhi_netdev->chain = devm_kzalloc(&mhi_dev->dev,
						sizeof(*mhi_netdev->chain),
						GFP_KERNEL);
			if (!mhi_netdev->chain)
				return -ENOMEM;
		}

		ret = mhi_netdev_enable_iface(mhi_netdev);
		if (ret)
			return ret;

		/* create ipc log buffer */
	snprintf(node_name, sizeof(node_name), "%s_%04x_%02u.%02u.%02u_%u",
		 mhi_netdev->interface_name, mhi_dev->dev_id, mhi_dev->domain,
		 mhi_dev->bus, mhi_dev->slot, mhi_netdev->alias);
		snprintf(node_name, sizeof(node_name),
			 "%s_%04x_%02u.%02u.%02u_%u",
			 mhi_netdev->interface_name, mhi_dev->dev_id,
			 mhi_dev->domain, mhi_dev->bus, mhi_dev->slot,
			 mhi_netdev->alias);
		mhi_netdev->ipc_log = ipc_log_context_create(IPC_LOG_PAGES,
							     node_name, 0);
	mhi_netdev->msg_lvl = MHI_MSG_LVL_ERROR;
		mhi_netdev->ipc_log_lvl = IPC_LOG_LVL;

		mhi_netdev_create_debugfs(mhi_netdev);
	}

	/* move mhi channels to start state */
	ret = mhi_prepare_for_transfer(mhi_dev);
@@ -776,18 +882,25 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
	if (ret)
		goto error_start;

	/* link child node with parent node if it's children dev */
	if (p_netdev)
		p_netdev->rsc_dev = mhi_netdev;

	/* now we have a pool of buffers allocated, queue to hardware
	 * by triggering a napi_poll
	 */
	napi_schedule(&mhi_netdev->napi);
	napi_schedule(mhi_netdev->napi);

	return 0;

error_start:
	if (phandle)
		return ret;

	netif_stop_queue(mhi_netdev->ndev);
	napi_disable(&mhi_netdev->napi);
	napi_disable(mhi_netdev->napi);
	unregister_netdev(mhi_netdev->ndev);
	netif_napi_del(&mhi_netdev->napi);
	netif_napi_del(mhi_netdev->napi);
	free_netdev(mhi_netdev->ndev);

	return ret;
@@ -796,6 +909,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
static const struct mhi_device_id mhi_netdev_match_table[] = {
	{ .chan = "IP_HW0" },
	{ .chan = "IP_HW_ADPL" },
	{ .chan = "IP_HW0_RSC" },
	{},
};