Merge "slim_msm: support non-blocking writes from slimbus MSM controller"
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index c662a2b..924a028 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -1,4 +1,4 @@
-/* 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 @@
* 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 @@
}
}
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 @@
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 @@
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 @@
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 @@
if (dev->err) {
ret = dev->err;
dev->err = 0;
- dev->wr_comp = NULL;
}
mutex_unlock(&dev->tx_lock);
if (ret) {
@@ -1183,6 +1178,10 @@
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 @@
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_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 @@
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");
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index c29b71e..2498298 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -101,15 +101,13 @@
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 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
kthread_stop(dev->rx_msgq_thread);
iounmap(dev->bam.base);
iounmap(dev->base);
+ kfree(dev->wr_comp);
kfree(dev);
return 0;
}
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 915bf88..11741b8 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -11,6 +11,7 @@
*/
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
+#include <linux/delay.h>
#include <linux/slimbus/slimbus.h>
#include <mach/sps.h>
#include "slim-msm.h"
@@ -390,9 +391,9 @@
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 @@
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;
}
- ret = sps_get_iovec(pipe, &iovec);
- if (ret || iovec.addr == 0) {
- dev_err(dev->dev, "sps_get_iovec() failed 0x%x\n", ret);
+ while (!ret) {
+ ret = sps_get_iovec(pipe, &iovec);
+ if (ret || iovec.addr == 0) {
+ 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;
+
+ mutex_lock(&dev->tx_buf_lock);
+ if (!getbuf) {
+ msm_slim_tx_msg_return(dev);
+ mutex_unlock(&dev->tx_buf_lock);
return NULL;
}
- /* Calculate buffer index */
- dev->tx_idx = ((int)(iovec.addr - mem->phys_base)) / SLIM_MSGQ_BUF_LEN;
+ 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 @@
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 @@
}
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 @@
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;
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 9673208..5f12f24 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -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 @@
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 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 @@
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);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index fecf5ec..27b92ba 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1100,7 +1100,7 @@
} 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;
}