123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2021, The Linux Foundation. All rights reserved.
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/mailbox_client.h>
- #include <linux/seq_file.h>
- #include <linux/debugfs.h>
- #include <linux/platform_device.h>
- #include <linux/mailbox/qmp.h>
- #include <linux/uaccess.h>
- #include <linux/mailbox_controller.h>
- #include "tmecom.h"
- struct tmecom {
- struct device *dev;
- struct mbox_client cl;
- struct mbox_chan *chan;
- struct mutex lock;
- struct qmp_pkt pkt;
- wait_queue_head_t waitq;
- void *txbuf;
- bool rx_done;
- };
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- #include <linux/tme_hwkm_master_defs.h>
- #include <linux/tme_hwkm_master.h>
- char dpkt[MBOX_MAX_MSG_LEN + 1];
- struct dentry *debugfs_file;
- #endif /* CONFIG_DEBUG_FS */
- static struct tmecom *tmedev;
- /**
- * tmecom_msg_hdr - Request/Response message header between HLOS and TME.
- *
- * This header is proceeding any request specific parameters.
- * The transaction id is used to match request with response.
- *
- * Note: glink/QMP layer provides the rx/tx data size, so user payload size
- * is calculated by reducing the header size.
- */
- struct tmecom_msg_hdr {
- unsigned int reserved; /* for future use */
- unsigned int txnid; /* transaction id */
- } __packed;
- #define TMECOM_TX_HDR_SIZE sizeof(struct tmecom_msg_hdr)
- #define CBOR_NUM_BYTES (sizeof(unsigned int))
- #define TMECOM_RX_HDR_SIZE (TMECOM_TX_HDR_SIZE + CBOR_NUM_BYTES)
- /*
- * CBOR encode emulation
- * Prepend tmecom_msg_hdr space
- * CBOR tag is prepended in request
- */
- static inline size_t tmecom_encode(struct tmecom *tdev, const void *reqbuf,
- size_t size)
- {
- unsigned int *msg = tdev->txbuf + TMECOM_TX_HDR_SIZE;
- unsigned int *src = (unsigned int *)reqbuf;
- memcpy(msg, src, size);
- return (size + TMECOM_TX_HDR_SIZE);
- }
- /*
- * CBOR decode emulation
- * Strip tmecom_msg_hdr & CBOR tag
- */
- static inline size_t tmecom_decode(struct tmecom *tdev, void *respbuf)
- {
- unsigned int *msg = tdev->pkt.data + TMECOM_RX_HDR_SIZE;
- unsigned int *rbuf = (unsigned int *)respbuf;
- memcpy(rbuf, msg, (tdev->pkt.size - TMECOM_RX_HDR_SIZE));
- return (tdev->pkt.size - TMECOM_RX_HDR_SIZE);
- }
- static bool tmecom_check_rx_done(struct tmecom *tdev)
- {
- return tdev->rx_done;
- }
- int tmecom_process_request(const void *reqbuf, size_t reqsize, void *respbuf,
- size_t *respsize)
- {
- struct tmecom *tdev = tmedev;
- long time_left = 0;
- int ret = 0;
- /*
- * Check to handle if probe is not successful or not completed yet
- */
- if (!tdev) {
- pr_err("%s: tmecom dev is NULL\n", __func__);
- return -ENODEV;
- }
- if (!reqbuf || !reqsize || (reqsize > MBOX_MAX_MSG_LEN)) {
- dev_err(tdev->dev, "invalid reqbuf or reqsize\n");
- return -EINVAL;
- }
- if (!respbuf || !respsize || (*respsize > MBOX_MAX_MSG_LEN)) {
- dev_err(tdev->dev, "invalid respbuf or respsize\n");
- return -EINVAL;
- }
- mutex_lock(&tdev->lock);
- tdev->rx_done = false;
- tdev->pkt.size = tmecom_encode(tdev, reqbuf, reqsize);
- /*
- * Controller expects a 4 byte aligned buffer
- */
- tdev->pkt.size = (tdev->pkt.size + 0x3) & ~0x3;
- tdev->pkt.data = tdev->txbuf;
- pr_debug("tmecom encoded request size = %u\n", tdev->pkt.size);
- print_hex_dump_bytes("tmecom sending bytes : ",
- DUMP_PREFIX_ADDRESS, tdev->pkt.data, tdev->pkt.size);
- if (mbox_send_message(tdev->chan, &tdev->pkt) < 0) {
- dev_err(tdev->dev, "failed to send qmp message\n");
- ret = -EAGAIN;
- goto err_exit;
- }
- time_left = wait_event_interruptible_timeout(tdev->waitq,
- tmecom_check_rx_done(tdev), tdev->cl.tx_tout);
- if (!time_left) {
- dev_err(tdev->dev, "request timed out\n");
- ret = -ETIMEDOUT;
- goto err_exit;
- }
- dev_info(tdev->dev, "response received\n");
- pr_debug("tmecom received size = %u\n", tdev->pkt.size);
- print_hex_dump_bytes("tmecom received bytes : ",
- DUMP_PREFIX_ADDRESS, tdev->pkt.data, tdev->pkt.size);
- *respsize = tmecom_decode(tdev, respbuf);
- tdev->rx_done = false;
- ret = 0;
- err_exit:
- mutex_unlock(&tdev->lock);
- return ret;
- }
- EXPORT_SYMBOL(tmecom_process_request);
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- static ssize_t tmecom_debugfs_write(struct file *file,
- const char __user *userstr, size_t len, loff_t *pos)
- {
- int ret = 0;
- size_t rxlen = 0;
- struct tme_ext_err_info *err_info = (struct tme_ext_err_info *)dpkt;
- if (!len || (len > MBOX_MAX_MSG_LEN)) {
- pr_err("invalid message length\n");
- return -EINVAL;
- }
- memset(dpkt, 0, sizeof(*dpkt));
- ret = copy_from_user(dpkt, userstr, len);
- if (ret) {
- pr_err("%s copy from user failed, ret=%d\n", __func__, ret);
- return len;
- }
- tmecom_process_request(dpkt, len, dpkt, &rxlen);
- print_hex_dump_bytes("tmecom decoded bytes : ",
- DUMP_PREFIX_ADDRESS, dpkt, rxlen);
- pr_debug("calling TME_HWKM_CMD_BROADCAST_TP_KEY api\n");
- ret = tme_hwkm_master_broadcast_transportkey(err_info);
- if (ret == 0)
- pr_debug("%s successful\n", __func__);
- return len;
- }
- static const struct file_operations tmecom_debugfs_ops = {
- .open = simple_open,
- .write = tmecom_debugfs_write,
- };
- #endif /* CONFIG_DEBUG_FS */
- static void tmecom_receive_message(struct mbox_client *client, void *message)
- {
- struct tmecom *tdev = dev_get_drvdata(client->dev);
- struct qmp_pkt *pkt = NULL;
- pr_debug("%s entered\n", __func__);
- if (!message) {
- dev_err(tdev->dev, "spurious message received\n");
- goto tmecom_receive_end;
- }
- if (tdev->rx_done) {
- dev_err(tdev->dev, "tmecom response pending\n");
- goto tmecom_receive_end;
- }
- pkt = (struct qmp_pkt *)message;
- tdev->pkt.size = pkt->size;
- tdev->pkt.data = pkt->data;
- tdev->rx_done = true;
- tmecom_receive_end:
- wake_up_interruptible(&tdev->waitq);
- }
- static int tmecom_probe(struct platform_device *pdev)
- {
- struct tmecom *tdev;
- const char *label;
- char name[32];
- tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
- if (!tdev)
- return -ENOMEM;
- tdev->cl.dev = &pdev->dev;
- tdev->cl.tx_block = true;
- tdev->cl.tx_tout = 500;
- tdev->cl.knows_txdone = false;
- tdev->cl.rx_callback = tmecom_receive_message;
- label = of_get_property(pdev->dev.of_node, "mbox-names", NULL);
- if (!label)
- return -EINVAL;
- snprintf(name, 32, "%s_send_message", label);
- tdev->chan = mbox_request_channel(&tdev->cl, 0);
- if (IS_ERR(tdev->chan)) {
- dev_err(&pdev->dev, "failed to get mbox channel\n");
- return PTR_ERR(tdev->chan);
- }
- mutex_init(&tdev->lock);
- if (tdev->chan) {
- tdev->txbuf =
- devm_kzalloc(&pdev->dev, MBOX_MAX_MSG_LEN, GFP_KERNEL);
- if (!tdev->txbuf) {
- dev_err(&pdev->dev, "message buffer alloc faile\n");
- return -ENOMEM;
- }
- }
- init_waitqueue_head(&tdev->waitq);
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- debugfs_file = debugfs_create_file(name, 0220, NULL, tdev,
- &tmecom_debugfs_ops);
- if (!debugfs_file)
- goto err;
- #endif /* CONFIG_DEBUG_FS */
- tdev->rx_done = false;
- tdev->dev = &pdev->dev;
- dev_set_drvdata(&pdev->dev, tdev);
- tmedev = tdev;
- dev_info(&pdev->dev, "tmecom probe success\n");
- return 0;
- err:
- mbox_free_channel(tdev->chan);
- return -ENOMEM;
- }
- static int tmecom_remove(struct platform_device *pdev)
- {
- struct tmecom *tdev = platform_get_drvdata(pdev);
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- debugfs_remove(debugfs_file);
- #endif /* CONFIG_DEBUG_FS */
- if (tdev->chan)
- mbox_free_channel(tdev->chan);
- dev_info(&pdev->dev, "tmecom remove success\n");
- return 0;
- }
- static const struct of_device_id tmecom_match_tbl[] = {
- {.compatible = "qcom,tmecom-qmp-client"},
- {},
- };
- static struct platform_driver tmecom_driver = {
- .probe = tmecom_probe,
- .remove = tmecom_remove,
- .driver = {
- .name = "tmecom-qmp-client",
- .suppress_bind_attrs = true,
- .of_match_table = tmecom_match_tbl,
- },
- };
- module_platform_driver(tmecom_driver);
- MODULE_DESCRIPTION("MSM TMECom QTI mailbox protocol client");
- MODULE_LICENSE("GPL v2");
|