aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc/qcom
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@sonymobile.com>2016-02-18 01:39:06 -0500
committerAndy Gross <andy.gross@linaro.org>2016-03-30 18:21:12 -0400
commit028021d29ea069390e1f60c6aa5b3511d218454b (patch)
treef110524d269f059264b93a64b9f7223727a61461 /drivers/soc/qcom
parentd5933855c0eb0a4103cf5db784cfdd4d7a85cd56 (diff)
soc: qcom: smd: Support opening additional channels
With the qcom_smd_open_channel() API we allow SMD devices to open additional SMD channels, to allow implementation of multi-channel SMD devices - like Bluetooth. Channels are opened from the same edge as the calling SMD device is tied to. Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Andy Gross <andy.gross@linaro.org>
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r--drivers/soc/qcom/smd.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c
index c3fa0fd724f7..b6434c4be86a 100644
--- a/drivers/soc/qcom/smd.c
+++ b/drivers/soc/qcom/smd.c
@@ -129,6 +129,8 @@ struct qcom_smd_edge {
129 129
130 unsigned smem_available; 130 unsigned smem_available;
131 131
132 wait_queue_head_t new_channel_event;
133
132 struct work_struct scan_work; 134 struct work_struct scan_work;
133 struct work_struct state_work; 135 struct work_struct state_work;
134}; 136};
@@ -1042,6 +1044,77 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *qsdrv)
1042} 1044}
1043EXPORT_SYMBOL(qcom_smd_driver_unregister); 1045EXPORT_SYMBOL(qcom_smd_driver_unregister);
1044 1046
1047static struct qcom_smd_channel *
1048qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name)
1049{
1050 struct qcom_smd_channel *channel;
1051 struct qcom_smd_channel *ret = NULL;
1052 unsigned long flags;
1053 unsigned state;
1054
1055 spin_lock_irqsave(&edge->channels_lock, flags);
1056 list_for_each_entry(channel, &edge->channels, list) {
1057 if (strcmp(channel->name, name))
1058 continue;
1059
1060 state = GET_RX_CHANNEL_INFO(channel, state);
1061 if (state != SMD_CHANNEL_OPENING &&
1062 state != SMD_CHANNEL_OPENED)
1063 continue;
1064
1065 ret = channel;
1066 break;
1067 }
1068 spin_unlock_irqrestore(&edge->channels_lock, flags);
1069
1070 return ret;
1071}
1072
1073/**
1074 * qcom_smd_open_channel() - claim additional channels on the same edge
1075 * @sdev: smd_device handle
1076 * @name: channel name
1077 * @cb: callback method to use for incoming data
1078 *
1079 * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't
1080 * ready.
1081 */
1082struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev,
1083 const char *name,
1084 qcom_smd_cb_t cb)
1085{
1086 struct qcom_smd_channel *channel;
1087 struct qcom_smd_edge *edge = sdev->channel->edge;
1088 int ret;
1089
1090 /* Wait up to HZ for the channel to appear */
1091 ret = wait_event_interruptible_timeout(edge->new_channel_event,
1092 (channel = qcom_smd_find_channel(edge, name)) != NULL,
1093 HZ);
1094 if (!ret)
1095 return ERR_PTR(-ETIMEDOUT);
1096
1097 if (channel->state != SMD_CHANNEL_CLOSED) {
1098 dev_err(&sdev->dev, "channel %s is busy\n", channel->name);
1099 return ERR_PTR(-EBUSY);
1100 }
1101
1102 channel->qsdev = sdev;
1103 ret = qcom_smd_channel_open(channel, cb);
1104 if (ret) {
1105 channel->qsdev = NULL;
1106 return ERR_PTR(ret);
1107 }
1108
1109 /*
1110 * Append the list of channel to the channels associated with the sdev
1111 */
1112 list_add_tail(&channel->dev_list, &sdev->channel->dev_list);
1113
1114 return channel;
1115}
1116EXPORT_SYMBOL(qcom_smd_open_channel);
1117
1045/* 1118/*
1046 * Allocate the qcom_smd_channel object for a newly found smd channel, 1119 * Allocate the qcom_smd_channel object for a newly found smd channel,
1047 * retrieving and validating the smem items involved. 1120 * retrieving and validating the smem items involved.
@@ -1178,6 +1251,8 @@ static void qcom_channel_scan_worker(struct work_struct *work)
1178 1251
1179 dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); 1252 dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name);
1180 set_bit(i, edge->allocated[tbl]); 1253 set_bit(i, edge->allocated[tbl]);
1254
1255 wake_up_interruptible(&edge->new_channel_event);
1181 } 1256 }
1182 } 1257 }
1183 1258
@@ -1341,6 +1416,7 @@ static int qcom_smd_probe(struct platform_device *pdev)
1341 for_each_available_child_of_node(pdev->dev.of_node, node) { 1416 for_each_available_child_of_node(pdev->dev.of_node, node) {
1342 edge = &smd->edges[i++]; 1417 edge = &smd->edges[i++];
1343 edge->smd = smd; 1418 edge->smd = smd;
1419 init_waitqueue_head(&edge->new_channel_event);
1344 1420
1345 ret = qcom_smd_parse_edge(&pdev->dev, node, edge); 1421 ret = qcom_smd_parse_edge(&pdev->dev, node, edge);
1346 if (ret) 1422 if (ret)