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