diff options
-rw-r--r-- | MAINTAINERS | 3 | ||||
-rw-r--r-- | drivers/soc/qcom/smd.c | 229 | ||||
-rw-r--r-- | drivers/soc/qcom/spm.c | 8 | ||||
-rw-r--r-- | include/linux/soc/qcom/smd.h | 8 | ||||
-rw-r--r-- | include/linux/soc/qcom/smem_state.h | 35 |
5 files changed, 220 insertions, 63 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 03e00c7c88eb..ce1769341475 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1470,7 +1470,10 @@ F: arch/arm/boot/dts/qcom-*.dts | |||
1470 | F: arch/arm/boot/dts/qcom-*.dtsi | 1470 | F: arch/arm/boot/dts/qcom-*.dtsi |
1471 | F: arch/arm/mach-qcom/ | 1471 | F: arch/arm/mach-qcom/ |
1472 | F: arch/arm64/boot/dts/qcom/* | 1472 | F: arch/arm64/boot/dts/qcom/* |
1473 | F: drivers/i2c/busses/i2c-qup.c | ||
1474 | F: drivers/clk/qcom/ | ||
1473 | F: drivers/soc/qcom/ | 1475 | F: drivers/soc/qcom/ |
1476 | F: drivers/spi/spi-qup.c | ||
1474 | F: drivers/tty/serial/msm_serial.h | 1477 | F: drivers/tty/serial/msm_serial.h |
1475 | F: drivers/tty/serial/msm_serial.c | 1478 | F: drivers/tty/serial/msm_serial.c |
1476 | F: drivers/*/pm8???-* | 1479 | F: drivers/*/pm8???-* |
diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c index 498fd0581a45..b6434c4be86a 100644 --- a/drivers/soc/qcom/smd.c +++ b/drivers/soc/qcom/smd.c | |||
@@ -106,9 +106,9 @@ static const struct { | |||
106 | * @channels: list of all channels detected on this edge | 106 | * @channels: list of all channels detected on this edge |
107 | * @channels_lock: guard for modifications of @channels | 107 | * @channels_lock: guard for modifications of @channels |
108 | * @allocated: array of bitmaps representing already allocated channels | 108 | * @allocated: array of bitmaps representing already allocated channels |
109 | * @need_rescan: flag that the @work needs to scan smem for new channels | ||
110 | * @smem_available: last available amount of smem triggering a channel scan | 109 | * @smem_available: last available amount of smem triggering a channel scan |
111 | * @work: work item for edge house keeping | 110 | * @scan_work: work item for discovering new channels |
111 | * @state_work: work item for edge state changes | ||
112 | */ | 112 | */ |
113 | struct qcom_smd_edge { | 113 | struct qcom_smd_edge { |
114 | struct qcom_smd *smd; | 114 | struct qcom_smd *smd; |
@@ -127,10 +127,12 @@ struct qcom_smd_edge { | |||
127 | 127 | ||
128 | DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE); | 128 | DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE); |
129 | 129 | ||
130 | bool need_rescan; | ||
131 | unsigned smem_available; | 130 | unsigned smem_available; |
132 | 131 | ||
133 | struct work_struct work; | 132 | wait_queue_head_t new_channel_event; |
133 | |||
134 | struct work_struct scan_work; | ||
135 | struct work_struct state_work; | ||
134 | }; | 136 | }; |
135 | 137 | ||
136 | /* | 138 | /* |
@@ -186,13 +188,14 @@ struct qcom_smd_channel { | |||
186 | int fifo_size; | 188 | int fifo_size; |
187 | 189 | ||
188 | void *bounce_buffer; | 190 | void *bounce_buffer; |
189 | int (*cb)(struct qcom_smd_device *, const void *, size_t); | 191 | qcom_smd_cb_t cb; |
190 | 192 | ||
191 | spinlock_t recv_lock; | 193 | spinlock_t recv_lock; |
192 | 194 | ||
193 | int pkt_size; | 195 | int pkt_size; |
194 | 196 | ||
195 | struct list_head list; | 197 | struct list_head list; |
198 | struct list_head dev_list; | ||
196 | }; | 199 | }; |
197 | 200 | ||
198 | /** | 201 | /** |
@@ -378,6 +381,19 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) | |||
378 | } | 381 | } |
379 | 382 | ||
380 | /* | 383 | /* |
384 | * Set the callback for a channel, with appropriate locking | ||
385 | */ | ||
386 | static void qcom_smd_channel_set_callback(struct qcom_smd_channel *channel, | ||
387 | qcom_smd_cb_t cb) | ||
388 | { | ||
389 | unsigned long flags; | ||
390 | |||
391 | spin_lock_irqsave(&channel->recv_lock, flags); | ||
392 | channel->cb = cb; | ||
393 | spin_unlock_irqrestore(&channel->recv_lock, flags); | ||
394 | }; | ||
395 | |||
396 | /* | ||
381 | * Calculate the amount of data available in the rx fifo | 397 | * Calculate the amount of data available in the rx fifo |
382 | */ | 398 | */ |
383 | static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel) | 399 | static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel) |
@@ -601,7 +617,8 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) | |||
601 | struct qcom_smd_edge *edge = data; | 617 | struct qcom_smd_edge *edge = data; |
602 | struct qcom_smd_channel *channel; | 618 | struct qcom_smd_channel *channel; |
603 | unsigned available; | 619 | unsigned available; |
604 | bool kick_worker = false; | 620 | bool kick_scanner = false; |
621 | bool kick_state = false; | ||
605 | 622 | ||
606 | /* | 623 | /* |
607 | * Handle state changes or data on each of the channels on this edge | 624 | * Handle state changes or data on each of the channels on this edge |
@@ -609,7 +626,7 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) | |||
609 | spin_lock(&edge->channels_lock); | 626 | spin_lock(&edge->channels_lock); |
610 | list_for_each_entry(channel, &edge->channels, list) { | 627 | list_for_each_entry(channel, &edge->channels, list) { |
611 | spin_lock(&channel->recv_lock); | 628 | spin_lock(&channel->recv_lock); |
612 | kick_worker |= qcom_smd_channel_intr(channel); | 629 | kick_state |= qcom_smd_channel_intr(channel); |
613 | spin_unlock(&channel->recv_lock); | 630 | spin_unlock(&channel->recv_lock); |
614 | } | 631 | } |
615 | spin_unlock(&edge->channels_lock); | 632 | spin_unlock(&edge->channels_lock); |
@@ -622,12 +639,13 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) | |||
622 | available = qcom_smem_get_free_space(edge->remote_pid); | 639 | available = qcom_smem_get_free_space(edge->remote_pid); |
623 | if (available != edge->smem_available) { | 640 | if (available != edge->smem_available) { |
624 | edge->smem_available = available; | 641 | edge->smem_available = available; |
625 | edge->need_rescan = true; | 642 | kick_scanner = true; |
626 | kick_worker = true; | ||
627 | } | 643 | } |
628 | 644 | ||
629 | if (kick_worker) | 645 | if (kick_scanner) |
630 | schedule_work(&edge->work); | 646 | schedule_work(&edge->scan_work); |
647 | if (kick_state) | ||
648 | schedule_work(&edge->state_work); | ||
631 | 649 | ||
632 | return IRQ_HANDLED; | 650 | return IRQ_HANDLED; |
633 | } | 651 | } |
@@ -793,18 +811,12 @@ static int qcom_smd_dev_match(struct device *dev, struct device_driver *drv) | |||
793 | } | 811 | } |
794 | 812 | ||
795 | /* | 813 | /* |
796 | * Probe the smd client. | 814 | * Helper for opening a channel |
797 | * | ||
798 | * The remote side have indicated that it want the channel to be opened, so | ||
799 | * complete the state handshake and probe our client driver. | ||
800 | */ | 815 | */ |
801 | static int qcom_smd_dev_probe(struct device *dev) | 816 | static int qcom_smd_channel_open(struct qcom_smd_channel *channel, |
817 | qcom_smd_cb_t cb) | ||
802 | { | 818 | { |
803 | struct qcom_smd_device *qsdev = to_smd_device(dev); | ||
804 | struct qcom_smd_driver *qsdrv = to_smd_driver(dev); | ||
805 | struct qcom_smd_channel *channel = qsdev->channel; | ||
806 | size_t bb_size; | 819 | size_t bb_size; |
807 | int ret; | ||
808 | 820 | ||
809 | /* | 821 | /* |
810 | * Packets are maximum 4k, but reduce if the fifo is smaller | 822 | * Packets are maximum 4k, but reduce if the fifo is smaller |
@@ -814,12 +826,44 @@ static int qcom_smd_dev_probe(struct device *dev) | |||
814 | if (!channel->bounce_buffer) | 826 | if (!channel->bounce_buffer) |
815 | return -ENOMEM; | 827 | return -ENOMEM; |
816 | 828 | ||
817 | channel->cb = qsdrv->callback; | 829 | qcom_smd_channel_set_callback(channel, cb); |
818 | |||
819 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING); | 830 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING); |
820 | |||
821 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED); | 831 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED); |
822 | 832 | ||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | /* | ||
837 | * Helper for closing and resetting a channel | ||
838 | */ | ||
839 | static void qcom_smd_channel_close(struct qcom_smd_channel *channel) | ||
840 | { | ||
841 | qcom_smd_channel_set_callback(channel, NULL); | ||
842 | |||
843 | kfree(channel->bounce_buffer); | ||
844 | channel->bounce_buffer = NULL; | ||
845 | |||
846 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED); | ||
847 | qcom_smd_channel_reset(channel); | ||
848 | } | ||
849 | |||
850 | /* | ||
851 | * Probe the smd client. | ||
852 | * | ||
853 | * The remote side have indicated that it want the channel to be opened, so | ||
854 | * complete the state handshake and probe our client driver. | ||
855 | */ | ||
856 | static int qcom_smd_dev_probe(struct device *dev) | ||
857 | { | ||
858 | struct qcom_smd_device *qsdev = to_smd_device(dev); | ||
859 | struct qcom_smd_driver *qsdrv = to_smd_driver(dev); | ||
860 | struct qcom_smd_channel *channel = qsdev->channel; | ||
861 | int ret; | ||
862 | |||
863 | ret = qcom_smd_channel_open(channel, qsdrv->callback); | ||
864 | if (ret) | ||
865 | return ret; | ||
866 | |||
823 | ret = qsdrv->probe(qsdev); | 867 | ret = qsdrv->probe(qsdev); |
824 | if (ret) | 868 | if (ret) |
825 | goto err; | 869 | goto err; |
@@ -831,11 +875,7 @@ static int qcom_smd_dev_probe(struct device *dev) | |||
831 | err: | 875 | err: |
832 | dev_err(&qsdev->dev, "probe failed\n"); | 876 | dev_err(&qsdev->dev, "probe failed\n"); |
833 | 877 | ||
834 | channel->cb = NULL; | 878 | qcom_smd_channel_close(channel); |
835 | kfree(channel->bounce_buffer); | ||
836 | channel->bounce_buffer = NULL; | ||
837 | |||
838 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED); | ||
839 | return ret; | 879 | return ret; |
840 | } | 880 | } |
841 | 881 | ||
@@ -850,16 +890,15 @@ static int qcom_smd_dev_remove(struct device *dev) | |||
850 | struct qcom_smd_device *qsdev = to_smd_device(dev); | 890 | struct qcom_smd_device *qsdev = to_smd_device(dev); |
851 | struct qcom_smd_driver *qsdrv = to_smd_driver(dev); | 891 | struct qcom_smd_driver *qsdrv = to_smd_driver(dev); |
852 | struct qcom_smd_channel *channel = qsdev->channel; | 892 | struct qcom_smd_channel *channel = qsdev->channel; |
853 | unsigned long flags; | 893 | struct qcom_smd_channel *tmp; |
894 | struct qcom_smd_channel *ch; | ||
854 | 895 | ||
855 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING); | 896 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING); |
856 | 897 | ||
857 | /* | 898 | /* |
858 | * Make sure we don't race with the code receiving data. | 899 | * Make sure we don't race with the code receiving data. |
859 | */ | 900 | */ |
860 | spin_lock_irqsave(&channel->recv_lock, flags); | 901 | qcom_smd_channel_set_callback(channel, NULL); |
861 | channel->cb = NULL; | ||
862 | spin_unlock_irqrestore(&channel->recv_lock, flags); | ||
863 | 902 | ||
864 | /* Wake up any sleepers in qcom_smd_send() */ | 903 | /* Wake up any sleepers in qcom_smd_send() */ |
865 | wake_up_interruptible(&channel->fblockread_event); | 904 | wake_up_interruptible(&channel->fblockread_event); |
@@ -872,15 +911,14 @@ static int qcom_smd_dev_remove(struct device *dev) | |||
872 | qsdrv->remove(qsdev); | 911 | qsdrv->remove(qsdev); |
873 | 912 | ||
874 | /* | 913 | /* |
875 | * The client is now gone, cleanup and reset the channel state. | 914 | * The client is now gone, close and release all channels associated |
915 | * with this sdev | ||
876 | */ | 916 | */ |
877 | channel->qsdev = NULL; | 917 | list_for_each_entry_safe(ch, tmp, &channel->dev_list, dev_list) { |
878 | kfree(channel->bounce_buffer); | 918 | qcom_smd_channel_close(ch); |
879 | channel->bounce_buffer = NULL; | 919 | list_del(&ch->dev_list); |
880 | 920 | ch->qsdev = NULL; | |
881 | qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED); | 921 | } |
882 | |||
883 | qcom_smd_channel_reset(channel); | ||
884 | 922 | ||
885 | return 0; | 923 | return 0; |
886 | } | 924 | } |
@@ -1006,6 +1044,77 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *qsdrv) | |||
1006 | } | 1044 | } |
1007 | EXPORT_SYMBOL(qcom_smd_driver_unregister); | 1045 | EXPORT_SYMBOL(qcom_smd_driver_unregister); |
1008 | 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 | |||
1009 | /* | 1118 | /* |
1010 | * 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, |
1011 | * retrieving and validating the smem items involved. | 1120 | * retrieving and validating the smem items involved. |
@@ -1027,6 +1136,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed | |||
1027 | if (!channel) | 1136 | if (!channel) |
1028 | return ERR_PTR(-ENOMEM); | 1137 | return ERR_PTR(-ENOMEM); |
1029 | 1138 | ||
1139 | INIT_LIST_HEAD(&channel->dev_list); | ||
1030 | channel->edge = edge; | 1140 | channel->edge = edge; |
1031 | channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL); | 1141 | channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL); |
1032 | if (!channel->name) | 1142 | if (!channel->name) |
@@ -1089,8 +1199,9 @@ free_name_and_channel: | |||
1089 | * qcom_smd_create_channel() to create representations of these and add | 1199 | * qcom_smd_create_channel() to create representations of these and add |
1090 | * them to the edge's list of channels. | 1200 | * them to the edge's list of channels. |
1091 | */ | 1201 | */ |
1092 | static void qcom_discover_channels(struct qcom_smd_edge *edge) | 1202 | static void qcom_channel_scan_worker(struct work_struct *work) |
1093 | { | 1203 | { |
1204 | struct qcom_smd_edge *edge = container_of(work, struct qcom_smd_edge, scan_work); | ||
1094 | struct qcom_smd_alloc_entry *alloc_tbl; | 1205 | struct qcom_smd_alloc_entry *alloc_tbl; |
1095 | struct qcom_smd_alloc_entry *entry; | 1206 | struct qcom_smd_alloc_entry *entry; |
1096 | struct qcom_smd_channel *channel; | 1207 | struct qcom_smd_channel *channel; |
@@ -1140,10 +1251,12 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge) | |||
1140 | 1251 | ||
1141 | dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); | 1252 | dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); |
1142 | set_bit(i, edge->allocated[tbl]); | 1253 | set_bit(i, edge->allocated[tbl]); |
1254 | |||
1255 | wake_up_interruptible(&edge->new_channel_event); | ||
1143 | } | 1256 | } |
1144 | } | 1257 | } |
1145 | 1258 | ||
1146 | schedule_work(&edge->work); | 1259 | schedule_work(&edge->state_work); |
1147 | } | 1260 | } |
1148 | 1261 | ||
1149 | /* | 1262 | /* |
@@ -1151,29 +1264,23 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge) | |||
1151 | * then scans all registered channels for state changes that should be handled | 1264 | * then scans all registered channels for state changes that should be handled |
1152 | * by creating or destroying smd client devices for the registered channels. | 1265 | * by creating or destroying smd client devices for the registered channels. |
1153 | * | 1266 | * |
1154 | * LOCKING: edge->channels_lock is not needed to be held during the traversal | 1267 | * LOCKING: edge->channels_lock only needs to cover the list operations, as the |
1155 | * of the channels list as it's done synchronously with the only writer. | 1268 | * worker is killed before any channels are deallocated |
1156 | */ | 1269 | */ |
1157 | static void qcom_channel_state_worker(struct work_struct *work) | 1270 | static void qcom_channel_state_worker(struct work_struct *work) |
1158 | { | 1271 | { |
1159 | struct qcom_smd_channel *channel; | 1272 | struct qcom_smd_channel *channel; |
1160 | struct qcom_smd_edge *edge = container_of(work, | 1273 | struct qcom_smd_edge *edge = container_of(work, |
1161 | struct qcom_smd_edge, | 1274 | struct qcom_smd_edge, |
1162 | work); | 1275 | state_work); |
1163 | unsigned remote_state; | 1276 | unsigned remote_state; |
1164 | 1277 | unsigned long flags; | |
1165 | /* | ||
1166 | * Rescan smem if we have reason to belive that there are new channels. | ||
1167 | */ | ||
1168 | if (edge->need_rescan) { | ||
1169 | edge->need_rescan = false; | ||
1170 | qcom_discover_channels(edge); | ||
1171 | } | ||
1172 | 1278 | ||
1173 | /* | 1279 | /* |
1174 | * Register a device for any closed channel where the remote processor | 1280 | * Register a device for any closed channel where the remote processor |
1175 | * is showing interest in opening the channel. | 1281 | * is showing interest in opening the channel. |
1176 | */ | 1282 | */ |
1283 | spin_lock_irqsave(&edge->channels_lock, flags); | ||
1177 | list_for_each_entry(channel, &edge->channels, list) { | 1284 | list_for_each_entry(channel, &edge->channels, list) { |
1178 | if (channel->state != SMD_CHANNEL_CLOSED) | 1285 | if (channel->state != SMD_CHANNEL_CLOSED) |
1179 | continue; | 1286 | continue; |
@@ -1183,7 +1290,9 @@ static void qcom_channel_state_worker(struct work_struct *work) | |||
1183 | remote_state != SMD_CHANNEL_OPENED) | 1290 | remote_state != SMD_CHANNEL_OPENED) |
1184 | continue; | 1291 | continue; |
1185 | 1292 | ||
1293 | spin_unlock_irqrestore(&edge->channels_lock, flags); | ||
1186 | qcom_smd_create_device(channel); | 1294 | qcom_smd_create_device(channel); |
1295 | spin_lock_irqsave(&edge->channels_lock, flags); | ||
1187 | } | 1296 | } |
1188 | 1297 | ||
1189 | /* | 1298 | /* |
@@ -1200,8 +1309,11 @@ static void qcom_channel_state_worker(struct work_struct *work) | |||
1200 | remote_state == SMD_CHANNEL_OPENED) | 1309 | remote_state == SMD_CHANNEL_OPENED) |
1201 | continue; | 1310 | continue; |
1202 | 1311 | ||
1312 | spin_unlock_irqrestore(&edge->channels_lock, flags); | ||
1203 | qcom_smd_destroy_device(channel); | 1313 | qcom_smd_destroy_device(channel); |
1314 | spin_lock_irqsave(&edge->channels_lock, flags); | ||
1204 | } | 1315 | } |
1316 | spin_unlock_irqrestore(&edge->channels_lock, flags); | ||
1205 | } | 1317 | } |
1206 | 1318 | ||
1207 | /* | 1319 | /* |
@@ -1219,7 +1331,8 @@ static int qcom_smd_parse_edge(struct device *dev, | |||
1219 | INIT_LIST_HEAD(&edge->channels); | 1331 | INIT_LIST_HEAD(&edge->channels); |
1220 | spin_lock_init(&edge->channels_lock); | 1332 | spin_lock_init(&edge->channels_lock); |
1221 | 1333 | ||
1222 | INIT_WORK(&edge->work, qcom_channel_state_worker); | 1334 | INIT_WORK(&edge->scan_work, qcom_channel_scan_worker); |
1335 | INIT_WORK(&edge->state_work, qcom_channel_state_worker); | ||
1223 | 1336 | ||
1224 | edge->of_node = of_node_get(node); | 1337 | edge->of_node = of_node_get(node); |
1225 | 1338 | ||
@@ -1303,13 +1416,13 @@ static int qcom_smd_probe(struct platform_device *pdev) | |||
1303 | for_each_available_child_of_node(pdev->dev.of_node, node) { | 1416 | for_each_available_child_of_node(pdev->dev.of_node, node) { |
1304 | edge = &smd->edges[i++]; | 1417 | edge = &smd->edges[i++]; |
1305 | edge->smd = smd; | 1418 | edge->smd = smd; |
1419 | init_waitqueue_head(&edge->new_channel_event); | ||
1306 | 1420 | ||
1307 | ret = qcom_smd_parse_edge(&pdev->dev, node, edge); | 1421 | ret = qcom_smd_parse_edge(&pdev->dev, node, edge); |
1308 | if (ret) | 1422 | if (ret) |
1309 | continue; | 1423 | continue; |
1310 | 1424 | ||
1311 | edge->need_rescan = true; | 1425 | schedule_work(&edge->scan_work); |
1312 | schedule_work(&edge->work); | ||
1313 | } | 1426 | } |
1314 | 1427 | ||
1315 | platform_set_drvdata(pdev, smd); | 1428 | platform_set_drvdata(pdev, smd); |
@@ -1332,8 +1445,10 @@ static int qcom_smd_remove(struct platform_device *pdev) | |||
1332 | edge = &smd->edges[i]; | 1445 | edge = &smd->edges[i]; |
1333 | 1446 | ||
1334 | disable_irq(edge->irq); | 1447 | disable_irq(edge->irq); |
1335 | cancel_work_sync(&edge->work); | 1448 | cancel_work_sync(&edge->scan_work); |
1449 | cancel_work_sync(&edge->state_work); | ||
1336 | 1450 | ||
1451 | /* No need to lock here, because the writer is gone */ | ||
1337 | list_for_each_entry(channel, &edge->channels, list) { | 1452 | list_for_each_entry(channel, &edge->channels, list) { |
1338 | if (!channel->qsdev) | 1453 | if (!channel->qsdev) |
1339 | continue; | 1454 | continue; |
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 5548a31e1a39..f324451e0940 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. | 2 | * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. |
3 | * Copyright (c) 2014,2015, Linaro Ltd. | 3 | * Copyright (c) 2014,2015, Linaro Ltd. |
4 | * | 4 | * |
5 | * SAW power controller driver | ||
6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 and | 8 | * it under the terms of the GNU General Public License version 2 and |
7 | * only version 2 as published by the Free Software Foundation. | 9 | * only version 2 as published by the Free Software Foundation. |
@@ -12,7 +14,6 @@ | |||
12 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
13 | */ | 15 | */ |
14 | 16 | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
17 | #include <linux/init.h> | 18 | #include <linux/init.h> |
18 | #include <linux/io.h> | 19 | #include <linux/io.h> |
@@ -378,8 +379,5 @@ static struct platform_driver spm_driver = { | |||
378 | .of_match_table = spm_match_table, | 379 | .of_match_table = spm_match_table, |
379 | }, | 380 | }, |
380 | }; | 381 | }; |
381 | module_platform_driver(spm_driver); | ||
382 | 382 | ||
383 | MODULE_LICENSE("GPL v2"); | 383 | builtin_platform_driver(spm_driver); |
384 | MODULE_DESCRIPTION("SAW power controller driver"); | ||
385 | MODULE_ALIAS("platform:saw"); | ||
diff --git a/include/linux/soc/qcom/smd.h b/include/linux/soc/qcom/smd.h index d0cb6d189a0a..bd51c8a9d807 100644 --- a/include/linux/soc/qcom/smd.h +++ b/include/linux/soc/qcom/smd.h | |||
@@ -26,6 +26,8 @@ struct qcom_smd_device { | |||
26 | struct qcom_smd_channel *channel; | 26 | struct qcom_smd_channel *channel; |
27 | }; | 27 | }; |
28 | 28 | ||
29 | typedef int (*qcom_smd_cb_t)(struct qcom_smd_device *, const void *, size_t); | ||
30 | |||
29 | /** | 31 | /** |
30 | * struct qcom_smd_driver - smd driver struct | 32 | * struct qcom_smd_driver - smd driver struct |
31 | * @driver: underlying device driver | 33 | * @driver: underlying device driver |
@@ -42,7 +44,7 @@ struct qcom_smd_driver { | |||
42 | 44 | ||
43 | int (*probe)(struct qcom_smd_device *dev); | 45 | int (*probe)(struct qcom_smd_device *dev); |
44 | void (*remove)(struct qcom_smd_device *dev); | 46 | void (*remove)(struct qcom_smd_device *dev); |
45 | int (*callback)(struct qcom_smd_device *, const void *, size_t); | 47 | qcom_smd_cb_t callback; |
46 | }; | 48 | }; |
47 | 49 | ||
48 | int qcom_smd_driver_register(struct qcom_smd_driver *drv); | 50 | int qcom_smd_driver_register(struct qcom_smd_driver *drv); |
@@ -54,4 +56,8 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *drv); | |||
54 | 56 | ||
55 | int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); | 57 | int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); |
56 | 58 | ||
59 | struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev, | ||
60 | const char *name, | ||
61 | qcom_smd_cb_t cb); | ||
62 | |||
57 | #endif | 63 | #endif |
diff --git a/include/linux/soc/qcom/smem_state.h b/include/linux/soc/qcom/smem_state.h index f35e1512fcaa..7b88697929e9 100644 --- a/include/linux/soc/qcom/smem_state.h +++ b/include/linux/soc/qcom/smem_state.h | |||
@@ -1,12 +1,17 @@ | |||
1 | #ifndef __QCOM_SMEM_STATE__ | 1 | #ifndef __QCOM_SMEM_STATE__ |
2 | #define __QCOM_SMEM_STATE__ | 2 | #define __QCOM_SMEM_STATE__ |
3 | 3 | ||
4 | #include <linux/errno.h> | ||
5 | |||
6 | struct device_node; | ||
4 | struct qcom_smem_state; | 7 | struct qcom_smem_state; |
5 | 8 | ||
6 | struct qcom_smem_state_ops { | 9 | struct qcom_smem_state_ops { |
7 | int (*update_bits)(void *, u32, u32); | 10 | int (*update_bits)(void *, u32, u32); |
8 | }; | 11 | }; |
9 | 12 | ||
13 | #ifdef CONFIG_QCOM_SMEM_STATE | ||
14 | |||
10 | struct qcom_smem_state *qcom_smem_state_get(struct device *dev, const char *con_id, unsigned *bit); | 15 | struct qcom_smem_state *qcom_smem_state_get(struct device *dev, const char *con_id, unsigned *bit); |
11 | void qcom_smem_state_put(struct qcom_smem_state *); | 16 | void qcom_smem_state_put(struct qcom_smem_state *); |
12 | 17 | ||
@@ -15,4 +20,34 @@ int qcom_smem_state_update_bits(struct qcom_smem_state *state, u32 mask, u32 val | |||
15 | struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, const struct qcom_smem_state_ops *ops, void *data); | 20 | struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, const struct qcom_smem_state_ops *ops, void *data); |
16 | void qcom_smem_state_unregister(struct qcom_smem_state *state); | 21 | void qcom_smem_state_unregister(struct qcom_smem_state *state); |
17 | 22 | ||
23 | #else | ||
24 | |||
25 | static inline struct qcom_smem_state *qcom_smem_state_get(struct device *dev, | ||
26 | const char *con_id, unsigned *bit) | ||
27 | { | ||
28 | return ERR_PTR(-EINVAL); | ||
29 | } | ||
30 | |||
31 | static inline void qcom_smem_state_put(struct qcom_smem_state *state) | ||
32 | { | ||
33 | } | ||
34 | |||
35 | static inline int qcom_smem_state_update_bits(struct qcom_smem_state *state, | ||
36 | u32 mask, u32 value) | ||
37 | { | ||
38 | return -EINVAL; | ||
39 | } | ||
40 | |||
41 | static inline struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, | ||
42 | const struct qcom_smem_state_ops *ops, void *data) | ||
43 | { | ||
44 | return ERR_PTR(-EINVAL); | ||
45 | } | ||
46 | |||
47 | static inline void qcom_smem_state_unregister(struct qcom_smem_state *state) | ||
48 | { | ||
49 | } | ||
50 | |||
51 | #endif | ||
52 | |||
18 | #endif | 53 | #endif |