1*65f0ecb9SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0 2*65f0ecb9SAlexander Usyskin /* Copyright (C) 2023-2026 Intel Corporation */ 3*65f0ecb9SAlexander Usyskin #include <linux/dev_printk.h> 4*65f0ecb9SAlexander Usyskin #include <linux/dma-mapping.h> 5*65f0ecb9SAlexander Usyskin #include <linux/errno.h> 6*65f0ecb9SAlexander Usyskin #include <linux/slab.h> 7*65f0ecb9SAlexander Usyskin #include <linux/string.h> 8*65f0ecb9SAlexander Usyskin #include <linux/timekeeping.h> 9*65f0ecb9SAlexander Usyskin 10*65f0ecb9SAlexander Usyskin #include "issei_dev.h" 11*65f0ecb9SAlexander Usyskin #include "hw_msg.h" 12*65f0ecb9SAlexander Usyskin 13*65f0ecb9SAlexander Usyskin static inline size_t __issei_dma_size(const struct issei_dma *dma) 14*65f0ecb9SAlexander Usyskin { 15*65f0ecb9SAlexander Usyskin return dma->length.h2f + dma->length.f2h + dma->length.ctl; 16*65f0ecb9SAlexander Usyskin } 17*65f0ecb9SAlexander Usyskin 18*65f0ecb9SAlexander Usyskin /** 19*65f0ecb9SAlexander Usyskin * issei_dmam_setup - setup DMA buffer and clean it 20*65f0ecb9SAlexander Usyskin * @idev: issei device object 21*65f0ecb9SAlexander Usyskin * 22*65f0ecb9SAlexander Usyskin * Return: 0 on success, <0 on failures 23*65f0ecb9SAlexander Usyskin */ 24*65f0ecb9SAlexander Usyskin int issei_dmam_setup(struct issei_device *idev) 25*65f0ecb9SAlexander Usyskin { 26*65f0ecb9SAlexander Usyskin struct issei_dma *dma = &idev->dma; 27*65f0ecb9SAlexander Usyskin size_t size; 28*65f0ecb9SAlexander Usyskin 29*65f0ecb9SAlexander Usyskin size = __issei_dma_size(dma); 30*65f0ecb9SAlexander Usyskin if (!size) 31*65f0ecb9SAlexander Usyskin return -EINVAL; 32*65f0ecb9SAlexander Usyskin 33*65f0ecb9SAlexander Usyskin if (!dma->vaddr) 34*65f0ecb9SAlexander Usyskin dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr, 35*65f0ecb9SAlexander Usyskin GFP_KERNEL | __GFP_ZERO); 36*65f0ecb9SAlexander Usyskin if (dma->vaddr) 37*65f0ecb9SAlexander Usyskin memset(dma->vaddr, 0, size); 38*65f0ecb9SAlexander Usyskin return dma->vaddr ? 0 : -ENOMEM; 39*65f0ecb9SAlexander Usyskin } 40*65f0ecb9SAlexander Usyskin 41*65f0ecb9SAlexander Usyskin static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma) 42*65f0ecb9SAlexander Usyskin { 43*65f0ecb9SAlexander Usyskin return dma->vaddr + dma->length.h2f + dma->length.f2h; 44*65f0ecb9SAlexander Usyskin } 45*65f0ecb9SAlexander Usyskin 46*65f0ecb9SAlexander Usyskin static bool __issei_dma_is_read_busy(struct issei_dma *dma) 47*65f0ecb9SAlexander Usyskin { 48*65f0ecb9SAlexander Usyskin struct control_buffer *ctl = __dma_get_ctl_buf(dma); 49*65f0ecb9SAlexander Usyskin 50*65f0ecb9SAlexander Usyskin return ctl->f2h_counter_wr != ctl->f2h_counter_rd; 51*65f0ecb9SAlexander Usyskin } 52*65f0ecb9SAlexander Usyskin 53*65f0ecb9SAlexander Usyskin static bool __issei_dma_is_write_busy(struct issei_dma *dma) 54*65f0ecb9SAlexander Usyskin { 55*65f0ecb9SAlexander Usyskin struct control_buffer *ctl = __dma_get_ctl_buf(dma); 56*65f0ecb9SAlexander Usyskin 57*65f0ecb9SAlexander Usyskin return ctl->h2f_counter_wr != ctl->h2f_counter_rd; 58*65f0ecb9SAlexander Usyskin } 59*65f0ecb9SAlexander Usyskin 60*65f0ecb9SAlexander Usyskin static void __issei_dma_read_finalize(struct issei_device *idev) 61*65f0ecb9SAlexander Usyskin { 62*65f0ecb9SAlexander Usyskin struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); 63*65f0ecb9SAlexander Usyskin 64*65f0ecb9SAlexander Usyskin dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); 65*65f0ecb9SAlexander Usyskin /* No need to check overflow - the firmware counters overflow the same way */ 66*65f0ecb9SAlexander Usyskin ctl->f2h_counter_rd++; 67*65f0ecb9SAlexander Usyskin } 68*65f0ecb9SAlexander Usyskin 69*65f0ecb9SAlexander Usyskin static void __issei_dma_write_finalize(struct issei_device *idev) 70*65f0ecb9SAlexander Usyskin { 71*65f0ecb9SAlexander Usyskin struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); 72*65f0ecb9SAlexander Usyskin 73*65f0ecb9SAlexander Usyskin dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); 74*65f0ecb9SAlexander Usyskin /* No need to check overflow - the firmware counters overflow the same way */ 75*65f0ecb9SAlexander Usyskin ctl->h2f_counter_wr++; 76*65f0ecb9SAlexander Usyskin } 77*65f0ecb9SAlexander Usyskin 78*65f0ecb9SAlexander Usyskin /** 79*65f0ecb9SAlexander Usyskin * issei_dma_write - write data package to DMA 80*65f0ecb9SAlexander Usyskin * @idev: issei device object 81*65f0ecb9SAlexander Usyskin * @data: data atructure 82*65f0ecb9SAlexander Usyskin * 83*65f0ecb9SAlexander Usyskin * Return: 0 on success, <0 on failures 84*65f0ecb9SAlexander Usyskin */ 85*65f0ecb9SAlexander Usyskin int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data) 86*65f0ecb9SAlexander Usyskin { 87*65f0ecb9SAlexander Usyskin u8 *write_buf = idev->dma.vaddr; 88*65f0ecb9SAlexander Usyskin struct ham_message_header *hdr = (struct ham_message_header *)write_buf; 89*65f0ecb9SAlexander Usyskin 90*65f0ecb9SAlexander Usyskin if (data->length > idev->dma.length.h2f - sizeof(*hdr)) { 91*65f0ecb9SAlexander Usyskin dev_err(&idev->dev, "Message is too big\n"); 92*65f0ecb9SAlexander Usyskin return -EMSGSIZE; 93*65f0ecb9SAlexander Usyskin } 94*65f0ecb9SAlexander Usyskin 95*65f0ecb9SAlexander Usyskin if (__issei_dma_is_write_busy(&idev->dma)) { 96*65f0ecb9SAlexander Usyskin if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) { 97*65f0ecb9SAlexander Usyskin dev_err(&idev->dev, "Write stuck in queue\n"); 98*65f0ecb9SAlexander Usyskin return -EIO; 99*65f0ecb9SAlexander Usyskin } 100*65f0ecb9SAlexander Usyskin dev_info(&idev->dev, "Write is busy\n"); 101*65f0ecb9SAlexander Usyskin return -EBUSY; 102*65f0ecb9SAlexander Usyskin } 103*65f0ecb9SAlexander Usyskin 104*65f0ecb9SAlexander Usyskin hdr->length = data->length; 105*65f0ecb9SAlexander Usyskin hdr->fw_id = data->fw_id; 106*65f0ecb9SAlexander Usyskin hdr->host_id = data->host_id; 107*65f0ecb9SAlexander Usyskin hdr->flags = data->flags; 108*65f0ecb9SAlexander Usyskin hdr->status = data->status; 109*65f0ecb9SAlexander Usyskin hdr->reserved = 0; 110*65f0ecb9SAlexander Usyskin 111*65f0ecb9SAlexander Usyskin memcpy(write_buf + sizeof(*hdr), data->buf, data->length); 112*65f0ecb9SAlexander Usyskin 113*65f0ecb9SAlexander Usyskin __issei_dma_write_finalize(idev); 114*65f0ecb9SAlexander Usyskin idev->last_write_ts = ktime_get(); 115*65f0ecb9SAlexander Usyskin return 0; 116*65f0ecb9SAlexander Usyskin } 117*65f0ecb9SAlexander Usyskin 118*65f0ecb9SAlexander Usyskin /** 119*65f0ecb9SAlexander Usyskin * issei_dma_read - read data package from DMA 120*65f0ecb9SAlexander Usyskin * @idev: issei device object 121*65f0ecb9SAlexander Usyskin * @data: data atructure 122*65f0ecb9SAlexander Usyskin * 123*65f0ecb9SAlexander Usyskin * Return: %0 on success, <0 on failures 124*65f0ecb9SAlexander Usyskin */ 125*65f0ecb9SAlexander Usyskin int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data) 126*65f0ecb9SAlexander Usyskin { 127*65f0ecb9SAlexander Usyskin u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f; 128*65f0ecb9SAlexander Usyskin struct ham_message_header *hdr = (struct ham_message_header *)read_buf; 129*65f0ecb9SAlexander Usyskin 130*65f0ecb9SAlexander Usyskin if (!__issei_dma_is_read_busy(&idev->dma)) { 131*65f0ecb9SAlexander Usyskin dev_dbg(&idev->dev, "Nothing to read\n"); 132*65f0ecb9SAlexander Usyskin return -ENODATA; 133*65f0ecb9SAlexander Usyskin } 134*65f0ecb9SAlexander Usyskin 135*65f0ecb9SAlexander Usyskin dev_dbg(&idev->dev, "Reading header\n"); 136*65f0ecb9SAlexander Usyskin data->length = hdr->length; 137*65f0ecb9SAlexander Usyskin data->fw_id = hdr->fw_id; 138*65f0ecb9SAlexander Usyskin data->host_id = hdr->host_id; 139*65f0ecb9SAlexander Usyskin data->flags = hdr->flags; 140*65f0ecb9SAlexander Usyskin data->status = hdr->status; 141*65f0ecb9SAlexander Usyskin 142*65f0ecb9SAlexander Usyskin if (data->length > idev->dma.length.f2h - sizeof(*hdr)) { 143*65f0ecb9SAlexander Usyskin dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n", 144*65f0ecb9SAlexander Usyskin data->length, idev->dma.length.f2h - sizeof(*hdr)); 145*65f0ecb9SAlexander Usyskin return -EIO; 146*65f0ecb9SAlexander Usyskin } 147*65f0ecb9SAlexander Usyskin 148*65f0ecb9SAlexander Usyskin dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length); 149*65f0ecb9SAlexander Usyskin data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL); 150*65f0ecb9SAlexander Usyskin if (!data->buf) 151*65f0ecb9SAlexander Usyskin return -ENOMEM; 152*65f0ecb9SAlexander Usyskin __issei_dma_read_finalize(idev); 153*65f0ecb9SAlexander Usyskin return 0; 154*65f0ecb9SAlexander Usyskin } 155