xref: /linux/drivers/accel/qaic/qaic_timesync.c (revision 140d5d411ab17926b47c9fce213fc065c12f8bea)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 /* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
4 
5 #include <linux/io.h>
6 #include <linux/kernel.h>
7 #include <linux/math64.h>
8 #include <linux/mhi.h>
9 #include <linux/mod_devicetable.h>
10 #include <linux/module.h>
11 #include <linux/time64.h>
12 #include <linux/timer.h>
13 
14 #include "qaic.h"
15 #include "qaic_timesync.h"
16 
17 #define QTIMER_REG_OFFSET			0xa28
18 #define QAIC_TIMESYNC_SIGNATURE			0x55aa
19 #define QAIC_CONV_QTIMER_TO_US(qtimer)		(mul_u64_u32_div(qtimer, 10, 192))
20 
21 static unsigned int timesync_delay_ms = 1000; /* 1 sec default */
22 module_param(timesync_delay_ms, uint, 0600);
23 MODULE_PARM_DESC(timesync_delay_ms, "Delay in ms between two consecutive timesync operations");
24 
25 enum qts_msg_type {
26 	QAIC_TS_CMD_TO_HOST,
27 	QAIC_TS_SYNC_REQ,
28 	QAIC_TS_ACK_TO_HOST,
29 	QAIC_TS_MSG_TYPE_MAX
30 };
31 
32 /**
33  * struct qts_hdr - Timesync message header structure.
34  * @signature: Unique signature to identify the timesync message.
35  * @reserved_1: Reserved for future use.
36  * @reserved_2: Reserved for future use.
37  * @msg_type: sub-type of the timesync message.
38  * @reserved_3: Reserved for future use.
39  */
40 struct qts_hdr {
41 	__le16	signature;
42 	__le16	reserved_1;
43 	u8	reserved_2;
44 	u8	msg_type;
45 	__le16	reserved_3;
46 } __packed;
47 
48 /**
49  * struct qts_timeval - Structure to carry time information.
50  * @tv_sec: Seconds part of the time.
51  * @tv_usec: uS (microseconds) part of the time.
52  */
53 struct qts_timeval {
54 	__le64	tv_sec;
55 	__le64	tv_usec;
56 } __packed;
57 
58 /**
59  * struct qts_host_time_sync_msg_data - Structure to denote the timesync message.
60  * @header: Header of the timesync message.
61  * @data: Time information.
62  */
63 struct qts_host_time_sync_msg_data {
64 	struct	qts_hdr header;
65 	struct	qts_timeval data;
66 } __packed;
67 
68 /**
69  * struct mqts_dev - MHI QAIC Timesync Control device.
70  * @qdev: Pointer to the root device struct driven by QAIC driver.
71  * @mhi_dev: Pointer to associated MHI device.
72  * @timer: Timer handle used for timesync.
73  * @qtimer_addr: Device QTimer register pointer.
74  * @buff_in_use: atomic variable to track if the sync_msg buffer is in use.
75  * @dev: Device pointer to qdev->pdev->dev stored for easy access.
76  * @sync_msg: Buffer used to send timesync message over MHI.
77  */
78 struct mqts_dev {
79 	struct qaic_device *qdev;
80 	struct mhi_device *mhi_dev;
81 	struct timer_list timer;
82 	void __iomem *qtimer_addr;
83 	atomic_t buff_in_use;
84 	struct device *dev;
85 	struct qts_host_time_sync_msg_data *sync_msg;
86 };
87 
88 struct qts_resp_msg {
89 	struct qts_hdr	hdr;
90 } __packed;
91 
92 struct qts_resp {
93 	struct qts_resp_msg	data;
94 	struct work_struct	work;
95 	struct qaic_device	*qdev;
96 };
97 
98 #ifdef readq
99 static u64 read_qtimer(const volatile void __iomem *addr)
100 {
101 	return readq(addr);
102 }
103 #else
104 static u64 read_qtimer(const volatile void __iomem *addr)
105 {
106 	u64 low, high;
107 
108 	low = readl(addr);
109 	high = readl(addr + sizeof(u32));
110 	return low | (high << 32);
111 }
112 #endif
113 
114 static void qaic_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
115 {
116 	struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
117 
118 	dev_dbg(mqtsdev->dev, "%s status: %d xfer_len: %zu\n", __func__,
119 		mhi_result->transaction_status, mhi_result->bytes_xferd);
120 
121 	atomic_set(&mqtsdev->buff_in_use, 0);
122 }
123 
124 static void qaic_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
125 {
126 	struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
127 
128 	dev_err(mqtsdev->dev, "%s no data expected on dl channel\n", __func__);
129 }
130 
131 static void qaic_timesync_timer(struct timer_list *t)
132 {
133 	struct mqts_dev *mqtsdev = timer_container_of(mqtsdev, t, timer);
134 	struct qts_host_time_sync_msg_data *sync_msg;
135 	u64 device_qtimer_us;
136 	u64 device_qtimer;
137 	u64 host_time_us;
138 	u64 offset_us;
139 	u64 host_sec;
140 	int ret;
141 
142 	if (atomic_read(&mqtsdev->buff_in_use)) {
143 		dev_dbg(mqtsdev->dev, "%s buffer not free, schedule next cycle\n", __func__);
144 		goto mod_timer;
145 	}
146 	atomic_set(&mqtsdev->buff_in_use, 1);
147 
148 	sync_msg = mqtsdev->sync_msg;
149 	sync_msg->header.signature = cpu_to_le16(QAIC_TIMESYNC_SIGNATURE);
150 	sync_msg->header.msg_type = QAIC_TS_SYNC_REQ;
151 	/* Read host UTC time and convert to uS*/
152 	host_time_us = div_u64(ktime_get_real_ns(), NSEC_PER_USEC);
153 	device_qtimer = read_qtimer(mqtsdev->qtimer_addr);
154 	device_qtimer_us = QAIC_CONV_QTIMER_TO_US(device_qtimer);
155 	/* Offset between host UTC and device time */
156 	offset_us = host_time_us - device_qtimer_us;
157 
158 	host_sec = div_u64(offset_us, USEC_PER_SEC);
159 	sync_msg->data.tv_usec = cpu_to_le64(offset_us - host_sec * USEC_PER_SEC);
160 	sync_msg->data.tv_sec = cpu_to_le64(host_sec);
161 	ret = mhi_queue_buf(mqtsdev->mhi_dev, DMA_TO_DEVICE, sync_msg, sizeof(*sync_msg), MHI_EOT);
162 	if (ret && (ret != -EAGAIN)) {
163 		dev_err(mqtsdev->dev, "%s unable to queue to mhi:%d\n", __func__, ret);
164 		return;
165 	} else if (ret == -EAGAIN) {
166 		atomic_set(&mqtsdev->buff_in_use, 0);
167 	}
168 
169 mod_timer:
170 	ret = mod_timer(t, jiffies + msecs_to_jiffies(timesync_delay_ms));
171 	if (ret)
172 		dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret);
173 }
174 
175 void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev)
176 {
177 	struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
178 
179 	timer_delete_sync(&mqtsdev->timer);
180 }
181 
182 static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
183 {
184 	struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
185 	struct mqts_dev *mqtsdev;
186 	struct timer_list *timer;
187 	int ret;
188 
189 	mqtsdev = kzalloc_obj(*mqtsdev);
190 	if (!mqtsdev) {
191 		ret = -ENOMEM;
192 		goto out;
193 	}
194 
195 	timer = &mqtsdev->timer;
196 	mqtsdev->mhi_dev = mhi_dev;
197 	mqtsdev->qdev = qdev;
198 	mqtsdev->dev = &qdev->pdev->dev;
199 
200 	mqtsdev->sync_msg = kzalloc_obj(*mqtsdev->sync_msg);
201 	if (!mqtsdev->sync_msg) {
202 		ret = -ENOMEM;
203 		goto free_mqts_dev;
204 	}
205 	atomic_set(&mqtsdev->buff_in_use, 0);
206 
207 	ret = mhi_prepare_for_transfer(mhi_dev);
208 	if (ret)
209 		goto free_sync_msg;
210 
211 	/* Qtimer register pointer */
212 	mqtsdev->qtimer_addr = qdev->bar_mhi + QTIMER_REG_OFFSET;
213 	timer_setup(timer, qaic_timesync_timer, 0);
214 	timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
215 	add_timer(timer);
216 	dev_set_drvdata(&mhi_dev->dev, mqtsdev);
217 	qdev->mqts_ch = mhi_dev;
218 
219 	return 0;
220 
221 free_sync_msg:
222 	kfree(mqtsdev->sync_msg);
223 free_mqts_dev:
224 	kfree(mqtsdev);
225 out:
226 	return ret;
227 };
228 
229 static void qaic_timesync_remove(struct mhi_device *mhi_dev)
230 {
231 	struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
232 
233 	mqtsdev->qdev->mqts_ch = NULL;
234 	timer_delete_sync(&mqtsdev->timer);
235 	mhi_unprepare_from_transfer(mqtsdev->mhi_dev);
236 	kfree(mqtsdev->sync_msg);
237 	kfree(mqtsdev);
238 }
239 
240 static const struct mhi_device_id qaic_timesync_match_table[] = {
241 	{ .chan = "QAIC_TIMESYNC_PERIODIC"},
242 	{},
243 };
244 
245 MODULE_DEVICE_TABLE(mhi, qaic_timesync_match_table);
246 
247 static struct mhi_driver qaic_timesync_driver = {
248 	.id_table = qaic_timesync_match_table,
249 	.remove = qaic_timesync_remove,
250 	.probe = qaic_timesync_probe,
251 	.ul_xfer_cb = qaic_timesync_ul_xfer_cb,
252 	.dl_xfer_cb = qaic_timesync_dl_xfer_cb,
253 	.driver = {
254 		.name = "qaic_timesync_periodic",
255 	},
256 };
257 
258 static void qaic_boot_timesync_worker(struct work_struct *work)
259 {
260 	struct qts_resp *resp = container_of(work, struct qts_resp, work);
261 	struct qts_host_time_sync_msg_data *req;
262 	struct qts_resp_msg data = resp->data;
263 	struct qaic_device *qdev = resp->qdev;
264 	struct mhi_device *mhi_dev;
265 	struct timespec64 ts;
266 	int ret;
267 
268 	mhi_dev = qdev->qts_ch;
269 	/* Queue the response message beforehand to avoid race conditions */
270 	ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT);
271 	if (ret) {
272 		kfree(resp);
273 		dev_warn(&mhi_dev->dev, "Failed to re-queue response buffer %d\n", ret);
274 		return;
275 	}
276 
277 	switch (data.hdr.msg_type) {
278 	case QAIC_TS_CMD_TO_HOST:
279 		req = kzalloc_obj(*req);
280 		if (!req)
281 			break;
282 
283 		req->header = data.hdr;
284 		req->header.msg_type = QAIC_TS_SYNC_REQ;
285 		ktime_get_real_ts64(&ts);
286 		req->data.tv_sec = cpu_to_le64(ts.tv_sec);
287 		req->data.tv_usec = cpu_to_le64(div_u64(ts.tv_nsec, NSEC_PER_USEC));
288 
289 		ret = mhi_queue_buf(mhi_dev, DMA_TO_DEVICE, req, sizeof(*req), MHI_EOT);
290 		if (ret) {
291 			kfree(req);
292 			dev_dbg(&mhi_dev->dev, "Failed to send request message. Error %d\n", ret);
293 		}
294 		break;
295 	case QAIC_TS_ACK_TO_HOST:
296 		dev_dbg(&mhi_dev->dev, "ACK received from device\n");
297 		break;
298 	default:
299 		dev_err(&mhi_dev->dev, "Invalid message type %u.\n", data.hdr.msg_type);
300 	}
301 }
302 
303 static int qaic_boot_timesync_queue_resp(struct mhi_device *mhi_dev, struct qaic_device *qdev)
304 {
305 	struct qts_resp *resp;
306 	int ret;
307 
308 	resp = kzalloc_obj(*resp);
309 	if (!resp)
310 		return -ENOMEM;
311 
312 	resp->qdev = qdev;
313 	INIT_WORK(&resp->work, qaic_boot_timesync_worker);
314 
315 	ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT);
316 	if (ret) {
317 		kfree(resp);
318 		dev_warn(&mhi_dev->dev, "Failed to queue response buffer %d\n", ret);
319 		return ret;
320 	}
321 
322 	return 0;
323 }
324 
325 static void qaic_boot_timesync_remove(struct mhi_device *mhi_dev)
326 {
327 	struct qaic_device *qdev;
328 
329 	qdev = dev_get_drvdata(&mhi_dev->dev);
330 	mhi_unprepare_from_transfer(qdev->qts_ch);
331 	qdev->qts_ch = NULL;
332 }
333 
334 static int qaic_boot_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
335 {
336 	struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
337 	int ret;
338 
339 	ret = mhi_prepare_for_transfer(mhi_dev);
340 	if (ret)
341 		return ret;
342 
343 	qdev->qts_ch = mhi_dev;
344 	dev_set_drvdata(&mhi_dev->dev, qdev);
345 
346 	ret = qaic_boot_timesync_queue_resp(mhi_dev, qdev);
347 	if (ret) {
348 		dev_set_drvdata(&mhi_dev->dev, NULL);
349 		qdev->qts_ch = NULL;
350 		mhi_unprepare_from_transfer(mhi_dev);
351 	}
352 
353 	return ret;
354 }
355 
356 static void qaic_boot_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
357 {
358 	kfree(mhi_result->buf_addr);
359 }
360 
361 static void qaic_boot_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
362 {
363 	struct qts_resp *resp = container_of(mhi_result->buf_addr, struct qts_resp, data);
364 
365 	if (mhi_result->transaction_status || mhi_result->bytes_xferd != sizeof(resp->data)) {
366 		kfree(resp);
367 		return;
368 	}
369 
370 	queue_work(resp->qdev->qts_wq, &resp->work);
371 }
372 
373 static const struct mhi_device_id qaic_boot_timesync_match_table[] = {
374 	{ .chan = "QAIC_TIMESYNC"},
375 	{},
376 };
377 
378 static struct mhi_driver qaic_boot_timesync_driver = {
379 	.id_table = qaic_boot_timesync_match_table,
380 	.remove = qaic_boot_timesync_remove,
381 	.probe = qaic_boot_timesync_probe,
382 	.ul_xfer_cb = qaic_boot_timesync_ul_xfer_cb,
383 	.dl_xfer_cb = qaic_boot_timesync_dl_xfer_cb,
384 	.driver = {
385 		.name = "qaic_timesync",
386 	},
387 };
388 
389 int qaic_timesync_init(void)
390 {
391 	int ret;
392 
393 	ret = mhi_driver_register(&qaic_timesync_driver);
394 	if (ret)
395 		return ret;
396 	ret = mhi_driver_register(&qaic_boot_timesync_driver);
397 
398 	return ret;
399 }
400 
401 void qaic_timesync_deinit(void)
402 {
403 	mhi_driver_unregister(&qaic_boot_timesync_driver);
404 	mhi_driver_unregister(&qaic_timesync_driver);
405 }
406