18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2016-2018 Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 68c2ecf20Sopenharmony_ci#include <linux/mei.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "mei_dev.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/** 118c2ecf20Sopenharmony_ci * mei_dmam_dscr_alloc() - allocate a managed coherent buffer 128c2ecf20Sopenharmony_ci * for the dma descriptor 138c2ecf20Sopenharmony_ci * @dev: mei_device 148c2ecf20Sopenharmony_ci * @dscr: dma descriptor 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Return: 178c2ecf20Sopenharmony_ci * * 0 - on success or zero allocation request 188c2ecf20Sopenharmony_ci * * -EINVAL - if size is not power of 2 198c2ecf20Sopenharmony_ci * * -ENOMEM - of allocation has failed 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_cistatic int mei_dmam_dscr_alloc(struct mei_device *dev, 228c2ecf20Sopenharmony_ci struct mei_dma_dscr *dscr) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci if (!dscr->size) 258c2ecf20Sopenharmony_ci return 0; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (WARN_ON(!is_power_of_2(dscr->size))) 288c2ecf20Sopenharmony_ci return -EINVAL; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (dscr->vaddr) 318c2ecf20Sopenharmony_ci return 0; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci dscr->vaddr = dmam_alloc_coherent(dev->dev, dscr->size, &dscr->daddr, 348c2ecf20Sopenharmony_ci GFP_KERNEL); 358c2ecf20Sopenharmony_ci if (!dscr->vaddr) 368c2ecf20Sopenharmony_ci return -ENOMEM; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/** 428c2ecf20Sopenharmony_ci * mei_dmam_dscr_free() - free a managed coherent buffer 438c2ecf20Sopenharmony_ci * from the dma descriptor 448c2ecf20Sopenharmony_ci * @dev: mei_device 458c2ecf20Sopenharmony_ci * @dscr: dma descriptor 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistatic void mei_dmam_dscr_free(struct mei_device *dev, 488c2ecf20Sopenharmony_ci struct mei_dma_dscr *dscr) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci if (!dscr->vaddr) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci dmam_free_coherent(dev->dev, dscr->size, dscr->vaddr, dscr->daddr); 548c2ecf20Sopenharmony_ci dscr->vaddr = NULL; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * mei_dmam_ring_free() - free dma ring buffers 598c2ecf20Sopenharmony_ci * @dev: mei device 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_civoid mei_dmam_ring_free(struct mei_device *dev) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (i = 0; i < DMA_DSCR_NUM; i++) 668c2ecf20Sopenharmony_ci mei_dmam_dscr_free(dev, &dev->dr_dscr[i]); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/** 708c2ecf20Sopenharmony_ci * mei_dmam_ring_alloc() - allocate dma ring buffers 718c2ecf20Sopenharmony_ci * @dev: mei device 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Return: -ENOMEM on allocation failure 0 otherwise 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ciint mei_dmam_ring_alloc(struct mei_device *dev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci for (i = 0; i < DMA_DSCR_NUM; i++) 808c2ecf20Sopenharmony_ci if (mei_dmam_dscr_alloc(dev, &dev->dr_dscr[i])) 818c2ecf20Sopenharmony_ci goto err; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cierr: 868c2ecf20Sopenharmony_ci mei_dmam_ring_free(dev); 878c2ecf20Sopenharmony_ci return -ENOMEM; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * mei_dma_ring_is_allocated() - check if dma ring is allocated 928c2ecf20Sopenharmony_ci * @dev: mei device 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * Return: true if dma ring is allocated 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_cibool mei_dma_ring_is_allocated(struct mei_device *dev) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline 1028c2ecf20Sopenharmony_cistruct hbm_dma_ring_ctrl *mei_dma_ring_ctrl(struct mei_device *dev) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return (struct hbm_dma_ring_ctrl *)dev->dr_dscr[DMA_DSCR_CTRL].vaddr; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/** 1088c2ecf20Sopenharmony_ci * mei_dma_ring_reset() - reset the dma control block 1098c2ecf20Sopenharmony_ci * @dev: mei device 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_civoid mei_dma_ring_reset(struct mei_device *dev) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (!ctrl) 1168c2ecf20Sopenharmony_ci return; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci memset(ctrl, 0, sizeof(*ctrl)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * mei_dma_copy_from() - copy from dma ring into buffer 1238c2ecf20Sopenharmony_ci * @dev: mei device 1248c2ecf20Sopenharmony_ci * @buf: data buffer 1258c2ecf20Sopenharmony_ci * @offset: offset in slots. 1268c2ecf20Sopenharmony_ci * @n: number of slots to copy. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf, 1298c2ecf20Sopenharmony_ci u32 offset, u32 n) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned char *dbuf = dev->dr_dscr[DMA_DSCR_DEVICE].vaddr; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci size_t b_offset = offset << 2; 1348c2ecf20Sopenharmony_ci size_t b_n = n << 2; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci memcpy(buf, dbuf + b_offset, b_n); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return b_n; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/** 1428c2ecf20Sopenharmony_ci * mei_dma_copy_to() - copy to a buffer to the dma ring 1438c2ecf20Sopenharmony_ci * @dev: mei device 1448c2ecf20Sopenharmony_ci * @buf: data buffer 1458c2ecf20Sopenharmony_ci * @offset: offset in slots. 1468c2ecf20Sopenharmony_ci * @n: number of slots to copy. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cistatic size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf, 1498c2ecf20Sopenharmony_ci u32 offset, u32 n) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci unsigned char *hbuf = dev->dr_dscr[DMA_DSCR_HOST].vaddr; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci size_t b_offset = offset << 2; 1548c2ecf20Sopenharmony_ci size_t b_n = n << 2; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci memcpy(hbuf + b_offset, buf, b_n); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return b_n; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * mei_dma_ring_read() - read data from the ring 1638c2ecf20Sopenharmony_ci * @dev: mei device 1648c2ecf20Sopenharmony_ci * @buf: buffer to read into: may be NULL in case of droping the data. 1658c2ecf20Sopenharmony_ci * @len: length to read. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_civoid mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev); 1708c2ecf20Sopenharmony_ci u32 dbuf_depth; 1718c2ecf20Sopenharmony_ci u32 rd_idx, rem, slots; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (WARN_ON(!ctrl)) 1748c2ecf20Sopenharmony_ci return; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "reading from dma %u bytes\n", len); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!len) 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dbuf_depth = dev->dr_dscr[DMA_DSCR_DEVICE].size >> 2; 1828c2ecf20Sopenharmony_ci rd_idx = READ_ONCE(ctrl->dbuf_rd_idx) & (dbuf_depth - 1); 1838c2ecf20Sopenharmony_ci slots = mei_data2slots(len); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* if buf is NULL we drop the packet by advancing the pointer.*/ 1868c2ecf20Sopenharmony_ci if (!buf) 1878c2ecf20Sopenharmony_ci goto out; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (rd_idx + slots > dbuf_depth) { 1908c2ecf20Sopenharmony_ci buf += mei_dma_copy_from(dev, buf, rd_idx, dbuf_depth - rd_idx); 1918c2ecf20Sopenharmony_ci rem = slots - (dbuf_depth - rd_idx); 1928c2ecf20Sopenharmony_ci rd_idx = 0; 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci rem = slots; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mei_dma_copy_from(dev, buf, rd_idx, rem); 1988c2ecf20Sopenharmony_ciout: 1998c2ecf20Sopenharmony_ci WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic inline u32 mei_dma_ring_hbuf_depth(struct mei_device *dev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return dev->dr_dscr[DMA_DSCR_HOST].size >> 2; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * mei_dma_ring_empty_slots() - calaculate number of empty slots in dma ring 2098c2ecf20Sopenharmony_ci * @dev: mei_device 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Return: number of empty slots 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ciu32 mei_dma_ring_empty_slots(struct mei_device *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev); 2168c2ecf20Sopenharmony_ci u32 wr_idx, rd_idx, hbuf_depth, empty; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!mei_dma_ring_is_allocated(dev)) 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (WARN_ON(!ctrl)) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* easier to work in slots */ 2258c2ecf20Sopenharmony_ci hbuf_depth = mei_dma_ring_hbuf_depth(dev); 2268c2ecf20Sopenharmony_ci rd_idx = READ_ONCE(ctrl->hbuf_rd_idx); 2278c2ecf20Sopenharmony_ci wr_idx = READ_ONCE(ctrl->hbuf_wr_idx); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (rd_idx > wr_idx) 2308c2ecf20Sopenharmony_ci empty = rd_idx - wr_idx; 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci empty = hbuf_depth - (wr_idx - rd_idx); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return empty; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/** 2388c2ecf20Sopenharmony_ci * mei_dma_ring_write - write data to dma ring host buffer 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * @dev: mei_device 2418c2ecf20Sopenharmony_ci * @buf: data will be written 2428c2ecf20Sopenharmony_ci * @len: data length 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_civoid mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev); 2478c2ecf20Sopenharmony_ci u32 hbuf_depth; 2488c2ecf20Sopenharmony_ci u32 wr_idx, rem, slots; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (WARN_ON(!ctrl)) 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "writing to dma %u bytes\n", len); 2548c2ecf20Sopenharmony_ci hbuf_depth = mei_dma_ring_hbuf_depth(dev); 2558c2ecf20Sopenharmony_ci wr_idx = READ_ONCE(ctrl->hbuf_wr_idx) & (hbuf_depth - 1); 2568c2ecf20Sopenharmony_ci slots = mei_data2slots(len); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (wr_idx + slots > hbuf_depth) { 2598c2ecf20Sopenharmony_ci buf += mei_dma_copy_to(dev, buf, wr_idx, hbuf_depth - wr_idx); 2608c2ecf20Sopenharmony_ci rem = slots - (hbuf_depth - wr_idx); 2618c2ecf20Sopenharmony_ci wr_idx = 0; 2628c2ecf20Sopenharmony_ci } else { 2638c2ecf20Sopenharmony_ci rem = slots; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci mei_dma_copy_to(dev, buf, wr_idx, rem); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci WRITE_ONCE(ctrl->hbuf_wr_idx, ctrl->hbuf_wr_idx + slots); 2698c2ecf20Sopenharmony_ci} 270