162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ISHTP DMA I/F functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2003-2016, Intel Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/wait.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1362306a36Sopenharmony_ci#include "ishtp-dev.h" 1462306a36Sopenharmony_ci#include "client.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/** 1762306a36Sopenharmony_ci * ishtp_cl_alloc_dma_buf() - Allocate DMA RX and TX buffer 1862306a36Sopenharmony_ci * @dev: ishtp device 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Allocate RX and TX DMA buffer once during bus setup. 2162306a36Sopenharmony_ci * It allocates 1MB, RX and TX DMA buffer, which are divided 2262306a36Sopenharmony_ci * into slots. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_civoid ishtp_cl_alloc_dma_buf(struct ishtp_device *dev) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci dma_addr_t h; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (dev->ishtp_host_dma_tx_buf) 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci dev->ishtp_host_dma_tx_buf_size = 1024*1024; 3262306a36Sopenharmony_ci dev->ishtp_host_dma_rx_buf_size = 1024*1024; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* Allocate Tx buffer and init usage bitmap */ 3562306a36Sopenharmony_ci dev->ishtp_host_dma_tx_buf = dma_alloc_coherent(dev->devc, 3662306a36Sopenharmony_ci dev->ishtp_host_dma_tx_buf_size, 3762306a36Sopenharmony_ci &h, GFP_KERNEL); 3862306a36Sopenharmony_ci if (dev->ishtp_host_dma_tx_buf) 3962306a36Sopenharmony_ci dev->ishtp_host_dma_tx_buf_phys = h; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci dev->ishtp_dma_num_slots = dev->ishtp_host_dma_tx_buf_size / 4262306a36Sopenharmony_ci DMA_SLOT_SIZE; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci dev->ishtp_dma_tx_map = kcalloc(dev->ishtp_dma_num_slots, 4562306a36Sopenharmony_ci sizeof(uint8_t), 4662306a36Sopenharmony_ci GFP_KERNEL); 4762306a36Sopenharmony_ci spin_lock_init(&dev->ishtp_dma_tx_lock); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Allocate Rx buffer */ 5062306a36Sopenharmony_ci dev->ishtp_host_dma_rx_buf = dma_alloc_coherent(dev->devc, 5162306a36Sopenharmony_ci dev->ishtp_host_dma_rx_buf_size, 5262306a36Sopenharmony_ci &h, GFP_KERNEL); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (dev->ishtp_host_dma_rx_buf) 5562306a36Sopenharmony_ci dev->ishtp_host_dma_rx_buf_phys = h; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * ishtp_cl_free_dma_buf() - Free DMA RX and TX buffer 6062306a36Sopenharmony_ci * @dev: ishtp device 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Free DMA buffer when all clients are released. This is 6362306a36Sopenharmony_ci * only happens during error path in ISH built in driver 6462306a36Sopenharmony_ci * model 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_civoid ishtp_cl_free_dma_buf(struct ishtp_device *dev) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci dma_addr_t h; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (dev->ishtp_host_dma_tx_buf) { 7162306a36Sopenharmony_ci h = dev->ishtp_host_dma_tx_buf_phys; 7262306a36Sopenharmony_ci dma_free_coherent(dev->devc, dev->ishtp_host_dma_tx_buf_size, 7362306a36Sopenharmony_ci dev->ishtp_host_dma_tx_buf, h); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (dev->ishtp_host_dma_rx_buf) { 7762306a36Sopenharmony_ci h = dev->ishtp_host_dma_rx_buf_phys; 7862306a36Sopenharmony_ci dma_free_coherent(dev->devc, dev->ishtp_host_dma_rx_buf_size, 7962306a36Sopenharmony_ci dev->ishtp_host_dma_rx_buf, h); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci kfree(dev->ishtp_dma_tx_map); 8362306a36Sopenharmony_ci dev->ishtp_host_dma_tx_buf = NULL; 8462306a36Sopenharmony_ci dev->ishtp_host_dma_rx_buf = NULL; 8562306a36Sopenharmony_ci dev->ishtp_dma_tx_map = NULL; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * ishtp_cl_get_dma_send_buf() - Get a DMA memory slot 9062306a36Sopenharmony_ci * @dev: ishtp device 9162306a36Sopenharmony_ci * @size: Size of memory to get 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Find and return free address of "size" bytes in dma tx buffer. 9462306a36Sopenharmony_ci * the function will mark this address as "in-used" memory. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Return: NULL when no free buffer else a buffer to copy 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_civoid *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev, 9962306a36Sopenharmony_ci uint32_t size) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned long flags; 10262306a36Sopenharmony_ci int i, j, free; 10362306a36Sopenharmony_ci /* additional slot is needed if there is rem */ 10462306a36Sopenharmony_ci int required_slots = (size / DMA_SLOT_SIZE) 10562306a36Sopenharmony_ci + 1 * (size % DMA_SLOT_SIZE != 0); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!dev->ishtp_dma_tx_map) { 10862306a36Sopenharmony_ci dev_err(dev->devc, "Fail to allocate Tx map\n"); 10962306a36Sopenharmony_ci return NULL; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); 11362306a36Sopenharmony_ci for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) { 11462306a36Sopenharmony_ci free = 1; 11562306a36Sopenharmony_ci for (j = 0; j < required_slots; j++) 11662306a36Sopenharmony_ci if (dev->ishtp_dma_tx_map[i+j]) { 11762306a36Sopenharmony_ci free = 0; 11862306a36Sopenharmony_ci i += j; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci if (free) { 12262306a36Sopenharmony_ci /* mark memory as "caught" */ 12362306a36Sopenharmony_ci for (j = 0; j < required_slots; j++) 12462306a36Sopenharmony_ci dev->ishtp_dma_tx_map[i+j] = 1; 12562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); 12662306a36Sopenharmony_ci return (i * DMA_SLOT_SIZE) + 12762306a36Sopenharmony_ci (unsigned char *)dev->ishtp_host_dma_tx_buf; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); 13162306a36Sopenharmony_ci dev_err(dev->devc, "No free DMA buffer to send msg\n"); 13262306a36Sopenharmony_ci return NULL; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * ishtp_cl_release_dma_acked_mem() - Release DMA memory slot 13762306a36Sopenharmony_ci * @dev: ishtp device 13862306a36Sopenharmony_ci * @msg_addr: message address of slot 13962306a36Sopenharmony_ci * @size: Size of memory to get 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Release_dma_acked_mem - returnes the acked memory to free list. 14262306a36Sopenharmony_ci * (from msg_addr, size bytes long) 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_civoid ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev, 14562306a36Sopenharmony_ci void *msg_addr, 14662306a36Sopenharmony_ci uint8_t size) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long flags; 14962306a36Sopenharmony_ci int acked_slots = (size / DMA_SLOT_SIZE) 15062306a36Sopenharmony_ci + 1 * (size % DMA_SLOT_SIZE != 0); 15162306a36Sopenharmony_ci int i, j; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if ((msg_addr - dev->ishtp_host_dma_tx_buf) % DMA_SLOT_SIZE) { 15462306a36Sopenharmony_ci dev_err(dev->devc, "Bad DMA Tx ack address\n"); 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!dev->ishtp_dma_tx_map) { 15962306a36Sopenharmony_ci dev_err(dev->devc, "Fail to allocate Tx map\n"); 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE; 16462306a36Sopenharmony_ci spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); 16562306a36Sopenharmony_ci for (j = 0; j < acked_slots; j++) { 16662306a36Sopenharmony_ci if ((i + j) >= dev->ishtp_dma_num_slots || 16762306a36Sopenharmony_ci !dev->ishtp_dma_tx_map[i+j]) { 16862306a36Sopenharmony_ci /* no such slot, or memory is already free */ 16962306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); 17062306a36Sopenharmony_ci dev_err(dev->devc, "Bad DMA Tx ack address\n"); 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci dev->ishtp_dma_tx_map[i+j] = 0; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); 17662306a36Sopenharmony_ci} 177