Path: blob/master/drivers/dma/amd/ptdma/ptdma-dmaengine.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* AMD Passthrough DMA device driver3* -- Based on the CCP driver4*5* Copyright (C) 2016,2021 Advanced Micro Devices, Inc.6*7* Author: Sanjay R Mehta <[email protected]>8* Author: Gary R Hook <[email protected]>9*/1011#include <linux/bitfield.h>12#include "ptdma.h"13#include "../ae4dma/ae4dma.h"14#include "../../dmaengine.h"1516static char *ae4_error_codes[] = {17"",18"ERR 01: INVALID HEADER DW0",19"ERR 02: INVALID STATUS",20"ERR 03: INVALID LENGTH - 4 BYTE ALIGNMENT",21"ERR 04: INVALID SRC ADDR - 4 BYTE ALIGNMENT",22"ERR 05: INVALID DST ADDR - 4 BYTE ALIGNMENT",23"ERR 06: INVALID ALIGNMENT",24"ERR 07: INVALID DESCRIPTOR",25};2627static void ae4_log_error(struct pt_device *d, int e)28{29/* ERR 01 - 07 represents Invalid AE4 errors */30if (e <= 7)31dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", ae4_error_codes[e], e);32/* ERR 08 - 15 represents Invalid Descriptor errors */33else if (e > 7 && e <= 15)34dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "INVALID DESCRIPTOR", e);35/* ERR 16 - 31 represents Firmware errors */36else if (e > 15 && e <= 31)37dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "FIRMWARE ERROR", e);38/* ERR 32 - 63 represents Fatal errors */39else if (e > 31 && e <= 63)40dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "FATAL ERROR", e);41/* ERR 64 - 255 represents PTE errors */42else if (e > 63 && e <= 255)43dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "PTE ERROR", e);44else45dev_info(d->dev, "Unknown AE4DMA error");46}4748void ae4_check_status_error(struct ae4_cmd_queue *ae4cmd_q, int idx)49{50struct pt_cmd_queue *cmd_q = &ae4cmd_q->cmd_q;51struct ae4dma_desc desc;52u8 status;5354memcpy(&desc, &cmd_q->qbase[idx], sizeof(struct ae4dma_desc));55status = desc.dw1.status;56if (status && status != AE4_DESC_COMPLETED) {57cmd_q->cmd_error = desc.dw1.err_code;58if (cmd_q->cmd_error)59ae4_log_error(cmd_q->pt, cmd_q->cmd_error);60}61}62EXPORT_SYMBOL_GPL(ae4_check_status_error);6364static inline struct pt_dma_chan *to_pt_chan(struct dma_chan *dma_chan)65{66return container_of(dma_chan, struct pt_dma_chan, vc.chan);67}6869static inline struct pt_dma_desc *to_pt_desc(struct virt_dma_desc *vd)70{71return container_of(vd, struct pt_dma_desc, vd);72}7374static void pt_free_chan_resources(struct dma_chan *dma_chan)75{76struct pt_dma_chan *chan = to_pt_chan(dma_chan);7778vchan_free_chan_resources(&chan->vc);79}8081static void pt_synchronize(struct dma_chan *dma_chan)82{83struct pt_dma_chan *chan = to_pt_chan(dma_chan);8485vchan_synchronize(&chan->vc);86}8788static void pt_do_cleanup(struct virt_dma_desc *vd)89{90struct pt_dma_desc *desc = to_pt_desc(vd);91struct pt_device *pt = desc->pt;9293kmem_cache_free(pt->dma_desc_cache, desc);94}9596static struct pt_cmd_queue *pt_get_cmd_queue(struct pt_device *pt, struct pt_dma_chan *chan)97{98struct ae4_cmd_queue *ae4cmd_q;99struct pt_cmd_queue *cmd_q;100struct ae4_device *ae4;101102if (pt->ver == AE4_DMA_VERSION) {103ae4 = container_of(pt, struct ae4_device, pt);104ae4cmd_q = &ae4->ae4cmd_q[chan->id];105cmd_q = &ae4cmd_q->cmd_q;106} else {107cmd_q = &pt->cmd_q;108}109110return cmd_q;111}112113static int ae4_core_execute_cmd(struct ae4dma_desc *desc, struct ae4_cmd_queue *ae4cmd_q)114{115bool soc = FIELD_GET(DWORD0_SOC, desc->dwouv.dw0);116struct pt_cmd_queue *cmd_q = &ae4cmd_q->cmd_q;117118if (soc) {119desc->dwouv.dw0 |= FIELD_PREP(DWORD0_IOC, desc->dwouv.dw0);120desc->dwouv.dw0 &= ~DWORD0_SOC;121}122123mutex_lock(&ae4cmd_q->cmd_lock);124memcpy(&cmd_q->qbase[ae4cmd_q->tail_wi], desc, sizeof(struct ae4dma_desc));125ae4cmd_q->q_cmd_count++;126ae4cmd_q->tail_wi = (ae4cmd_q->tail_wi + 1) % CMD_Q_LEN;127writel(ae4cmd_q->tail_wi, cmd_q->reg_control + AE4_WR_IDX_OFF);128mutex_unlock(&ae4cmd_q->cmd_lock);129130wake_up(&ae4cmd_q->q_w);131132return 0;133}134135static int pt_core_perform_passthru_ae4(struct pt_cmd_queue *cmd_q,136struct pt_passthru_engine *pt_engine)137{138struct ae4_cmd_queue *ae4cmd_q = container_of(cmd_q, struct ae4_cmd_queue, cmd_q);139struct ae4dma_desc desc;140141cmd_q->cmd_error = 0;142cmd_q->total_pt_ops++;143memset(&desc, 0, sizeof(desc));144desc.dwouv.dws.byte0 = CMD_AE4_DESC_DW0_VAL;145146desc.dw1.status = 0;147desc.dw1.err_code = 0;148desc.dw1.desc_id = 0;149150desc.length = pt_engine->src_len;151152desc.src_lo = upper_32_bits(pt_engine->src_dma);153desc.src_hi = lower_32_bits(pt_engine->src_dma);154desc.dst_lo = upper_32_bits(pt_engine->dst_dma);155desc.dst_hi = lower_32_bits(pt_engine->dst_dma);156157return ae4_core_execute_cmd(&desc, ae4cmd_q);158}159160static int pt_dma_start_desc(struct pt_dma_desc *desc, struct pt_dma_chan *chan)161{162struct pt_passthru_engine *pt_engine;163struct pt_device *pt;164struct pt_cmd *pt_cmd;165struct pt_cmd_queue *cmd_q;166167desc->issued_to_hw = 1;168169pt_cmd = &desc->pt_cmd;170pt = pt_cmd->pt;171172cmd_q = pt_get_cmd_queue(pt, chan);173174pt_engine = &pt_cmd->passthru;175176pt->tdata.cmd = pt_cmd;177178/* Execute the command */179if (pt->ver == AE4_DMA_VERSION)180pt_cmd->ret = pt_core_perform_passthru_ae4(cmd_q, pt_engine);181else182pt_cmd->ret = pt_core_perform_passthru(cmd_q, pt_engine);183184return 0;185}186187static struct pt_dma_desc *pt_next_dma_desc(struct pt_dma_chan *chan)188{189/* Get the next DMA descriptor on the active list */190struct virt_dma_desc *vd = vchan_next_desc(&chan->vc);191192return vd ? to_pt_desc(vd) : NULL;193}194195static struct pt_dma_desc *pt_handle_active_desc(struct pt_dma_chan *chan,196struct pt_dma_desc *desc)197{198struct dma_async_tx_descriptor *tx_desc;199struct virt_dma_desc *vd;200struct pt_device *pt;201unsigned long flags;202203pt = chan->pt;204/* Loop over descriptors until one is found with commands */205do {206if (desc) {207if (!desc->issued_to_hw) {208/* No errors, keep going */209if (desc->status != DMA_ERROR)210return desc;211}212213tx_desc = &desc->vd.tx;214vd = &desc->vd;215} else {216tx_desc = NULL;217}218219spin_lock_irqsave(&chan->vc.lock, flags);220221if (pt->ver != AE4_DMA_VERSION && desc) {222if (desc->status != DMA_COMPLETE) {223if (desc->status != DMA_ERROR)224desc->status = DMA_COMPLETE;225226dma_cookie_complete(tx_desc);227dma_descriptor_unmap(tx_desc);228list_del(&desc->vd.node);229} else {230/* Don't handle it twice */231tx_desc = NULL;232}233}234235desc = pt_next_dma_desc(chan);236237spin_unlock_irqrestore(&chan->vc.lock, flags);238239if (pt->ver != AE4_DMA_VERSION && tx_desc) {240dmaengine_desc_get_callback_invoke(tx_desc, NULL);241dma_run_dependencies(tx_desc);242vchan_vdesc_fini(vd);243}244} while (desc);245246return NULL;247}248249static inline bool ae4_core_queue_full(struct pt_cmd_queue *cmd_q)250{251u32 front_wi = readl(cmd_q->reg_control + AE4_WR_IDX_OFF);252u32 rear_ri = readl(cmd_q->reg_control + AE4_RD_IDX_OFF);253254if (((MAX_CMD_QLEN + front_wi - rear_ri) % MAX_CMD_QLEN) >= (MAX_CMD_QLEN - 1))255return true;256257return false;258}259260static void pt_cmd_callback(void *data, int err)261{262struct pt_dma_desc *desc = data;263struct ae4_cmd_queue *ae4cmd_q;264struct dma_chan *dma_chan;265struct pt_dma_chan *chan;266struct ae4_device *ae4;267struct pt_device *pt;268int ret;269270if (err == -EINPROGRESS)271return;272273dma_chan = desc->vd.tx.chan;274chan = to_pt_chan(dma_chan);275pt = chan->pt;276277if (err)278desc->status = DMA_ERROR;279280while (true) {281if (pt->ver == AE4_DMA_VERSION) {282ae4 = container_of(pt, struct ae4_device, pt);283ae4cmd_q = &ae4->ae4cmd_q[chan->id];284285if (ae4cmd_q->q_cmd_count >= (CMD_Q_LEN - 1) ||286ae4_core_queue_full(&ae4cmd_q->cmd_q)) {287wake_up(&ae4cmd_q->q_w);288289if (wait_for_completion_timeout(&ae4cmd_q->cmp,290msecs_to_jiffies(AE4_TIME_OUT))291== 0) {292dev_err(pt->dev, "TIMEOUT %d:\n", ae4cmd_q->id);293break;294}295296reinit_completion(&ae4cmd_q->cmp);297continue;298}299}300301/* Check for DMA descriptor completion */302desc = pt_handle_active_desc(chan, desc);303304/* Don't submit cmd if no descriptor or DMA is paused */305if (!desc)306break;307308ret = pt_dma_start_desc(desc, chan);309if (!ret)310break;311312desc->status = DMA_ERROR;313}314}315316static struct pt_dma_desc *pt_alloc_dma_desc(struct pt_dma_chan *chan,317unsigned long flags)318{319struct pt_dma_desc *desc;320321desc = kmem_cache_zalloc(chan->pt->dma_desc_cache, GFP_NOWAIT);322if (!desc)323return NULL;324325vchan_tx_prep(&chan->vc, &desc->vd, flags);326327desc->pt = chan->pt;328desc->pt->cmd_q.int_en = !!(flags & DMA_PREP_INTERRUPT);329desc->issued_to_hw = 0;330desc->status = DMA_IN_PROGRESS;331332return desc;333}334335static void pt_cmd_callback_work(void *data, int err)336{337struct dma_async_tx_descriptor *tx_desc;338struct pt_dma_desc *desc = data;339struct dma_chan *dma_chan;340struct virt_dma_desc *vd;341struct pt_dma_chan *chan;342unsigned long flags;343344if (!desc)345return;346347dma_chan = desc->vd.tx.chan;348chan = to_pt_chan(dma_chan);349350if (err == -EINPROGRESS)351return;352353tx_desc = &desc->vd.tx;354vd = &desc->vd;355356if (err)357desc->status = DMA_ERROR;358359spin_lock_irqsave(&chan->vc.lock, flags);360if (desc->status != DMA_COMPLETE) {361if (desc->status != DMA_ERROR)362desc->status = DMA_COMPLETE;363364dma_cookie_complete(tx_desc);365dma_descriptor_unmap(tx_desc);366} else {367tx_desc = NULL;368}369spin_unlock_irqrestore(&chan->vc.lock, flags);370371if (tx_desc) {372dmaengine_desc_get_callback_invoke(tx_desc, NULL);373dma_run_dependencies(tx_desc);374list_del(&desc->vd.node);375vchan_vdesc_fini(vd);376}377}378379static struct pt_dma_desc *pt_create_desc(struct dma_chan *dma_chan,380dma_addr_t dst,381dma_addr_t src,382unsigned int len,383unsigned long flags)384{385struct pt_dma_chan *chan = to_pt_chan(dma_chan);386struct pt_passthru_engine *pt_engine;387struct pt_device *pt = chan->pt;388struct ae4_cmd_queue *ae4cmd_q;389struct pt_dma_desc *desc;390struct ae4_device *ae4;391struct pt_cmd *pt_cmd;392393desc = pt_alloc_dma_desc(chan, flags);394if (!desc)395return NULL;396397pt_cmd = &desc->pt_cmd;398pt_cmd->pt = pt;399pt_engine = &pt_cmd->passthru;400pt_cmd->engine = PT_ENGINE_PASSTHRU;401pt_engine->src_dma = src;402pt_engine->dst_dma = dst;403pt_engine->src_len = len;404pt_cmd->pt_cmd_callback = pt_cmd_callback;405pt_cmd->data = desc;406407desc->len = len;408409if (pt->ver == AE4_DMA_VERSION) {410pt_cmd->pt_cmd_callback = pt_cmd_callback_work;411ae4 = container_of(pt, struct ae4_device, pt);412ae4cmd_q = &ae4->ae4cmd_q[chan->id];413mutex_lock(&ae4cmd_q->cmd_lock);414list_add_tail(&pt_cmd->entry, &ae4cmd_q->cmd);415mutex_unlock(&ae4cmd_q->cmd_lock);416}417418return desc;419}420421static struct dma_async_tx_descriptor *422pt_prep_dma_memcpy(struct dma_chan *dma_chan, dma_addr_t dst,423dma_addr_t src, size_t len, unsigned long flags)424{425struct pt_dma_desc *desc;426427desc = pt_create_desc(dma_chan, dst, src, len, flags);428if (!desc)429return NULL;430431return &desc->vd.tx;432}433434static struct dma_async_tx_descriptor *435pt_prep_dma_interrupt(struct dma_chan *dma_chan, unsigned long flags)436{437struct pt_dma_chan *chan = to_pt_chan(dma_chan);438struct pt_dma_desc *desc;439440desc = pt_alloc_dma_desc(chan, flags);441if (!desc)442return NULL;443444return &desc->vd.tx;445}446447static void pt_issue_pending(struct dma_chan *dma_chan)448{449struct pt_dma_chan *chan = to_pt_chan(dma_chan);450struct pt_dma_desc *desc;451struct pt_device *pt;452unsigned long flags;453bool engine_is_idle = true;454455pt = chan->pt;456457spin_lock_irqsave(&chan->vc.lock, flags);458459desc = pt_next_dma_desc(chan);460if (desc && pt->ver != AE4_DMA_VERSION)461engine_is_idle = false;462463vchan_issue_pending(&chan->vc);464465desc = pt_next_dma_desc(chan);466467spin_unlock_irqrestore(&chan->vc.lock, flags);468469/* If there was nothing active, start processing */470if (engine_is_idle && desc)471pt_cmd_callback(desc, 0);472}473474static void pt_check_status_trans_ae4(struct pt_device *pt, struct pt_cmd_queue *cmd_q)475{476struct ae4_cmd_queue *ae4cmd_q = container_of(cmd_q, struct ae4_cmd_queue, cmd_q);477int i;478479for (i = 0; i < CMD_Q_LEN; i++)480ae4_check_status_error(ae4cmd_q, i);481}482483static enum dma_status484pt_tx_status(struct dma_chan *c, dma_cookie_t cookie,485struct dma_tx_state *txstate)486{487struct pt_dma_chan *chan = to_pt_chan(c);488struct pt_device *pt = chan->pt;489struct pt_cmd_queue *cmd_q;490491cmd_q = pt_get_cmd_queue(pt, chan);492493if (pt->ver == AE4_DMA_VERSION)494pt_check_status_trans_ae4(pt, cmd_q);495else496pt_check_status_trans(pt, cmd_q);497498return dma_cookie_status(c, cookie, txstate);499}500501static int pt_pause(struct dma_chan *dma_chan)502{503struct pt_dma_chan *chan = to_pt_chan(dma_chan);504struct pt_device *pt = chan->pt;505struct pt_cmd_queue *cmd_q;506unsigned long flags;507508spin_lock_irqsave(&chan->vc.lock, flags);509cmd_q = pt_get_cmd_queue(pt, chan);510pt_stop_queue(cmd_q);511spin_unlock_irqrestore(&chan->vc.lock, flags);512513return 0;514}515516static int pt_resume(struct dma_chan *dma_chan)517{518struct pt_dma_chan *chan = to_pt_chan(dma_chan);519struct pt_dma_desc *desc = NULL;520struct pt_device *pt = chan->pt;521struct pt_cmd_queue *cmd_q;522unsigned long flags;523524spin_lock_irqsave(&chan->vc.lock, flags);525cmd_q = pt_get_cmd_queue(pt, chan);526pt_start_queue(cmd_q);527desc = pt_next_dma_desc(chan);528spin_unlock_irqrestore(&chan->vc.lock, flags);529530/* If there was something active, re-start */531if (desc)532pt_cmd_callback(desc, 0);533534return 0;535}536537static int pt_terminate_all(struct dma_chan *dma_chan)538{539struct pt_dma_chan *chan = to_pt_chan(dma_chan);540struct pt_device *pt = chan->pt;541struct pt_cmd_queue *cmd_q;542unsigned long flags;543LIST_HEAD(head);544545cmd_q = pt_get_cmd_queue(pt, chan);546if (pt->ver == AE4_DMA_VERSION)547pt_stop_queue(cmd_q);548else549iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010);550551spin_lock_irqsave(&chan->vc.lock, flags);552vchan_get_all_descriptors(&chan->vc, &head);553spin_unlock_irqrestore(&chan->vc.lock, flags);554555vchan_dma_desc_free_list(&chan->vc, &head);556vchan_free_chan_resources(&chan->vc);557558return 0;559}560561int pt_dmaengine_register(struct pt_device *pt)562{563struct dma_device *dma_dev = &pt->dma_dev;564struct ae4_cmd_queue *ae4cmd_q = NULL;565struct ae4_device *ae4 = NULL;566struct pt_dma_chan *chan;567char *desc_cache_name;568int ret, i;569570if (pt->ver == AE4_DMA_VERSION)571ae4 = container_of(pt, struct ae4_device, pt);572573if (ae4)574pt->pt_dma_chan = devm_kcalloc(pt->dev, ae4->cmd_q_count,575sizeof(*pt->pt_dma_chan), GFP_KERNEL);576else577pt->pt_dma_chan = devm_kzalloc(pt->dev, sizeof(*pt->pt_dma_chan),578GFP_KERNEL);579580if (!pt->pt_dma_chan)581return -ENOMEM;582583desc_cache_name = devm_kasprintf(pt->dev, GFP_KERNEL,584"%s-dmaengine-desc-cache",585dev_name(pt->dev));586if (!desc_cache_name)587return -ENOMEM;588589pt->dma_desc_cache = kmem_cache_create(desc_cache_name,590sizeof(struct pt_dma_desc), 0,591SLAB_HWCACHE_ALIGN, NULL);592if (!pt->dma_desc_cache)593return -ENOMEM;594595dma_dev->dev = pt->dev;596dma_dev->src_addr_widths = DMA_SLAVE_BUSWIDTH_64_BYTES;597dma_dev->dst_addr_widths = DMA_SLAVE_BUSWIDTH_64_BYTES;598dma_dev->directions = DMA_MEM_TO_MEM;599dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;600dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);601dma_cap_set(DMA_INTERRUPT, dma_dev->cap_mask);602603/*604* PTDMA is intended to be used with the AMD NTB devices, hence605* marking it as DMA_PRIVATE.606*/607dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);608609INIT_LIST_HEAD(&dma_dev->channels);610611/* Set base and prep routines */612dma_dev->device_free_chan_resources = pt_free_chan_resources;613dma_dev->device_prep_dma_memcpy = pt_prep_dma_memcpy;614dma_dev->device_prep_dma_interrupt = pt_prep_dma_interrupt;615dma_dev->device_issue_pending = pt_issue_pending;616dma_dev->device_tx_status = pt_tx_status;617dma_dev->device_pause = pt_pause;618dma_dev->device_resume = pt_resume;619dma_dev->device_terminate_all = pt_terminate_all;620dma_dev->device_synchronize = pt_synchronize;621622if (ae4) {623for (i = 0; i < ae4->cmd_q_count; i++) {624chan = pt->pt_dma_chan + i;625ae4cmd_q = &ae4->ae4cmd_q[i];626chan->id = ae4cmd_q->id;627chan->pt = pt;628chan->vc.desc_free = pt_do_cleanup;629vchan_init(&chan->vc, dma_dev);630}631} else {632chan = pt->pt_dma_chan;633chan->pt = pt;634chan->vc.desc_free = pt_do_cleanup;635vchan_init(&chan->vc, dma_dev);636}637638ret = dma_async_device_register(dma_dev);639if (ret)640goto err_reg;641642return 0;643644err_reg:645kmem_cache_destroy(pt->dma_desc_cache);646647return ret;648}649EXPORT_SYMBOL_GPL(pt_dmaengine_register);650651void pt_dmaengine_unregister(struct pt_device *pt)652{653struct dma_device *dma_dev = &pt->dma_dev;654655dma_async_device_unregister(dma_dev);656657kmem_cache_destroy(pt->dma_desc_cache);658}659660661