slimbus: Provide separate APIs to connect source and sink ports
Source and sink ports for a channel may be connected from different
clients.
Multiple sink ports and 1 source port can be connected to a channel.
Ports are freed/zeroed when channel is removed.
Change-Id: I165bb9db4fb43a9a884ea14ffed08f8d11b65c39
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index 1ac301f..6733396 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -25,6 +25,8 @@
#define SLIM_HDL_TO_FLOW(hdl) (((u32)(hdl) & 0xFF0000) >> 16)
#define SLIM_HDL_TO_PORT(hdl) ((u32)(hdl) & 0xFF)
+#define SLIM_HDL_TO_CHIDX(hdl) ((u16)(hdl) & 0xFF)
+
#define SLIM_SLAVE_PORT(p, la) (((la)<<16) | (p))
#define SLIM_MGR_PORT(p) ((0xFF << 16) | (p))
#define SLIM_LA_MANAGER 0xFF
@@ -1104,75 +1106,117 @@
}
/*
- * slim_connect_ports: Connect port(s) to channel.
+ * slim_connect_src: Connect source port to channel.
* @sb: client handle
- * @srch: source handles to be connected to this channel
- * @nrsc: number of source ports
- * @sinkh: sink handle to be connected to this channel
+ * @srch: source handle to be connected to this channel
* @chanh: Channel with which the ports need to be associated with.
- * Per slimbus specification, a channel may have multiple source-ports and 1
- * sink port.Channel specified in chanh needs to be allocated first.
+ * Per slimbus specification, a channel may have 1 source port.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if source is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
*/
-int slim_connect_ports(struct slim_device *sb, u32 *srch, int nsrc, u32 sinkh,
- u16 chanh)
+int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh)
+{
+ struct slim_controller *ctrl = sb->ctrl;
+ int ret;
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+ struct slim_ich *slc = &ctrl->chans[chan];
+ enum slim_port_flow flow = SLIM_HDL_TO_FLOW(srch);
+
+ if (flow != SLIM_SRC)
+ return -EINVAL;
+
+ mutex_lock(&ctrl->m_ctrl);
+
+ if (slc->state == SLIM_CH_FREE) {
+ ret = -ENOTCONN;
+ goto connect_src_err;
+ }
+ /*
+ * Once channel is removed, its ports can be considered disconnected
+ * So its ports can be reassigned. Source port is zeroed
+ * when channel is deallocated.
+ */
+ if (slc->srch) {
+ ret = -EALREADY;
+ goto connect_src_err;
+ }
+
+ ret = connect_port_ch(ctrl, chan, srch, SLIM_SRC);
+
+ if (!ret)
+ slc->srch = srch;
+
+connect_src_err:
+ mutex_unlock(&ctrl->m_ctrl);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(slim_connect_src);
+
+/*
+ * slim_connect_sink: Connect sink port(s) to channel.
+ * @sb: client handle
+ * @sinkh: sink handle(s) to be connected to this channel
+ * @nsink: number of sinks
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have multiple sink-ports.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if sink is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ */
+int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink, u16 chanh)
{
struct slim_controller *ctrl = sb->ctrl;
int j;
int ret = 0;
- u8 chan = (u8)(chanh & 0xFF);
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh);
struct slim_ich *slc = &ctrl->chans[chan];
+ if (!sinkh || !nsink)
+ return -EINVAL;
+
mutex_lock(&ctrl->m_ctrl);
- /* Make sure the channel is not already pending reconf. or active */
- if (slc->state >= SLIM_CH_PENDING_ACTIVE) {
- dev_err(&ctrl->dev, "Channel %d already active", chan);
- ret = -EISCONN;
- goto connect_port_err;
- }
/*
* Once channel is removed, its ports can be considered disconnected
- * So its ports can be reassigned. Source port array is freed
- * when channel is deallocated.
+ * So its ports can be reassigned. Sink ports are freed when channel
+ * is deallocated.
*/
- slc->srch = krealloc(slc->srch, (sizeof(u32) * nsrc), GFP_KERNEL);
- if (!slc->srch) {
- ret = -ENOMEM;
- goto connect_port_err;
+ if (slc->state == SLIM_CH_FREE) {
+ ret = -ENOTCONN;
+ goto connect_sink_err;
}
- /* connect source */
- for (j = 0; j < nsrc; j++) {
- ret = connect_port_ch(ctrl, chan, srch[j], SLIM_SRC);
+ for (j = 0; j < nsink; j++) {
+ enum slim_port_flow flow = SLIM_HDL_TO_FLOW(sinkh[j]);
+ if (flow != SLIM_SINK)
+ ret = -EINVAL;
+ else
+ ret = connect_port_ch(ctrl, chan, sinkh[j], SLIM_SINK);
if (ret) {
- for ( ; j >= 0 ; j--)
- disconnect_port_ch(ctrl,
- srch[j]);
- kfree(slc->srch);
- slc->srch = NULL;
- goto connect_port_err;
+ for (j = j - 1; j >= 0; j--)
+ disconnect_port_ch(ctrl, sinkh[j]);
+ goto connect_sink_err;
}
}
- /* connect sink */
- ret = connect_port_ch(ctrl, chan, sinkh, SLIM_SINK);
- if (ret) {
- for (j = 0; j < nsrc; j++)
- disconnect_port_ch(ctrl, srch[j]);
- kfree(slc->srch);
- slc->srch = NULL;
- goto connect_port_err;
+
+ slc->sinkh = krealloc(slc->sinkh, (sizeof(u32) * (slc->nsink + nsink)),
+ GFP_KERNEL);
+ if (!slc->sinkh) {
+ ret = -ENOMEM;
+ for (j = 0; j < nsink; j++)
+ disconnect_port_ch(ctrl, sinkh[j]);
+ goto connect_sink_err;
}
- memcpy(slc->srch, srch, (sizeof(u32) * nsrc));
- slc->nsrc = nsrc;
- if (sinkh)
- slc->sinkh = sinkh;
+ memcpy(slc->sinkh + slc->nsink, sinkh, (sizeof(u32) * nsink));
+ slc->nsink += nsink;
-connect_port_err:
+connect_sink_err:
mutex_unlock(&ctrl->m_ctrl);
return ret;
}
-EXPORT_SYMBOL_GPL(slim_connect_ports);
+EXPORT_SYMBOL_GPL(slim_connect_sink);
/*
* slim_disconnect_ports: Disconnect port(s) from channel
@@ -1332,8 +1376,8 @@
slc->state = SLIM_CH_ALLOCATED;
slc->newintr = 0;
slc->newoff = 0;
- for (i = 0; i < slc->nsrc; i++) {
- ph = slc->srch[i];
+ for (i = 0; i < slc->nsink; i++) {
+ ph = slc->sinkh[i];
la = SLIM_HDL_TO_LA(ph);
/*
* For ports managed by manager's ported device, no need to send
@@ -1344,11 +1388,15 @@
ctrl->ports[SLIM_HDL_TO_PORT(ph)].state = SLIM_P_UNCFG;
}
- ph = slc->sinkh;
+ ph = slc->srch;
la = SLIM_HDL_TO_LA(ph);
if (la == SLIM_LA_MANAGER)
ctrl->ports[SLIM_HDL_TO_PORT(ph)].state = SLIM_P_UNCFG;
+ kfree(slc->sinkh);
+ slc->sinkh = NULL;
+ slc->srch = 0;
+ slc->nsink = 0;
return 0;
}
@@ -1608,7 +1656,7 @@
int slim_dealloc_ch(struct slim_device *sb, u16 chanh)
{
struct slim_controller *ctrl = sb->ctrl;
- u8 chan = (u8)(chanh & 0xFF);
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh);
struct slim_ich *slc = &ctrl->chans[chan];
if (!ctrl)
return -EINVAL;
@@ -1631,8 +1679,6 @@
return -EISCONN;
}
slc->ref--;
- kfree(slc->srch);
- slc->srch = NULL;
slc->state = SLIM_CH_FREE;
mutex_unlock(&ctrl->m_ctrl);
dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
@@ -1647,7 +1693,7 @@
*/
enum slim_ch_state slim_get_ch_state(struct slim_device *sb, u16 chanh)
{
- u8 chan = (u8)(chanh & 0xFF);
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh);
struct slim_ich *slc = &sb->ctrl->chans[chan];
return slc->state;
}
@@ -1678,7 +1724,7 @@
return -EINVAL;
mutex_lock(&ctrl->m_ctrl);
for (i = 0; i < nchan; i++) {
- u8 chan = (u8)(chanh[i] & 0xFF);
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
struct slim_ich *slc = &ctrl->chans[chan];
dev_dbg(&ctrl->dev, "define_ch: ch:%d, state:%d", chan,
(int)ctrl->chans[chan].state);
@@ -1713,7 +1759,7 @@
if (grp)
*grph = chanh[0];
for (i = 0; i < nchan; i++) {
- u8 chan = (u8)(chanh[i] & 0xFF);
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
struct slim_ich *slc = &ctrl->chans[chan];
if (slc->state == SLIM_CH_ALLOCATED)
slc->state = SLIM_CH_DEFINED;
@@ -2265,7 +2311,7 @@
for (; last > 0; last--) {
struct slim_ich *slc1 = slc;
struct slim_ich *slc2;
- u8 next = (u8)(slc1->nextgrp & 0xFF);
+ u8 next = SLIM_HDL_TO_CHIDX(slc1->nextgrp);
slc2 = &ctrl->chans[next];
for (second = 1; second <= last && slc2 &&
(slc2->state == SLIM_CH_ACTIVE ||
@@ -2280,7 +2326,7 @@
break;
}
slc1 = slc2;
- next = (u8)(slc1->nextgrp & 0xFF);
+ next = SLIM_HDL_TO_CHIDX(slc1->nextgrp);
slc2 = &ctrl->chans[next];
}
if (slc2 == NULL)
@@ -2692,7 +2738,7 @@
struct slim_ich *slc;
int ret = 0;
/* Get rid of the group flag in MSB if any */
- u8 chan = (u8)(chanh & 0xFF);
+ u8 chan = SLIM_HDL_TO_CHIDX(chanh);
mutex_lock(&sb->sldev_reconf);
mutex_lock(&ctrl->m_ctrl);
do {
@@ -2729,7 +2775,7 @@
}
if (!(slc->nextgrp & SLIM_END_GRP))
- chan = (u8)(slc->nextgrp & 0xFF);
+ chan = SLIM_HDL_TO_CHIDX(slc->nextgrp);
} while (!(slc->nextgrp & SLIM_END_GRP));
mutex_unlock(&ctrl->m_ctrl);
if (!ret && commit == true)
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index d161d41..448d9ab 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -378,9 +378,9 @@
* @seglen: Segment length of this channel.
* @rootexp: root exponent of this channel. Rate can be found using rootexp and
* coefficient. Used during scheduling.
- * @srch: Source ports used by this channel.
- * @nsrc: number of source ports used by this channel.
- * @sinkh: Sink port used by this channel.
+ * @srch: Source port used by this channel.
+ * @sinkh: Sink ports used by this channel.
+ * @nsink: number of sink ports used by this channel.
* @chan: Channel number sent on hardware lines for this channel. May not be
* equal to array-index into chans if client requested to use number beyond
* channel-array for the controller.
@@ -404,9 +404,9 @@
u32 newintr;
u32 seglen;
u8 rootexp;
- u32 *srch;
- int nsrc;
- u32 sinkh;
+ u32 srch;
+ u32 *sinkh;
+ int nsink;
u8 chan;
int ref;
int def;
@@ -760,19 +760,31 @@
u32 ph, u8 **done_buf, u32 *done_len);
/*
- * slim_connect_ports: Connect port(s) to channel.
+ * slim_connect_src: Connect source port to channel.
* @sb: client handle
- * @srch: source handles to be connected to this channel
- * @nrsc: number of source ports
- * @sinkh: sink handle to be connected to this channel
+ * @srch: source handle to be connected to this channel
* @chanh: Channel with which the ports need to be associated with.
- * Per slimbus specification, a channel may have multiple source-ports and 1
- * sink port.Channel specified in chanh needs to be allocated first.
+ * Per slimbus specification, a channel may have 1 source port.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if source is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
*/
-extern int slim_connect_ports(struct slim_device *sb, u32 *srch, int nsrc,
- u32 sinkh, u16 chanh);
+extern int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh);
/*
+ * slim_connect_sink: Connect sink port(s) to channel.
+ * @sb: client handle
+ * @sinkh: sink handle(s) to be connected to this channel
+ * @nsink: number of sinks
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have multiple sink-ports.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if sink is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ */
+extern int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink,
+ u16 chanh);
+/*
* slim_disconnect_ports: Disconnect port(s) from channel
* @sb: client handle
* @ph: ports to be disconnected