|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
| 2 | +/* |
| 3 | + * AMD Passthru DMA device driver |
| 4 | + * -- Based on the CCP driver |
| 5 | + * |
| 6 | + * Copyright (C) 2016,2021 Advanced Micro Devices, Inc. |
| 7 | + * |
| 8 | + * Author: Sanjay R Mehta <[email protected]> |
| 9 | + * Author: Gary R Hook <[email protected]> |
| 10 | + */ |
| 11 | + |
| 12 | +#include <linux/bitfield.h> |
| 13 | +#include <linux/dma-mapping.h> |
| 14 | +#include <linux/debugfs.h> |
| 15 | +#include <linux/interrupt.h> |
| 16 | +#include <linux/kernel.h> |
| 17 | +#include <linux/module.h> |
| 18 | +#include <linux/pci.h> |
| 19 | + |
| 20 | +#include "ptdma.h" |
| 21 | + |
| 22 | +/* Human-readable error strings */ |
| 23 | +static char *pt_error_codes[] = { |
| 24 | + "", |
| 25 | + "ERR 01: ILLEGAL_ENGINE", |
| 26 | + "ERR 03: ILLEGAL_FUNCTION_TYPE", |
| 27 | + "ERR 04: ILLEGAL_FUNCTION_MODE", |
| 28 | + "ERR 06: ILLEGAL_FUNCTION_SIZE", |
| 29 | + "ERR 08: ILLEGAL_FUNCTION_RSVD", |
| 30 | + "ERR 09: ILLEGAL_BUFFER_LENGTH", |
| 31 | + "ERR 10: VLSB_FAULT", |
| 32 | + "ERR 11: ILLEGAL_MEM_ADDR", |
| 33 | + "ERR 12: ILLEGAL_MEM_SEL", |
| 34 | + "ERR 13: ILLEGAL_CONTEXT_ID", |
| 35 | + "ERR 15: 0xF Reserved", |
| 36 | + "ERR 18: CMD_TIMEOUT", |
| 37 | + "ERR 19: IDMA0_AXI_SLVERR", |
| 38 | + "ERR 20: IDMA0_AXI_DECERR", |
| 39 | + "ERR 21: 0x15 Reserved", |
| 40 | + "ERR 22: IDMA1_AXI_SLAVE_FAULT", |
| 41 | + "ERR 23: IDMA1_AIXI_DECERR", |
| 42 | + "ERR 24: 0x18 Reserved", |
| 43 | + "ERR 27: 0x1B Reserved", |
| 44 | + "ERR 38: ODMA0_AXI_SLVERR", |
| 45 | + "ERR 39: ODMA0_AXI_DECERR", |
| 46 | + "ERR 40: 0x28 Reserved", |
| 47 | + "ERR 41: ODMA1_AXI_SLVERR", |
| 48 | + "ERR 42: ODMA1_AXI_DECERR", |
| 49 | + "ERR 43: LSB_PARITY_ERR", |
| 50 | +}; |
| 51 | + |
| 52 | +static void pt_log_error(struct pt_device *d, int e) |
| 53 | +{ |
| 54 | + dev_err(d->dev, "PTDMA error: %s (0x%x)\n", pt_error_codes[e], e); |
| 55 | +} |
| 56 | + |
| 57 | +void pt_start_queue(struct pt_cmd_queue *cmd_q) |
| 58 | +{ |
| 59 | + /* Turn on the run bit */ |
| 60 | + iowrite32(cmd_q->qcontrol | CMD_Q_RUN, cmd_q->reg_control); |
| 61 | +} |
| 62 | + |
| 63 | +void pt_stop_queue(struct pt_cmd_queue *cmd_q) |
| 64 | +{ |
| 65 | + /* Turn off the run bit */ |
| 66 | + iowrite32(cmd_q->qcontrol & ~CMD_Q_RUN, cmd_q->reg_control); |
| 67 | +} |
| 68 | + |
| 69 | +static int pt_core_execute_cmd(struct ptdma_desc *desc, struct pt_cmd_queue *cmd_q) |
| 70 | +{ |
| 71 | + bool soc = FIELD_GET(DWORD0_SOC, desc->dw0); |
| 72 | + u8 *q_desc = (u8 *)&cmd_q->qbase[cmd_q->qidx]; |
| 73 | + u32 tail; |
| 74 | + |
| 75 | + if (soc) { |
| 76 | + desc->dw0 |= FIELD_PREP(DWORD0_IOC, desc->dw0); |
| 77 | + desc->dw0 &= ~DWORD0_SOC; |
| 78 | + } |
| 79 | + mutex_lock(&cmd_q->q_mutex); |
| 80 | + |
| 81 | + /* Copy 32-byte command descriptor to hw queue. */ |
| 82 | + memcpy(q_desc, desc, 32); |
| 83 | + cmd_q->qidx = (cmd_q->qidx + 1) % CMD_Q_LEN; |
| 84 | + |
| 85 | + /* The data used by this command must be flushed to memory */ |
| 86 | + wmb(); |
| 87 | + |
| 88 | + /* Write the new tail address back to the queue register */ |
| 89 | + tail = lower_32_bits(cmd_q->qdma_tail + cmd_q->qidx * Q_DESC_SIZE); |
| 90 | + iowrite32(tail, cmd_q->reg_control + 0x0004); |
| 91 | + |
| 92 | + /* Turn the queue back on using our cached control register */ |
| 93 | + pt_start_queue(cmd_q); |
| 94 | + mutex_unlock(&cmd_q->q_mutex); |
| 95 | + |
| 96 | + return 0; |
| 97 | +} |
| 98 | + |
| 99 | +int pt_core_perform_passthru(struct pt_cmd_queue *cmd_q, |
| 100 | + struct pt_passthru_engine *pt_engine) |
| 101 | +{ |
| 102 | + struct ptdma_desc desc; |
| 103 | + |
| 104 | + cmd_q->cmd_error = 0; |
| 105 | + memset(&desc, 0, sizeof(desc)); |
| 106 | + desc.dw0 = CMD_DESC_DW0_VAL; |
| 107 | + desc.length = pt_engine->src_len; |
| 108 | + desc.src_lo = lower_32_bits(pt_engine->src_dma); |
| 109 | + desc.dw3.src_hi = upper_32_bits(pt_engine->src_dma); |
| 110 | + desc.dst_lo = lower_32_bits(pt_engine->dst_dma); |
| 111 | + desc.dw5.dst_hi = upper_32_bits(pt_engine->dst_dma); |
| 112 | + |
| 113 | + return pt_core_execute_cmd(&desc, cmd_q); |
| 114 | +} |
| 115 | + |
| 116 | +static inline void pt_core_disable_queue_interrupts(struct pt_device *pt) |
| 117 | +{ |
| 118 | + iowrite32(0, pt->cmd_q.reg_control + 0x000C); |
| 119 | +} |
| 120 | + |
| 121 | +static inline void pt_core_enable_queue_interrupts(struct pt_device *pt) |
| 122 | +{ |
| 123 | + iowrite32(SUPPORTED_INTERRUPTS, pt->cmd_q.reg_control + 0x000C); |
| 124 | +} |
| 125 | + |
| 126 | +static irqreturn_t pt_core_irq_handler(int irq, void *data) |
| 127 | +{ |
| 128 | + struct pt_device *pt = data; |
| 129 | + struct pt_cmd_queue *cmd_q = &pt->cmd_q; |
| 130 | + u32 status; |
| 131 | + |
| 132 | + pt_core_disable_queue_interrupts(pt); |
| 133 | + status = ioread32(cmd_q->reg_control + 0x0010); |
| 134 | + if (status) { |
| 135 | + cmd_q->int_status = status; |
| 136 | + cmd_q->q_status = ioread32(cmd_q->reg_control + 0x0100); |
| 137 | + cmd_q->q_int_status = ioread32(cmd_q->reg_control + 0x0104); |
| 138 | + |
| 139 | + /* On error, only save the first error value */ |
| 140 | + if ((status & INT_ERROR) && !cmd_q->cmd_error) |
| 141 | + cmd_q->cmd_error = CMD_Q_ERROR(cmd_q->q_status); |
| 142 | + |
| 143 | + /* Acknowledge the interrupt */ |
| 144 | + iowrite32(status, cmd_q->reg_control + 0x0010); |
| 145 | + pt_core_enable_queue_interrupts(pt); |
| 146 | + } |
| 147 | + return IRQ_HANDLED; |
| 148 | +} |
| 149 | + |
| 150 | +int pt_core_init(struct pt_device *pt) |
| 151 | +{ |
| 152 | + char dma_pool_name[MAX_DMAPOOL_NAME_LEN]; |
| 153 | + struct pt_cmd_queue *cmd_q = &pt->cmd_q; |
| 154 | + u32 dma_addr_lo, dma_addr_hi; |
| 155 | + struct device *dev = pt->dev; |
| 156 | + struct dma_pool *dma_pool; |
| 157 | + int ret; |
| 158 | + |
| 159 | + /* Allocate a dma pool for the queue */ |
| 160 | + snprintf(dma_pool_name, sizeof(dma_pool_name), "%s_q", dev_name(pt->dev)); |
| 161 | + |
| 162 | + dma_pool = dma_pool_create(dma_pool_name, dev, |
| 163 | + PT_DMAPOOL_MAX_SIZE, |
| 164 | + PT_DMAPOOL_ALIGN, 0); |
| 165 | + if (!dma_pool) |
| 166 | + return -ENOMEM; |
| 167 | + |
| 168 | + /* ptdma core initialisation */ |
| 169 | + iowrite32(CMD_CONFIG_VHB_EN, pt->io_regs + CMD_CONFIG_OFFSET); |
| 170 | + iowrite32(CMD_QUEUE_PRIO, pt->io_regs + CMD_QUEUE_PRIO_OFFSET); |
| 171 | + iowrite32(CMD_TIMEOUT_DISABLE, pt->io_regs + CMD_TIMEOUT_OFFSET); |
| 172 | + iowrite32(CMD_CLK_GATE_CONFIG, pt->io_regs + CMD_CLK_GATE_CTL_OFFSET); |
| 173 | + iowrite32(CMD_CONFIG_REQID, pt->io_regs + CMD_REQID_CONFIG_OFFSET); |
| 174 | + |
| 175 | + cmd_q->pt = pt; |
| 176 | + cmd_q->dma_pool = dma_pool; |
| 177 | + mutex_init(&cmd_q->q_mutex); |
| 178 | + |
| 179 | + /* Page alignment satisfies our needs for N <= 128 */ |
| 180 | + cmd_q->qsize = Q_SIZE(Q_DESC_SIZE); |
| 181 | + cmd_q->qbase = dma_alloc_coherent(dev, cmd_q->qsize, |
| 182 | + &cmd_q->qbase_dma, |
| 183 | + GFP_KERNEL); |
| 184 | + if (!cmd_q->qbase) { |
| 185 | + dev_err(dev, "unable to allocate command queue\n"); |
| 186 | + ret = -ENOMEM; |
| 187 | + goto e_dma_alloc; |
| 188 | + } |
| 189 | + |
| 190 | + cmd_q->qidx = 0; |
| 191 | + |
| 192 | + /* Preset some register values */ |
| 193 | + cmd_q->reg_control = pt->io_regs + CMD_Q_STATUS_INCR; |
| 194 | + |
| 195 | + /* Turn off the queues and disable interrupts until ready */ |
| 196 | + pt_core_disable_queue_interrupts(pt); |
| 197 | + |
| 198 | + cmd_q->qcontrol = 0; /* Start with nothing */ |
| 199 | + iowrite32(cmd_q->qcontrol, cmd_q->reg_control); |
| 200 | + |
| 201 | + ioread32(cmd_q->reg_control + 0x0104); |
| 202 | + ioread32(cmd_q->reg_control + 0x0100); |
| 203 | + |
| 204 | + /* Clear the interrupt status */ |
| 205 | + iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010); |
| 206 | + |
| 207 | + /* Request an irq */ |
| 208 | + ret = request_irq(pt->pt_irq, pt_core_irq_handler, 0, dev_name(pt->dev), pt); |
| 209 | + if (ret) |
| 210 | + goto e_pool; |
| 211 | + |
| 212 | + /* Update the device registers with queue information. */ |
| 213 | + cmd_q->qcontrol &= ~CMD_Q_SIZE; |
| 214 | + cmd_q->qcontrol |= FIELD_PREP(CMD_Q_SIZE, QUEUE_SIZE_VAL); |
| 215 | + |
| 216 | + cmd_q->qdma_tail = cmd_q->qbase_dma; |
| 217 | + dma_addr_lo = lower_32_bits(cmd_q->qdma_tail); |
| 218 | + iowrite32((u32)dma_addr_lo, cmd_q->reg_control + 0x0004); |
| 219 | + iowrite32((u32)dma_addr_lo, cmd_q->reg_control + 0x0008); |
| 220 | + |
| 221 | + dma_addr_hi = upper_32_bits(cmd_q->qdma_tail); |
| 222 | + cmd_q->qcontrol |= (dma_addr_hi << 16); |
| 223 | + iowrite32(cmd_q->qcontrol, cmd_q->reg_control); |
| 224 | + |
| 225 | + pt_core_enable_queue_interrupts(pt); |
| 226 | + |
| 227 | + return 0; |
| 228 | + |
| 229 | +e_dma_alloc: |
| 230 | + dma_free_coherent(dev, cmd_q->qsize, cmd_q->qbase, cmd_q->qbase_dma); |
| 231 | + |
| 232 | +e_pool: |
| 233 | + dev_err(dev, "unable to allocate an IRQ\n"); |
| 234 | + dma_pool_destroy(pt->cmd_q.dma_pool); |
| 235 | + |
| 236 | + return ret; |
| 237 | +} |
| 238 | + |
| 239 | +void pt_core_destroy(struct pt_device *pt) |
| 240 | +{ |
| 241 | + struct device *dev = pt->dev; |
| 242 | + struct pt_cmd_queue *cmd_q = &pt->cmd_q; |
| 243 | + struct pt_cmd *cmd; |
| 244 | + |
| 245 | + /* Disable and clear interrupts */ |
| 246 | + pt_core_disable_queue_interrupts(pt); |
| 247 | + |
| 248 | + /* Turn off the run bit */ |
| 249 | + pt_stop_queue(cmd_q); |
| 250 | + |
| 251 | + /* Clear the interrupt status */ |
| 252 | + iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010); |
| 253 | + ioread32(cmd_q->reg_control + 0x0104); |
| 254 | + ioread32(cmd_q->reg_control + 0x0100); |
| 255 | + |
| 256 | + free_irq(pt->pt_irq, pt); |
| 257 | + |
| 258 | + dma_free_coherent(dev, cmd_q->qsize, cmd_q->qbase, |
| 259 | + cmd_q->qbase_dma); |
| 260 | + |
| 261 | + /* Flush the cmd queue */ |
| 262 | + while (!list_empty(&pt->cmd)) { |
| 263 | + /* Invoke the callback directly with an error code */ |
| 264 | + cmd = list_first_entry(&pt->cmd, struct pt_cmd, entry); |
| 265 | + list_del(&cmd->entry); |
| 266 | + cmd->pt_cmd_callback(cmd->data, -ENODEV); |
| 267 | + } |
| 268 | +} |
0 commit comments