xref: /openbmc/linux/drivers/misc/mei/dma-ring.c (revision 1e55b609)
1ce0925e8STomas Winkler // SPDX-License-Identifier: GPL-2.0
2ce0925e8STomas Winkler /*
3ce0925e8STomas Winkler  * Copyright(c) 2016-2018 Intel Corporation. All rights reserved.
4ce0925e8STomas Winkler  */
5ce0925e8STomas Winkler #include <linux/dma-mapping.h>
6ce0925e8STomas Winkler #include <linux/mei.h>
7ce0925e8STomas Winkler 
8ce0925e8STomas Winkler #include "mei_dev.h"
9ce0925e8STomas Winkler 
10ce0925e8STomas Winkler /**
11ce0925e8STomas Winkler  * mei_dmam_dscr_alloc() - allocate a managed coherent buffer
12ce0925e8STomas Winkler  *     for the dma descriptor
13ce0925e8STomas Winkler  * @dev: mei_device
14ce0925e8STomas Winkler  * @dscr: dma descriptor
15ce0925e8STomas Winkler  *
16ce0925e8STomas Winkler  * Return:
17ce0925e8STomas Winkler  * * 0       - on success or zero allocation request
18ce0925e8STomas Winkler  * * -EINVAL - if size is not power of 2
19ce0925e8STomas Winkler  * * -ENOMEM - of allocation has failed
20ce0925e8STomas Winkler  */
mei_dmam_dscr_alloc(struct mei_device * dev,struct mei_dma_dscr * dscr)21ce0925e8STomas Winkler static int mei_dmam_dscr_alloc(struct mei_device *dev,
22ce0925e8STomas Winkler 			       struct mei_dma_dscr *dscr)
23ce0925e8STomas Winkler {
24ce0925e8STomas Winkler 	if (!dscr->size)
25ce0925e8STomas Winkler 		return 0;
26ce0925e8STomas Winkler 
27ce0925e8STomas Winkler 	if (WARN_ON(!is_power_of_2(dscr->size)))
28ce0925e8STomas Winkler 		return -EINVAL;
29ce0925e8STomas Winkler 
30ce0925e8STomas Winkler 	if (dscr->vaddr)
31ce0925e8STomas Winkler 		return 0;
32ce0925e8STomas Winkler 
33ce0925e8STomas Winkler 	dscr->vaddr = dmam_alloc_coherent(dev->dev, dscr->size, &dscr->daddr,
34ce0925e8STomas Winkler 					  GFP_KERNEL);
35ce0925e8STomas Winkler 	if (!dscr->vaddr)
36ce0925e8STomas Winkler 		return -ENOMEM;
37ce0925e8STomas Winkler 
38ce0925e8STomas Winkler 	return 0;
39ce0925e8STomas Winkler }
40ce0925e8STomas Winkler 
41ce0925e8STomas Winkler /**
42ce0925e8STomas Winkler  * mei_dmam_dscr_free() - free a managed coherent buffer
43ce0925e8STomas Winkler  *     from the dma descriptor
44ce0925e8STomas Winkler  * @dev: mei_device
45ce0925e8STomas Winkler  * @dscr: dma descriptor
46ce0925e8STomas Winkler  */
mei_dmam_dscr_free(struct mei_device * dev,struct mei_dma_dscr * dscr)47ce0925e8STomas Winkler static void mei_dmam_dscr_free(struct mei_device *dev,
48ce0925e8STomas Winkler 			       struct mei_dma_dscr *dscr)
49ce0925e8STomas Winkler {
50ce0925e8STomas Winkler 	if (!dscr->vaddr)
51ce0925e8STomas Winkler 		return;
52ce0925e8STomas Winkler 
53ce0925e8STomas Winkler 	dmam_free_coherent(dev->dev, dscr->size, dscr->vaddr, dscr->daddr);
54ce0925e8STomas Winkler 	dscr->vaddr = NULL;
55ce0925e8STomas Winkler }
56ce0925e8STomas Winkler 
57ce0925e8STomas Winkler /**
58ce0925e8STomas Winkler  * mei_dmam_ring_free() - free dma ring buffers
59ce0925e8STomas Winkler  * @dev: mei device
60ce0925e8STomas Winkler  */
mei_dmam_ring_free(struct mei_device * dev)61ce0925e8STomas Winkler void mei_dmam_ring_free(struct mei_device *dev)
62ce0925e8STomas Winkler {
63ce0925e8STomas Winkler 	int i;
64ce0925e8STomas Winkler 
65ce0925e8STomas Winkler 	for (i = 0; i < DMA_DSCR_NUM; i++)
66ce0925e8STomas Winkler 		mei_dmam_dscr_free(dev, &dev->dr_dscr[i]);
67ce0925e8STomas Winkler }
68ce0925e8STomas Winkler 
69ce0925e8STomas Winkler /**
70ce0925e8STomas Winkler  * mei_dmam_ring_alloc() - allocate dma ring buffers
71ce0925e8STomas Winkler  * @dev: mei device
72ce0925e8STomas Winkler  *
73ce0925e8STomas Winkler  * Return: -ENOMEM on allocation failure 0 otherwise
74ce0925e8STomas Winkler  */
mei_dmam_ring_alloc(struct mei_device * dev)75ce0925e8STomas Winkler int mei_dmam_ring_alloc(struct mei_device *dev)
76ce0925e8STomas Winkler {
77ce0925e8STomas Winkler 	int i;
78ce0925e8STomas Winkler 
79ce0925e8STomas Winkler 	for (i = 0; i < DMA_DSCR_NUM; i++)
80ce0925e8STomas Winkler 		if (mei_dmam_dscr_alloc(dev, &dev->dr_dscr[i]))
81ce0925e8STomas Winkler 			goto err;
82ce0925e8STomas Winkler 
83ce0925e8STomas Winkler 	return 0;
84ce0925e8STomas Winkler 
85ce0925e8STomas Winkler err:
86ce0925e8STomas Winkler 	mei_dmam_ring_free(dev);
87ce0925e8STomas Winkler 	return -ENOMEM;
88ce0925e8STomas Winkler }
89ce0925e8STomas Winkler 
90ce0925e8STomas Winkler /**
91ce0925e8STomas Winkler  * mei_dma_ring_is_allocated() - check if dma ring is allocated
92ce0925e8STomas Winkler  * @dev: mei device
93ce0925e8STomas Winkler  *
94ce0925e8STomas Winkler  * Return: true if dma ring is allocated
95ce0925e8STomas Winkler  */
mei_dma_ring_is_allocated(struct mei_device * dev)96ce0925e8STomas Winkler bool mei_dma_ring_is_allocated(struct mei_device *dev)
97ce0925e8STomas Winkler {
98ce0925e8STomas Winkler 	return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr;
99ce0925e8STomas Winkler }
1002513eb0dSTomas Winkler 
1012513eb0dSTomas Winkler static inline
mei_dma_ring_ctrl(struct mei_device * dev)1022513eb0dSTomas Winkler struct hbm_dma_ring_ctrl *mei_dma_ring_ctrl(struct mei_device *dev)
1032513eb0dSTomas Winkler {
1042513eb0dSTomas Winkler 	return (struct hbm_dma_ring_ctrl *)dev->dr_dscr[DMA_DSCR_CTRL].vaddr;
1052513eb0dSTomas Winkler }
1062513eb0dSTomas Winkler 
1072513eb0dSTomas Winkler /**
1082513eb0dSTomas Winkler  * mei_dma_ring_reset() - reset the dma control block
1092513eb0dSTomas Winkler  * @dev: mei device
1102513eb0dSTomas Winkler  */
mei_dma_ring_reset(struct mei_device * dev)1112513eb0dSTomas Winkler void mei_dma_ring_reset(struct mei_device *dev)
1122513eb0dSTomas Winkler {
1132513eb0dSTomas Winkler 	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
1142513eb0dSTomas Winkler 
1152513eb0dSTomas Winkler 	if (!ctrl)
1162513eb0dSTomas Winkler 		return;
1172513eb0dSTomas Winkler 
1182513eb0dSTomas Winkler 	memset(ctrl, 0, sizeof(*ctrl));
1192513eb0dSTomas Winkler }
1206316321fSTomas Winkler 
1216316321fSTomas Winkler /**
1226316321fSTomas Winkler  * mei_dma_copy_from() - copy from dma ring into buffer
1236316321fSTomas Winkler  * @dev: mei device
1246316321fSTomas Winkler  * @buf: data buffer
1256316321fSTomas Winkler  * @offset: offset in slots.
1266316321fSTomas Winkler  * @n: number of slots to copy.
1276316321fSTomas Winkler  */
mei_dma_copy_from(struct mei_device * dev,unsigned char * buf,u32 offset,u32 n)1286316321fSTomas Winkler static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
1296316321fSTomas Winkler 				u32 offset, u32 n)
1306316321fSTomas Winkler {
1316316321fSTomas Winkler 	unsigned char *dbuf = dev->dr_dscr[DMA_DSCR_DEVICE].vaddr;
1326316321fSTomas Winkler 
1336316321fSTomas Winkler 	size_t b_offset = offset << 2;
1346316321fSTomas Winkler 	size_t b_n = n << 2;
1356316321fSTomas Winkler 
1366316321fSTomas Winkler 	memcpy(buf, dbuf + b_offset, b_n);
1376316321fSTomas Winkler 
1386316321fSTomas Winkler 	return b_n;
1396316321fSTomas Winkler }
1406316321fSTomas Winkler 
1416316321fSTomas Winkler /**
142c30362ccSTomas Winkler  * mei_dma_copy_to() - copy to a buffer to the dma ring
143c30362ccSTomas Winkler  * @dev: mei device
144c30362ccSTomas Winkler  * @buf: data buffer
145c30362ccSTomas Winkler  * @offset: offset in slots.
146c30362ccSTomas Winkler  * @n: number of slots to copy.
147c30362ccSTomas Winkler  */
mei_dma_copy_to(struct mei_device * dev,unsigned char * buf,u32 offset,u32 n)148c30362ccSTomas Winkler static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
149c30362ccSTomas Winkler 			      u32 offset, u32 n)
150c30362ccSTomas Winkler {
151c30362ccSTomas Winkler 	unsigned char *hbuf = dev->dr_dscr[DMA_DSCR_HOST].vaddr;
152c30362ccSTomas Winkler 
153c30362ccSTomas Winkler 	size_t b_offset = offset << 2;
154c30362ccSTomas Winkler 	size_t b_n = n << 2;
155c30362ccSTomas Winkler 
156c30362ccSTomas Winkler 	memcpy(hbuf + b_offset, buf, b_n);
157c30362ccSTomas Winkler 
158c30362ccSTomas Winkler 	return b_n;
159c30362ccSTomas Winkler }
160c30362ccSTomas Winkler 
161c30362ccSTomas Winkler /**
1626316321fSTomas Winkler  * mei_dma_ring_read() - read data from the ring
1636316321fSTomas Winkler  * @dev: mei device
1646316321fSTomas Winkler  * @buf: buffer to read into: may be NULL in case of droping the data.
1656316321fSTomas Winkler  * @len: length to read.
1666316321fSTomas Winkler  */
mei_dma_ring_read(struct mei_device * dev,unsigned char * buf,u32 len)1676316321fSTomas Winkler void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
1686316321fSTomas Winkler {
1696316321fSTomas Winkler 	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
1706316321fSTomas Winkler 	u32 dbuf_depth;
1716316321fSTomas Winkler 	u32 rd_idx, rem, slots;
1726316321fSTomas Winkler 
1736316321fSTomas Winkler 	if (WARN_ON(!ctrl))
1746316321fSTomas Winkler 		return;
1756316321fSTomas Winkler 
1766316321fSTomas Winkler 	dev_dbg(dev->dev, "reading from dma %u bytes\n", len);
1776316321fSTomas Winkler 
1786316321fSTomas Winkler 	if (!len)
1796316321fSTomas Winkler 		return;
1806316321fSTomas Winkler 
1816316321fSTomas Winkler 	dbuf_depth = dev->dr_dscr[DMA_DSCR_DEVICE].size >> 2;
1826316321fSTomas Winkler 	rd_idx = READ_ONCE(ctrl->dbuf_rd_idx) & (dbuf_depth - 1);
1836316321fSTomas Winkler 	slots = mei_data2slots(len);
1846316321fSTomas Winkler 
1856316321fSTomas Winkler 	/* if buf is NULL we drop the packet by advancing the pointer.*/
1866316321fSTomas Winkler 	if (!buf)
1876316321fSTomas Winkler 		goto out;
1886316321fSTomas Winkler 
1896316321fSTomas Winkler 	if (rd_idx + slots > dbuf_depth) {
1906316321fSTomas Winkler 		buf += mei_dma_copy_from(dev, buf, rd_idx, dbuf_depth - rd_idx);
1916316321fSTomas Winkler 		rem = slots - (dbuf_depth - rd_idx);
1926316321fSTomas Winkler 		rd_idx = 0;
1936316321fSTomas Winkler 	} else {
1946316321fSTomas Winkler 		rem = slots;
1956316321fSTomas Winkler 	}
1966316321fSTomas Winkler 
1976316321fSTomas Winkler 	mei_dma_copy_from(dev, buf, rd_idx, rem);
1986316321fSTomas Winkler out:
1996316321fSTomas Winkler 	WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots);
2006316321fSTomas Winkler }
201c30362ccSTomas Winkler 
mei_dma_ring_hbuf_depth(struct mei_device * dev)202c30362ccSTomas Winkler static inline u32 mei_dma_ring_hbuf_depth(struct mei_device *dev)
203c30362ccSTomas Winkler {
204c30362ccSTomas Winkler 	return dev->dr_dscr[DMA_DSCR_HOST].size >> 2;
205c30362ccSTomas Winkler }
206c30362ccSTomas Winkler 
207c30362ccSTomas Winkler /**
208c30362ccSTomas Winkler  * mei_dma_ring_empty_slots() - calaculate number of empty slots in dma ring
209c30362ccSTomas Winkler  * @dev: mei_device
210c30362ccSTomas Winkler  *
211c30362ccSTomas Winkler  * Return: number of empty slots
212c30362ccSTomas Winkler  */
mei_dma_ring_empty_slots(struct mei_device * dev)213c30362ccSTomas Winkler u32 mei_dma_ring_empty_slots(struct mei_device *dev)
214c30362ccSTomas Winkler {
215c30362ccSTomas Winkler 	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
216c30362ccSTomas Winkler 	u32 wr_idx, rd_idx, hbuf_depth, empty;
217c30362ccSTomas Winkler 
218c30362ccSTomas Winkler 	if (!mei_dma_ring_is_allocated(dev))
219c30362ccSTomas Winkler 		return 0;
220c30362ccSTomas Winkler 
221c30362ccSTomas Winkler 	if (WARN_ON(!ctrl))
222c30362ccSTomas Winkler 		return 0;
223c30362ccSTomas Winkler 
224c30362ccSTomas Winkler 	/* easier to work in slots */
225c30362ccSTomas Winkler 	hbuf_depth = mei_dma_ring_hbuf_depth(dev);
226c30362ccSTomas Winkler 	rd_idx = READ_ONCE(ctrl->hbuf_rd_idx);
227c30362ccSTomas Winkler 	wr_idx = READ_ONCE(ctrl->hbuf_wr_idx);
228c30362ccSTomas Winkler 
229c30362ccSTomas Winkler 	if (rd_idx > wr_idx)
230c30362ccSTomas Winkler 		empty = rd_idx - wr_idx;
231c30362ccSTomas Winkler 	else
232c30362ccSTomas Winkler 		empty = hbuf_depth - (wr_idx - rd_idx);
233c30362ccSTomas Winkler 
234c30362ccSTomas Winkler 	return empty;
235c30362ccSTomas Winkler }
236c30362ccSTomas Winkler 
237c30362ccSTomas Winkler /**
238c30362ccSTomas Winkler  * mei_dma_ring_write - write data to dma ring host buffer
239c30362ccSTomas Winkler  *
240c30362ccSTomas Winkler  * @dev: mei_device
241c30362ccSTomas Winkler  * @buf: data will be written
242c30362ccSTomas Winkler  * @len: data length
243c30362ccSTomas Winkler  */
mei_dma_ring_write(struct mei_device * dev,unsigned char * buf,u32 len)244c30362ccSTomas Winkler void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len)
245c30362ccSTomas Winkler {
246c30362ccSTomas Winkler 	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
247c30362ccSTomas Winkler 	u32 hbuf_depth;
248c30362ccSTomas Winkler 	u32 wr_idx, rem, slots;
249c30362ccSTomas Winkler 
250c30362ccSTomas Winkler 	if (WARN_ON(!ctrl))
251c30362ccSTomas Winkler 		return;
252c30362ccSTomas Winkler 
253c30362ccSTomas Winkler 	dev_dbg(dev->dev, "writing to dma %u bytes\n", len);
254c30362ccSTomas Winkler 	hbuf_depth = mei_dma_ring_hbuf_depth(dev);
255c30362ccSTomas Winkler 	wr_idx = READ_ONCE(ctrl->hbuf_wr_idx) & (hbuf_depth - 1);
256c30362ccSTomas Winkler 	slots = mei_data2slots(len);
257c30362ccSTomas Winkler 
258c30362ccSTomas Winkler 	if (wr_idx + slots > hbuf_depth) {
259c30362ccSTomas Winkler 		buf += mei_dma_copy_to(dev, buf, wr_idx, hbuf_depth - wr_idx);
260c30362ccSTomas Winkler 		rem = slots - (hbuf_depth - wr_idx);
261c30362ccSTomas Winkler 		wr_idx = 0;
262c30362ccSTomas Winkler 	} else {
263c30362ccSTomas Winkler 		rem = slots;
264c30362ccSTomas Winkler 	}
265c30362ccSTomas Winkler 
266c30362ccSTomas Winkler 	mei_dma_copy_to(dev, buf, wr_idx, rem);
267c30362ccSTomas Winkler 
268c30362ccSTomas Winkler 	WRITE_ONCE(ctrl->hbuf_wr_idx, ctrl->hbuf_wr_idx + slots);
269c30362ccSTomas Winkler }
270