162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD Passthru DMA device driver 462306a36Sopenharmony_ci * -- Based on the CCP driver 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2016,2021 Advanced Micro Devices, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Sanjay R Mehta <sanju.mehta@amd.com> 962306a36Sopenharmony_ci * Author: Gary R Hook <gary.hook@amd.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/bitfield.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/debugfs.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "ptdma.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Human-readable error strings */ 2362306a36Sopenharmony_cistatic char *pt_error_codes[] = { 2462306a36Sopenharmony_ci "", 2562306a36Sopenharmony_ci "ERR 01: ILLEGAL_ENGINE", 2662306a36Sopenharmony_ci "ERR 03: ILLEGAL_FUNCTION_TYPE", 2762306a36Sopenharmony_ci "ERR 04: ILLEGAL_FUNCTION_MODE", 2862306a36Sopenharmony_ci "ERR 06: ILLEGAL_FUNCTION_SIZE", 2962306a36Sopenharmony_ci "ERR 08: ILLEGAL_FUNCTION_RSVD", 3062306a36Sopenharmony_ci "ERR 09: ILLEGAL_BUFFER_LENGTH", 3162306a36Sopenharmony_ci "ERR 10: VLSB_FAULT", 3262306a36Sopenharmony_ci "ERR 11: ILLEGAL_MEM_ADDR", 3362306a36Sopenharmony_ci "ERR 12: ILLEGAL_MEM_SEL", 3462306a36Sopenharmony_ci "ERR 13: ILLEGAL_CONTEXT_ID", 3562306a36Sopenharmony_ci "ERR 15: 0xF Reserved", 3662306a36Sopenharmony_ci "ERR 18: CMD_TIMEOUT", 3762306a36Sopenharmony_ci "ERR 19: IDMA0_AXI_SLVERR", 3862306a36Sopenharmony_ci "ERR 20: IDMA0_AXI_DECERR", 3962306a36Sopenharmony_ci "ERR 21: 0x15 Reserved", 4062306a36Sopenharmony_ci "ERR 22: IDMA1_AXI_SLAVE_FAULT", 4162306a36Sopenharmony_ci "ERR 23: IDMA1_AIXI_DECERR", 4262306a36Sopenharmony_ci "ERR 24: 0x18 Reserved", 4362306a36Sopenharmony_ci "ERR 27: 0x1B Reserved", 4462306a36Sopenharmony_ci "ERR 38: ODMA0_AXI_SLVERR", 4562306a36Sopenharmony_ci "ERR 39: ODMA0_AXI_DECERR", 4662306a36Sopenharmony_ci "ERR 40: 0x28 Reserved", 4762306a36Sopenharmony_ci "ERR 41: ODMA1_AXI_SLVERR", 4862306a36Sopenharmony_ci "ERR 42: ODMA1_AXI_DECERR", 4962306a36Sopenharmony_ci "ERR 43: LSB_PARITY_ERR", 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void pt_log_error(struct pt_device *d, int e) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci dev_err(d->dev, "PTDMA error: %s (0x%x)\n", pt_error_codes[e], e); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_civoid pt_start_queue(struct pt_cmd_queue *cmd_q) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci /* Turn on the run bit */ 6062306a36Sopenharmony_ci iowrite32(cmd_q->qcontrol | CMD_Q_RUN, cmd_q->reg_control); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_civoid pt_stop_queue(struct pt_cmd_queue *cmd_q) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci /* Turn off the run bit */ 6662306a36Sopenharmony_ci iowrite32(cmd_q->qcontrol & ~CMD_Q_RUN, cmd_q->reg_control); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int pt_core_execute_cmd(struct ptdma_desc *desc, struct pt_cmd_queue *cmd_q) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci bool soc = FIELD_GET(DWORD0_SOC, desc->dw0); 7262306a36Sopenharmony_ci u8 *q_desc = (u8 *)&cmd_q->qbase[cmd_q->qidx]; 7362306a36Sopenharmony_ci u32 tail; 7462306a36Sopenharmony_ci unsigned long flags; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (soc) { 7762306a36Sopenharmony_ci desc->dw0 |= FIELD_PREP(DWORD0_IOC, desc->dw0); 7862306a36Sopenharmony_ci desc->dw0 &= ~DWORD0_SOC; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci spin_lock_irqsave(&cmd_q->q_lock, flags); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Copy 32-byte command descriptor to hw queue. */ 8362306a36Sopenharmony_ci memcpy(q_desc, desc, 32); 8462306a36Sopenharmony_ci cmd_q->qidx = (cmd_q->qidx + 1) % CMD_Q_LEN; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* The data used by this command must be flushed to memory */ 8762306a36Sopenharmony_ci wmb(); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Write the new tail address back to the queue register */ 9062306a36Sopenharmony_ci tail = lower_32_bits(cmd_q->qdma_tail + cmd_q->qidx * Q_DESC_SIZE); 9162306a36Sopenharmony_ci iowrite32(tail, cmd_q->reg_control + 0x0004); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Turn the queue back on using our cached control register */ 9462306a36Sopenharmony_ci pt_start_queue(cmd_q); 9562306a36Sopenharmony_ci spin_unlock_irqrestore(&cmd_q->q_lock, flags); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint pt_core_perform_passthru(struct pt_cmd_queue *cmd_q, 10162306a36Sopenharmony_ci struct pt_passthru_engine *pt_engine) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct ptdma_desc desc; 10462306a36Sopenharmony_ci struct pt_device *pt = container_of(cmd_q, struct pt_device, cmd_q); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci cmd_q->cmd_error = 0; 10762306a36Sopenharmony_ci cmd_q->total_pt_ops++; 10862306a36Sopenharmony_ci memset(&desc, 0, sizeof(desc)); 10962306a36Sopenharmony_ci desc.dw0 = CMD_DESC_DW0_VAL; 11062306a36Sopenharmony_ci desc.length = pt_engine->src_len; 11162306a36Sopenharmony_ci desc.src_lo = lower_32_bits(pt_engine->src_dma); 11262306a36Sopenharmony_ci desc.dw3.src_hi = upper_32_bits(pt_engine->src_dma); 11362306a36Sopenharmony_ci desc.dst_lo = lower_32_bits(pt_engine->dst_dma); 11462306a36Sopenharmony_ci desc.dw5.dst_hi = upper_32_bits(pt_engine->dst_dma); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (cmd_q->int_en) 11762306a36Sopenharmony_ci pt_core_enable_queue_interrupts(pt); 11862306a36Sopenharmony_ci else 11962306a36Sopenharmony_ci pt_core_disable_queue_interrupts(pt); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return pt_core_execute_cmd(&desc, cmd_q); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void pt_do_cmd_complete(unsigned long data) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct pt_tasklet_data *tdata = (struct pt_tasklet_data *)data; 12762306a36Sopenharmony_ci struct pt_cmd *cmd = tdata->cmd; 12862306a36Sopenharmony_ci struct pt_cmd_queue *cmd_q = &cmd->pt->cmd_q; 12962306a36Sopenharmony_ci u32 tail; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (cmd_q->cmd_error) { 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Log the error and flush the queue by 13462306a36Sopenharmony_ci * moving the head pointer 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci tail = lower_32_bits(cmd_q->qdma_tail + cmd_q->qidx * Q_DESC_SIZE); 13762306a36Sopenharmony_ci pt_log_error(cmd_q->pt, cmd_q->cmd_error); 13862306a36Sopenharmony_ci iowrite32(tail, cmd_q->reg_control + 0x0008); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci cmd->pt_cmd_callback(cmd->data, cmd->ret); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid pt_check_status_trans(struct pt_device *pt, struct pt_cmd_queue *cmd_q) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 status; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci status = ioread32(cmd_q->reg_control + 0x0010); 14962306a36Sopenharmony_ci if (status) { 15062306a36Sopenharmony_ci cmd_q->int_status = status; 15162306a36Sopenharmony_ci cmd_q->q_status = ioread32(cmd_q->reg_control + 0x0100); 15262306a36Sopenharmony_ci cmd_q->q_int_status = ioread32(cmd_q->reg_control + 0x0104); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* On error, only save the first error value */ 15562306a36Sopenharmony_ci if ((status & INT_ERROR) && !cmd_q->cmd_error) 15662306a36Sopenharmony_ci cmd_q->cmd_error = CMD_Q_ERROR(cmd_q->q_status); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Acknowledge the completion */ 15962306a36Sopenharmony_ci iowrite32(status, cmd_q->reg_control + 0x0010); 16062306a36Sopenharmony_ci pt_do_cmd_complete((ulong)&pt->tdata); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic irqreturn_t pt_core_irq_handler(int irq, void *data) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct pt_device *pt = data; 16762306a36Sopenharmony_ci struct pt_cmd_queue *cmd_q = &pt->cmd_q; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci pt_core_disable_queue_interrupts(pt); 17062306a36Sopenharmony_ci pt->total_interrupts++; 17162306a36Sopenharmony_ci pt_check_status_trans(pt, cmd_q); 17262306a36Sopenharmony_ci pt_core_enable_queue_interrupts(pt); 17362306a36Sopenharmony_ci return IRQ_HANDLED; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciint pt_core_init(struct pt_device *pt) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci char dma_pool_name[MAX_DMAPOOL_NAME_LEN]; 17962306a36Sopenharmony_ci struct pt_cmd_queue *cmd_q = &pt->cmd_q; 18062306a36Sopenharmony_ci u32 dma_addr_lo, dma_addr_hi; 18162306a36Sopenharmony_ci struct device *dev = pt->dev; 18262306a36Sopenharmony_ci struct dma_pool *dma_pool; 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Allocate a dma pool for the queue */ 18662306a36Sopenharmony_ci snprintf(dma_pool_name, sizeof(dma_pool_name), "%s_q", dev_name(pt->dev)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci dma_pool = dma_pool_create(dma_pool_name, dev, 18962306a36Sopenharmony_ci PT_DMAPOOL_MAX_SIZE, 19062306a36Sopenharmony_ci PT_DMAPOOL_ALIGN, 0); 19162306a36Sopenharmony_ci if (!dma_pool) 19262306a36Sopenharmony_ci return -ENOMEM; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* ptdma core initialisation */ 19562306a36Sopenharmony_ci iowrite32(CMD_CONFIG_VHB_EN, pt->io_regs + CMD_CONFIG_OFFSET); 19662306a36Sopenharmony_ci iowrite32(CMD_QUEUE_PRIO, pt->io_regs + CMD_QUEUE_PRIO_OFFSET); 19762306a36Sopenharmony_ci iowrite32(CMD_TIMEOUT_DISABLE, pt->io_regs + CMD_TIMEOUT_OFFSET); 19862306a36Sopenharmony_ci iowrite32(CMD_CLK_GATE_CONFIG, pt->io_regs + CMD_CLK_GATE_CTL_OFFSET); 19962306a36Sopenharmony_ci iowrite32(CMD_CONFIG_REQID, pt->io_regs + CMD_REQID_CONFIG_OFFSET); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cmd_q->pt = pt; 20262306a36Sopenharmony_ci cmd_q->dma_pool = dma_pool; 20362306a36Sopenharmony_ci spin_lock_init(&cmd_q->q_lock); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Page alignment satisfies our needs for N <= 128 */ 20662306a36Sopenharmony_ci cmd_q->qsize = Q_SIZE(Q_DESC_SIZE); 20762306a36Sopenharmony_ci cmd_q->qbase = dma_alloc_coherent(dev, cmd_q->qsize, 20862306a36Sopenharmony_ci &cmd_q->qbase_dma, 20962306a36Sopenharmony_ci GFP_KERNEL); 21062306a36Sopenharmony_ci if (!cmd_q->qbase) { 21162306a36Sopenharmony_ci dev_err(dev, "unable to allocate command queue\n"); 21262306a36Sopenharmony_ci ret = -ENOMEM; 21362306a36Sopenharmony_ci goto e_destroy_pool; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci cmd_q->qidx = 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Preset some register values */ 21962306a36Sopenharmony_ci cmd_q->reg_control = pt->io_regs + CMD_Q_STATUS_INCR; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Turn off the queues and disable interrupts until ready */ 22262306a36Sopenharmony_ci pt_core_disable_queue_interrupts(pt); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci cmd_q->qcontrol = 0; /* Start with nothing */ 22562306a36Sopenharmony_ci iowrite32(cmd_q->qcontrol, cmd_q->reg_control); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ioread32(cmd_q->reg_control + 0x0104); 22862306a36Sopenharmony_ci ioread32(cmd_q->reg_control + 0x0100); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Clear the interrupt status */ 23162306a36Sopenharmony_ci iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Request an irq */ 23462306a36Sopenharmony_ci ret = request_irq(pt->pt_irq, pt_core_irq_handler, 0, dev_name(pt->dev), pt); 23562306a36Sopenharmony_ci if (ret) { 23662306a36Sopenharmony_ci dev_err(dev, "unable to allocate an IRQ\n"); 23762306a36Sopenharmony_ci goto e_free_dma; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Update the device registers with queue information. */ 24162306a36Sopenharmony_ci cmd_q->qcontrol &= ~CMD_Q_SIZE; 24262306a36Sopenharmony_ci cmd_q->qcontrol |= FIELD_PREP(CMD_Q_SIZE, QUEUE_SIZE_VAL); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci cmd_q->qdma_tail = cmd_q->qbase_dma; 24562306a36Sopenharmony_ci dma_addr_lo = lower_32_bits(cmd_q->qdma_tail); 24662306a36Sopenharmony_ci iowrite32((u32)dma_addr_lo, cmd_q->reg_control + 0x0004); 24762306a36Sopenharmony_ci iowrite32((u32)dma_addr_lo, cmd_q->reg_control + 0x0008); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dma_addr_hi = upper_32_bits(cmd_q->qdma_tail); 25062306a36Sopenharmony_ci cmd_q->qcontrol |= (dma_addr_hi << 16); 25162306a36Sopenharmony_ci iowrite32(cmd_q->qcontrol, cmd_q->reg_control); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci pt_core_enable_queue_interrupts(pt); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Register the DMA engine support */ 25662306a36Sopenharmony_ci ret = pt_dmaengine_register(pt); 25762306a36Sopenharmony_ci if (ret) 25862306a36Sopenharmony_ci goto e_free_irq; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Set up debugfs entries */ 26162306a36Sopenharmony_ci ptdma_debugfs_setup(pt); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cie_free_irq: 26662306a36Sopenharmony_ci free_irq(pt->pt_irq, pt); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cie_free_dma: 26962306a36Sopenharmony_ci dma_free_coherent(dev, cmd_q->qsize, cmd_q->qbase, cmd_q->qbase_dma); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cie_destroy_pool: 27262306a36Sopenharmony_ci dma_pool_destroy(pt->cmd_q.dma_pool); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_civoid pt_core_destroy(struct pt_device *pt) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct device *dev = pt->dev; 28062306a36Sopenharmony_ci struct pt_cmd_queue *cmd_q = &pt->cmd_q; 28162306a36Sopenharmony_ci struct pt_cmd *cmd; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Unregister the DMA engine */ 28462306a36Sopenharmony_ci pt_dmaengine_unregister(pt); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Disable and clear interrupts */ 28762306a36Sopenharmony_ci pt_core_disable_queue_interrupts(pt); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Turn off the run bit */ 29062306a36Sopenharmony_ci pt_stop_queue(cmd_q); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Clear the interrupt status */ 29362306a36Sopenharmony_ci iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010); 29462306a36Sopenharmony_ci ioread32(cmd_q->reg_control + 0x0104); 29562306a36Sopenharmony_ci ioread32(cmd_q->reg_control + 0x0100); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci free_irq(pt->pt_irq, pt); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci dma_free_coherent(dev, cmd_q->qsize, cmd_q->qbase, 30062306a36Sopenharmony_ci cmd_q->qbase_dma); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Flush the cmd queue */ 30362306a36Sopenharmony_ci while (!list_empty(&pt->cmd)) { 30462306a36Sopenharmony_ci /* Invoke the callback directly with an error code */ 30562306a36Sopenharmony_ci cmd = list_first_entry(&pt->cmd, struct pt_cmd, entry); 30662306a36Sopenharmony_ci list_del(&cmd->entry); 30762306a36Sopenharmony_ci cmd->pt_cmd_callback(cmd->data, -ENODEV); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci} 310