xref: /linux/drivers/misc/issei/dma.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023-2026 Intel Corporation */
3 #include <linux/dev_printk.h>
4 #include <linux/dma-mapping.h>
5 #include <linux/errno.h>
6 #include <linux/slab.h>
7 #include <linux/string.h>
8 #include <linux/timekeeping.h>
9 
10 #include "issei_dev.h"
11 #include "hw_msg.h"
12 
13 static inline size_t __issei_dma_size(const struct issei_dma *dma)
14 {
15 	return dma->length.h2f + dma->length.f2h + dma->length.ctl;
16 }
17 
18 /**
19  * issei_dmam_setup - setup DMA buffer and clean it
20  * @idev: issei device object
21  *
22  * Return: 0 on success, <0 on failures
23  */
24 int issei_dmam_setup(struct issei_device *idev)
25 {
26 	struct issei_dma *dma = &idev->dma;
27 	size_t size;
28 
29 	size = __issei_dma_size(dma);
30 	if (!size)
31 		return -EINVAL;
32 
33 	if (!dma->vaddr)
34 		dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr,
35 						 GFP_KERNEL | __GFP_ZERO);
36 	if (dma->vaddr)
37 		memset(dma->vaddr, 0, size);
38 	return dma->vaddr ? 0 : -ENOMEM;
39 }
40 
41 static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma)
42 {
43 	return dma->vaddr + dma->length.h2f + dma->length.f2h;
44 }
45 
46 static bool __issei_dma_is_read_busy(struct issei_dma *dma)
47 {
48 	struct control_buffer *ctl = __dma_get_ctl_buf(dma);
49 
50 	return ctl->f2h_counter_wr != ctl->f2h_counter_rd;
51 }
52 
53 static bool __issei_dma_is_write_busy(struct issei_dma *dma)
54 {
55 	struct control_buffer *ctl = __dma_get_ctl_buf(dma);
56 
57 	return ctl->h2f_counter_wr != ctl->h2f_counter_rd;
58 }
59 
60 static void __issei_dma_read_finalize(struct issei_device *idev)
61 {
62 	struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
63 
64 	dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd);
65 	/* No need to check overflow - the firmware counters overflow the same way */
66 	ctl->f2h_counter_rd++;
67 }
68 
69 static void __issei_dma_write_finalize(struct issei_device *idev)
70 {
71 	struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
72 
73 	dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr);
74 	/* No need to check overflow - the firmware counters overflow the same way */
75 	ctl->h2f_counter_wr++;
76 }
77 
78 /**
79  * issei_dma_write - write data package to DMA
80  * @idev: issei device object
81  * @data: data atructure
82  *
83  * Return: 0 on success, <0 on failures
84  */
85 int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data)
86 {
87 	u8 *write_buf = idev->dma.vaddr;
88 	struct ham_message_header *hdr = (struct ham_message_header *)write_buf;
89 
90 	if (data->length > idev->dma.length.h2f - sizeof(*hdr)) {
91 		dev_err(&idev->dev, "Message is too big\n");
92 		return -EMSGSIZE;
93 	}
94 
95 	if (__issei_dma_is_write_busy(&idev->dma)) {
96 		if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) {
97 			dev_err(&idev->dev, "Write stuck in queue\n");
98 			return -EIO;
99 		}
100 		dev_info(&idev->dev, "Write is busy\n");
101 		return -EBUSY;
102 	}
103 
104 	hdr->length = data->length;
105 	hdr->fw_id = data->fw_id;
106 	hdr->host_id = data->host_id;
107 	hdr->flags = data->flags;
108 	hdr->status = data->status;
109 	hdr->reserved = 0;
110 
111 	memcpy(write_buf + sizeof(*hdr), data->buf, data->length);
112 
113 	__issei_dma_write_finalize(idev);
114 	idev->last_write_ts = ktime_get();
115 	return 0;
116 }
117 
118 /**
119  * issei_dma_read - read data package from DMA
120  * @idev: issei device object
121  * @data: data atructure
122  *
123  * Return: %0 on success, <0 on failures
124  */
125 int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data)
126 {
127 	u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f;
128 	struct ham_message_header *hdr = (struct ham_message_header *)read_buf;
129 
130 	if (!__issei_dma_is_read_busy(&idev->dma)) {
131 		dev_dbg(&idev->dev, "Nothing to read\n");
132 		return -ENODATA;
133 	}
134 
135 	dev_dbg(&idev->dev, "Reading header\n");
136 	data->length = hdr->length;
137 	data->fw_id = hdr->fw_id;
138 	data->host_id = hdr->host_id;
139 	data->flags = hdr->flags;
140 	data->status = hdr->status;
141 
142 	if (data->length > idev->dma.length.f2h - sizeof(*hdr)) {
143 		dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n",
144 			data->length, idev->dma.length.f2h - sizeof(*hdr));
145 		return -EIO;
146 	}
147 
148 	dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length);
149 	data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL);
150 	if (!data->buf)
151 		return -ENOMEM;
152 	__issei_dma_read_finalize(idev);
153 	return 0;
154 }
155