···106106 * @channels: list of all channels detected on this edge107107 * @channels_lock: guard for modifications of @channels108108 * @allocated: array of bitmaps representing already allocated channels109109- * @need_rescan: flag that the @work needs to scan smem for new channels110109 * @smem_available: last available amount of smem triggering a channel scan111111- * @work: work item for edge house keeping110110+ * @scan_work: work item for discovering new channels111111+ * @state_work: work item for edge state changes112112 */113113struct qcom_smd_edge {114114 struct qcom_smd *smd;···127127128128 DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE);129129130130- bool need_rescan;131130 unsigned smem_available;132131133133- struct work_struct work;132132+ wait_queue_head_t new_channel_event;133133+134134+ struct work_struct scan_work;135135+ struct work_struct state_work;134136};135137136138/*···188186 int fifo_size;189187190188 void *bounce_buffer;191191- int (*cb)(struct qcom_smd_device *, const void *, size_t);189189+ qcom_smd_cb_t cb;192190193191 spinlock_t recv_lock;194192195193 int pkt_size;196194195195+ void *drvdata;196196+197197 struct list_head list;198198+ struct list_head dev_list;198199};199200200201/**···383378}384379385380/*381381+ * Set the callback for a channel, with appropriate locking382382+ */383383+static void qcom_smd_channel_set_callback(struct qcom_smd_channel *channel,384384+ qcom_smd_cb_t cb)385385+{386386+ unsigned long flags;387387+388388+ spin_lock_irqsave(&channel->recv_lock, flags);389389+ channel->cb = cb;390390+ spin_unlock_irqrestore(&channel->recv_lock, flags);391391+};392392+393393+/*386394 * Calculate the amount of data available in the rx fifo387395 */388396static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel)···515497 */516498static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel)517499{518518- struct qcom_smd_device *qsdev = channel->qsdev;519500 unsigned tail;520501 size_t len;521502 void *ptr;···534517 len = channel->pkt_size;535518 }536519537537- ret = channel->cb(qsdev, ptr, len);520520+ ret = channel->cb(channel, ptr, len);538521 if (ret < 0)539522 return ret;540523···618601 struct qcom_smd_edge *edge = data;619602 struct qcom_smd_channel *channel;620603 unsigned available;621621- bool kick_worker = false;604604+ bool kick_scanner = false;605605+ bool kick_state = false;622606623607 /*624608 * Handle state changes or data on each of the channels on this edge···627609 spin_lock(&edge->channels_lock);628610 list_for_each_entry(channel, &edge->channels, list) {629611 spin_lock(&channel->recv_lock);630630- kick_worker |= qcom_smd_channel_intr(channel);612612+ kick_state |= qcom_smd_channel_intr(channel);631613 spin_unlock(&channel->recv_lock);632614 }633615 spin_unlock(&edge->channels_lock);···640622 available = qcom_smem_get_free_space(edge->remote_pid);641623 if (available != edge->smem_available) {642624 edge->smem_available = available;643643- edge->need_rescan = true;644644- kick_worker = true;625625+ kick_scanner = true;645626 }646627647647- if (kick_worker)648648- schedule_work(&edge->work);628628+ if (kick_scanner)629629+ schedule_work(&edge->scan_work);630630+ if (kick_state)631631+ schedule_work(&edge->state_work);649632650633 return IRQ_HANDLED;651634}···812793}813794814795/*796796+ * Helper for opening a channel797797+ */798798+static int qcom_smd_channel_open(struct qcom_smd_channel *channel,799799+ qcom_smd_cb_t cb)800800+{801801+ size_t bb_size;802802+803803+ /*804804+ * Packets are maximum 4k, but reduce if the fifo is smaller805805+ */806806+ bb_size = min(channel->fifo_size, SZ_4K);807807+ channel->bounce_buffer = kmalloc(bb_size, GFP_KERNEL);808808+ if (!channel->bounce_buffer)809809+ return -ENOMEM;810810+811811+ qcom_smd_channel_set_callback(channel, cb);812812+ qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING);813813+ qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED);814814+815815+ return 0;816816+}817817+818818+/*819819+ * Helper for closing and resetting a channel820820+ */821821+static void qcom_smd_channel_close(struct qcom_smd_channel *channel)822822+{823823+ qcom_smd_channel_set_callback(channel, NULL);824824+825825+ kfree(channel->bounce_buffer);826826+ channel->bounce_buffer = NULL;827827+828828+ qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED);829829+ qcom_smd_channel_reset(channel);830830+}831831+832832+/*815833 * Probe the smd client.816834 *817835 * The remote side have indicated that it want the channel to be opened, so···859803 struct qcom_smd_device *qsdev = to_smd_device(dev);860804 struct qcom_smd_driver *qsdrv = to_smd_driver(dev);861805 struct qcom_smd_channel *channel = qsdev->channel;862862- size_t bb_size;863806 int ret;864807865865- /*866866- * Packets are maximum 4k, but reduce if the fifo is smaller867867- */868868- bb_size = min(channel->fifo_size, SZ_4K);869869- channel->bounce_buffer = kmalloc(bb_size, GFP_KERNEL);870870- if (!channel->bounce_buffer)871871- return -ENOMEM;872872-873873- channel->cb = qsdrv->callback;874874-875875- qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING);876876-877877- qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED);808808+ ret = qcom_smd_channel_open(channel, qsdrv->callback);809809+ if (ret)810810+ return ret;878811879812 ret = qsdrv->probe(qsdev);880813 if (ret)···876831err:877832 dev_err(&qsdev->dev, "probe failed\n");878833879879- channel->cb = NULL;880880- kfree(channel->bounce_buffer);881881- channel->bounce_buffer = NULL;882882-883883- qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED);834834+ qcom_smd_channel_close(channel);884835 return ret;885836}886837···891850 struct qcom_smd_device *qsdev = to_smd_device(dev);892851 struct qcom_smd_driver *qsdrv = to_smd_driver(dev);893852 struct qcom_smd_channel *channel = qsdev->channel;894894- unsigned long flags;853853+ struct qcom_smd_channel *tmp;854854+ struct qcom_smd_channel *ch;895855896856 qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING);897857898858 /*899859 * Make sure we don't race with the code receiving data.900860 */901901- spin_lock_irqsave(&channel->recv_lock, flags);902902- channel->cb = NULL;903903- spin_unlock_irqrestore(&channel->recv_lock, flags);861861+ qcom_smd_channel_set_callback(channel, NULL);904862905863 /* Wake up any sleepers in qcom_smd_send() */906864 wake_up_interruptible(&channel->fblockread_event);···912872 qsdrv->remove(qsdev);913873914874 /*915915- * The client is now gone, cleanup and reset the channel state.875875+ * The client is now gone, close and release all channels associated876876+ * with this sdev916877 */917917- channel->qsdev = NULL;918918- kfree(channel->bounce_buffer);919919- channel->bounce_buffer = NULL;920920-921921- qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED);922922-923923- qcom_smd_channel_reset(channel);878878+ list_for_each_entry_safe(ch, tmp, &channel->dev_list, dev_list) {879879+ qcom_smd_channel_close(ch);880880+ list_del(&ch->dev_list);881881+ ch->qsdev = NULL;882882+ }924883925884 return 0;926885}···1035996}1036997EXPORT_SYMBOL(qcom_smd_driver_register);1037998999999+void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)10001000+{10011001+ return channel->drvdata;10021002+}10031003+EXPORT_SYMBOL(qcom_smd_get_drvdata);10041004+10051005+void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)10061006+{10071007+ channel->drvdata = data;10081008+}10091009+EXPORT_SYMBOL(qcom_smd_set_drvdata);10101010+10381011/**10391012 * qcom_smd_driver_unregister - unregister a smd driver10401013 * @qsdrv: qcom_smd_driver struct···10561005 driver_unregister(&qsdrv->driver);10571006}10581007EXPORT_SYMBOL(qcom_smd_driver_unregister);10081008+10091009+static struct qcom_smd_channel *10101010+qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name)10111011+{10121012+ struct qcom_smd_channel *channel;10131013+ struct qcom_smd_channel *ret = NULL;10141014+ unsigned long flags;10151015+ unsigned state;10161016+10171017+ spin_lock_irqsave(&edge->channels_lock, flags);10181018+ list_for_each_entry(channel, &edge->channels, list) {10191019+ if (strcmp(channel->name, name))10201020+ continue;10211021+10221022+ state = GET_RX_CHANNEL_INFO(channel, state);10231023+ if (state != SMD_CHANNEL_OPENING &&10241024+ state != SMD_CHANNEL_OPENED)10251025+ continue;10261026+10271027+ ret = channel;10281028+ break;10291029+ }10301030+ spin_unlock_irqrestore(&edge->channels_lock, flags);10311031+10321032+ return ret;10331033+}10341034+10351035+/**10361036+ * qcom_smd_open_channel() - claim additional channels on the same edge10371037+ * @sdev: smd_device handle10381038+ * @name: channel name10391039+ * @cb: callback method to use for incoming data10401040+ *10411041+ * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't10421042+ * ready.10431043+ */10441044+struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,10451045+ const char *name,10461046+ qcom_smd_cb_t cb)10471047+{10481048+ struct qcom_smd_channel *channel;10491049+ struct qcom_smd_device *sdev = parent->qsdev;10501050+ struct qcom_smd_edge *edge = parent->edge;10511051+ int ret;10521052+10531053+ /* Wait up to HZ for the channel to appear */10541054+ ret = wait_event_interruptible_timeout(edge->new_channel_event,10551055+ (channel = qcom_smd_find_channel(edge, name)) != NULL,10561056+ HZ);10571057+ if (!ret)10581058+ return ERR_PTR(-ETIMEDOUT);10591059+10601060+ if (channel->state != SMD_CHANNEL_CLOSED) {10611061+ dev_err(&sdev->dev, "channel %s is busy\n", channel->name);10621062+ return ERR_PTR(-EBUSY);10631063+ }10641064+10651065+ channel->qsdev = sdev;10661066+ ret = qcom_smd_channel_open(channel, cb);10671067+ if (ret) {10681068+ channel->qsdev = NULL;10691069+ return ERR_PTR(ret);10701070+ }10711071+10721072+ /*10731073+ * Append the list of channel to the channels associated with the sdev10741074+ */10751075+ list_add_tail(&channel->dev_list, &sdev->channel->dev_list);10761076+10771077+ return channel;10781078+}10791079+EXPORT_SYMBOL(qcom_smd_open_channel);1059108010601081/*10611082 * Allocate the qcom_smd_channel object for a newly found smd channel,···11501027 if (!channel)11511028 return ERR_PTR(-ENOMEM);1152102910301030+ INIT_LIST_HEAD(&channel->dev_list);11531031 channel->edge = edge;11541032 channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL);11551033 if (!channel->name)···12131089 * qcom_smd_create_channel() to create representations of these and add12141090 * them to the edge's list of channels.12151091 */12161216-static void qcom_discover_channels(struct qcom_smd_edge *edge)10921092+static void qcom_channel_scan_worker(struct work_struct *work)12171093{10941094+ struct qcom_smd_edge *edge = container_of(work, struct qcom_smd_edge, scan_work);12181095 struct qcom_smd_alloc_entry *alloc_tbl;12191096 struct qcom_smd_alloc_entry *entry;12201097 struct qcom_smd_channel *channel;···1265114012661141 dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name);12671142 set_bit(i, edge->allocated[tbl]);11431143+11441144+ wake_up_interruptible(&edge->new_channel_event);12681145 }12691146 }1270114712711271- schedule_work(&edge->work);11481148+ schedule_work(&edge->state_work);12721149}1273115012741151/*···12781151 * then scans all registered channels for state changes that should be handled12791152 * by creating or destroying smd client devices for the registered channels.12801153 *12811281- * LOCKING: edge->channels_lock is not needed to be held during the traversal12821282- * of the channels list as it's done synchronously with the only writer.11541154+ * LOCKING: edge->channels_lock only needs to cover the list operations, as the11551155+ * worker is killed before any channels are deallocated12831156 */12841157static void qcom_channel_state_worker(struct work_struct *work)12851158{12861159 struct qcom_smd_channel *channel;12871160 struct qcom_smd_edge *edge = container_of(work,12881161 struct qcom_smd_edge,12891289- work);11621162+ state_work);12901163 unsigned remote_state;12911291-12921292- /*12931293- * Rescan smem if we have reason to belive that there are new channels.12941294- */12951295- if (edge->need_rescan) {12961296- edge->need_rescan = false;12971297- qcom_discover_channels(edge);12981298- }11641164+ unsigned long flags;1299116513001166 /*13011167 * Register a device for any closed channel where the remote processor13021168 * is showing interest in opening the channel.13031169 */11701170+ spin_lock_irqsave(&edge->channels_lock, flags);13041171 list_for_each_entry(channel, &edge->channels, list) {13051172 if (channel->state != SMD_CHANNEL_CLOSED)13061173 continue;···13041183 remote_state != SMD_CHANNEL_OPENED)13051184 continue;1306118511861186+ spin_unlock_irqrestore(&edge->channels_lock, flags);13071187 qcom_smd_create_device(channel);11881188+ spin_lock_irqsave(&edge->channels_lock, flags);13081189 }1309119013101191 /*···13231200 remote_state == SMD_CHANNEL_OPENED)13241201 continue;1325120212031203+ spin_unlock_irqrestore(&edge->channels_lock, flags);13261204 qcom_smd_destroy_device(channel);12051205+ spin_lock_irqsave(&edge->channels_lock, flags);13271206 }12071207+ spin_unlock_irqrestore(&edge->channels_lock, flags);13281208}1329120913301210/*···13451219 INIT_LIST_HEAD(&edge->channels);13461220 spin_lock_init(&edge->channels_lock);1347122113481348- INIT_WORK(&edge->work, qcom_channel_state_worker);12221222+ INIT_WORK(&edge->scan_work, qcom_channel_scan_worker);12231223+ INIT_WORK(&edge->state_work, qcom_channel_state_worker);1349122413501225 edge->of_node = of_node_get(node);13511226···14301303 for_each_available_child_of_node(pdev->dev.of_node, node) {14311304 edge = &smd->edges[i++];14321305 edge->smd = smd;13061306+ init_waitqueue_head(&edge->new_channel_event);1433130714341308 ret = qcom_smd_parse_edge(&pdev->dev, node, edge);14351309 if (ret)14361310 continue;1437131114381438- edge->need_rescan = true;14391439- schedule_work(&edge->work);13121312+ schedule_work(&edge->scan_work);14401313 }1441131414421315 platform_set_drvdata(pdev, smd);···14591332 edge = &smd->edges[i];1460133314611334 disable_irq(edge->irq);14621462- cancel_work_sync(&edge->work);13351335+ cancel_work_sync(&edge->scan_work);13361336+ cancel_work_sync(&edge->state_work);1463133713381338+ /* No need to lock here, because the writer is gone */14641339 list_for_each_entry(channel, &edge->channels, list) {14651340 if (!channel->qsdev)14661341 continue;
···22 * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.33 * Copyright (c) 2014,2015, Linaro Ltd.44 *55+ * SAW power controller driver66+ *57 * This program is free software; you can redistribute it and/or modify68 * it under the terms of the GNU General Public License version 2 and79 * only version 2 as published by the Free Software Foundation.···1412 * GNU General Public License for more details.1513 */16141717-#include <linux/module.h>1815#include <linux/kernel.h>1916#include <linux/init.h>2017#include <linux/io.h>···379378 .of_match_table = spm_match_table,380379 },381380};382382-module_platform_driver(spm_driver);383381384384-MODULE_LICENSE("GPL v2");385385-MODULE_DESCRIPTION("SAW power controller driver");386386-MODULE_ALIAS("platform:saw");382382+builtin_platform_driver(spm_driver);
+4-4
drivers/soc/qcom/wcnss_ctrl.c
···100100101101/**102102 * wcnss_ctrl_smd_callback() - handler from SMD responses103103- * @qsdev: smd device handle103103+ * @channel: smd channel handle104104 * @data: pointer to the incoming data packet105105 * @count: size of the incoming data packet106106 *107107 * Handles any incoming packets from the remote WCNSS_CTRL service.108108 */109109-static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev,109109+static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,110110 const void *data,111111 size_t count)112112{113113- struct wcnss_ctrl *wcnss = dev_get_drvdata(&qsdev->dev);113113+ struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(channel);114114 const struct wcnss_download_nv_resp *nvresp;115115 const struct wcnss_version_resp *version;116116 const struct wcnss_msg_hdr *hdr = data;···246246 init_completion(&wcnss->ack);247247 INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv);248248249249- dev_set_drvdata(&sdev->dev, wcnss);249249+ qcom_smd_set_drvdata(sdev->channel, wcnss);250250251251 return wcnss_request_version(wcnss);252252}
+32-1
include/linux/soc/qcom/smd.h
···2626 struct qcom_smd_channel *channel;2727};28282929+typedef int (*qcom_smd_cb_t)(struct qcom_smd_channel *, const void *, size_t);3030+2931/**3032 * struct qcom_smd_driver - smd driver struct3133 * @driver: underlying device driver···44424543 int (*probe)(struct qcom_smd_device *dev);4644 void (*remove)(struct qcom_smd_device *dev);4747- int (*callback)(struct qcom_smd_device *, const void *, size_t);4545+ qcom_smd_cb_t callback;4846};49475048#if IS_ENABLED(CONFIG_QCOM_SMD)···5250int qcom_smd_driver_register(struct qcom_smd_driver *drv);5351void qcom_smd_driver_unregister(struct qcom_smd_driver *drv);54525353+struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel,5454+ const char *name,5555+ qcom_smd_cb_t cb);5656+void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel);5757+void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);5558int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);5959+56605761#else5862···6860}69617062static inline void qcom_smd_driver_unregister(struct qcom_smd_driver *drv)6363+{6464+ /* This shouldn't be possible */6565+ WARN_ON(1);6666+}6767+6868+static inline struct qcom_smd_channel *6969+qcom_smd_open_channel(struct qcom_smd_channel *channel,7070+ const char *name,7171+ qcom_smd_cb_t cb)7272+{7373+ /* This shouldn't be possible */7474+ WARN_ON(1);7575+ return NULL;7676+}7777+7878+void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)7979+{8080+ /* This shouldn't be possible */8181+ WARN_ON(1);8282+ return NULL;8383+}8484+8585+void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)7186{7287 /* This shouldn't be possible */7388 WARN_ON(1);