18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ISHTP DMA I/F functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2003-2016, Intel Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/sched.h>
108c2ecf20Sopenharmony_ci#include <linux/wait.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include "ishtp-dev.h"
148c2ecf20Sopenharmony_ci#include "client.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/**
178c2ecf20Sopenharmony_ci * ishtp_cl_alloc_dma_buf() - Allocate DMA RX and TX buffer
188c2ecf20Sopenharmony_ci * @dev: ishtp device
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Allocate RX and TX DMA buffer once during bus setup.
218c2ecf20Sopenharmony_ci * It allocates 1MB, RX and TX DMA buffer, which are divided
228c2ecf20Sopenharmony_ci * into slots.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_civoid	ishtp_cl_alloc_dma_buf(struct ishtp_device *dev)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	dma_addr_t	h;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (dev->ishtp_host_dma_tx_buf)
298c2ecf20Sopenharmony_ci		return;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	dev->ishtp_host_dma_tx_buf_size = 1024*1024;
328c2ecf20Sopenharmony_ci	dev->ishtp_host_dma_rx_buf_size = 1024*1024;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* Allocate Tx buffer and init usage bitmap */
358c2ecf20Sopenharmony_ci	dev->ishtp_host_dma_tx_buf = dma_alloc_coherent(dev->devc,
368c2ecf20Sopenharmony_ci					dev->ishtp_host_dma_tx_buf_size,
378c2ecf20Sopenharmony_ci					&h, GFP_KERNEL);
388c2ecf20Sopenharmony_ci	if (dev->ishtp_host_dma_tx_buf)
398c2ecf20Sopenharmony_ci		dev->ishtp_host_dma_tx_buf_phys = h;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	dev->ishtp_dma_num_slots = dev->ishtp_host_dma_tx_buf_size /
428c2ecf20Sopenharmony_ci						DMA_SLOT_SIZE;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	dev->ishtp_dma_tx_map = kcalloc(dev->ishtp_dma_num_slots,
458c2ecf20Sopenharmony_ci					sizeof(uint8_t),
468c2ecf20Sopenharmony_ci					GFP_KERNEL);
478c2ecf20Sopenharmony_ci	spin_lock_init(&dev->ishtp_dma_tx_lock);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* Allocate Rx buffer */
508c2ecf20Sopenharmony_ci	dev->ishtp_host_dma_rx_buf = dma_alloc_coherent(dev->devc,
518c2ecf20Sopenharmony_ci					dev->ishtp_host_dma_rx_buf_size,
528c2ecf20Sopenharmony_ci					 &h, GFP_KERNEL);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (dev->ishtp_host_dma_rx_buf)
558c2ecf20Sopenharmony_ci		dev->ishtp_host_dma_rx_buf_phys = h;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/**
598c2ecf20Sopenharmony_ci * ishtp_cl_free_dma_buf() - Free DMA RX and TX buffer
608c2ecf20Sopenharmony_ci * @dev: ishtp device
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * Free DMA buffer when all clients are released. This is
638c2ecf20Sopenharmony_ci * only happens during error path in ISH built in driver
648c2ecf20Sopenharmony_ci * model
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_civoid	ishtp_cl_free_dma_buf(struct ishtp_device *dev)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	dma_addr_t	h;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (dev->ishtp_host_dma_tx_buf) {
718c2ecf20Sopenharmony_ci		h = dev->ishtp_host_dma_tx_buf_phys;
728c2ecf20Sopenharmony_ci		dma_free_coherent(dev->devc, dev->ishtp_host_dma_tx_buf_size,
738c2ecf20Sopenharmony_ci				  dev->ishtp_host_dma_tx_buf, h);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (dev->ishtp_host_dma_rx_buf) {
778c2ecf20Sopenharmony_ci		h = dev->ishtp_host_dma_rx_buf_phys;
788c2ecf20Sopenharmony_ci		dma_free_coherent(dev->devc, dev->ishtp_host_dma_rx_buf_size,
798c2ecf20Sopenharmony_ci				  dev->ishtp_host_dma_rx_buf, h);
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	kfree(dev->ishtp_dma_tx_map);
838c2ecf20Sopenharmony_ci	dev->ishtp_host_dma_tx_buf = NULL;
848c2ecf20Sopenharmony_ci	dev->ishtp_host_dma_rx_buf = NULL;
858c2ecf20Sopenharmony_ci	dev->ishtp_dma_tx_map = NULL;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/*
898c2ecf20Sopenharmony_ci * ishtp_cl_get_dma_send_buf() - Get a DMA memory slot
908c2ecf20Sopenharmony_ci * @dev:	ishtp device
918c2ecf20Sopenharmony_ci * @size:	Size of memory to get
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * Find and return free address of "size" bytes in dma tx buffer.
948c2ecf20Sopenharmony_ci * the function will mark this address as "in-used" memory.
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * Return: NULL when no free buffer else a buffer to copy
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_civoid *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev,
998c2ecf20Sopenharmony_ci				uint32_t size)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	unsigned long	flags;
1028c2ecf20Sopenharmony_ci	int i, j, free;
1038c2ecf20Sopenharmony_ci	/* additional slot is needed if there is rem */
1048c2ecf20Sopenharmony_ci	int required_slots = (size / DMA_SLOT_SIZE)
1058c2ecf20Sopenharmony_ci		+ 1 * (size % DMA_SLOT_SIZE != 0);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!dev->ishtp_dma_tx_map) {
1088c2ecf20Sopenharmony_ci		dev_err(dev->devc, "Fail to allocate Tx map\n");
1098c2ecf20Sopenharmony_ci		return NULL;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
1138c2ecf20Sopenharmony_ci	for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) {
1148c2ecf20Sopenharmony_ci		free = 1;
1158c2ecf20Sopenharmony_ci		for (j = 0; j < required_slots; j++)
1168c2ecf20Sopenharmony_ci			if (dev->ishtp_dma_tx_map[i+j]) {
1178c2ecf20Sopenharmony_ci				free = 0;
1188c2ecf20Sopenharmony_ci				i += j;
1198c2ecf20Sopenharmony_ci				break;
1208c2ecf20Sopenharmony_ci			}
1218c2ecf20Sopenharmony_ci		if (free) {
1228c2ecf20Sopenharmony_ci			/* mark memory as "caught" */
1238c2ecf20Sopenharmony_ci			for (j = 0; j < required_slots; j++)
1248c2ecf20Sopenharmony_ci				dev->ishtp_dma_tx_map[i+j] = 1;
1258c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
1268c2ecf20Sopenharmony_ci			return (i * DMA_SLOT_SIZE) +
1278c2ecf20Sopenharmony_ci				(unsigned char *)dev->ishtp_host_dma_tx_buf;
1288c2ecf20Sopenharmony_ci		}
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
1318c2ecf20Sopenharmony_ci	dev_err(dev->devc, "No free DMA buffer to send msg\n");
1328c2ecf20Sopenharmony_ci	return NULL;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/*
1368c2ecf20Sopenharmony_ci * ishtp_cl_release_dma_acked_mem() - Release DMA memory slot
1378c2ecf20Sopenharmony_ci * @dev:	ishtp device
1388c2ecf20Sopenharmony_ci * @msg_addr:	message address of slot
1398c2ecf20Sopenharmony_ci * @size:	Size of memory to get
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * Release_dma_acked_mem - returnes the acked memory to free list.
1428c2ecf20Sopenharmony_ci * (from msg_addr, size bytes long)
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_civoid ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev,
1458c2ecf20Sopenharmony_ci				    void *msg_addr,
1468c2ecf20Sopenharmony_ci				    uint8_t size)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	unsigned long	flags;
1498c2ecf20Sopenharmony_ci	int acked_slots = (size / DMA_SLOT_SIZE)
1508c2ecf20Sopenharmony_ci		+ 1 * (size % DMA_SLOT_SIZE != 0);
1518c2ecf20Sopenharmony_ci	int i, j;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if ((msg_addr - dev->ishtp_host_dma_tx_buf) % DMA_SLOT_SIZE) {
1548c2ecf20Sopenharmony_ci		dev_err(dev->devc, "Bad DMA Tx ack address\n");
1558c2ecf20Sopenharmony_ci		return;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (!dev->ishtp_dma_tx_map) {
1598c2ecf20Sopenharmony_ci		dev_err(dev->devc, "Fail to allocate Tx map\n");
1608c2ecf20Sopenharmony_ci		return;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE;
1648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
1658c2ecf20Sopenharmony_ci	for (j = 0; j < acked_slots; j++) {
1668c2ecf20Sopenharmony_ci		if ((i + j) >= dev->ishtp_dma_num_slots ||
1678c2ecf20Sopenharmony_ci					!dev->ishtp_dma_tx_map[i+j]) {
1688c2ecf20Sopenharmony_ci			/* no such slot, or memory is already free */
1698c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
1708c2ecf20Sopenharmony_ci			dev_err(dev->devc, "Bad DMA Tx ack address\n");
1718c2ecf20Sopenharmony_ci			return;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		dev->ishtp_dma_tx_map[i+j] = 0;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
1768c2ecf20Sopenharmony_ci}
177