162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Sample fifo dma implementation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kfifo.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * This module shows how to handle fifo dma operations.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* fifo size in elements (bytes) */
1762306a36Sopenharmony_ci#define FIFO_SIZE	32
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic struct kfifo fifo;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int __init example_init(void)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	int			i;
2462306a36Sopenharmony_ci	unsigned int		ret;
2562306a36Sopenharmony_ci	unsigned int		nents;
2662306a36Sopenharmony_ci	struct scatterlist	sg[10];
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	printk(KERN_INFO "DMA fifo test start\n");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (kfifo_alloc(&fifo, FIFO_SIZE, GFP_KERNEL)) {
3162306a36Sopenharmony_ci		printk(KERN_WARNING "error kfifo_alloc\n");
3262306a36Sopenharmony_ci		return -ENOMEM;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	printk(KERN_INFO "queue size: %u\n", kfifo_size(&fifo));
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	kfifo_in(&fifo, "test", 4);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	for (i = 0; i != 9; i++)
4062306a36Sopenharmony_ci		kfifo_put(&fifo, i);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* kick away first byte */
4362306a36Sopenharmony_ci	kfifo_skip(&fifo);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/*
4862306a36Sopenharmony_ci	 * Configure the kfifo buffer to receive data from DMA input.
4962306a36Sopenharmony_ci	 *
5062306a36Sopenharmony_ci	 *  .--------------------------------------.
5162306a36Sopenharmony_ci	 *  | 0 | 1 | 2 | ... | 12 | 13 | ... | 31 |
5262306a36Sopenharmony_ci	 *  |---|------------------|---------------|
5362306a36Sopenharmony_ci	 *   \_/ \________________/ \_____________/
5462306a36Sopenharmony_ci	 *    \          \                  \
5562306a36Sopenharmony_ci	 *     \          \_allocated data   \
5662306a36Sopenharmony_ci	 *      \_*free space*                \_*free space*
5762306a36Sopenharmony_ci	 *
5862306a36Sopenharmony_ci	 * We need two different SG entries: one for the free space area at the
5962306a36Sopenharmony_ci	 * end of the kfifo buffer (19 bytes) and another for the first free
6062306a36Sopenharmony_ci	 * byte at the beginning, after the kfifo_skip().
6162306a36Sopenharmony_ci	 */
6262306a36Sopenharmony_ci	sg_init_table(sg, ARRAY_SIZE(sg));
6362306a36Sopenharmony_ci	nents = kfifo_dma_in_prepare(&fifo, sg, ARRAY_SIZE(sg), FIFO_SIZE);
6462306a36Sopenharmony_ci	printk(KERN_INFO "DMA sgl entries: %d\n", nents);
6562306a36Sopenharmony_ci	if (!nents) {
6662306a36Sopenharmony_ci		/* fifo is full and no sgl was created */
6762306a36Sopenharmony_ci		printk(KERN_WARNING "error kfifo_dma_in_prepare\n");
6862306a36Sopenharmony_ci		return -EIO;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* receive data */
7262306a36Sopenharmony_ci	printk(KERN_INFO "scatterlist for receive:\n");
7362306a36Sopenharmony_ci	for (i = 0; i < nents; i++) {
7462306a36Sopenharmony_ci		printk(KERN_INFO
7562306a36Sopenharmony_ci		"sg[%d] -> "
7662306a36Sopenharmony_ci		"page %p offset 0x%.8x length 0x%.8x\n",
7762306a36Sopenharmony_ci			i, sg_page(&sg[i]), sg[i].offset, sg[i].length);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		if (sg_is_last(&sg[i]))
8062306a36Sopenharmony_ci			break;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* put here your code to setup and exectute the dma operation */
8462306a36Sopenharmony_ci	/* ... */
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* example: zero bytes received */
8762306a36Sopenharmony_ci	ret = 0;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* finish the dma operation and update the received data */
9062306a36Sopenharmony_ci	kfifo_dma_in_finish(&fifo, ret);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Prepare to transmit data, example: 8 bytes */
9362306a36Sopenharmony_ci	nents = kfifo_dma_out_prepare(&fifo, sg, ARRAY_SIZE(sg), 8);
9462306a36Sopenharmony_ci	printk(KERN_INFO "DMA sgl entries: %d\n", nents);
9562306a36Sopenharmony_ci	if (!nents) {
9662306a36Sopenharmony_ci		/* no data was available and no sgl was created */
9762306a36Sopenharmony_ci		printk(KERN_WARNING "error kfifo_dma_out_prepare\n");
9862306a36Sopenharmony_ci		return -EIO;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	printk(KERN_INFO "scatterlist for transmit:\n");
10262306a36Sopenharmony_ci	for (i = 0; i < nents; i++) {
10362306a36Sopenharmony_ci		printk(KERN_INFO
10462306a36Sopenharmony_ci		"sg[%d] -> "
10562306a36Sopenharmony_ci		"page %p offset 0x%.8x length 0x%.8x\n",
10662306a36Sopenharmony_ci			i, sg_page(&sg[i]), sg[i].offset, sg[i].length);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		if (sg_is_last(&sg[i]))
10962306a36Sopenharmony_ci			break;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* put here your code to setup and exectute the dma operation */
11362306a36Sopenharmony_ci	/* ... */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* example: 5 bytes transmitted */
11662306a36Sopenharmony_ci	ret = 5;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* finish the dma operation and update the transmitted data */
11962306a36Sopenharmony_ci	kfifo_dma_out_finish(&fifo, ret);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ret = kfifo_len(&fifo);
12262306a36Sopenharmony_ci	printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (ret != 7) {
12562306a36Sopenharmony_ci		printk(KERN_WARNING "size mismatch: test failed");
12662306a36Sopenharmony_ci		return -EIO;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci	printk(KERN_INFO "test passed\n");
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void __exit example_exit(void)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	kfifo_free(&fifo);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cimodule_init(example_init);
13962306a36Sopenharmony_cimodule_exit(example_exit);
14062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14162306a36Sopenharmony_ciMODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
142