162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ 462306a36Sopenharmony_ci/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/bits.h> 862306a36Sopenharmony_ci#include <linux/completion.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dma-buf.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/kref.h> 1462306a36Sopenharmony_ci#include <linux/list.h> 1562306a36Sopenharmony_ci#include <linux/math64.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/moduleparam.h> 1862306a36Sopenharmony_ci#include <linux/scatterlist.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/srcu.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci#include <linux/wait.h> 2462306a36Sopenharmony_ci#include <drm/drm_file.h> 2562306a36Sopenharmony_ci#include <drm/drm_gem.h> 2662306a36Sopenharmony_ci#include <drm/drm_prime.h> 2762306a36Sopenharmony_ci#include <drm/drm_print.h> 2862306a36Sopenharmony_ci#include <uapi/drm/qaic_accel.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "qaic.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define SEM_VAL_MASK GENMASK_ULL(11, 0) 3362306a36Sopenharmony_ci#define SEM_INDEX_MASK GENMASK_ULL(4, 0) 3462306a36Sopenharmony_ci#define BULK_XFER BIT(3) 3562306a36Sopenharmony_ci#define GEN_COMPLETION BIT(4) 3662306a36Sopenharmony_ci#define INBOUND_XFER 1 3762306a36Sopenharmony_ci#define OUTBOUND_XFER 2 3862306a36Sopenharmony_ci#define REQHP_OFF 0x0 /* we read this */ 3962306a36Sopenharmony_ci#define REQTP_OFF 0x4 /* we write this */ 4062306a36Sopenharmony_ci#define RSPHP_OFF 0x8 /* we write this */ 4162306a36Sopenharmony_ci#define RSPTP_OFF 0xc /* we read this */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define ENCODE_SEM(val, index, sync, cmd, flags) \ 4462306a36Sopenharmony_ci ({ \ 4562306a36Sopenharmony_ci FIELD_PREP(GENMASK(11, 0), (val)) | \ 4662306a36Sopenharmony_ci FIELD_PREP(GENMASK(20, 16), (index)) | \ 4762306a36Sopenharmony_ci FIELD_PREP(BIT(22), (sync)) | \ 4862306a36Sopenharmony_ci FIELD_PREP(GENMASK(26, 24), (cmd)) | \ 4962306a36Sopenharmony_ci FIELD_PREP(GENMASK(30, 29), (flags)) | \ 5062306a36Sopenharmony_ci FIELD_PREP(BIT(31), (cmd) ? 1 : 0); \ 5162306a36Sopenharmony_ci }) 5262306a36Sopenharmony_ci#define NUM_EVENTS 128 5362306a36Sopenharmony_ci#define NUM_DELAYS 10 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic unsigned int wait_exec_default_timeout_ms = 5000; /* 5 sec default */ 5662306a36Sopenharmony_cimodule_param(wait_exec_default_timeout_ms, uint, 0600); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(wait_exec_default_timeout_ms, "Default timeout for DRM_IOCTL_QAIC_WAIT_BO"); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic unsigned int datapath_poll_interval_us = 100; /* 100 usec default */ 6062306a36Sopenharmony_cimodule_param(datapath_poll_interval_us, uint, 0600); 6162306a36Sopenharmony_ciMODULE_PARM_DESC(datapath_poll_interval_us, 6262306a36Sopenharmony_ci "Amount of time to sleep between activity when datapath polling is enabled"); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct dbc_req { 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * A request ID is assigned to each memory handle going in DMA queue. 6762306a36Sopenharmony_ci * As a single memory handle can enqueue multiple elements in DMA queue 6862306a36Sopenharmony_ci * all of them will have the same request ID. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci __le16 req_id; 7162306a36Sopenharmony_ci /* Future use */ 7262306a36Sopenharmony_ci __u8 seq_id; 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * Special encoded variable 7562306a36Sopenharmony_ci * 7 0 - Do not force to generate MSI after DMA is completed 7662306a36Sopenharmony_ci * 1 - Force to generate MSI after DMA is completed 7762306a36Sopenharmony_ci * 6:5 Reserved 7862306a36Sopenharmony_ci * 4 1 - Generate completion element in the response queue 7962306a36Sopenharmony_ci * 0 - No Completion Code 8062306a36Sopenharmony_ci * 3 0 - DMA request is a Link list transfer 8162306a36Sopenharmony_ci * 1 - DMA request is a Bulk transfer 8262306a36Sopenharmony_ci * 2 Reserved 8362306a36Sopenharmony_ci * 1:0 00 - No DMA transfer involved 8462306a36Sopenharmony_ci * 01 - DMA transfer is part of inbound transfer 8562306a36Sopenharmony_ci * 10 - DMA transfer has outbound transfer 8662306a36Sopenharmony_ci * 11 - NA 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci __u8 cmd; 8962306a36Sopenharmony_ci __le32 resv; 9062306a36Sopenharmony_ci /* Source address for the transfer */ 9162306a36Sopenharmony_ci __le64 src_addr; 9262306a36Sopenharmony_ci /* Destination address for the transfer */ 9362306a36Sopenharmony_ci __le64 dest_addr; 9462306a36Sopenharmony_ci /* Length of transfer request */ 9562306a36Sopenharmony_ci __le32 len; 9662306a36Sopenharmony_ci __le32 resv2; 9762306a36Sopenharmony_ci /* Doorbell address */ 9862306a36Sopenharmony_ci __le64 db_addr; 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Special encoded variable 10162306a36Sopenharmony_ci * 7 1 - Doorbell(db) write 10262306a36Sopenharmony_ci * 0 - No doorbell write 10362306a36Sopenharmony_ci * 6:2 Reserved 10462306a36Sopenharmony_ci * 1:0 00 - 32 bit access, db address must be aligned to 32bit-boundary 10562306a36Sopenharmony_ci * 01 - 16 bit access, db address must be aligned to 16bit-boundary 10662306a36Sopenharmony_ci * 10 - 8 bit access, db address must be aligned to 8bit-boundary 10762306a36Sopenharmony_ci * 11 - Reserved 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci __u8 db_len; 11062306a36Sopenharmony_ci __u8 resv3; 11162306a36Sopenharmony_ci __le16 resv4; 11262306a36Sopenharmony_ci /* 32 bit data written to doorbell address */ 11362306a36Sopenharmony_ci __le32 db_data; 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * Special encoded variable 11662306a36Sopenharmony_ci * All the fields of sem_cmdX are passed from user and all are ORed 11762306a36Sopenharmony_ci * together to form sem_cmd. 11862306a36Sopenharmony_ci * 0:11 Semaphore value 11962306a36Sopenharmony_ci * 15:12 Reserved 12062306a36Sopenharmony_ci * 20:16 Semaphore index 12162306a36Sopenharmony_ci * 21 Reserved 12262306a36Sopenharmony_ci * 22 Semaphore Sync 12362306a36Sopenharmony_ci * 23 Reserved 12462306a36Sopenharmony_ci * 26:24 Semaphore command 12562306a36Sopenharmony_ci * 28:27 Reserved 12662306a36Sopenharmony_ci * 29 Semaphore DMA out bound sync fence 12762306a36Sopenharmony_ci * 30 Semaphore DMA in bound sync fence 12862306a36Sopenharmony_ci * 31 Enable semaphore command 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci __le32 sem_cmd0; 13162306a36Sopenharmony_ci __le32 sem_cmd1; 13262306a36Sopenharmony_ci __le32 sem_cmd2; 13362306a36Sopenharmony_ci __le32 sem_cmd3; 13462306a36Sopenharmony_ci} __packed; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistruct dbc_rsp { 13762306a36Sopenharmony_ci /* Request ID of the memory handle whose DMA transaction is completed */ 13862306a36Sopenharmony_ci __le16 req_id; 13962306a36Sopenharmony_ci /* Status of the DMA transaction. 0 : Success otherwise failure */ 14062306a36Sopenharmony_ci __le16 status; 14162306a36Sopenharmony_ci} __packed; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciinline int get_dbc_req_elem_size(void) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return sizeof(struct dbc_req); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciinline int get_dbc_rsp_elem_size(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci return sizeof(struct dbc_rsp); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void free_slice(struct kref *kref) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct bo_slice *slice = container_of(kref, struct bo_slice, ref_count); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci list_del(&slice->slice); 15862306a36Sopenharmony_ci drm_gem_object_put(&slice->bo->base); 15962306a36Sopenharmony_ci sg_free_table(slice->sgt); 16062306a36Sopenharmony_ci kfree(slice->sgt); 16162306a36Sopenharmony_ci kfree(slice->reqs); 16262306a36Sopenharmony_ci kfree(slice); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int clone_range_of_sgt_for_slice(struct qaic_device *qdev, struct sg_table **sgt_out, 16662306a36Sopenharmony_ci struct sg_table *sgt_in, u64 size, u64 offset) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci int total_len, len, nents, offf = 0, offl = 0; 16962306a36Sopenharmony_ci struct scatterlist *sg, *sgn, *sgf, *sgl; 17062306a36Sopenharmony_ci struct sg_table *sgt; 17162306a36Sopenharmony_ci int ret, j; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* find out number of relevant nents needed for this mem */ 17462306a36Sopenharmony_ci total_len = 0; 17562306a36Sopenharmony_ci sgf = NULL; 17662306a36Sopenharmony_ci sgl = NULL; 17762306a36Sopenharmony_ci nents = 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci size = size ? size : PAGE_SIZE; 18062306a36Sopenharmony_ci for (sg = sgt_in->sgl; sg; sg = sg_next(sg)) { 18162306a36Sopenharmony_ci len = sg_dma_len(sg); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!len) 18462306a36Sopenharmony_ci continue; 18562306a36Sopenharmony_ci if (offset >= total_len && offset < total_len + len) { 18662306a36Sopenharmony_ci sgf = sg; 18762306a36Sopenharmony_ci offf = offset - total_len; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci if (sgf) 19062306a36Sopenharmony_ci nents++; 19162306a36Sopenharmony_ci if (offset + size >= total_len && 19262306a36Sopenharmony_ci offset + size <= total_len + len) { 19362306a36Sopenharmony_ci sgl = sg; 19462306a36Sopenharmony_ci offl = offset + size - total_len; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci total_len += len; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!sgf || !sgl) { 20162306a36Sopenharmony_ci ret = -EINVAL; 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 20662306a36Sopenharmony_ci if (!sgt) { 20762306a36Sopenharmony_ci ret = -ENOMEM; 20862306a36Sopenharmony_ci goto out; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = sg_alloc_table(sgt, nents, GFP_KERNEL); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci goto free_sgt; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* copy relevant sg node and fix page and length */ 21662306a36Sopenharmony_ci sgn = sgf; 21762306a36Sopenharmony_ci for_each_sgtable_sg(sgt, sg, j) { 21862306a36Sopenharmony_ci memcpy(sg, sgn, sizeof(*sg)); 21962306a36Sopenharmony_ci if (sgn == sgf) { 22062306a36Sopenharmony_ci sg_dma_address(sg) += offf; 22162306a36Sopenharmony_ci sg_dma_len(sg) -= offf; 22262306a36Sopenharmony_ci sg_set_page(sg, sg_page(sgn), sg_dma_len(sg), offf); 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci offf = 0; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci if (sgn == sgl) { 22762306a36Sopenharmony_ci sg_dma_len(sg) = offl - offf; 22862306a36Sopenharmony_ci sg_set_page(sg, sg_page(sgn), offl - offf, offf); 22962306a36Sopenharmony_ci sg_mark_end(sg); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci sgn = sg_next(sgn); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci *sgt_out = sgt; 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cifree_sgt: 23962306a36Sopenharmony_ci kfree(sgt); 24062306a36Sopenharmony_ciout: 24162306a36Sopenharmony_ci *sgt_out = NULL; 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int encode_reqs(struct qaic_device *qdev, struct bo_slice *slice, 24662306a36Sopenharmony_ci struct qaic_attach_slice_entry *req) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci __le64 db_addr = cpu_to_le64(req->db_addr); 24962306a36Sopenharmony_ci __le32 db_data = cpu_to_le32(req->db_data); 25062306a36Sopenharmony_ci struct scatterlist *sg; 25162306a36Sopenharmony_ci __u8 cmd = BULK_XFER; 25262306a36Sopenharmony_ci int presync_sem; 25362306a36Sopenharmony_ci u64 dev_addr; 25462306a36Sopenharmony_ci __u8 db_len; 25562306a36Sopenharmony_ci int i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!slice->no_xfer) 25862306a36Sopenharmony_ci cmd |= (slice->dir == DMA_TO_DEVICE ? INBOUND_XFER : OUTBOUND_XFER); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (req->db_len && !IS_ALIGNED(req->db_addr, req->db_len / 8)) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci presync_sem = req->sem0.presync + req->sem1.presync + req->sem2.presync + req->sem3.presync; 26462306a36Sopenharmony_ci if (presync_sem > 1) 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci presync_sem = req->sem0.presync << 0 | req->sem1.presync << 1 | 26862306a36Sopenharmony_ci req->sem2.presync << 2 | req->sem3.presync << 3; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci switch (req->db_len) { 27162306a36Sopenharmony_ci case 32: 27262306a36Sopenharmony_ci db_len = BIT(7); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case 16: 27562306a36Sopenharmony_ci db_len = BIT(7) | 1; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case 8: 27862306a36Sopenharmony_ci db_len = BIT(7) | 2; 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case 0: 28162306a36Sopenharmony_ci db_len = 0; /* doorbell is not active for this command */ 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci default: 28462306a36Sopenharmony_ci return -EINVAL; /* should never hit this */ 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * When we end up splitting up a single request (ie a buf slice) into 28962306a36Sopenharmony_ci * multiple DMA requests, we have to manage the sync data carefully. 29062306a36Sopenharmony_ci * There can only be one presync sem. That needs to be on every xfer 29162306a36Sopenharmony_ci * so that the DMA engine doesn't transfer data before the receiver is 29262306a36Sopenharmony_ci * ready. We only do the doorbell and postsync sems after the xfer. 29362306a36Sopenharmony_ci * To guarantee previous xfers for the request are complete, we use a 29462306a36Sopenharmony_ci * fence. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci dev_addr = req->dev_addr; 29762306a36Sopenharmony_ci for_each_sgtable_sg(slice->sgt, sg, i) { 29862306a36Sopenharmony_ci slice->reqs[i].cmd = cmd; 29962306a36Sopenharmony_ci slice->reqs[i].src_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ? 30062306a36Sopenharmony_ci sg_dma_address(sg) : dev_addr); 30162306a36Sopenharmony_ci slice->reqs[i].dest_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ? 30262306a36Sopenharmony_ci dev_addr : sg_dma_address(sg)); 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * sg_dma_len(sg) returns size of a DMA segment, maximum DMA 30562306a36Sopenharmony_ci * segment size is set to UINT_MAX by qaic and hence return 30662306a36Sopenharmony_ci * values of sg_dma_len(sg) can never exceed u32 range. So, 30762306a36Sopenharmony_ci * by down sizing we are not corrupting the value. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci slice->reqs[i].len = cpu_to_le32((u32)sg_dma_len(sg)); 31062306a36Sopenharmony_ci switch (presync_sem) { 31162306a36Sopenharmony_ci case BIT(0): 31262306a36Sopenharmony_ci slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val, 31362306a36Sopenharmony_ci req->sem0.index, 31462306a36Sopenharmony_ci req->sem0.presync, 31562306a36Sopenharmony_ci req->sem0.cmd, 31662306a36Sopenharmony_ci req->sem0.flags)); 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci case BIT(1): 31962306a36Sopenharmony_ci slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val, 32062306a36Sopenharmony_ci req->sem1.index, 32162306a36Sopenharmony_ci req->sem1.presync, 32262306a36Sopenharmony_ci req->sem1.cmd, 32362306a36Sopenharmony_ci req->sem1.flags)); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci case BIT(2): 32662306a36Sopenharmony_ci slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val, 32762306a36Sopenharmony_ci req->sem2.index, 32862306a36Sopenharmony_ci req->sem2.presync, 32962306a36Sopenharmony_ci req->sem2.cmd, 33062306a36Sopenharmony_ci req->sem2.flags)); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci case BIT(3): 33362306a36Sopenharmony_ci slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val, 33462306a36Sopenharmony_ci req->sem3.index, 33562306a36Sopenharmony_ci req->sem3.presync, 33662306a36Sopenharmony_ci req->sem3.cmd, 33762306a36Sopenharmony_ci req->sem3.flags)); 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci dev_addr += sg_dma_len(sg); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci /* add post transfer stuff to last segment */ 34362306a36Sopenharmony_ci i--; 34462306a36Sopenharmony_ci slice->reqs[i].cmd |= GEN_COMPLETION; 34562306a36Sopenharmony_ci slice->reqs[i].db_addr = db_addr; 34662306a36Sopenharmony_ci slice->reqs[i].db_len = db_len; 34762306a36Sopenharmony_ci slice->reqs[i].db_data = db_data; 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Add a fence if we have more than one request going to the hardware 35062306a36Sopenharmony_ci * representing the entirety of the user request, and the user request 35162306a36Sopenharmony_ci * has no presync condition. 35262306a36Sopenharmony_ci * Fences are expensive, so we try to avoid them. We rely on the 35362306a36Sopenharmony_ci * hardware behavior to avoid needing one when there is a presync 35462306a36Sopenharmony_ci * condition. When a presync exists, all requests for that same 35562306a36Sopenharmony_ci * presync will be queued into a fifo. Thus, since we queue the 35662306a36Sopenharmony_ci * post xfer activity only on the last request we queue, the hardware 35762306a36Sopenharmony_ci * will ensure that the last queued request is processed last, thus 35862306a36Sopenharmony_ci * making sure the post xfer activity happens at the right time without 35962306a36Sopenharmony_ci * a fence. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci if (i && !presync_sem) 36262306a36Sopenharmony_ci req->sem0.flags |= (slice->dir == DMA_TO_DEVICE ? 36362306a36Sopenharmony_ci QAIC_SEM_INSYNCFENCE : QAIC_SEM_OUTSYNCFENCE); 36462306a36Sopenharmony_ci slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val, req->sem0.index, 36562306a36Sopenharmony_ci req->sem0.presync, req->sem0.cmd, 36662306a36Sopenharmony_ci req->sem0.flags)); 36762306a36Sopenharmony_ci slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val, req->sem1.index, 36862306a36Sopenharmony_ci req->sem1.presync, req->sem1.cmd, 36962306a36Sopenharmony_ci req->sem1.flags)); 37062306a36Sopenharmony_ci slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val, req->sem2.index, 37162306a36Sopenharmony_ci req->sem2.presync, req->sem2.cmd, 37262306a36Sopenharmony_ci req->sem2.flags)); 37362306a36Sopenharmony_ci slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val, req->sem3.index, 37462306a36Sopenharmony_ci req->sem3.presync, req->sem3.cmd, 37562306a36Sopenharmony_ci req->sem3.flags)); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo, 38162306a36Sopenharmony_ci struct qaic_attach_slice_entry *slice_ent) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct sg_table *sgt = NULL; 38462306a36Sopenharmony_ci struct bo_slice *slice; 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = clone_range_of_sgt_for_slice(qdev, &sgt, bo->sgt, slice_ent->size, slice_ent->offset); 38862306a36Sopenharmony_ci if (ret) 38962306a36Sopenharmony_ci goto out; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci slice = kmalloc(sizeof(*slice), GFP_KERNEL); 39262306a36Sopenharmony_ci if (!slice) { 39362306a36Sopenharmony_ci ret = -ENOMEM; 39462306a36Sopenharmony_ci goto free_sgt; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci slice->reqs = kcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL); 39862306a36Sopenharmony_ci if (!slice->reqs) { 39962306a36Sopenharmony_ci ret = -ENOMEM; 40062306a36Sopenharmony_ci goto free_slice; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci slice->no_xfer = !slice_ent->size; 40462306a36Sopenharmony_ci slice->sgt = sgt; 40562306a36Sopenharmony_ci slice->nents = sgt->nents; 40662306a36Sopenharmony_ci slice->dir = bo->dir; 40762306a36Sopenharmony_ci slice->bo = bo; 40862306a36Sopenharmony_ci slice->size = slice_ent->size; 40962306a36Sopenharmony_ci slice->offset = slice_ent->offset; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = encode_reqs(qdev, slice, slice_ent); 41262306a36Sopenharmony_ci if (ret) 41362306a36Sopenharmony_ci goto free_req; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci bo->total_slice_nents += sgt->nents; 41662306a36Sopenharmony_ci kref_init(&slice->ref_count); 41762306a36Sopenharmony_ci drm_gem_object_get(&bo->base); 41862306a36Sopenharmony_ci list_add_tail(&slice->slice, &bo->slices); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cifree_req: 42362306a36Sopenharmony_ci kfree(slice->reqs); 42462306a36Sopenharmony_cifree_slice: 42562306a36Sopenharmony_ci kfree(slice); 42662306a36Sopenharmony_cifree_sgt: 42762306a36Sopenharmony_ci sg_free_table(sgt); 42862306a36Sopenharmony_ci kfree(sgt); 42962306a36Sopenharmony_ciout: 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 size) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct scatterlist *sg; 43662306a36Sopenharmony_ci struct sg_table *sgt; 43762306a36Sopenharmony_ci struct page **pages; 43862306a36Sopenharmony_ci int *pages_order; 43962306a36Sopenharmony_ci int buf_extra; 44062306a36Sopenharmony_ci int max_order; 44162306a36Sopenharmony_ci int nr_pages; 44262306a36Sopenharmony_ci int ret = 0; 44362306a36Sopenharmony_ci int i, j, k; 44462306a36Sopenharmony_ci int order; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (size) { 44762306a36Sopenharmony_ci nr_pages = DIV_ROUND_UP(size, PAGE_SIZE); 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * calculate how much extra we are going to allocate, to remove 45062306a36Sopenharmony_ci * later 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci buf_extra = (PAGE_SIZE - size % PAGE_SIZE) % PAGE_SIZE; 45362306a36Sopenharmony_ci max_order = min(MAX_ORDER - 1, get_order(size)); 45462306a36Sopenharmony_ci } else { 45562306a36Sopenharmony_ci /* allocate a single page for book keeping */ 45662306a36Sopenharmony_ci nr_pages = 1; 45762306a36Sopenharmony_ci buf_extra = 0; 45862306a36Sopenharmony_ci max_order = 0; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci pages = kvmalloc_array(nr_pages, sizeof(*pages) + sizeof(*pages_order), GFP_KERNEL); 46262306a36Sopenharmony_ci if (!pages) { 46362306a36Sopenharmony_ci ret = -ENOMEM; 46462306a36Sopenharmony_ci goto out; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci pages_order = (void *)pages + sizeof(*pages) * nr_pages; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * Allocate requested memory using alloc_pages. It is possible to allocate 47062306a36Sopenharmony_ci * the requested memory in multiple chunks by calling alloc_pages 47162306a36Sopenharmony_ci * multiple times. Use SG table to handle multiple allocated pages. 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci i = 0; 47462306a36Sopenharmony_ci while (nr_pages > 0) { 47562306a36Sopenharmony_ci order = min(get_order(nr_pages * PAGE_SIZE), max_order); 47662306a36Sopenharmony_ci while (1) { 47762306a36Sopenharmony_ci pages[i] = alloc_pages(GFP_KERNEL | GFP_HIGHUSER | 47862306a36Sopenharmony_ci __GFP_NOWARN | __GFP_ZERO | 47962306a36Sopenharmony_ci (order ? __GFP_NORETRY : __GFP_RETRY_MAYFAIL), 48062306a36Sopenharmony_ci order); 48162306a36Sopenharmony_ci if (pages[i]) 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci if (!order--) { 48462306a36Sopenharmony_ci ret = -ENOMEM; 48562306a36Sopenharmony_ci goto free_partial_alloc; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci max_order = order; 49062306a36Sopenharmony_ci pages_order[i] = order; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci nr_pages -= 1 << order; 49362306a36Sopenharmony_ci if (nr_pages <= 0) 49462306a36Sopenharmony_ci /* account for over allocation */ 49562306a36Sopenharmony_ci buf_extra += abs(nr_pages) * PAGE_SIZE; 49662306a36Sopenharmony_ci i++; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); 50062306a36Sopenharmony_ci if (!sgt) { 50162306a36Sopenharmony_ci ret = -ENOMEM; 50262306a36Sopenharmony_ci goto free_partial_alloc; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (sg_alloc_table(sgt, i, GFP_KERNEL)) { 50662306a36Sopenharmony_ci ret = -ENOMEM; 50762306a36Sopenharmony_ci goto free_sgt; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Populate the SG table with the allocated memory pages */ 51162306a36Sopenharmony_ci sg = sgt->sgl; 51262306a36Sopenharmony_ci for (k = 0; k < i; k++, sg = sg_next(sg)) { 51362306a36Sopenharmony_ci /* Last entry requires special handling */ 51462306a36Sopenharmony_ci if (k < i - 1) { 51562306a36Sopenharmony_ci sg_set_page(sg, pages[k], PAGE_SIZE << pages_order[k], 0); 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci sg_set_page(sg, pages[k], (PAGE_SIZE << pages_order[k]) - buf_extra, 0); 51862306a36Sopenharmony_ci sg_mark_end(sg); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci kvfree(pages); 52362306a36Sopenharmony_ci *sgt_out = sgt; 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cifree_sgt: 52762306a36Sopenharmony_ci kfree(sgt); 52862306a36Sopenharmony_cifree_partial_alloc: 52962306a36Sopenharmony_ci for (j = 0; j < i; j++) 53062306a36Sopenharmony_ci __free_pages(pages[j], pages_order[j]); 53162306a36Sopenharmony_ci kvfree(pages); 53262306a36Sopenharmony_ciout: 53362306a36Sopenharmony_ci *sgt_out = NULL; 53462306a36Sopenharmony_ci return ret; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic bool invalid_sem(struct qaic_sem *sem) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci if (sem->val & ~SEM_VAL_MASK || sem->index & ~SEM_INDEX_MASK || 54062306a36Sopenharmony_ci !(sem->presync == 0 || sem->presync == 1) || sem->pad || 54162306a36Sopenharmony_ci sem->flags & ~(QAIC_SEM_INSYNCFENCE | QAIC_SEM_OUTSYNCFENCE) || 54262306a36Sopenharmony_ci sem->cmd > QAIC_SEM_WAIT_GT_0) 54362306a36Sopenharmony_ci return true; 54462306a36Sopenharmony_ci return false; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int qaic_validate_req(struct qaic_device *qdev, struct qaic_attach_slice_entry *slice_ent, 54862306a36Sopenharmony_ci u32 count, u64 total_size) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci int i; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 55362306a36Sopenharmony_ci if (!(slice_ent[i].db_len == 32 || slice_ent[i].db_len == 16 || 55462306a36Sopenharmony_ci slice_ent[i].db_len == 8 || slice_ent[i].db_len == 0) || 55562306a36Sopenharmony_ci invalid_sem(&slice_ent[i].sem0) || invalid_sem(&slice_ent[i].sem1) || 55662306a36Sopenharmony_ci invalid_sem(&slice_ent[i].sem2) || invalid_sem(&slice_ent[i].sem3)) 55762306a36Sopenharmony_ci return -EINVAL; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (slice_ent[i].offset + slice_ent[i].size > total_size) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void qaic_free_sgt(struct sg_table *sgt) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct scatterlist *sg; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci for (sg = sgt->sgl; sg; sg = sg_next(sg)) 57162306a36Sopenharmony_ci if (sg_page(sg)) 57262306a36Sopenharmony_ci __free_pages(sg_page(sg), get_order(sg->length)); 57362306a36Sopenharmony_ci sg_free_table(sgt); 57462306a36Sopenharmony_ci kfree(sgt); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void qaic_gem_print_info(struct drm_printer *p, unsigned int indent, 57862306a36Sopenharmony_ci const struct drm_gem_object *obj) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct qaic_bo *bo = to_qaic_bo(obj); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci drm_printf_indent(p, indent, "user requested size=%llu\n", bo->size); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic const struct vm_operations_struct drm_vm_ops = { 58662306a36Sopenharmony_ci .open = drm_gem_vm_open, 58762306a36Sopenharmony_ci .close = drm_gem_vm_close, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct qaic_bo *bo = to_qaic_bo(obj); 59362306a36Sopenharmony_ci unsigned long offset = 0; 59462306a36Sopenharmony_ci struct scatterlist *sg; 59562306a36Sopenharmony_ci int ret = 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (obj->import_attach) 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { 60162306a36Sopenharmony_ci if (sg_page(sg)) { 60262306a36Sopenharmony_ci ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), 60362306a36Sopenharmony_ci sg->length, vma->vm_page_prot); 60462306a36Sopenharmony_ci if (ret) 60562306a36Sopenharmony_ci goto out; 60662306a36Sopenharmony_ci offset += sg->length; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ciout: 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic void qaic_free_object(struct drm_gem_object *obj) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct qaic_bo *bo = to_qaic_bo(obj); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (obj->import_attach) { 61962306a36Sopenharmony_ci /* DMABUF/PRIME Path */ 62062306a36Sopenharmony_ci drm_prime_gem_destroy(obj, NULL); 62162306a36Sopenharmony_ci } else { 62262306a36Sopenharmony_ci /* Private buffer allocation path */ 62362306a36Sopenharmony_ci qaic_free_sgt(bo->sgt); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci drm_gem_object_release(obj); 62762306a36Sopenharmony_ci kfree(bo); 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic const struct drm_gem_object_funcs qaic_gem_funcs = { 63162306a36Sopenharmony_ci .free = qaic_free_object, 63262306a36Sopenharmony_ci .print_info = qaic_gem_print_info, 63362306a36Sopenharmony_ci .mmap = qaic_gem_object_mmap, 63462306a36Sopenharmony_ci .vm_ops = &drm_vm_ops, 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic struct qaic_bo *qaic_alloc_init_bo(void) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct qaic_bo *bo; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci bo = kzalloc(sizeof(*bo), GFP_KERNEL); 64262306a36Sopenharmony_ci if (!bo) 64362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci INIT_LIST_HEAD(&bo->slices); 64662306a36Sopenharmony_ci init_completion(&bo->xfer_done); 64762306a36Sopenharmony_ci complete_all(&bo->xfer_done); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return bo; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ciint qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct qaic_create_bo *args = data; 65562306a36Sopenharmony_ci int usr_rcu_id, qdev_rcu_id; 65662306a36Sopenharmony_ci struct drm_gem_object *obj; 65762306a36Sopenharmony_ci struct qaic_device *qdev; 65862306a36Sopenharmony_ci struct qaic_user *usr; 65962306a36Sopenharmony_ci struct qaic_bo *bo; 66062306a36Sopenharmony_ci size_t size; 66162306a36Sopenharmony_ci int ret; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (args->pad) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci size = PAGE_ALIGN(args->size); 66762306a36Sopenharmony_ci if (size == 0) 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci usr = file_priv->driver_priv; 67162306a36Sopenharmony_ci usr_rcu_id = srcu_read_lock(&usr->qddev_lock); 67262306a36Sopenharmony_ci if (!usr->qddev) { 67362306a36Sopenharmony_ci ret = -ENODEV; 67462306a36Sopenharmony_ci goto unlock_usr_srcu; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci qdev = usr->qddev->qdev; 67862306a36Sopenharmony_ci qdev_rcu_id = srcu_read_lock(&qdev->dev_lock); 67962306a36Sopenharmony_ci if (qdev->in_reset) { 68062306a36Sopenharmony_ci ret = -ENODEV; 68162306a36Sopenharmony_ci goto unlock_dev_srcu; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci bo = qaic_alloc_init_bo(); 68562306a36Sopenharmony_ci if (IS_ERR(bo)) { 68662306a36Sopenharmony_ci ret = PTR_ERR(bo); 68762306a36Sopenharmony_ci goto unlock_dev_srcu; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci obj = &bo->base; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci drm_gem_private_object_init(dev, obj, size); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci obj->funcs = &qaic_gem_funcs; 69462306a36Sopenharmony_ci ret = create_sgt(qdev, &bo->sgt, size); 69562306a36Sopenharmony_ci if (ret) 69662306a36Sopenharmony_ci goto free_bo; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci bo->size = args->size; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci ret = drm_gem_handle_create(file_priv, obj, &args->handle); 70162306a36Sopenharmony_ci if (ret) 70262306a36Sopenharmony_ci goto free_sgt; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci bo->handle = args->handle; 70562306a36Sopenharmony_ci drm_gem_object_put(obj); 70662306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 70762306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cifree_sgt: 71262306a36Sopenharmony_ci qaic_free_sgt(bo->sgt); 71362306a36Sopenharmony_cifree_bo: 71462306a36Sopenharmony_ci kfree(bo); 71562306a36Sopenharmony_ciunlock_dev_srcu: 71662306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 71762306a36Sopenharmony_ciunlock_usr_srcu: 71862306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 71962306a36Sopenharmony_ci return ret; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ciint qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci struct qaic_mmap_bo *args = data; 72562306a36Sopenharmony_ci int usr_rcu_id, qdev_rcu_id; 72662306a36Sopenharmony_ci struct drm_gem_object *obj; 72762306a36Sopenharmony_ci struct qaic_device *qdev; 72862306a36Sopenharmony_ci struct qaic_user *usr; 72962306a36Sopenharmony_ci int ret; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci usr = file_priv->driver_priv; 73262306a36Sopenharmony_ci usr_rcu_id = srcu_read_lock(&usr->qddev_lock); 73362306a36Sopenharmony_ci if (!usr->qddev) { 73462306a36Sopenharmony_ci ret = -ENODEV; 73562306a36Sopenharmony_ci goto unlock_usr_srcu; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci qdev = usr->qddev->qdev; 73962306a36Sopenharmony_ci qdev_rcu_id = srcu_read_lock(&qdev->dev_lock); 74062306a36Sopenharmony_ci if (qdev->in_reset) { 74162306a36Sopenharmony_ci ret = -ENODEV; 74262306a36Sopenharmony_ci goto unlock_dev_srcu; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, args->handle); 74662306a36Sopenharmony_ci if (!obj) { 74762306a36Sopenharmony_ci ret = -ENOENT; 74862306a36Sopenharmony_ci goto unlock_dev_srcu; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci ret = drm_gem_create_mmap_offset(obj); 75262306a36Sopenharmony_ci if (ret == 0) 75362306a36Sopenharmony_ci args->offset = drm_vma_node_offset_addr(&obj->vma_node); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci drm_gem_object_put(obj); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ciunlock_dev_srcu: 75862306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 75962306a36Sopenharmony_ciunlock_usr_srcu: 76062306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistruct drm_gem_object *qaic_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct dma_buf_attachment *attach; 76762306a36Sopenharmony_ci struct drm_gem_object *obj; 76862306a36Sopenharmony_ci struct qaic_bo *bo; 76962306a36Sopenharmony_ci int ret; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci bo = qaic_alloc_init_bo(); 77262306a36Sopenharmony_ci if (IS_ERR(bo)) { 77362306a36Sopenharmony_ci ret = PTR_ERR(bo); 77462306a36Sopenharmony_ci goto out; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci obj = &bo->base; 77862306a36Sopenharmony_ci get_dma_buf(dma_buf); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci attach = dma_buf_attach(dma_buf, dev->dev); 78162306a36Sopenharmony_ci if (IS_ERR(attach)) { 78262306a36Sopenharmony_ci ret = PTR_ERR(attach); 78362306a36Sopenharmony_ci goto attach_fail; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (!attach->dmabuf->size) { 78762306a36Sopenharmony_ci ret = -EINVAL; 78862306a36Sopenharmony_ci goto size_align_fail; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci drm_gem_private_object_init(dev, obj, attach->dmabuf->size); 79262306a36Sopenharmony_ci /* 79362306a36Sopenharmony_ci * skipping dma_buf_map_attachment() as we do not know the direction 79462306a36Sopenharmony_ci * just yet. Once the direction is known in the subsequent IOCTL to 79562306a36Sopenharmony_ci * attach slicing, we can do it then. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci obj->funcs = &qaic_gem_funcs; 79962306a36Sopenharmony_ci obj->import_attach = attach; 80062306a36Sopenharmony_ci obj->resv = dma_buf->resv; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return obj; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cisize_align_fail: 80562306a36Sopenharmony_ci dma_buf_detach(dma_buf, attach); 80662306a36Sopenharmony_ciattach_fail: 80762306a36Sopenharmony_ci dma_buf_put(dma_buf); 80862306a36Sopenharmony_ci kfree(bo); 80962306a36Sopenharmony_ciout: 81062306a36Sopenharmony_ci return ERR_PTR(ret); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int qaic_prepare_import_bo(struct qaic_bo *bo, struct qaic_attach_slice_hdr *hdr) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct drm_gem_object *obj = &bo->base; 81662306a36Sopenharmony_ci struct sg_table *sgt; 81762306a36Sopenharmony_ci int ret; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (obj->import_attach->dmabuf->size < hdr->size) 82062306a36Sopenharmony_ci return -EINVAL; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci sgt = dma_buf_map_attachment(obj->import_attach, hdr->dir); 82362306a36Sopenharmony_ci if (IS_ERR(sgt)) { 82462306a36Sopenharmony_ci ret = PTR_ERR(sgt); 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci bo->sgt = sgt; 82962306a36Sopenharmony_ci bo->size = hdr->size; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int qaic_prepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo, 83562306a36Sopenharmony_ci struct qaic_attach_slice_hdr *hdr) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci int ret; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (bo->size != hdr->size) 84062306a36Sopenharmony_ci return -EINVAL; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci ret = dma_map_sgtable(&qdev->pdev->dev, bo->sgt, hdr->dir, 0); 84362306a36Sopenharmony_ci if (ret) 84462306a36Sopenharmony_ci return -EFAULT; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int qaic_prepare_bo(struct qaic_device *qdev, struct qaic_bo *bo, 85062306a36Sopenharmony_ci struct qaic_attach_slice_hdr *hdr) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci int ret; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (bo->base.import_attach) 85562306a36Sopenharmony_ci ret = qaic_prepare_import_bo(bo, hdr); 85662306a36Sopenharmony_ci else 85762306a36Sopenharmony_ci ret = qaic_prepare_export_bo(qdev, bo, hdr); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (ret == 0) 86062306a36Sopenharmony_ci bo->dir = hdr->dir; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void qaic_unprepare_import_bo(struct qaic_bo *bo) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci dma_buf_unmap_attachment(bo->base.import_attach, bo->sgt, bo->dir); 86862306a36Sopenharmony_ci bo->sgt = NULL; 86962306a36Sopenharmony_ci bo->size = 0; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void qaic_unprepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci dma_unmap_sgtable(&qdev->pdev->dev, bo->sgt, bo->dir, 0); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic void qaic_unprepare_bo(struct qaic_device *qdev, struct qaic_bo *bo) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci if (bo->base.import_attach) 88062306a36Sopenharmony_ci qaic_unprepare_import_bo(bo); 88162306a36Sopenharmony_ci else 88262306a36Sopenharmony_ci qaic_unprepare_export_bo(qdev, bo); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci bo->dir = 0; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void qaic_free_slices_bo(struct qaic_bo *bo) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct bo_slice *slice, *temp; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci list_for_each_entry_safe(slice, temp, &bo->slices, slice) 89262306a36Sopenharmony_ci kref_put(&slice->ref_count, free_slice); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic int qaic_attach_slicing_bo(struct qaic_device *qdev, struct qaic_bo *bo, 89662306a36Sopenharmony_ci struct qaic_attach_slice_hdr *hdr, 89762306a36Sopenharmony_ci struct qaic_attach_slice_entry *slice_ent) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci int ret, i; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci for (i = 0; i < hdr->count; i++) { 90262306a36Sopenharmony_ci ret = qaic_map_one_slice(qdev, bo, &slice_ent[i]); 90362306a36Sopenharmony_ci if (ret) { 90462306a36Sopenharmony_ci qaic_free_slices_bo(bo); 90562306a36Sopenharmony_ci return ret; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (bo->total_slice_nents > qdev->dbc[hdr->dbc_id].nelem) { 91062306a36Sopenharmony_ci qaic_free_slices_bo(bo); 91162306a36Sopenharmony_ci return -ENOSPC; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci bo->sliced = true; 91562306a36Sopenharmony_ci bo->nr_slice = hdr->count; 91662306a36Sopenharmony_ci list_add_tail(&bo->bo_list, &qdev->dbc[hdr->dbc_id].bo_lists); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ciint qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct qaic_attach_slice_entry *slice_ent; 92462306a36Sopenharmony_ci struct qaic_attach_slice *args = data; 92562306a36Sopenharmony_ci int rcu_id, usr_rcu_id, qdev_rcu_id; 92662306a36Sopenharmony_ci struct dma_bridge_chan *dbc; 92762306a36Sopenharmony_ci struct drm_gem_object *obj; 92862306a36Sopenharmony_ci struct qaic_device *qdev; 92962306a36Sopenharmony_ci unsigned long arg_size; 93062306a36Sopenharmony_ci struct qaic_user *usr; 93162306a36Sopenharmony_ci u8 __user *user_data; 93262306a36Sopenharmony_ci struct qaic_bo *bo; 93362306a36Sopenharmony_ci int ret; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (args->hdr.count == 0) 93662306a36Sopenharmony_ci return -EINVAL; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci arg_size = args->hdr.count * sizeof(*slice_ent); 93962306a36Sopenharmony_ci if (arg_size / args->hdr.count != sizeof(*slice_ent)) 94062306a36Sopenharmony_ci return -EINVAL; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (args->hdr.size == 0) 94362306a36Sopenharmony_ci return -EINVAL; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE)) 94662306a36Sopenharmony_ci return -EINVAL; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (args->data == 0) 94962306a36Sopenharmony_ci return -EINVAL; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci usr = file_priv->driver_priv; 95262306a36Sopenharmony_ci usr_rcu_id = srcu_read_lock(&usr->qddev_lock); 95362306a36Sopenharmony_ci if (!usr->qddev) { 95462306a36Sopenharmony_ci ret = -ENODEV; 95562306a36Sopenharmony_ci goto unlock_usr_srcu; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci qdev = usr->qddev->qdev; 95962306a36Sopenharmony_ci qdev_rcu_id = srcu_read_lock(&qdev->dev_lock); 96062306a36Sopenharmony_ci if (qdev->in_reset) { 96162306a36Sopenharmony_ci ret = -ENODEV; 96262306a36Sopenharmony_ci goto unlock_dev_srcu; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (args->hdr.dbc_id >= qdev->num_dbc) { 96662306a36Sopenharmony_ci ret = -EINVAL; 96762306a36Sopenharmony_ci goto unlock_dev_srcu; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci user_data = u64_to_user_ptr(args->data); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci slice_ent = kzalloc(arg_size, GFP_KERNEL); 97362306a36Sopenharmony_ci if (!slice_ent) { 97462306a36Sopenharmony_ci ret = -EINVAL; 97562306a36Sopenharmony_ci goto unlock_dev_srcu; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ret = copy_from_user(slice_ent, user_data, arg_size); 97962306a36Sopenharmony_ci if (ret) { 98062306a36Sopenharmony_ci ret = -EFAULT; 98162306a36Sopenharmony_ci goto free_slice_ent; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, args->hdr.size); 98562306a36Sopenharmony_ci if (ret) 98662306a36Sopenharmony_ci goto free_slice_ent; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, args->hdr.handle); 98962306a36Sopenharmony_ci if (!obj) { 99062306a36Sopenharmony_ci ret = -ENOENT; 99162306a36Sopenharmony_ci goto free_slice_ent; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci bo = to_qaic_bo(obj); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (bo->sliced) { 99762306a36Sopenharmony_ci ret = -EINVAL; 99862306a36Sopenharmony_ci goto put_bo; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci dbc = &qdev->dbc[args->hdr.dbc_id]; 100262306a36Sopenharmony_ci rcu_id = srcu_read_lock(&dbc->ch_lock); 100362306a36Sopenharmony_ci if (dbc->usr != usr) { 100462306a36Sopenharmony_ci ret = -EINVAL; 100562306a36Sopenharmony_ci goto unlock_ch_srcu; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci ret = qaic_prepare_bo(qdev, bo, &args->hdr); 100962306a36Sopenharmony_ci if (ret) 101062306a36Sopenharmony_ci goto unlock_ch_srcu; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci ret = qaic_attach_slicing_bo(qdev, bo, &args->hdr, slice_ent); 101362306a36Sopenharmony_ci if (ret) 101462306a36Sopenharmony_ci goto unprepare_bo; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (args->hdr.dir == DMA_TO_DEVICE) 101762306a36Sopenharmony_ci dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, args->hdr.dir); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci bo->dbc = dbc; 102062306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 102162306a36Sopenharmony_ci drm_gem_object_put(obj); 102262306a36Sopenharmony_ci kfree(slice_ent); 102362306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 102462306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return 0; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ciunprepare_bo: 102962306a36Sopenharmony_ci qaic_unprepare_bo(qdev, bo); 103062306a36Sopenharmony_ciunlock_ch_srcu: 103162306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 103262306a36Sopenharmony_ciput_bo: 103362306a36Sopenharmony_ci drm_gem_object_put(obj); 103462306a36Sopenharmony_cifree_slice_ent: 103562306a36Sopenharmony_ci kfree(slice_ent); 103662306a36Sopenharmony_ciunlock_dev_srcu: 103762306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 103862306a36Sopenharmony_ciunlock_usr_srcu: 103962306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 104062306a36Sopenharmony_ci return ret; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, u32 dbc_id, 104462306a36Sopenharmony_ci u32 head, u32 *ptail) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id]; 104762306a36Sopenharmony_ci struct dbc_req *reqs = slice->reqs; 104862306a36Sopenharmony_ci u32 tail = *ptail; 104962306a36Sopenharmony_ci u32 avail; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci avail = head - tail; 105262306a36Sopenharmony_ci if (head <= tail) 105362306a36Sopenharmony_ci avail += dbc->nelem; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci --avail; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (avail < slice->nents) 105862306a36Sopenharmony_ci return -EAGAIN; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (tail + slice->nents > dbc->nelem) { 106162306a36Sopenharmony_ci avail = dbc->nelem - tail; 106262306a36Sopenharmony_ci avail = min_t(u32, avail, slice->nents); 106362306a36Sopenharmony_ci memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, 106462306a36Sopenharmony_ci sizeof(*reqs) * avail); 106562306a36Sopenharmony_ci reqs += avail; 106662306a36Sopenharmony_ci avail = slice->nents - avail; 106762306a36Sopenharmony_ci if (avail) 106862306a36Sopenharmony_ci memcpy(dbc->req_q_base, reqs, sizeof(*reqs) * avail); 106962306a36Sopenharmony_ci } else { 107062306a36Sopenharmony_ci memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, 107162306a36Sopenharmony_ci sizeof(*reqs) * slice->nents); 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci *ptail = (tail + slice->nents) % dbc->nelem; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci return 0; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/* 108062306a36Sopenharmony_ci * Based on the value of resize we may only need to transmit first_n 108162306a36Sopenharmony_ci * entries and the last entry, with last_bytes to send from the last entry. 108262306a36Sopenharmony_ci * Note that first_n could be 0. 108362306a36Sopenharmony_ci */ 108462306a36Sopenharmony_cistatic inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, 108562306a36Sopenharmony_ci u64 resize, u32 dbc_id, u32 head, u32 *ptail) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id]; 108862306a36Sopenharmony_ci struct dbc_req *reqs = slice->reqs; 108962306a36Sopenharmony_ci struct dbc_req *last_req; 109062306a36Sopenharmony_ci u32 tail = *ptail; 109162306a36Sopenharmony_ci u64 total_bytes; 109262306a36Sopenharmony_ci u64 last_bytes; 109362306a36Sopenharmony_ci u32 first_n; 109462306a36Sopenharmony_ci u32 avail; 109562306a36Sopenharmony_ci int ret; 109662306a36Sopenharmony_ci int i; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci avail = head - tail; 109962306a36Sopenharmony_ci if (head <= tail) 110062306a36Sopenharmony_ci avail += dbc->nelem; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci --avail; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci total_bytes = 0; 110562306a36Sopenharmony_ci for (i = 0; i < slice->nents; i++) { 110662306a36Sopenharmony_ci total_bytes += le32_to_cpu(reqs[i].len); 110762306a36Sopenharmony_ci if (total_bytes >= resize) 110862306a36Sopenharmony_ci break; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (total_bytes < resize) { 111262306a36Sopenharmony_ci /* User space should have used the full buffer path. */ 111362306a36Sopenharmony_ci ret = -EINVAL; 111462306a36Sopenharmony_ci return ret; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci first_n = i; 111862306a36Sopenharmony_ci last_bytes = i ? resize + le32_to_cpu(reqs[i].len) - total_bytes : resize; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (avail < (first_n + 1)) 112162306a36Sopenharmony_ci return -EAGAIN; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (first_n) { 112462306a36Sopenharmony_ci if (tail + first_n > dbc->nelem) { 112562306a36Sopenharmony_ci avail = dbc->nelem - tail; 112662306a36Sopenharmony_ci avail = min_t(u32, avail, first_n); 112762306a36Sopenharmony_ci memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, 112862306a36Sopenharmony_ci sizeof(*reqs) * avail); 112962306a36Sopenharmony_ci last_req = reqs + avail; 113062306a36Sopenharmony_ci avail = first_n - avail; 113162306a36Sopenharmony_ci if (avail) 113262306a36Sopenharmony_ci memcpy(dbc->req_q_base, last_req, sizeof(*reqs) * avail); 113362306a36Sopenharmony_ci } else { 113462306a36Sopenharmony_ci memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, 113562306a36Sopenharmony_ci sizeof(*reqs) * first_n); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* Copy over the last entry. Here we need to adjust len to the left over 114062306a36Sopenharmony_ci * size, and set src and dst to the entry it is copied to. 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci last_req = dbc->req_q_base + (tail + first_n) % dbc->nelem * get_dbc_req_elem_size(); 114362306a36Sopenharmony_ci memcpy(last_req, reqs + slice->nents - 1, sizeof(*reqs)); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* 114662306a36Sopenharmony_ci * last_bytes holds size of a DMA segment, maximum DMA segment size is 114762306a36Sopenharmony_ci * set to UINT_MAX by qaic and hence last_bytes can never exceed u32 114862306a36Sopenharmony_ci * range. So, by down sizing we are not corrupting the value. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci last_req->len = cpu_to_le32((u32)last_bytes); 115162306a36Sopenharmony_ci last_req->src_addr = reqs[first_n].src_addr; 115262306a36Sopenharmony_ci last_req->dest_addr = reqs[first_n].dest_addr; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci *ptail = (tail + first_n + 1) % dbc->nelem; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *file_priv, 116062306a36Sopenharmony_ci struct qaic_execute_entry *exec, unsigned int count, 116162306a36Sopenharmony_ci bool is_partial, struct dma_bridge_chan *dbc, u32 head, 116262306a36Sopenharmony_ci u32 *tail) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec; 116562306a36Sopenharmony_ci struct drm_gem_object *obj; 116662306a36Sopenharmony_ci struct bo_slice *slice; 116762306a36Sopenharmony_ci unsigned long flags; 116862306a36Sopenharmony_ci struct qaic_bo *bo; 116962306a36Sopenharmony_ci bool queued; 117062306a36Sopenharmony_ci int i, j; 117162306a36Sopenharmony_ci int ret; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci for (i = 0; i < count; i++) { 117462306a36Sopenharmony_ci /* 117562306a36Sopenharmony_ci * ref count will be decremented when the transfer of this 117662306a36Sopenharmony_ci * buffer is complete. It is inside dbc_irq_threaded_fn(). 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, 117962306a36Sopenharmony_ci is_partial ? pexec[i].handle : exec[i].handle); 118062306a36Sopenharmony_ci if (!obj) { 118162306a36Sopenharmony_ci ret = -ENOENT; 118262306a36Sopenharmony_ci goto failed_to_send_bo; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci bo = to_qaic_bo(obj); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (!bo->sliced) { 118862306a36Sopenharmony_ci ret = -EINVAL; 118962306a36Sopenharmony_ci goto failed_to_send_bo; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (is_partial && pexec[i].resize > bo->size) { 119362306a36Sopenharmony_ci ret = -EINVAL; 119462306a36Sopenharmony_ci goto failed_to_send_bo; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci spin_lock_irqsave(&dbc->xfer_lock, flags); 119862306a36Sopenharmony_ci queued = bo->queued; 119962306a36Sopenharmony_ci bo->queued = true; 120062306a36Sopenharmony_ci if (queued) { 120162306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 120262306a36Sopenharmony_ci ret = -EINVAL; 120362306a36Sopenharmony_ci goto failed_to_send_bo; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci bo->req_id = dbc->next_req_id++; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci list_for_each_entry(slice, &bo->slices, slice) { 120962306a36Sopenharmony_ci /* 121062306a36Sopenharmony_ci * If this slice does not fall under the given 121162306a36Sopenharmony_ci * resize then skip this slice and continue the loop 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_ci if (is_partial && pexec[i].resize && pexec[i].resize <= slice->offset) 121462306a36Sopenharmony_ci continue; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci for (j = 0; j < slice->nents; j++) 121762306a36Sopenharmony_ci slice->reqs[j].req_id = cpu_to_le16(bo->req_id); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* 122062306a36Sopenharmony_ci * If it is a partial execute ioctl call then check if 122162306a36Sopenharmony_ci * resize has cut this slice short then do a partial copy 122262306a36Sopenharmony_ci * else do complete copy 122362306a36Sopenharmony_ci */ 122462306a36Sopenharmony_ci if (is_partial && pexec[i].resize && 122562306a36Sopenharmony_ci pexec[i].resize < slice->offset + slice->size) 122662306a36Sopenharmony_ci ret = copy_partial_exec_reqs(qdev, slice, 122762306a36Sopenharmony_ci pexec[i].resize - slice->offset, 122862306a36Sopenharmony_ci dbc->id, head, tail); 122962306a36Sopenharmony_ci else 123062306a36Sopenharmony_ci ret = copy_exec_reqs(qdev, slice, dbc->id, head, tail); 123162306a36Sopenharmony_ci if (ret) { 123262306a36Sopenharmony_ci bo->queued = false; 123362306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 123462306a36Sopenharmony_ci goto failed_to_send_bo; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci reinit_completion(&bo->xfer_done); 123862306a36Sopenharmony_ci list_add_tail(&bo->xfer_list, &dbc->xfer_list); 123962306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 124062306a36Sopenharmony_ci dma_sync_sgtable_for_device(&qdev->pdev->dev, bo->sgt, bo->dir); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return 0; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cifailed_to_send_bo: 124662306a36Sopenharmony_ci if (likely(obj)) 124762306a36Sopenharmony_ci drm_gem_object_put(obj); 124862306a36Sopenharmony_ci for (j = 0; j < i; j++) { 124962306a36Sopenharmony_ci spin_lock_irqsave(&dbc->xfer_lock, flags); 125062306a36Sopenharmony_ci bo = list_last_entry(&dbc->xfer_list, struct qaic_bo, xfer_list); 125162306a36Sopenharmony_ci obj = &bo->base; 125262306a36Sopenharmony_ci bo->queued = false; 125362306a36Sopenharmony_ci list_del(&bo->xfer_list); 125462306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 125562306a36Sopenharmony_ci dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir); 125662306a36Sopenharmony_ci drm_gem_object_put(obj); 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci return ret; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic void update_profiling_data(struct drm_file *file_priv, 126262306a36Sopenharmony_ci struct qaic_execute_entry *exec, unsigned int count, 126362306a36Sopenharmony_ci bool is_partial, u64 received_ts, u64 submit_ts, u32 queue_level) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec; 126662306a36Sopenharmony_ci struct drm_gem_object *obj; 126762306a36Sopenharmony_ci struct qaic_bo *bo; 126862306a36Sopenharmony_ci int i; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * Since we already committed the BO to hardware, the only way 127362306a36Sopenharmony_ci * this should fail is a pending signal. We can't cancel the 127462306a36Sopenharmony_ci * submit to hardware, so we have to just skip the profiling 127562306a36Sopenharmony_ci * data. In case the signal is not fatal to the process, we 127662306a36Sopenharmony_ci * return success so that the user doesn't try to resubmit. 127762306a36Sopenharmony_ci */ 127862306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, 127962306a36Sopenharmony_ci is_partial ? pexec[i].handle : exec[i].handle); 128062306a36Sopenharmony_ci if (!obj) 128162306a36Sopenharmony_ci break; 128262306a36Sopenharmony_ci bo = to_qaic_bo(obj); 128362306a36Sopenharmony_ci bo->perf_stats.req_received_ts = received_ts; 128462306a36Sopenharmony_ci bo->perf_stats.req_submit_ts = submit_ts; 128562306a36Sopenharmony_ci bo->perf_stats.queue_level_before = queue_level; 128662306a36Sopenharmony_ci queue_level += bo->total_slice_nents; 128762306a36Sopenharmony_ci drm_gem_object_put(obj); 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistatic int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv, 129262306a36Sopenharmony_ci bool is_partial) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci struct qaic_execute *args = data; 129562306a36Sopenharmony_ci struct qaic_execute_entry *exec; 129662306a36Sopenharmony_ci struct dma_bridge_chan *dbc; 129762306a36Sopenharmony_ci int usr_rcu_id, qdev_rcu_id; 129862306a36Sopenharmony_ci struct qaic_device *qdev; 129962306a36Sopenharmony_ci struct qaic_user *usr; 130062306a36Sopenharmony_ci u8 __user *user_data; 130162306a36Sopenharmony_ci unsigned long n; 130262306a36Sopenharmony_ci u64 received_ts; 130362306a36Sopenharmony_ci u32 queue_level; 130462306a36Sopenharmony_ci u64 submit_ts; 130562306a36Sopenharmony_ci int rcu_id; 130662306a36Sopenharmony_ci u32 head; 130762306a36Sopenharmony_ci u32 tail; 130862306a36Sopenharmony_ci u64 size; 130962306a36Sopenharmony_ci int ret; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci received_ts = ktime_get_ns(); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec); 131462306a36Sopenharmony_ci n = (unsigned long)size * args->hdr.count; 131562306a36Sopenharmony_ci if (args->hdr.count == 0 || n / args->hdr.count != size) 131662306a36Sopenharmony_ci return -EINVAL; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci user_data = u64_to_user_ptr(args->data); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci exec = kcalloc(args->hdr.count, size, GFP_KERNEL); 132162306a36Sopenharmony_ci if (!exec) 132262306a36Sopenharmony_ci return -ENOMEM; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (copy_from_user(exec, user_data, n)) { 132562306a36Sopenharmony_ci ret = -EFAULT; 132662306a36Sopenharmony_ci goto free_exec; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci usr = file_priv->driver_priv; 133062306a36Sopenharmony_ci usr_rcu_id = srcu_read_lock(&usr->qddev_lock); 133162306a36Sopenharmony_ci if (!usr->qddev) { 133262306a36Sopenharmony_ci ret = -ENODEV; 133362306a36Sopenharmony_ci goto unlock_usr_srcu; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci qdev = usr->qddev->qdev; 133762306a36Sopenharmony_ci qdev_rcu_id = srcu_read_lock(&qdev->dev_lock); 133862306a36Sopenharmony_ci if (qdev->in_reset) { 133962306a36Sopenharmony_ci ret = -ENODEV; 134062306a36Sopenharmony_ci goto unlock_dev_srcu; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (args->hdr.dbc_id >= qdev->num_dbc) { 134462306a36Sopenharmony_ci ret = -EINVAL; 134562306a36Sopenharmony_ci goto unlock_dev_srcu; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci dbc = &qdev->dbc[args->hdr.dbc_id]; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci rcu_id = srcu_read_lock(&dbc->ch_lock); 135162306a36Sopenharmony_ci if (!dbc->usr || dbc->usr->handle != usr->handle) { 135262306a36Sopenharmony_ci ret = -EPERM; 135362306a36Sopenharmony_ci goto release_ch_rcu; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci head = readl(dbc->dbc_base + REQHP_OFF); 135762306a36Sopenharmony_ci tail = readl(dbc->dbc_base + REQTP_OFF); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (head == U32_MAX || tail == U32_MAX) { 136062306a36Sopenharmony_ci /* PCI link error */ 136162306a36Sopenharmony_ci ret = -ENODEV; 136262306a36Sopenharmony_ci goto release_ch_rcu; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci queue_level = head <= tail ? tail - head : dbc->nelem - (head - tail); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci ret = send_bo_list_to_device(qdev, file_priv, exec, args->hdr.count, is_partial, dbc, 136862306a36Sopenharmony_ci head, &tail); 136962306a36Sopenharmony_ci if (ret) 137062306a36Sopenharmony_ci goto release_ch_rcu; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* Finalize commit to hardware */ 137362306a36Sopenharmony_ci submit_ts = ktime_get_ns(); 137462306a36Sopenharmony_ci writel(tail, dbc->dbc_base + REQTP_OFF); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci update_profiling_data(file_priv, exec, args->hdr.count, is_partial, received_ts, 137762306a36Sopenharmony_ci submit_ts, queue_level); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (datapath_polling) 138062306a36Sopenharmony_ci schedule_work(&dbc->poll_work); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cirelease_ch_rcu: 138362306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 138462306a36Sopenharmony_ciunlock_dev_srcu: 138562306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 138662306a36Sopenharmony_ciunlock_usr_srcu: 138762306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 138862306a36Sopenharmony_cifree_exec: 138962306a36Sopenharmony_ci kfree(exec); 139062306a36Sopenharmony_ci return ret; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ciint qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci return __qaic_execute_bo_ioctl(dev, data, file_priv, false); 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ciint qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci return __qaic_execute_bo_ioctl(dev, data, file_priv, true); 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci/* 140462306a36Sopenharmony_ci * Our interrupt handling is a bit more complicated than a simple ideal, but 140562306a36Sopenharmony_ci * sadly necessary. 140662306a36Sopenharmony_ci * 140762306a36Sopenharmony_ci * Each dbc has a completion queue. Entries in the queue correspond to DMA 140862306a36Sopenharmony_ci * requests which the device has processed. The hardware already has a built 140962306a36Sopenharmony_ci * in irq mitigation. When the device puts an entry into the queue, it will 141062306a36Sopenharmony_ci * only trigger an interrupt if the queue was empty. Therefore, when adding 141162306a36Sopenharmony_ci * the Nth event to a non-empty queue, the hardware doesn't trigger an 141262306a36Sopenharmony_ci * interrupt. This means the host doesn't get additional interrupts signaling 141362306a36Sopenharmony_ci * the same thing - the queue has something to process. 141462306a36Sopenharmony_ci * This behavior can be overridden in the DMA request. 141562306a36Sopenharmony_ci * This means that when the host receives an interrupt, it is required to 141662306a36Sopenharmony_ci * drain the queue. 141762306a36Sopenharmony_ci * 141862306a36Sopenharmony_ci * This behavior is what NAPI attempts to accomplish, although we can't use 141962306a36Sopenharmony_ci * NAPI as we don't have a netdev. We use threaded irqs instead. 142062306a36Sopenharmony_ci * 142162306a36Sopenharmony_ci * However, there is a situation where the host drains the queue fast enough 142262306a36Sopenharmony_ci * that every event causes an interrupt. Typically this is not a problem as 142362306a36Sopenharmony_ci * the rate of events would be low. However, that is not the case with 142462306a36Sopenharmony_ci * lprnet for example. On an Intel Xeon D-2191 where we run 8 instances of 142562306a36Sopenharmony_ci * lprnet, the host receives roughly 80k interrupts per second from the device 142662306a36Sopenharmony_ci * (per /proc/interrupts). While NAPI documentation indicates the host should 142762306a36Sopenharmony_ci * just chug along, sadly that behavior causes instability in some hosts. 142862306a36Sopenharmony_ci * 142962306a36Sopenharmony_ci * Therefore, we implement an interrupt disable scheme similar to NAPI. The 143062306a36Sopenharmony_ci * key difference is that we will delay after draining the queue for a small 143162306a36Sopenharmony_ci * time to allow additional events to come in via polling. Using the above 143262306a36Sopenharmony_ci * lprnet workload, this reduces the number of interrupts processed from 143362306a36Sopenharmony_ci * ~80k/sec to about 64 in 5 minutes and appears to solve the system 143462306a36Sopenharmony_ci * instability. 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_ciirqreturn_t dbc_irq_handler(int irq, void *data) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci struct dma_bridge_chan *dbc = data; 143962306a36Sopenharmony_ci int rcu_id; 144062306a36Sopenharmony_ci u32 head; 144162306a36Sopenharmony_ci u32 tail; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci rcu_id = srcu_read_lock(&dbc->ch_lock); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (!dbc->usr) { 144662306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 144762306a36Sopenharmony_ci return IRQ_HANDLED; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci head = readl(dbc->dbc_base + RSPHP_OFF); 145162306a36Sopenharmony_ci if (head == U32_MAX) { /* PCI link error */ 145262306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 145362306a36Sopenharmony_ci return IRQ_NONE; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci tail = readl(dbc->dbc_base + RSPTP_OFF); 145762306a36Sopenharmony_ci if (tail == U32_MAX) { /* PCI link error */ 145862306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 145962306a36Sopenharmony_ci return IRQ_NONE; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (head == tail) { /* queue empty */ 146362306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 146462306a36Sopenharmony_ci return IRQ_NONE; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci disable_irq_nosync(irq); 146862306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 146962306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_civoid irq_polling_work(struct work_struct *work) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci struct dma_bridge_chan *dbc = container_of(work, struct dma_bridge_chan, poll_work); 147562306a36Sopenharmony_ci unsigned long flags; 147662306a36Sopenharmony_ci int rcu_id; 147762306a36Sopenharmony_ci u32 head; 147862306a36Sopenharmony_ci u32 tail; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci rcu_id = srcu_read_lock(&dbc->ch_lock); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci while (1) { 148362306a36Sopenharmony_ci if (dbc->qdev->in_reset) { 148462306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 148562306a36Sopenharmony_ci return; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci if (!dbc->usr) { 148862306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 148962306a36Sopenharmony_ci return; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci spin_lock_irqsave(&dbc->xfer_lock, flags); 149262306a36Sopenharmony_ci if (list_empty(&dbc->xfer_list)) { 149362306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 149462306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 149562306a36Sopenharmony_ci return; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci head = readl(dbc->dbc_base + RSPHP_OFF); 150062306a36Sopenharmony_ci if (head == U32_MAX) { /* PCI link error */ 150162306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 150262306a36Sopenharmony_ci return; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci tail = readl(dbc->dbc_base + RSPTP_OFF); 150662306a36Sopenharmony_ci if (tail == U32_MAX) { /* PCI link error */ 150762306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 150862306a36Sopenharmony_ci return; 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if (head != tail) { 151262306a36Sopenharmony_ci irq_wake_thread(dbc->irq, dbc); 151362306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 151462306a36Sopenharmony_ci return; 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci cond_resched(); 151862306a36Sopenharmony_ci usleep_range(datapath_poll_interval_us, 2 * datapath_poll_interval_us); 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ciirqreturn_t dbc_irq_threaded_fn(int irq, void *data) 152362306a36Sopenharmony_ci{ 152462306a36Sopenharmony_ci struct dma_bridge_chan *dbc = data; 152562306a36Sopenharmony_ci int event_count = NUM_EVENTS; 152662306a36Sopenharmony_ci int delay_count = NUM_DELAYS; 152762306a36Sopenharmony_ci struct qaic_device *qdev; 152862306a36Sopenharmony_ci struct qaic_bo *bo, *i; 152962306a36Sopenharmony_ci struct dbc_rsp *rsp; 153062306a36Sopenharmony_ci unsigned long flags; 153162306a36Sopenharmony_ci int rcu_id; 153262306a36Sopenharmony_ci u16 status; 153362306a36Sopenharmony_ci u16 req_id; 153462306a36Sopenharmony_ci u32 head; 153562306a36Sopenharmony_ci u32 tail; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci rcu_id = srcu_read_lock(&dbc->ch_lock); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci head = readl(dbc->dbc_base + RSPHP_OFF); 154062306a36Sopenharmony_ci if (head == U32_MAX) /* PCI link error */ 154162306a36Sopenharmony_ci goto error_out; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci qdev = dbc->qdev; 154462306a36Sopenharmony_ciread_fifo: 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (!event_count) { 154762306a36Sopenharmony_ci event_count = NUM_EVENTS; 154862306a36Sopenharmony_ci cond_resched(); 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci /* 155262306a36Sopenharmony_ci * if this channel isn't assigned or gets unassigned during processing 155362306a36Sopenharmony_ci * we have nothing further to do 155462306a36Sopenharmony_ci */ 155562306a36Sopenharmony_ci if (!dbc->usr) 155662306a36Sopenharmony_ci goto error_out; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci tail = readl(dbc->dbc_base + RSPTP_OFF); 155962306a36Sopenharmony_ci if (tail == U32_MAX) /* PCI link error */ 156062306a36Sopenharmony_ci goto error_out; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (head == tail) { /* queue empty */ 156362306a36Sopenharmony_ci if (delay_count) { 156462306a36Sopenharmony_ci --delay_count; 156562306a36Sopenharmony_ci usleep_range(100, 200); 156662306a36Sopenharmony_ci goto read_fifo; /* check for a new event */ 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci goto normal_out; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci delay_count = NUM_DELAYS; 157262306a36Sopenharmony_ci while (head != tail) { 157362306a36Sopenharmony_ci if (!event_count) 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci --event_count; 157662306a36Sopenharmony_ci rsp = dbc->rsp_q_base + head * sizeof(*rsp); 157762306a36Sopenharmony_ci req_id = le16_to_cpu(rsp->req_id); 157862306a36Sopenharmony_ci status = le16_to_cpu(rsp->status); 157962306a36Sopenharmony_ci if (status) 158062306a36Sopenharmony_ci pci_dbg(qdev->pdev, "req_id %d failed with status %d\n", req_id, status); 158162306a36Sopenharmony_ci spin_lock_irqsave(&dbc->xfer_lock, flags); 158262306a36Sopenharmony_ci /* 158362306a36Sopenharmony_ci * A BO can receive multiple interrupts, since a BO can be 158462306a36Sopenharmony_ci * divided into multiple slices and a buffer receives as many 158562306a36Sopenharmony_ci * interrupts as slices. So until it receives interrupts for 158662306a36Sopenharmony_ci * all the slices we cannot mark that buffer complete. 158762306a36Sopenharmony_ci */ 158862306a36Sopenharmony_ci list_for_each_entry_safe(bo, i, &dbc->xfer_list, xfer_list) { 158962306a36Sopenharmony_ci if (bo->req_id == req_id) 159062306a36Sopenharmony_ci bo->nr_slice_xfer_done++; 159162306a36Sopenharmony_ci else 159262306a36Sopenharmony_ci continue; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (bo->nr_slice_xfer_done < bo->nr_slice) 159562306a36Sopenharmony_ci break; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* 159862306a36Sopenharmony_ci * At this point we have received all the interrupts for 159962306a36Sopenharmony_ci * BO, which means BO execution is complete. 160062306a36Sopenharmony_ci */ 160162306a36Sopenharmony_ci dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir); 160262306a36Sopenharmony_ci bo->nr_slice_xfer_done = 0; 160362306a36Sopenharmony_ci bo->queued = false; 160462306a36Sopenharmony_ci list_del(&bo->xfer_list); 160562306a36Sopenharmony_ci bo->perf_stats.req_processed_ts = ktime_get_ns(); 160662306a36Sopenharmony_ci complete_all(&bo->xfer_done); 160762306a36Sopenharmony_ci drm_gem_object_put(&bo->base); 160862306a36Sopenharmony_ci break; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 161162306a36Sopenharmony_ci head = (head + 1) % dbc->nelem; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci /* 161562306a36Sopenharmony_ci * Update the head pointer of response queue and let the device know 161662306a36Sopenharmony_ci * that we have consumed elements from the queue. 161762306a36Sopenharmony_ci */ 161862306a36Sopenharmony_ci writel(head, dbc->dbc_base + RSPHP_OFF); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* elements might have been put in the queue while we were processing */ 162162306a36Sopenharmony_ci goto read_fifo; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cinormal_out: 162462306a36Sopenharmony_ci if (likely(!datapath_polling)) 162562306a36Sopenharmony_ci enable_irq(irq); 162662306a36Sopenharmony_ci else 162762306a36Sopenharmony_ci schedule_work(&dbc->poll_work); 162862306a36Sopenharmony_ci /* checking the fifo and enabling irqs is a race, missed event check */ 162962306a36Sopenharmony_ci tail = readl(dbc->dbc_base + RSPTP_OFF); 163062306a36Sopenharmony_ci if (tail != U32_MAX && head != tail) { 163162306a36Sopenharmony_ci if (likely(!datapath_polling)) 163262306a36Sopenharmony_ci disable_irq_nosync(irq); 163362306a36Sopenharmony_ci goto read_fifo; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 163662306a36Sopenharmony_ci return IRQ_HANDLED; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cierror_out: 163962306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 164062306a36Sopenharmony_ci if (likely(!datapath_polling)) 164162306a36Sopenharmony_ci enable_irq(irq); 164262306a36Sopenharmony_ci else 164362306a36Sopenharmony_ci schedule_work(&dbc->poll_work); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return IRQ_HANDLED; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ciint qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci struct qaic_wait *args = data; 165162306a36Sopenharmony_ci int usr_rcu_id, qdev_rcu_id; 165262306a36Sopenharmony_ci struct dma_bridge_chan *dbc; 165362306a36Sopenharmony_ci struct drm_gem_object *obj; 165462306a36Sopenharmony_ci struct qaic_device *qdev; 165562306a36Sopenharmony_ci unsigned long timeout; 165662306a36Sopenharmony_ci struct qaic_user *usr; 165762306a36Sopenharmony_ci struct qaic_bo *bo; 165862306a36Sopenharmony_ci int rcu_id; 165962306a36Sopenharmony_ci int ret; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (args->pad != 0) 166262306a36Sopenharmony_ci return -EINVAL; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci usr = file_priv->driver_priv; 166562306a36Sopenharmony_ci usr_rcu_id = srcu_read_lock(&usr->qddev_lock); 166662306a36Sopenharmony_ci if (!usr->qddev) { 166762306a36Sopenharmony_ci ret = -ENODEV; 166862306a36Sopenharmony_ci goto unlock_usr_srcu; 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci qdev = usr->qddev->qdev; 167262306a36Sopenharmony_ci qdev_rcu_id = srcu_read_lock(&qdev->dev_lock); 167362306a36Sopenharmony_ci if (qdev->in_reset) { 167462306a36Sopenharmony_ci ret = -ENODEV; 167562306a36Sopenharmony_ci goto unlock_dev_srcu; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (args->dbc_id >= qdev->num_dbc) { 167962306a36Sopenharmony_ci ret = -EINVAL; 168062306a36Sopenharmony_ci goto unlock_dev_srcu; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci dbc = &qdev->dbc[args->dbc_id]; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci rcu_id = srcu_read_lock(&dbc->ch_lock); 168662306a36Sopenharmony_ci if (dbc->usr != usr) { 168762306a36Sopenharmony_ci ret = -EPERM; 168862306a36Sopenharmony_ci goto unlock_ch_srcu; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, args->handle); 169262306a36Sopenharmony_ci if (!obj) { 169362306a36Sopenharmony_ci ret = -ENOENT; 169462306a36Sopenharmony_ci goto unlock_ch_srcu; 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci bo = to_qaic_bo(obj); 169862306a36Sopenharmony_ci timeout = args->timeout ? args->timeout : wait_exec_default_timeout_ms; 169962306a36Sopenharmony_ci timeout = msecs_to_jiffies(timeout); 170062306a36Sopenharmony_ci ret = wait_for_completion_interruptible_timeout(&bo->xfer_done, timeout); 170162306a36Sopenharmony_ci if (!ret) { 170262306a36Sopenharmony_ci ret = -ETIMEDOUT; 170362306a36Sopenharmony_ci goto put_obj; 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci if (ret > 0) 170662306a36Sopenharmony_ci ret = 0; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci if (!dbc->usr) 170962306a36Sopenharmony_ci ret = -EPERM; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ciput_obj: 171262306a36Sopenharmony_ci drm_gem_object_put(obj); 171362306a36Sopenharmony_ciunlock_ch_srcu: 171462306a36Sopenharmony_ci srcu_read_unlock(&dbc->ch_lock, rcu_id); 171562306a36Sopenharmony_ciunlock_dev_srcu: 171662306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 171762306a36Sopenharmony_ciunlock_usr_srcu: 171862306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 171962306a36Sopenharmony_ci return ret; 172062306a36Sopenharmony_ci} 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ciint qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 172362306a36Sopenharmony_ci{ 172462306a36Sopenharmony_ci struct qaic_perf_stats_entry *ent = NULL; 172562306a36Sopenharmony_ci struct qaic_perf_stats *args = data; 172662306a36Sopenharmony_ci int usr_rcu_id, qdev_rcu_id; 172762306a36Sopenharmony_ci struct drm_gem_object *obj; 172862306a36Sopenharmony_ci struct qaic_device *qdev; 172962306a36Sopenharmony_ci struct qaic_user *usr; 173062306a36Sopenharmony_ci struct qaic_bo *bo; 173162306a36Sopenharmony_ci int ret, i; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci usr = file_priv->driver_priv; 173462306a36Sopenharmony_ci usr_rcu_id = srcu_read_lock(&usr->qddev_lock); 173562306a36Sopenharmony_ci if (!usr->qddev) { 173662306a36Sopenharmony_ci ret = -ENODEV; 173762306a36Sopenharmony_ci goto unlock_usr_srcu; 173862306a36Sopenharmony_ci } 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci qdev = usr->qddev->qdev; 174162306a36Sopenharmony_ci qdev_rcu_id = srcu_read_lock(&qdev->dev_lock); 174262306a36Sopenharmony_ci if (qdev->in_reset) { 174362306a36Sopenharmony_ci ret = -ENODEV; 174462306a36Sopenharmony_ci goto unlock_dev_srcu; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (args->hdr.dbc_id >= qdev->num_dbc) { 174862306a36Sopenharmony_ci ret = -EINVAL; 174962306a36Sopenharmony_ci goto unlock_dev_srcu; 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci ent = kcalloc(args->hdr.count, sizeof(*ent), GFP_KERNEL); 175362306a36Sopenharmony_ci if (!ent) { 175462306a36Sopenharmony_ci ret = -EINVAL; 175562306a36Sopenharmony_ci goto unlock_dev_srcu; 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci ret = copy_from_user(ent, u64_to_user_ptr(args->data), args->hdr.count * sizeof(*ent)); 175962306a36Sopenharmony_ci if (ret) { 176062306a36Sopenharmony_ci ret = -EFAULT; 176162306a36Sopenharmony_ci goto free_ent; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci for (i = 0; i < args->hdr.count; i++) { 176562306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, ent[i].handle); 176662306a36Sopenharmony_ci if (!obj) { 176762306a36Sopenharmony_ci ret = -ENOENT; 176862306a36Sopenharmony_ci goto free_ent; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci bo = to_qaic_bo(obj); 177162306a36Sopenharmony_ci /* 177262306a36Sopenharmony_ci * perf stats ioctl is called before wait ioctl is complete then 177362306a36Sopenharmony_ci * the latency information is invalid. 177462306a36Sopenharmony_ci */ 177562306a36Sopenharmony_ci if (bo->perf_stats.req_processed_ts < bo->perf_stats.req_submit_ts) { 177662306a36Sopenharmony_ci ent[i].device_latency_us = 0; 177762306a36Sopenharmony_ci } else { 177862306a36Sopenharmony_ci ent[i].device_latency_us = div_u64((bo->perf_stats.req_processed_ts - 177962306a36Sopenharmony_ci bo->perf_stats.req_submit_ts), 1000); 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci ent[i].submit_latency_us = div_u64((bo->perf_stats.req_submit_ts - 178262306a36Sopenharmony_ci bo->perf_stats.req_received_ts), 1000); 178362306a36Sopenharmony_ci ent[i].queue_level_before = bo->perf_stats.queue_level_before; 178462306a36Sopenharmony_ci ent[i].num_queue_element = bo->total_slice_nents; 178562306a36Sopenharmony_ci drm_gem_object_put(obj); 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(args->data), ent, args->hdr.count * sizeof(*ent))) 178962306a36Sopenharmony_ci ret = -EFAULT; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cifree_ent: 179262306a36Sopenharmony_ci kfree(ent); 179362306a36Sopenharmony_ciunlock_dev_srcu: 179462306a36Sopenharmony_ci srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); 179562306a36Sopenharmony_ciunlock_usr_srcu: 179662306a36Sopenharmony_ci srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); 179762306a36Sopenharmony_ci return ret; 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci unsigned long flags; 180362306a36Sopenharmony_ci struct qaic_bo *bo; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci spin_lock_irqsave(&dbc->xfer_lock, flags); 180662306a36Sopenharmony_ci while (!list_empty(&dbc->xfer_list)) { 180762306a36Sopenharmony_ci bo = list_first_entry(&dbc->xfer_list, typeof(*bo), xfer_list); 180862306a36Sopenharmony_ci bo->queued = false; 180962306a36Sopenharmony_ci list_del(&bo->xfer_list); 181062306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 181162306a36Sopenharmony_ci dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir); 181262306a36Sopenharmony_ci complete_all(&bo->xfer_done); 181362306a36Sopenharmony_ci drm_gem_object_put(&bo->base); 181462306a36Sopenharmony_ci spin_lock_irqsave(&dbc->xfer_lock, flags); 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci spin_unlock_irqrestore(&dbc->xfer_lock, flags); 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ciint disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci if (!qdev->dbc[dbc_id].usr || qdev->dbc[dbc_id].usr->handle != usr->handle) 182262306a36Sopenharmony_ci return -EPERM; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci qdev->dbc[dbc_id].usr = NULL; 182562306a36Sopenharmony_ci synchronize_srcu(&qdev->dbc[dbc_id].ch_lock); 182662306a36Sopenharmony_ci return 0; 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci/** 183062306a36Sopenharmony_ci * enable_dbc - Enable the DBC. DBCs are disabled by removing the context of 183162306a36Sopenharmony_ci * user. Add user context back to DBC to enable it. This function trusts the 183262306a36Sopenharmony_ci * DBC ID passed and expects the DBC to be disabled. 183362306a36Sopenharmony_ci * @qdev: Qranium device handle 183462306a36Sopenharmony_ci * @dbc_id: ID of the DBC 183562306a36Sopenharmony_ci * @usr: User context 183662306a36Sopenharmony_ci */ 183762306a36Sopenharmony_civoid enable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr) 183862306a36Sopenharmony_ci{ 183962306a36Sopenharmony_ci qdev->dbc[dbc_id].usr = usr; 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_civoid wakeup_dbc(struct qaic_device *qdev, u32 dbc_id) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id]; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci dbc->usr = NULL; 184762306a36Sopenharmony_ci empty_xfer_list(qdev, dbc); 184862306a36Sopenharmony_ci synchronize_srcu(&dbc->ch_lock); 184962306a36Sopenharmony_ci /* 185062306a36Sopenharmony_ci * Threads holding channel lock, may add more elements in the xfer_list. 185162306a36Sopenharmony_ci * Flush out these elements from xfer_list. 185262306a36Sopenharmony_ci */ 185362306a36Sopenharmony_ci empty_xfer_list(qdev, dbc); 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_civoid release_dbc(struct qaic_device *qdev, u32 dbc_id) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci struct bo_slice *slice, *slice_temp; 185962306a36Sopenharmony_ci struct qaic_bo *bo, *bo_temp; 186062306a36Sopenharmony_ci struct dma_bridge_chan *dbc; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci dbc = &qdev->dbc[dbc_id]; 186362306a36Sopenharmony_ci if (!dbc->in_use) 186462306a36Sopenharmony_ci return; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci wakeup_dbc(qdev, dbc_id); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci dma_free_coherent(&qdev->pdev->dev, dbc->total_size, dbc->req_q_base, dbc->dma_addr); 186962306a36Sopenharmony_ci dbc->total_size = 0; 187062306a36Sopenharmony_ci dbc->req_q_base = NULL; 187162306a36Sopenharmony_ci dbc->dma_addr = 0; 187262306a36Sopenharmony_ci dbc->nelem = 0; 187362306a36Sopenharmony_ci dbc->usr = NULL; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci list_for_each_entry_safe(bo, bo_temp, &dbc->bo_lists, bo_list) { 187662306a36Sopenharmony_ci list_for_each_entry_safe(slice, slice_temp, &bo->slices, slice) 187762306a36Sopenharmony_ci kref_put(&slice->ref_count, free_slice); 187862306a36Sopenharmony_ci bo->sliced = false; 187962306a36Sopenharmony_ci INIT_LIST_HEAD(&bo->slices); 188062306a36Sopenharmony_ci bo->total_slice_nents = 0; 188162306a36Sopenharmony_ci bo->dir = 0; 188262306a36Sopenharmony_ci bo->dbc = NULL; 188362306a36Sopenharmony_ci bo->nr_slice = 0; 188462306a36Sopenharmony_ci bo->nr_slice_xfer_done = 0; 188562306a36Sopenharmony_ci bo->queued = false; 188662306a36Sopenharmony_ci bo->req_id = 0; 188762306a36Sopenharmony_ci init_completion(&bo->xfer_done); 188862306a36Sopenharmony_ci complete_all(&bo->xfer_done); 188962306a36Sopenharmony_ci list_del(&bo->bo_list); 189062306a36Sopenharmony_ci bo->perf_stats.req_received_ts = 0; 189162306a36Sopenharmony_ci bo->perf_stats.req_submit_ts = 0; 189262306a36Sopenharmony_ci bo->perf_stats.req_processed_ts = 0; 189362306a36Sopenharmony_ci bo->perf_stats.queue_level_before = 0; 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci dbc->in_use = false; 189762306a36Sopenharmony_ci wake_up(&dbc->dbc_release); 189862306a36Sopenharmony_ci} 1899