diff options
author | Bjorn Andersson <bjorn.andersson@sonymobile.com> | 2016-02-18 01:39:06 -0500 |
---|---|---|
committer | Andy Gross <andy.gross@linaro.org> | 2016-03-30 18:21:12 -0400 |
commit | 028021d29ea069390e1f60c6aa5b3511d218454b (patch) | |
tree | f110524d269f059264b93a64b9f7223727a61461 /drivers/soc | |
parent | d5933855c0eb0a4103cf5db784cfdd4d7a85cd56 (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')
-rw-r--r-- | drivers/soc/qcom/smd.c | 76 |
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 | } |
1043 | EXPORT_SYMBOL(qcom_smd_driver_unregister); | 1045 | EXPORT_SYMBOL(qcom_smd_driver_unregister); |
1044 | 1046 | ||
1047 | static struct qcom_smd_channel * | ||
1048 | qcom_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 | */ | ||
1082 | struct 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 | } | ||
1116 | EXPORT_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) |