clear-pkgs-linux-iot-lts2018/0195-SDW-Intel-Add-the-hand...

248 lines
7.5 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Hardik Shah <hardik.t.shah@intel.com>
Date: Fri, 29 Apr 2016 16:30:53 +0530
Subject: [PATCH] SDW: Intel: Add the handler for async message transfer.
Async message transfer is used by the bus driver
in case stream is split between two masters. Else
synchronous message transfer is always used.
Change-Id: I7fda7fd954f41941ab054cdc0c41de1e9ceca24a
Signed-off-by: Hardik Shah <hardik.t.shah@intel.com>
Reviewed-on:
---
drivers/sdw/sdw_cnl.c | 148 +++++++++++++++++++++++++++++++-----------
1 file changed, 109 insertions(+), 39 deletions(-)
diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c
index 21a61b4f010a..a5fc6db79f83 100644
--- a/drivers/sdw/sdw_cnl.c
+++ b/drivers/sdw/sdw_cnl.c
@@ -79,6 +79,12 @@ static inline void cnl_sdw_port_reg_writel(u32 __iomem *base, int offset,
return cnl_sdw_reg_writel(base, offset + port_num * 128, value);
}
+struct cnl_sdw_async_msg {
+ struct completion *async_xfer_complete;
+ struct sdw_msg *msg;
+ int length;
+};
+
struct cnl_sdw {
struct cnl_sdw_data data;
struct sdw_master *mstr;
@@ -100,6 +106,7 @@ struct cnl_sdw {
struct cnl_sdw_pdi_stream *out_pdm_streams;
struct mutex stream_lock;
spinlock_t ctrl_lock;
+ struct cnl_sdw_async_msg async_msg;
u32 response_buf[0x80];
bool sdw_link_status;
@@ -808,6 +815,46 @@ static void cnl_sdw_read_response(struct cnl_sdw *sdw)
}
}
+static enum sdw_command_response sdw_fill_message_response(
+ struct sdw_master *mstr,
+ struct sdw_msg *msg,
+ int count, int offset)
+{
+ int i, j;
+ int no_ack = 0, nack = 0;
+ struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr);
+
+ for (i = 0; i < count; i++) {
+ if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) {
+ no_ack = 1;
+ dev_err(&mstr->dev, "Ack not recevied\n");
+ if ((MCP_RESPONSE_NACK_MASK &
+ sdw->response_buf[i])) {
+ nack = 1;
+ dev_err(&mstr->dev, "NACK recevied\n");
+ }
+ }
+ break;
+ }
+ if (nack) {
+ dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr);
+ msg->len = 0;
+ return -EREMOTEIO;
+ } else if (no_ack) {
+ dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr);
+ msg->len = 0;
+ return -EREMOTEIO;
+ }
+ if (msg->flag == SDW_MSG_FLAG_WRITE)
+ return 0;
+ /* Response and Command has same base address */
+ for (j = 0; j < count; j++)
+ msg->buf[j + offset] =
+ (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT);
+ return 0;
+}
+
+
irqreturn_t cnl_sdw_irq_handler(int irq, void *context)
{
struct cnl_sdw *sdw = context;
@@ -840,7 +887,14 @@ irqreturn_t cnl_sdw_irq_handler(int irq, void *context)
if (int_status & (MCP_INTSTAT_RXWL_MASK << MCP_INTSTAT_RXWL_SHIFT)) {
cnl_sdw_read_response(sdw);
- complete(&sdw->tx_complete);
+ if (sdw->async_msg.async_xfer_complete) {
+ sdw_fill_message_response(mstr, sdw->async_msg.msg,
+ sdw->async_msg.length, 0);
+ complete(sdw->async_msg.async_xfer_complete);
+ sdw->async_msg.async_xfer_complete = NULL;
+ sdw->async_msg.msg = NULL;
+ } else
+ complete(&sdw->tx_complete);
}
if (int_status & (MCP_INTSTAT_CONTROLBUSCLASH_MASK <<
MCP_INTSTAT_CONTROLBUSCLASH_SHIFT)) {
@@ -945,16 +999,14 @@ static enum sdw_command_response cnl_program_scp_addr(struct sdw_master *mstr,
}
static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr,
- struct sdw_msg *msg, int cmd, int offset, int count)
+ struct sdw_msg *msg, int cmd, int offset, int count, bool async)
{
struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr);
struct cnl_sdw_data *data = &sdw->data;
- int i, j;
+ int j;
u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE;
- u32 response_base = SDW_CNL_MCP_RESPONSE_BASE;
- u32 cmd_data = 0, response_data;
+ u32 cmd_data = 0;
unsigned long time_left;
- int no_ack = 0, nack = 0;
u16 addr = msg->addr;
/* Program the watermark level upto number of count */
@@ -983,6 +1035,10 @@ static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr,
cmd_base, cmd_data);
cmd_base += SDW_CNL_CMD_WORD_LEN;
}
+
+ /* If Async dont wait for completion */
+ if (async)
+ return 0;
/* Wait for 3 second for timeout */
time_left = wait_for_completion_timeout(&sdw->tx_complete, 3 * HZ);
if (!time_left) {
@@ -990,39 +1046,52 @@ static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr,
msg->len = 0;
return -ETIMEDOUT;
}
- for (i = 0; i < count; i++) {
- if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) {
- no_ack = 1;
- dev_err(&mstr->dev, "Ack not recevied\n");
- if ((MCP_RESPONSE_NACK_MASK &
- sdw->response_buf[i])) {
- nack = 1;
- dev_err(&mstr->dev, "NACK recevied\n");
- }
- }
- break;
- }
- if (nack) {
- dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr);
- msg->len = 0;
- return -EREMOTEIO;
- } else if (no_ack) {
- dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr);
- msg->len = 0;
- return -EREMOTEIO;
+ return sdw_fill_message_response(mstr, msg, count, offset);
+}
+
+static enum sdw_command_response cnl_sdw_xfer_msg_async(struct sdw_master *mstr,
+ struct sdw_msg *msg, bool program_scp_addr_page,
+ struct sdw_async_xfer_data *data)
+{
+ int ret = 0, cmd;
+ struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr);
+
+ /* Only 1 message can be handled in Async fashion. This is used
+ * only for Bank switching where during aggregation it is required
+ * to synchronously switch the bank on more than 1 controller
+ */
+ if (msg->len > 1) {
+ ret = -EINVAL;
+ goto error;
}
- if (msg->flag == SDW_MSG_FLAG_WRITE)
- return 0;
- /* Response and Command has same base address */
- response_base = SDW_CNL_MCP_COMMAND_BASE;
- for (j = 0; j < count; j++) {
- response_data = cnl_sdw_reg_readl(data->sdw_regs,
- cmd_base);
- msg->buf[j + offset] =
- (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT);
- cmd_base += 4;
+ /* If scp addr programming fails goto error */
+ if (program_scp_addr_page)
+ ret = cnl_program_scp_addr(mstr, msg);
+ if (ret)
+ goto error;
+
+ switch (msg->flag) {
+ case SDW_MSG_FLAG_READ:
+ cmd = 0x2;
+ break;
+ case SDW_MSG_FLAG_WRITE:
+ cmd = 0x3;
+ break;
+ default:
+ dev_err(&mstr->dev, "Command not supported\n");
+ return -EINVAL;
}
- return 0;
+ sdw->async_msg.async_xfer_complete = &data->xfer_complete;
+ sdw->async_msg.msg = msg;
+ sdw->async_msg.length = msg->len;
+ /* Dont wait for reply, calling function will wait for reply. */
+ ret = sdw_xfer_msg(mstr, msg, cmd, 0, msg->len, true);
+ return ret;
+error:
+ msg->len = 0;
+ complete(&data->xfer_complete);
+ return -EINVAL;
+
}
static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr,
@@ -1052,14 +1121,14 @@ static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr,
for (i = 0; i < msg->len / SDW_CNL_MCP_COMMAND_LENGTH; i++) {
ret = sdw_xfer_msg(mstr, msg,
cmd, i * SDW_CNL_MCP_COMMAND_LENGTH,
- SDW_CNL_MCP_COMMAND_LENGTH);
+ SDW_CNL_MCP_COMMAND_LENGTH, false);
if (ret < 0)
break;
}
if (!(msg->len % SDW_CNL_MCP_COMMAND_LENGTH))
return ret;
ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH,
- msg->len % SDW_CNL_MCP_COMMAND_LENGTH);
+ msg->len % SDW_CNL_MCP_COMMAND_LENGTH, false);
if (ret < 0)
return -EINVAL;
return ret;
@@ -1561,6 +1630,7 @@ static const struct dev_pm_ops cnl_sdw_pm_ops = {
};
static struct sdw_master_ops cnl_sdw_master_ops = {
+ .xfer_msg_async = cnl_sdw_xfer_msg_async,
.xfer_msg = cnl_sdw_xfer_msg,
.xfer_bulk = cnl_sdw_xfer_bulk,
.monitor_handover = cnl_sdw_mon_handover,
--
https://clearlinux.org