Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rpmsg: qcom_smd: Access APCS through mailbox framework

Attempt to acquire the APCS IPC through the mailbox framework and fall
back to the old syscon based approach, to allow us to move away from
using the syscon.

Reviewed-by: Arun Kumar Neelakantam <aneela@codeaurora.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

+57 -20
+7 -1
Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt
··· 22 22 Definition: should specify the IRQ used by the remote processor to 23 23 signal this processor about communication related updates 24 24 25 - - qcom,ipc: 25 + - mboxes: 26 26 Usage: required 27 + Value type: <prop-encoded-array> 28 + Definition: reference to the associated doorbell in APCS, as described 29 + in mailbox/mailbox.txt 30 + 31 + - qcom,ipc: 32 + Usage: required, unless mboxes is specified 27 33 Value type: <prop-encoded-array> 28 34 Definition: three entries specifying the outgoing ipc bit used for 29 35 signaling the remote processor:
+1
drivers/rpmsg/Kconfig
··· 39 39 40 40 config RPMSG_QCOM_SMD 41 41 tristate "Qualcomm Shared Memory Driver (SMD)" 42 + depends on MAILBOX 42 43 depends on QCOM_SMEM 43 44 select RPMSG 44 45 help
+49 -19
drivers/rpmsg/qcom_smd.c
··· 14 14 15 15 #include <linux/interrupt.h> 16 16 #include <linux/io.h> 17 + #include <linux/mailbox_client.h> 17 18 #include <linux/mfd/syscon.h> 18 19 #include <linux/module.h> 19 20 #include <linux/of_irq.h> ··· 108 107 * @ipc_regmap: regmap handle holding the outgoing ipc register 109 108 * @ipc_offset: offset within @ipc_regmap of the register for ipc 110 109 * @ipc_bit: bit in the register at @ipc_offset of @ipc_regmap 110 + * @mbox_client: mailbox client handle 111 + * @mbox_chan: apcs ipc mailbox channel handle 111 112 * @channels: list of all channels detected on this edge 112 113 * @channels_lock: guard for modifications of @channels 113 114 * @allocated: array of bitmaps representing already allocated channels ··· 131 128 struct regmap *ipc_regmap; 132 129 int ipc_offset; 133 130 int ipc_bit; 131 + 132 + struct mbox_client mbox_client; 133 + struct mbox_chan *mbox_chan; 134 134 135 135 struct list_head channels; 136 136 spinlock_t channels_lock; ··· 372 366 { 373 367 struct qcom_smd_edge *edge = channel->edge; 374 368 375 - regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); 369 + if (edge->mbox_chan) { 370 + /* 371 + * We can ignore a failing mbox_send_message() as the only 372 + * possible cause is that the FIFO in the framework is full of 373 + * other writes to the same bit. 374 + */ 375 + mbox_send_message(edge->mbox_chan, NULL); 376 + mbox_client_txdone(edge->mbox_chan, 0); 377 + } else { 378 + regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); 379 + } 376 380 } 377 381 378 382 /* ··· 1342 1326 key = "qcom,remote-pid"; 1343 1327 of_property_read_u32(node, key, &edge->remote_pid); 1344 1328 1345 - syscon_np = of_parse_phandle(node, "qcom,ipc", 0); 1346 - if (!syscon_np) { 1347 - dev_err(dev, "no qcom,ipc node\n"); 1348 - return -ENODEV; 1349 - } 1329 + edge->mbox_client.dev = dev; 1330 + edge->mbox_client.knows_txdone = true; 1331 + edge->mbox_chan = mbox_request_channel(&edge->mbox_client, 0); 1332 + if (IS_ERR(edge->mbox_chan)) { 1333 + if (PTR_ERR(edge->mbox_chan) != -ENODEV) 1334 + return PTR_ERR(edge->mbox_chan); 1350 1335 1351 - edge->ipc_regmap = syscon_node_to_regmap(syscon_np); 1352 - if (IS_ERR(edge->ipc_regmap)) 1353 - return PTR_ERR(edge->ipc_regmap); 1336 + edge->mbox_chan = NULL; 1354 1337 1355 - key = "qcom,ipc"; 1356 - ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset); 1357 - if (ret < 0) { 1358 - dev_err(dev, "no offset in %s\n", key); 1359 - return -EINVAL; 1360 - } 1338 + syscon_np = of_parse_phandle(node, "qcom,ipc", 0); 1339 + if (!syscon_np) { 1340 + dev_err(dev, "no qcom,ipc node\n"); 1341 + return -ENODEV; 1342 + } 1361 1343 1362 - ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit); 1363 - if (ret < 0) { 1364 - dev_err(dev, "no bit in %s\n", key); 1365 - return -EINVAL; 1344 + edge->ipc_regmap = syscon_node_to_regmap(syscon_np); 1345 + if (IS_ERR(edge->ipc_regmap)) 1346 + return PTR_ERR(edge->ipc_regmap); 1347 + 1348 + key = "qcom,ipc"; 1349 + ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset); 1350 + if (ret < 0) { 1351 + dev_err(dev, "no offset in %s\n", key); 1352 + return -EINVAL; 1353 + } 1354 + 1355 + ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit); 1356 + if (ret < 0) { 1357 + dev_err(dev, "no bit in %s\n", key); 1358 + return -EINVAL; 1359 + } 1366 1360 } 1367 1361 1368 1362 ret = of_property_read_string(node, "label", &edge->name); ··· 1479 1453 return edge; 1480 1454 1481 1455 unregister_dev: 1456 + if (!IS_ERR_OR_NULL(edge->mbox_chan)) 1457 + mbox_free_channel(edge->mbox_chan); 1458 + 1482 1459 device_unregister(&edge->dev); 1483 1460 return ERR_PTR(ret); 1484 1461 } ··· 1510 1481 if (ret) 1511 1482 dev_warn(&edge->dev, "can't remove smd device: %d\n", ret); 1512 1483 1484 + mbox_free_channel(edge->mbox_chan); 1513 1485 device_unregister(&edge->dev); 1514 1486 1515 1487 return 0;