| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/math64.h>
- #include <linux/mhi.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/time64.h>
- #include <linux/timer.h>
- #include "qaic.h"
- #include "qaic_timesync.h"
- #define QTIMER_REG_OFFSET 0xa28
- #define QAIC_TIMESYNC_SIGNATURE 0x55aa
- #define QAIC_CONV_QTIMER_TO_US(qtimer) (mul_u64_u32_div(qtimer, 10, 192))
- static unsigned int timesync_delay_ms = 1000; /* 1 sec default */
- module_param(timesync_delay_ms, uint, 0600);
- MODULE_PARM_DESC(timesync_delay_ms, "Delay in ms between two consecutive timesync operations");
- enum qts_msg_type {
- QAIC_TS_CMD_TO_HOST,
- QAIC_TS_SYNC_REQ,
- QAIC_TS_ACK_TO_HOST,
- QAIC_TS_MSG_TYPE_MAX
- };
- /**
- * struct qts_hdr - Timesync message header structure.
- * @signature: Unique signature to identify the timesync message.
- * @reserved_1: Reserved for future use.
- * @reserved_2: Reserved for future use.
- * @msg_type: sub-type of the timesync message.
- * @reserved_3: Reserved for future use.
- */
- struct qts_hdr {
- __le16 signature;
- __le16 reserved_1;
- u8 reserved_2;
- u8 msg_type;
- __le16 reserved_3;
- } __packed;
- /**
- * struct qts_timeval - Structure to carry time information.
- * @tv_sec: Seconds part of the time.
- * @tv_usec: uS (microseconds) part of the time.
- */
- struct qts_timeval {
- __le64 tv_sec;
- __le64 tv_usec;
- } __packed;
- /**
- * struct qts_host_time_sync_msg_data - Structure to denote the timesync message.
- * @header: Header of the timesync message.
- * @data: Time information.
- */
- struct qts_host_time_sync_msg_data {
- struct qts_hdr header;
- struct qts_timeval data;
- } __packed;
- /**
- * struct mqts_dev - MHI QAIC Timesync Control device.
- * @qdev: Pointer to the root device struct driven by QAIC driver.
- * @mhi_dev: Pointer to associated MHI device.
- * @timer: Timer handle used for timesync.
- * @qtimer_addr: Device QTimer register pointer.
- * @buff_in_use: atomic variable to track if the sync_msg buffer is in use.
- * @dev: Device pointer to qdev->pdev->dev stored for easy access.
- * @sync_msg: Buffer used to send timesync message over MHI.
- */
- struct mqts_dev {
- struct qaic_device *qdev;
- struct mhi_device *mhi_dev;
- struct timer_list timer;
- void __iomem *qtimer_addr;
- atomic_t buff_in_use;
- struct device *dev;
- struct qts_host_time_sync_msg_data *sync_msg;
- };
- struct qts_resp_msg {
- struct qts_hdr hdr;
- } __packed;
- struct qts_resp {
- struct qts_resp_msg data;
- struct work_struct work;
- struct qaic_device *qdev;
- };
- #ifdef readq
- static u64 read_qtimer(const volatile void __iomem *addr)
- {
- return readq(addr);
- }
- #else
- static u64 read_qtimer(const volatile void __iomem *addr)
- {
- u64 low, high;
- low = readl(addr);
- high = readl(addr + sizeof(u32));
- return low | (high << 32);
- }
- #endif
- static void qaic_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
- {
- struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
- dev_dbg(mqtsdev->dev, "%s status: %d xfer_len: %zu\n", __func__,
- mhi_result->transaction_status, mhi_result->bytes_xferd);
- atomic_set(&mqtsdev->buff_in_use, 0);
- }
- static void qaic_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
- {
- struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
- dev_err(mqtsdev->dev, "%s no data expected on dl channel\n", __func__);
- }
- static void qaic_timesync_timer(struct timer_list *t)
- {
- struct mqts_dev *mqtsdev = from_timer(mqtsdev, t, timer);
- struct qts_host_time_sync_msg_data *sync_msg;
- u64 device_qtimer_us;
- u64 device_qtimer;
- u64 host_time_us;
- u64 offset_us;
- u64 host_sec;
- int ret;
- if (atomic_read(&mqtsdev->buff_in_use)) {
- dev_dbg(mqtsdev->dev, "%s buffer not free, schedule next cycle\n", __func__);
- goto mod_timer;
- }
- atomic_set(&mqtsdev->buff_in_use, 1);
- sync_msg = mqtsdev->sync_msg;
- sync_msg->header.signature = cpu_to_le16(QAIC_TIMESYNC_SIGNATURE);
- sync_msg->header.msg_type = QAIC_TS_SYNC_REQ;
- /* Read host UTC time and convert to uS*/
- host_time_us = div_u64(ktime_get_real_ns(), NSEC_PER_USEC);
- device_qtimer = read_qtimer(mqtsdev->qtimer_addr);
- device_qtimer_us = QAIC_CONV_QTIMER_TO_US(device_qtimer);
- /* Offset between host UTC and device time */
- offset_us = host_time_us - device_qtimer_us;
- host_sec = div_u64(offset_us, USEC_PER_SEC);
- sync_msg->data.tv_usec = cpu_to_le64(offset_us - host_sec * USEC_PER_SEC);
- sync_msg->data.tv_sec = cpu_to_le64(host_sec);
- ret = mhi_queue_buf(mqtsdev->mhi_dev, DMA_TO_DEVICE, sync_msg, sizeof(*sync_msg), MHI_EOT);
- if (ret && (ret != -EAGAIN)) {
- dev_err(mqtsdev->dev, "%s unable to queue to mhi:%d\n", __func__, ret);
- return;
- } else if (ret == -EAGAIN) {
- atomic_set(&mqtsdev->buff_in_use, 0);
- }
- mod_timer:
- ret = mod_timer(t, jiffies + msecs_to_jiffies(timesync_delay_ms));
- if (ret)
- dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret);
- }
- static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
- {
- struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
- struct mqts_dev *mqtsdev;
- struct timer_list *timer;
- int ret;
- mqtsdev = kzalloc(sizeof(*mqtsdev), GFP_KERNEL);
- if (!mqtsdev) {
- ret = -ENOMEM;
- goto out;
- }
- timer = &mqtsdev->timer;
- mqtsdev->mhi_dev = mhi_dev;
- mqtsdev->qdev = qdev;
- mqtsdev->dev = &qdev->pdev->dev;
- mqtsdev->sync_msg = kzalloc(sizeof(*mqtsdev->sync_msg), GFP_KERNEL);
- if (!mqtsdev->sync_msg) {
- ret = -ENOMEM;
- goto free_mqts_dev;
- }
- atomic_set(&mqtsdev->buff_in_use, 0);
- ret = mhi_prepare_for_transfer(mhi_dev);
- if (ret)
- goto free_sync_msg;
- /* Qtimer register pointer */
- mqtsdev->qtimer_addr = qdev->bar_0 + QTIMER_REG_OFFSET;
- timer_setup(timer, qaic_timesync_timer, 0);
- timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
- add_timer(timer);
- dev_set_drvdata(&mhi_dev->dev, mqtsdev);
- return 0;
- free_sync_msg:
- kfree(mqtsdev->sync_msg);
- free_mqts_dev:
- kfree(mqtsdev);
- out:
- return ret;
- };
- static void qaic_timesync_remove(struct mhi_device *mhi_dev)
- {
- struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
- del_timer_sync(&mqtsdev->timer);
- mhi_unprepare_from_transfer(mqtsdev->mhi_dev);
- kfree(mqtsdev->sync_msg);
- kfree(mqtsdev);
- }
- static const struct mhi_device_id qaic_timesync_match_table[] = {
- { .chan = "QAIC_TIMESYNC_PERIODIC"},
- {},
- };
- MODULE_DEVICE_TABLE(mhi, qaic_timesync_match_table);
- static struct mhi_driver qaic_timesync_driver = {
- .id_table = qaic_timesync_match_table,
- .remove = qaic_timesync_remove,
- .probe = qaic_timesync_probe,
- .ul_xfer_cb = qaic_timesync_ul_xfer_cb,
- .dl_xfer_cb = qaic_timesync_dl_xfer_cb,
- .driver = {
- .name = "qaic_timesync_periodic",
- },
- };
- static void qaic_boot_timesync_worker(struct work_struct *work)
- {
- struct qts_resp *resp = container_of(work, struct qts_resp, work);
- struct qts_host_time_sync_msg_data *req;
- struct qts_resp_msg data = resp->data;
- struct qaic_device *qdev = resp->qdev;
- struct mhi_device *mhi_dev;
- struct timespec64 ts;
- int ret;
- mhi_dev = qdev->qts_ch;
- /* Queue the response message beforehand to avoid race conditions */
- ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT);
- if (ret) {
- kfree(resp);
- dev_warn(&mhi_dev->dev, "Failed to re-queue response buffer %d\n", ret);
- return;
- }
- switch (data.hdr.msg_type) {
- case QAIC_TS_CMD_TO_HOST:
- req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- break;
- req->header = data.hdr;
- req->header.msg_type = QAIC_TS_SYNC_REQ;
- ktime_get_real_ts64(&ts);
- req->data.tv_sec = cpu_to_le64(ts.tv_sec);
- req->data.tv_usec = cpu_to_le64(div_u64(ts.tv_nsec, NSEC_PER_USEC));
- ret = mhi_queue_buf(mhi_dev, DMA_TO_DEVICE, req, sizeof(*req), MHI_EOT);
- if (ret) {
- kfree(req);
- dev_dbg(&mhi_dev->dev, "Failed to send request message. Error %d\n", ret);
- }
- break;
- case QAIC_TS_ACK_TO_HOST:
- dev_dbg(&mhi_dev->dev, "ACK received from device\n");
- break;
- default:
- dev_err(&mhi_dev->dev, "Invalid message type %u.\n", data.hdr.msg_type);
- }
- }
- static int qaic_boot_timesync_queue_resp(struct mhi_device *mhi_dev, struct qaic_device *qdev)
- {
- struct qts_resp *resp;
- int ret;
- resp = kzalloc(sizeof(*resp), GFP_KERNEL);
- if (!resp)
- return -ENOMEM;
- resp->qdev = qdev;
- INIT_WORK(&resp->work, qaic_boot_timesync_worker);
- ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT);
- if (ret) {
- kfree(resp);
- dev_warn(&mhi_dev->dev, "Failed to queue response buffer %d\n", ret);
- return ret;
- }
- return 0;
- }
- static void qaic_boot_timesync_remove(struct mhi_device *mhi_dev)
- {
- struct qaic_device *qdev;
- qdev = dev_get_drvdata(&mhi_dev->dev);
- mhi_unprepare_from_transfer(qdev->qts_ch);
- qdev->qts_ch = NULL;
- }
- static int qaic_boot_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
- {
- struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
- int ret;
- ret = mhi_prepare_for_transfer(mhi_dev);
- if (ret)
- return ret;
- qdev->qts_ch = mhi_dev;
- dev_set_drvdata(&mhi_dev->dev, qdev);
- ret = qaic_boot_timesync_queue_resp(mhi_dev, qdev);
- if (ret) {
- dev_set_drvdata(&mhi_dev->dev, NULL);
- qdev->qts_ch = NULL;
- mhi_unprepare_from_transfer(mhi_dev);
- }
- return ret;
- }
- static void qaic_boot_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
- {
- kfree(mhi_result->buf_addr);
- }
- static void qaic_boot_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
- {
- struct qts_resp *resp = container_of(mhi_result->buf_addr, struct qts_resp, data);
- if (mhi_result->transaction_status || mhi_result->bytes_xferd != sizeof(resp->data)) {
- kfree(resp);
- return;
- }
- queue_work(resp->qdev->qts_wq, &resp->work);
- }
- static const struct mhi_device_id qaic_boot_timesync_match_table[] = {
- { .chan = "QAIC_TIMESYNC"},
- {},
- };
- static struct mhi_driver qaic_boot_timesync_driver = {
- .id_table = qaic_boot_timesync_match_table,
- .remove = qaic_boot_timesync_remove,
- .probe = qaic_boot_timesync_probe,
- .ul_xfer_cb = qaic_boot_timesync_ul_xfer_cb,
- .dl_xfer_cb = qaic_boot_timesync_dl_xfer_cb,
- .driver = {
- .name = "qaic_timesync",
- },
- };
- int qaic_timesync_init(void)
- {
- int ret;
- ret = mhi_driver_register(&qaic_timesync_driver);
- if (ret)
- return ret;
- ret = mhi_driver_register(&qaic_boot_timesync_driver);
- return ret;
- }
- void qaic_timesync_deinit(void)
- {
- mhi_driver_unregister(&qaic_boot_timesync_driver);
- mhi_driver_unregister(&qaic_timesync_driver);
- }
|