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