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