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

Commit 60852402 authored by Sagar Dharia's avatar Sagar Dharia
Browse files

slim_msm: support non-blocking writes from slimbus MSM controller



Provide non-blocking write support so that clients can queue multiple
writes, or larger transfers without performance implications.
TX buffer is created and descriptors out of this buffer
are submitted to HW to transmit data.
Descriptor is returned to 'available' pool of TX buffer when HW
interrupt for transmission status is recieved.

Change-Id: Icc2af6bf4f770c8cadb9bd2ef7be0f95eb22c5d8
Signed-off-by: default avatarSagar Dharia <sdharia@codeaurora.org>
parent 4712ce04
Loading
Loading
Loading
Loading
+14 −12
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -207,8 +207,7 @@ static irqreturn_t msm_slim_interrupt(int irq, void *d)
		 * signalling completion/exiting ISR
		 */
		mb();
		if (dev->wr_comp)
			complete(dev->wr_comp);
		msm_slim_manage_tx_msgq(dev, false, NULL);
	}
	if (stat & MGR_INT_RX_MSG_RCVD) {
		u32 rx_buf[10];
@@ -372,8 +371,7 @@ static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
		}
	}
	txn->rl--;
	pbuf = msm_get_msg_buf(dev, txn->rl);
	dev->wr_comp = NULL;
	pbuf = msm_get_msg_buf(dev, txn->rl, &done);
	dev->err = 0;

	if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
@@ -438,11 +436,8 @@ static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
	if (txn->mt == SLIM_MSG_MT_CORE &&
		mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION)
		dev->reconf_busy = true;
	dev->wr_comp = &done;
	msm_send_msg_buf(dev, pbuf, txn->rl, MGR_TX_MSG);
	timeout = wait_for_completion_timeout(&done, HZ);
	if (!timeout)
		dev->wr_comp = NULL;
	if (mc == SLIM_MSG_MC_RECONFIGURE_NOW) {
		if ((txn->mc == (SLIM_MSG_MC_RECONFIGURE_NOW |
					SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
@@ -505,7 +500,9 @@ static int msm_set_laddr(struct slim_controller *ctrl, const u8 *ea,
retry_laddr:
	init_completion(&done);
	mutex_lock(&dev->tx_lock);
	buf = msm_get_msg_buf(dev, 9);
	buf = msm_get_msg_buf(dev, 9, &done);
	if (buf == NULL)
		return -ENOMEM;
	buf[0] = SLIM_MSG_ASM_FIRST_WORD(9, SLIM_MSG_MT_CORE,
					SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS,
					SLIM_MSG_DEST_LOGICALADDR,
@@ -513,7 +510,6 @@ retry_laddr:
	buf[1] = ea[3] | (ea[2] << 8) | (ea[1] << 16) | (ea[0] << 24);
	buf[2] = laddr;

	dev->wr_comp = &done;
	ret = msm_send_msg_buf(dev, buf, 9, MGR_TX_MSG);
	timeout = wait_for_completion_timeout(&done, HZ);
	if (!timeout)
@@ -521,7 +517,6 @@ retry_laddr:
	if (dev->err) {
		ret = dev->err;
		dev->err = 0;
		dev->wr_comp = NULL;
	}
	mutex_unlock(&dev->tx_lock);
	if (ret) {
@@ -1183,6 +1178,10 @@ static int msm_slim_probe(struct platform_device *pdev)
		ret = -ENOMEM;
		goto err_get_res_failed;
	}
	dev->wr_comp = kzalloc(sizeof(struct completion *) * MSM_TX_BUFS,
				GFP_KERNEL);
	if (!dev->wr_comp)
		return -ENOMEM;
	dev->dev = &pdev->dev;
	platform_set_drvdata(pdev, dev);
	slim_set_ctrldata(&dev->ctrl, dev);
@@ -1271,7 +1270,8 @@ static int msm_slim_probe(struct platform_device *pdev)
	dev->ctrl.dev.parent = &pdev->dev;
	dev->ctrl.dev.of_node = pdev->dev.of_node;

	ret = request_irq(dev->irq, msm_slim_interrupt, IRQF_TRIGGER_HIGH,
	ret = request_threaded_irq(dev->irq, NULL, msm_slim_interrupt,
				IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
				"msm_slim_irq", dev);
	if (ret) {
		dev_err(&pdev->dev, "request IRQ failed\n");
@@ -1400,6 +1400,7 @@ err_of_init_failed:
err_ioremap_bam_failed:
	iounmap(dev->base);
err_ioremap_failed:
	kfree(dev->wr_comp);
	kfree(dev);
err_get_res_failed:
	release_mem_region(bam_mem->start, resource_size(bam_mem));
@@ -1437,6 +1438,7 @@ static int msm_slim_remove(struct platform_device *pdev)
	kthread_stop(dev->rx_msgq_thread);
	iounmap(dev->bam.base);
	iounmap(dev->base);
	kfree(dev->wr_comp);
	kfree(dev);
	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						"slimbus_bam_physical");
+33 −10
Original line number Diff line number Diff line
@@ -101,15 +101,13 @@ static irqreturn_t ngd_slim_interrupt(int irq, void *d)
								dev->err);
		/* Guarantee that error interrupts are cleared */
		mb();
		if (dev->wr_comp)
			complete(dev->wr_comp);
		msm_slim_manage_tx_msgq(dev, false, NULL);

	} else if (stat & NGD_INT_TX_MSG_SENT) {
		writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
		/* Make sure interrupt is cleared */
		mb();
		if (dev->wr_comp)
			complete(dev->wr_comp);
		msm_slim_manage_tx_msgq(dev, false, NULL);
	}
	if (stat & NGD_INT_RX_MSG_RCVD) {
		u32 rx_buf[10];
@@ -286,6 +284,7 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
	u16 txn_mc = txn->mc;
	u8 wbuf[SLIM_MSGQ_BUF_LEN];
	bool report_sat = false;
	bool sync_wr = true;

	if (txn->mc == SLIM_USR_MC_REPORT_SATELLITE &&
		txn->mt == SLIM_MSG_MT_SRC_REFERRED_USER)
@@ -439,7 +438,25 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
		txn->rl = txn->len + 4;
	}
	txn->rl--;
	pbuf = msm_get_msg_buf(dev, txn->rl);

	if (txn->mt == SLIM_MSG_MT_CORE && txn->comp &&
		dev->use_tx_msgqs == MSM_MSGQ_ENABLED &&
		(txn_mc != SLIM_MSG_MC_REQUEST_INFORMATION &&
		 txn_mc != SLIM_MSG_MC_REQUEST_VALUE &&
		 txn_mc != SLIM_MSG_MC_REQUEST_CHANGE_VALUE &&
		 txn_mc != SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION)) {
		sync_wr = false;
		pbuf = msm_get_msg_buf(dev, txn->rl, txn->comp);
	} else if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
			dev->use_tx_msgqs == MSM_MSGQ_ENABLED &&
			txn->mc == SLIM_USR_MC_REPEAT_CHANGE_VALUE &&
			txn->comp) {
		sync_wr = false;
		pbuf = msm_get_msg_buf(dev, txn->rl, txn->comp);
	} else {
		pbuf = msm_get_msg_buf(dev, txn->rl, &tx_sent);
	}

	if (!pbuf) {
		SLIM_ERR(dev, "Message buffer unavailable\n");
		ret = -ENOMEM;
@@ -510,10 +527,9 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
	 */
	txn_mc = txn->mc;
	txn_mt = txn->mt;
	dev->wr_comp = &tx_sent;
	ret = msm_send_msg_buf(dev, pbuf, txn->rl,
			NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
	if (!ret) {
	if (!ret && sync_wr) {
		int timeout = wait_for_completion_timeout(&tx_sent, HZ);
		if (!timeout) {
			ret = -ETIMEDOUT;
@@ -529,7 +545,6 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
			ret = dev->err;
		}
	}
	dev->wr_comp = NULL;
	if (ret) {
		u32 conf, stat, rx_msgq, int_stat, int_en, int_clr;
		void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr,
@@ -1296,6 +1311,10 @@ static int ngd_slim_probe(struct platform_device *pdev)
		dev_err(&pdev->dev, "no memory for MSM slimbus controller\n");
		return PTR_ERR(dev);
	}
	dev->wr_comp = kzalloc(sizeof(struct completion *) * MSM_TX_BUFS,
				GFP_KERNEL);
	if (!dev->wr_comp)
		return -ENOMEM;
	dev->dev = &pdev->dev;
	platform_set_drvdata(pdev, dev);
	slim_set_ctrldata(&dev->ctrl, dev);
@@ -1379,6 +1398,7 @@ static int ngd_slim_probe(struct platform_device *pdev)
	init_completion(&dev->reconf);
	init_completion(&dev->ctrl_up);
	mutex_init(&dev->tx_lock);
	mutex_init(&dev->tx_buf_lock);
	spin_lock_init(&dev->rx_lock);
	dev->ee = 1;
	dev->irq = irq->start;
@@ -1406,8 +1426,9 @@ static int ngd_slim_probe(struct platform_device *pdev)
	dev->ctrl.dev.of_node = pdev->dev.of_node;
	dev->state = MSM_CTRL_DOWN;

	ret = request_irq(dev->irq, ngd_slim_interrupt,
			IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
	ret = request_threaded_irq(dev->irq, NULL,
			ngd_slim_interrupt,
			IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "ngd_slim_irq", dev);

	if (ret) {
		dev_err(&pdev->dev, "request IRQ failed\n");
@@ -1470,6 +1491,7 @@ err_ioremap_failed:
	if (dev->sysfs_created)
		sysfs_remove_file(&dev->dev->kobj,
				&dev_attr_debug_mask.attr);
	kfree(dev->wr_comp);
	kfree(dev);
	return ret;
}
@@ -1492,6 +1514,7 @@ static int ngd_slim_remove(struct platform_device *pdev)
	kthread_stop(dev->rx_msgq_thread);
	iounmap(dev->bam.base);
	iounmap(dev->base);
	kfree(dev->wr_comp);
	kfree(dev);
	return 0;
}
+110 −22
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 */
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/slimbus/slimbus.h>
#include <linux/msm-sps.h>
#include "slim-msm.h"
@@ -390,9 +391,9 @@ static int msm_slim_post_tx_msgq(struct msm_slim_ctrl *dev, u8 *buf, int len)
	struct msm_slim_endp *endpoint = &dev->tx_msgq;
	struct sps_mem_buffer *mem = &endpoint->buf;
	struct sps_pipe *pipe = endpoint->sps;
	int ix = (buf - (u8 *)mem->base) / SLIM_MSGQ_BUF_LEN;
	int ix = (buf - (u8 *)mem->base);

	phys_addr_t phys_addr = mem->phys_base + (SLIM_MSGQ_BUF_LEN * ix);
	phys_addr_t phys_addr = mem->phys_base + ix;

	for (ret = 0; ret < ((len + 3) >> 2); ret++)
		pr_debug("BAM TX buf[%d]:0x%x", ret, ((u32 *)buf)[ret]);
@@ -405,29 +406,110 @@ static int msm_slim_post_tx_msgq(struct msm_slim_ctrl *dev, u8 *buf, int len)
	return ret;
}

static u32 *msm_slim_tx_msgq_return(struct msm_slim_ctrl *dev)
void msm_slim_tx_msg_return(struct msm_slim_ctrl *dev)
{
	struct msm_slim_endp *endpoint = &dev->tx_msgq;
	struct sps_mem_buffer *mem = &endpoint->buf;
	struct sps_pipe *pipe = endpoint->sps;
	struct sps_iovec iovec;
	int ret;

	/* first transaction after establishing connection */
	if (dev->tx_idx == -1) {
		dev->tx_idx = 0;
		return mem->base;
	int idx, ret = 0;
	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
		/* use 1 buffer, non-blocking writes are not possible */
		if (dev->wr_comp[0]) {
			struct completion *comp = dev->wr_comp[0];
			dev->wr_comp[0] = NULL;
			complete(comp);
		}
		return;
	}
	while (!ret) {
		ret = sps_get_iovec(pipe, &iovec);
		if (ret || iovec.addr == 0) {
		dev_err(dev->dev, "sps_get_iovec() failed 0x%x\n", ret);
			if (ret)
				pr_err("SLIM TX get IOVEC failed:%d", ret);
			return;
		}
		idx = (int) ((iovec.addr - mem->phys_base) / SLIM_MSGQ_BUF_LEN);
		if (idx < MSM_TX_BUFS && dev->wr_comp[idx]) {
			struct completion *comp = dev->wr_comp[idx];
			dev->wr_comp[idx] = NULL;
			complete(comp);
		}
		/* reclaim all packets that were delivered out of order */
		if (idx != dev->tx_head)
			pr_err("SLIM OUT OF ORDER TX:idx:%d, head:%d", idx,
								dev->tx_head);
		while (idx == dev->tx_head) {
			dev->tx_head = (dev->tx_head + 1) % MSM_TX_BUFS;
			idx++;
			if (dev->tx_head == dev->tx_tail ||
					dev->wr_comp[idx] != NULL)
				break;
		}
	}
}

static u32 *msm_slim_modify_tx_buf(struct msm_slim_ctrl *dev,
					struct completion *comp)
{
	struct msm_slim_endp *endpoint = &dev->tx_msgq;
	struct sps_mem_buffer *mem = &endpoint->buf;
	u32 *retbuf = NULL;
	if ((dev->tx_tail + 1) % MSM_TX_BUFS == dev->tx_head)
		return NULL;

	retbuf = (u32 *)((u8 *)mem->base +
				(dev->tx_tail * SLIM_MSGQ_BUF_LEN));
	dev->wr_comp[dev->tx_tail] = comp;
	dev->tx_tail = (dev->tx_tail + 1) % MSM_TX_BUFS;
	return retbuf;
}
u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
					struct completion *comp)
{
	int ret = 0;
	int retries = 0;
	u32 *retbuf = NULL;

	/* Calculate buffer index */
	dev->tx_idx = ((int)(iovec.addr - mem->phys_base)) / SLIM_MSGQ_BUF_LEN;
	mutex_lock(&dev->tx_buf_lock);
	if (!getbuf) {
		msm_slim_tx_msg_return(dev);
		mutex_unlock(&dev->tx_buf_lock);
		return NULL;
	}

	retbuf = msm_slim_modify_tx_buf(dev, comp);
	if (retbuf) {
		mutex_unlock(&dev->tx_buf_lock);
		return retbuf;
	}

	return (u32 *)((u8 *)mem->base + (dev->tx_idx * SLIM_MSGQ_BUF_LEN));
	do {
		msm_slim_tx_msg_return(dev);
		retbuf = msm_slim_modify_tx_buf(dev, comp);
		if (!retbuf)
			ret = -EAGAIN;
		else {
			if (retries > 0)
				SLIM_INFO(dev, "SLIM TX retrieved:%d retries",
							retries);
			mutex_unlock(&dev->tx_buf_lock);
			return retbuf;
		}

		/*
		 * superframe size will vary based on clock gear
		 * 1 superframe will consume at least 1 message
		 * if HW is in good condition. With MX_RETRIES,
		 * make sure we wait for a [3, 10] superframes
		 * before deciding HW couldn't process descriptors
		 */
		usleep_range(100, 250);
		retries++;
	} while (ret && (retries < INIT_MX_RETRIES));

	mutex_unlock(&dev->tx_buf_lock);
	return NULL;
}

int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg)
@@ -445,16 +527,19 @@ int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg)
	return msm_slim_post_tx_msgq(dev, (u8 *)buf, len);
}

u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len)
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
			struct completion *comp)
{
	/*
	 * Currently we block a transaction until the current one completes.
	 * In case we need multiple transactions, use message Q
	 */
	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED)
	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
		dev->wr_comp[0] = comp;
		return dev->tx_buf;
	}

	return msm_slim_tx_msgq_return(dev);
	return msm_slim_manage_tx_msgq(dev, true, comp);
}

static void
@@ -604,7 +689,8 @@ int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
		}
		dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
	} else {
		dev->tx_idx = -1;
		dev->tx_tail = 0;
		dev->tx_head = 0;
		dev->use_tx_msgqs = MSM_MSGQ_ENABLED;
	}

@@ -711,16 +797,18 @@ static int msm_slim_init_tx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
	config->options = SPS_O_ERROR | SPS_O_NO_Q |
				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;

	/* Desc and TX buf are circular queues */
	/* Allocate memory for the FIFO descriptors */
	ret = msm_slim_sps_mem_alloc(dev, descr,
				MSM_TX_BUFS * sizeof(struct sps_iovec));
				(MSM_TX_BUFS + 1) * sizeof(struct sps_iovec));
	if (ret) {
		dev_err(dev->dev, "unable to allocate SPS descriptors\n");
		goto alloc_descr_failed;
	}

	/* Allocate memory for the message buffer(s), N descrs, 40-byte mesg */
	ret = msm_slim_sps_mem_alloc(dev, mem, MSM_TX_BUFS * SLIM_MSGQ_BUF_LEN);
	/* Allocate TX buffer from which descriptors are created */
	ret = msm_slim_sps_mem_alloc(dev, mem, ((MSM_TX_BUFS + 1) *
					SLIM_MSGQ_BUF_LEN));
	if (ret) {
		dev_err(dev->dev, "dma_alloc_coherent failed\n");
		goto alloc_buffer_failed;
+9 −4
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@
/* Per spec.max 40 bytes per received message */
#define SLIM_MSGQ_BUF_LEN	40

#define MSM_TX_BUFS	2
#define MSM_TX_BUFS		32

#define SLIM_USR_MC_GENERIC_ACK		0x25
#define SLIM_USR_MC_MASTER_CAPABILITY	0x0
@@ -236,14 +236,15 @@ struct msm_slim_ctrl {
	u8			msg_cnt;
	u32			tx_buf[10];
	u8			rx_msgs[MSM_CONCUR_MSG][SLIM_MSGQ_BUF_LEN];
	int			tx_idx;
	int			tx_tail;
	int			tx_head;
	spinlock_t		rx_lock;
	int			head;
	int			tail;
	int			irq;
	int			err;
	int			ee;
	struct completion	*wr_comp;
	struct completion	**wr_comp;
	struct msm_slim_sat	*satd[MSM_MAX_NSATS];
	struct msm_slim_endp	pipes[7];
	struct msm_slim_sps_bam	bam;
@@ -254,6 +255,7 @@ struct msm_slim_ctrl {
	struct clk		*rclk;
	struct clk		*hclk;
	struct mutex		tx_lock;
	struct mutex		tx_buf_lock;
	u8			pgdla;
	enum msm_slim_msgq	use_rx_msgqs;
	enum msm_slim_msgq	use_tx_msgqs;
@@ -372,7 +374,10 @@ enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
			u32 len, struct completion *comp);
int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg);
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len);
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
			struct completion *comp);
u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
			struct completion *comp);
int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
			u32 pipe_reg, bool remote);
+1 −1
Original line number Diff line number Diff line
@@ -1088,7 +1088,7 @@ int slim_xfer_msg(struct slim_controller *ctrl, struct slim_device *sbdev,
	} else
		ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR, mc, ec,
				SLIM_MSG_MT_CORE, rbuf, wbuf, len, mlen,
				NULL, sbdev->laddr, NULL);
				msg->comp, sbdev->laddr, NULL);
xfer_err:
	return ret;
}