[v3,2/4] dmaengine: dw-edma: Create a new dw_edma_core_ops structure to abstract controller operation
Message ID | 20230213132411.65524-3-cai.huoqing@linux.dev |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp2349030wrn; Mon, 13 Feb 2023 05:36:43 -0800 (PST) X-Google-Smtp-Source: AK7set/GZF7dowfcbltpbCBJwJ6DRNE+xtp1sOXO0a/jCZJ4f8uUO5WZQWSLEKB2zT/gG83zX4Hb X-Received: by 2002:a17:90b:1d81:b0:22c:d44:5669 with SMTP id pf1-20020a17090b1d8100b0022c0d445669mr8624841pjb.22.1676295403564; Mon, 13 Feb 2023 05:36:43 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676295403; cv=none; d=google.com; s=arc-20160816; b=UGeSOR/PRP7H1Jk4u7Rn2li2WC9A19Kk3FLumT/WPQ3IbV9dl9TlzcG5y9M4GvuK7b 0Pd54Fr4XiTuD+sgX/M/PAq2D8gP6rfBQubDgHMTJepEM8ez8nKCY9c4ANVlenIuPZ0T 7ogf4x75DjZ8mRYZeML5O1hqifd9q6h1b70ppBVwiKQzx2VhvjqxfViuw1GkkmsEdCl3 MNk5LIRIuBzdrTb8WcJqxiuCqpR8rCDpGoPI7kdE9tBEPjMlxd693dQTYtYox5RLp57M qnpN7YBSPHz9B/IbFgMvTjSWUqjYAuOoNmk3/QOJe6tBwsMXGR8IPsLrgaifx1QlbYSC RvNw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=3EqMwrGOz+jm0mszPNZB0XW9wyavpaseOxhiaj+8Q8o=; b=Fpt7bo1xBxUnuqEaYvZueta7goIrozAg/WaEp/rKEi2oEKJTD9blaHN/rUbHl/DIq3 thbRtY8WzZm5577Zhf1H0nfkPmOgLwJznmJDjvjT6OU8OIgwWYIfFXhTaieI1ejVPc13 3u9jqyc27OtyBzf1sJVobNwraA0DCGeXrk3gdGKauzr+kjChSvcdRVRaGAUe2fTbdjd1 duzdj5s/CHEINrB1HILXZilgP5iZa5XOyW8/APAFw7lQv9xVfsVr4/qkMdPgoe18VCzL PoY3Bw6XqMInzEHY/SXc26JUffiEex1Lb1C94PpU/JfjFZOAkoYsMYAEW6K31belViKl SSTQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linux.dev header.s=key1 header.b=sjfngHBj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.dev Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id m188-20020a633fc5000000b004cf4fcf9c15si2633366pga.52.2023.02.13.05.36.31; Mon, 13 Feb 2023 05:36:43 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@linux.dev header.s=key1 header.b=sjfngHBj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.dev Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229630AbjBMNbt (ORCPT <rfc822;tebrre53rla2o@gmail.com> + 99 others); Mon, 13 Feb 2023 08:31:49 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36892 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229877AbjBMNbr (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Mon, 13 Feb 2023 08:31:47 -0500 Received: from out-232.mta0.migadu.com (out-232.mta0.migadu.com [IPv6:2001:41d0:1004:224b::e8]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2F2E01B55F for <linux-kernel@vger.kernel.org>; Mon, 13 Feb 2023 05:31:33 -0800 (PST) X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1676294676; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3EqMwrGOz+jm0mszPNZB0XW9wyavpaseOxhiaj+8Q8o=; b=sjfngHBjx9M7sAOivsHCGGpNUGP/PcSlzQK5OaZEaLbsdz9bJNmBJr0a0GTuIQKeUSM5pj STJZEotmcJngssNGqJElukgSvbneuvPtHjVe2NCxEUNcgyKhZ2kR7AQeoqexWL10RwM+Ba RflxtUK+ZZHeb92d+E/N7itISHXPxT4= From: Cai Huoqing <cai.huoqing@linux.dev> To: Sergey.Semin@baikalelectronics.ru Cc: Cai huoqing <cai.huoqing@linux.dev>, Gustavo Pimentel <gustavo.pimentel@synopsys.com>, Vinod Koul <vkoul@kernel.org>, Jingoo Han <jingoohan1@gmail.com>, Lorenzo Pieralisi <lpieralisi@kernel.org>, =?utf-8?q?Krzysztof_Wilczy=C5=84?= =?utf-8?q?ski?= <kw@linux.com>, Rob Herring <robh@kernel.org>, Bjorn Helgaas <bhelgaas@google.com>, linux-kernel@vger.kernel.org, dmaengine@vger.kernel.org, linux-pci@vger.kernel.org Subject: [PATCH v3 2/4] dmaengine: dw-edma: Create a new dw_edma_core_ops structure to abstract controller operation Date: Mon, 13 Feb 2023 21:24:07 +0800 Message-Id: <20230213132411.65524-3-cai.huoqing@linux.dev> In-Reply-To: <20230213132411.65524-1-cai.huoqing@linux.dev> References: <20230213132411.65524-1-cai.huoqing@linux.dev> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1757723129204904010?= X-GMAIL-MSGID: =?utf-8?q?1757723129204904010?= |
Series |
dmaengine: dw-edma: Add support for native HDMA
|
|
Commit Message
Cai Huoqing
Feb. 13, 2023, 1:24 p.m. UTC
From: Cai huoqing <cai.huoqing@linux.dev> The structure dw_edma_core_ops has a set of the pointers abstracting out the DW eDMA vX and DW HDMA Native controllers. And use dw_edma_v0_core_register to set up operation. Signed-off-by: Cai huoqing <cai.huoqing@linux.dev> --- drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------ drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++ drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++---- drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +--- 4 files changed, 188 insertions(+), 69 deletions(-)
Comments
On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote: > From: Cai huoqing <cai.huoqing@linux.dev> > > The structure dw_edma_core_ops has a set of the pointers > abstracting out the DW eDMA vX and DW HDMA Native controllers. > And use dw_edma_v0_core_register to set up operation. > > Signed-off-by: Cai huoqing <cai.huoqing@linux.dev> > --- > drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------ > drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++ > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++---- > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +--- > 4 files changed, 188 insertions(+), 69 deletions(-) > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c > index 1906a836f0aa..c171809beba1 100644 > --- a/drivers/dma/dw-edma/dw-edma-core.c > +++ b/drivers/dma/dw-edma/dw-edma-core.c > @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) > > static void dw_edma_start_transfer(struct dw_edma_chan *chan) > { > + struct dw_edma *dw = chan->dw; > struct dw_edma_chunk *child; > struct dw_edma_desc *desc; > struct virt_dma_desc *vd; > @@ -200,7 +201,7 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan) > if (!child) > return; > > - dw_edma_v0_core_start(child, !desc->xfer_sz); > + dw_edma_core_start(dw, child, !desc->xfer_sz); > desc->xfer_sz += child->ll_region.sz; > dw_edma_free_burst(child); > list_del(&child->list); > @@ -285,7 +286,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) > chan->configured = false; > } else if (chan->status == EDMA_ST_IDLE) { > chan->configured = false; > - } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { > + } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { > /* > * The channel is in a false BUSY state, probably didn't > * receive or lost an interrupt > @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, > return dw_edma_device_transfer(&xfer); > } > > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > +void dw_edma_done_interrupt(struct dw_edma_chan *chan) > { > struct dw_edma_desc *desc; > struct virt_dma_desc *vd; > unsigned long flags; > > - dw_edma_v0_core_clear_done_int(chan); > + dw_edma_core_clear_done_int(chan); <+ | As I already said in v2 just call this method + in the IRQ-handle loop within the IP-core-specific module (see further for details). Thus you'll be able to drop the dw_edma_core_ops.clear_done_int pointer. The same concerns the dw_edma_core_ops.clear_abort_int callback. > > spin_lock_irqsave(&chan->vc.lock, flags); > vd = vchan_next_desc(&chan->vc); > @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > spin_unlock_irqrestore(&chan->vc.lock, flags); > } > > -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > { > struct virt_dma_desc *vd; > unsigned long flags; > > - dw_edma_v0_core_clear_abort_int(chan); > + dw_edma_core_clear_abort_int(chan); ditto > > spin_lock_irqsave(&chan->vc.lock, flags); > vd = vchan_next_desc(&chan->vc); > @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > { > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; > struct dw_edma_irq *dw_irq = data; > - struct dw_edma *dw = dw_irq->dw; > - unsigned long total, pos, val; > - unsigned long off; > - u32 mask; > - > - if (write) { > - total = dw->wr_ch_cnt; > - off = 0; > - mask = dw_irq->wr_mask; > - } else { > - total = dw->rd_ch_cnt; > - off = dw->wr_ch_cnt; > - mask = dw_irq->rd_mask; > - } > - > - val = dw_edma_v0_core_status_done_int(dw, write ? > - EDMA_DIR_WRITE : > - EDMA_DIR_READ); > - val &= mask; > - for_each_set_bit(pos, &val, total) { > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > - > - dw_edma_done_interrupt(chan); > - } > - > - val = dw_edma_v0_core_status_abort_int(dw, write ? > - EDMA_DIR_WRITE : > - EDMA_DIR_READ); > - val &= mask; > - for_each_set_bit(pos, &val, total) { > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > > - dw_edma_abort_interrupt(chan); > - } > + dw_edma_core_done_handle(dw_irq, dir); > + dw_edma_core_abort_handle(dw_irq, dir); > > return IRQ_HANDLED; > } > @@ -806,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) > > vchan_init(&chan->vc, dma); > > - dw_edma_v0_core_device_config(chan); > + dw_edma_core_ch_config(chan); > } > > /* Set DMA channel capabilities */ > @@ -951,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > dw->chip = chip; > > + dw_edma_v0_core_register(dw); > + > raw_spin_lock_init(&dw->lock); > > dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); > + dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); > dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); > > dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); > + dw_edma_core_ch_count(dw, EDMA_DIR_READ)); > dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); > > if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) > @@ -977,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > dev_name(chip->dev)); > > /* Disable eDMA, only to establish the ideal initial conditions */ > - dw_edma_v0_core_off(dw); > + dw_edma_core_off(dw); > > /* Request IRQs */ > err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); > @@ -990,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > goto err_irq_free; > > /* Turn debugfs on */ > - dw_edma_v0_core_debugfs_on(dw); > + dw_edma_core_debugfs_on(dw); > > chip->dw = dw; > > @@ -1016,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) > return -ENODEV; > > /* Disable eDMA */ > - dw_edma_v0_core_off(dw); > + dw_edma_core_off(dw); > > /* Free irqs */ > for (i = (dw->nr_irqs - 1); i >= 0; i--) > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h > index 0ab2b6dba880..2555ae0e1b8b 100644 > --- a/drivers/dma/dw-edma/dw-edma-core.h > +++ b/drivers/dma/dw-edma/dw-edma-core.h > @@ -111,6 +111,23 @@ struct dw_edma { > raw_spinlock_t lock; /* Only for legacy */ > > struct dw_edma_chip *chip; > + > + const struct dw_edma_core_ops *core; > +}; > + > +struct dw_edma_core_ops { > + void (*off)(struct dw_edma *dw); > + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); > + enum dma_status (*ch_status)(struct dw_edma_chan *chan); > + void (*clear_done_int)(struct dw_edma_chan *chan); > + void (*clear_abort_int)(struct dw_edma_chan *chan); As I noted above these methods could be called locally from the corresponding IP-core-specific module. Thus you won't need to define the callbacks. It shall simplify the interface a bit. > + void (*done_handle)(struct dw_edma_irq *dw_irq, > + enum dw_edma_dir dir); > + void (*abort_handle)(struct dw_edma_irq *dw_irq, > + enum dw_edma_dir dir); Seeing both of these callbacks (done,abort handlers) are called from the same IRQ-handler what about converting them to a single callback: irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir); ? The eDMA channels IRQ-handlers would look like this: --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -692,12 +692,16 @@ static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) { - return dw_edma_interrupt(irq, data, true); + struct dw_edma_irq *dw_irq = data; + + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE); } static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) { - return dw_edma_interrupt(irq, data, false); + struct dw_edma_irq *dw_irq = data; + + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ); } static irqreturn_t dw_edma_interrupt_common(int irq, void *data) Therefore you'll be able to completely drop the static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) function from drivers/dma/dw-edma/dw-edma-core.c. The function body would be almost fully moved to the handle_int() function of the eDMA/HDMA core-specific ops. For instance in case of the DW eDMA it would look like this: static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq, enum dw_edma_dir dir) { struct dw_edma *dw = dw_irq->dw; unsigned long total, pos, val; unsigned long off; u32 mask; if (dir == EDMA_DIR_WRITE) { total = dw->wr_ch_cnt; off = 0; mask = dw_irq->wr_mask; } else { total = dw->rd_ch_cnt; off = dw->wr_ch_cnt; mask = dw_irq->rd_mask; } val = dw_edma_v0_core_status_done_int(dw, dir) & mask; for_each_set_bit(pos, &val, total) { struct dw_edma_chan *chan = &dw->chan[pos + off]; dw_edma_v0_core_clear_done_int(chan); dw_edma_done_interrupt(chan); } val = dw_edma_v0_core_status_abort_int(dw, dir) & mask; for_each_set_bit(pos, &val, total) { struct dw_edma_chan *chan = &dw->chan[pos + off]; dw_edma_v0_core_clear_abort_int(chan); dw_edma_abort_interrupt(chan); } return IRQ_HANDLED; } What do you think? > + void (*start)(struct dw_edma_chunk *chunk, bool first); > + void (*ch_config)(struct dw_edma_chan *chan); > + void (*debugfs_on)(struct dw_edma *dw); > }; > > struct dw_edma_sg { > @@ -136,6 +153,9 @@ struct dw_edma_transfer { > enum dw_edma_xfer_type type; > }; > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan); > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan); > + > static inline > struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc) > { > @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan) > return vc2dw_edma_chan(to_virt_chan(dchan)); > } > > +static inline > +void dw_edma_core_off(struct dw_edma *dw) > +{ > + dw->core->off(dw); > +} > + > +static inline > +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > +{ > + return dw->core->ch_count(dw, dir); > +} > + > +static inline > +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + return dw->core->ch_status(chan); > +} > + > +static inline > +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + dw->core->clear_done_int(chan); > +} > + > +static inline > +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + dw->core->clear_abort_int(chan); > +} > + > +static inline > +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + > + dw->core->done_handle(dw_irq, dir); > +} > + > +static inline > +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + > + dw->core->abort_handle(dw_irq, dir); > +} As I suggested above these two methods can be replaced with a single one: dw_edma_core_handle_int(...). > + > +static inline > +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) > +{ > + dw->core->start(chunk, first); > +} > + > +static inline > +void dw_edma_core_ch_config(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + dw->core->ch_config(chan); > +} > + > +static inline > +void dw_edma_core_debugfs_on(struct dw_edma *dw) > +{ > + dw->core->debugfs_on(dw); > +} > + > #endif /* _DW_EDMA_CORE_H */ > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c > index 72e79a0c0a4e..73dcf72ba3a9 100644 > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c > @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, > readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) > > /* eDMA management callbacks */ > -void dw_edma_v0_core_off(struct dw_edma *dw) > +static void dw_edma_v0_core_off(struct dw_edma *dw) > { > SET_BOTH_32(dw, int_mask, > EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); > @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw) > SET_BOTH_32(dw, engine_en, 0); > } > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > { > u32 num_ch; > > @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > return (u16)num_ch; > } > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > u32 tmp; > @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > return DMA_ERROR; > } > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > > @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); > } > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > > @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); > } > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > { > return FIELD_GET(EDMA_V0_DONE_INT_MASK, > GET_RW_32(dw, dir, int_status)); > } > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > { > return FIELD_GET(EDMA_V0_ABORT_INT_MASK, > GET_RW_32(dw, dir, int_status)); > } > > +static > +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + unsigned long total, pos, val; > + unsigned long off; > + u32 mask; > + > + if (dir == EDMA_DIR_WRITE) { > + total = dw->wr_ch_cnt; > + off = 0; > + mask = dw_irq->wr_mask; > + } else { > + total = dw->rd_ch_cnt; > + off = dw->wr_ch_cnt; > + mask = dw_irq->rd_mask; > + } > + > + val = dw_edma_v0_core_status_done_int(dw, dir); > + val &= mask; > + for_each_set_bit(pos, &val, total) > + dw_edma_done_interrupt(&dw->chan[pos + off]); As I suggested above replace this with: + for_each_set_bit(pos, &val, total) { + struct dw_edma_chan *chan = &dw->chan[pos + off]; + + dw_edma_v0_core_clear_done_int(chan); + + dw_edma_done_interrupt(chan); + } Therefore you'll be able to drop the clear_done_int callback and its invocation from the dw_edma_done_interrupt() function. BTW Why did you change the dw_edma_v0_core_done_handle() semantics from taking the function pointers as an argument to calling the global version of dw_edma_done_interrupt() ? > +} > + > +static > +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + unsigned long total, pos, val; > + unsigned long off; > + u32 mask; > + > + if (dir == EDMA_DIR_WRITE) { > + total = dw->wr_ch_cnt; > + off = 0; > + mask = dw_irq->wr_mask; > + } else { > + total = dw->rd_ch_cnt; > + off = dw->wr_ch_cnt; > + mask = dw_irq->rd_mask; > + } > + > + val = dw_edma_v0_core_status_abort_int(dw, dir); > + val &= mask; > + for_each_set_bit(pos, &val, total) > + dw_edma_abort_interrupt(&dw->chan[pos + off]); ditto > +} > + > static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, > u32 control, u32 size, u64 sar, u64 dar) > { > @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) > dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); > } > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > { > struct dw_edma_chan *chan = chunk->chan; > struct dw_edma *dw = chan->dw; > @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); > } > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > u32 tmp = 0; > @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp); > break; > } > - > - return 0; > } > > /* eDMA debugfs callbacks */ > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > { > dw_edma_v0_debugfs_on(dw); > } > + > +static const struct dw_edma_core_ops edma_core = { > + .off = dw_edma_v0_core_off, > + .ch_count = dw_edma_v0_core_ch_count, > + .ch_status = dw_edma_v0_core_ch_status, > + .clear_done_int = dw_edma_v0_core_clear_done_int, > + .clear_abort_int = dw_edma_v0_core_clear_abort_int, > + .done_handle = dw_edma_v0_core_done_handle, > + .abort_handle = dw_edma_v0_core_abort_handle, If you take into account what I suggested above these four callbacks will be replaced with a single one handle_int. Thus the interface will look much simpler so will the whole IRQs-handling logic: irq: dw_edma_interrupt_{write,read}() +-> dw_edma_core_handle_int() (dw-edma-v0-core.c) +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c) +-> dw_edma_v0_core_clear_done_int() (dw-edma-v0-core.c) +-> dw_edma_done_interrupt() (dw-edma-core.c) +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c) +-> dw_edma_v0_core_clear_abort_int() (dw-edma-v0-core.c) +-> dw_edma_abort_interrupt() (dw-edma-core.c) In your case the calls hierarchy looks like this: irq: dw_edma_interrupt_{write,read}() +-> dw_edma_interrupt() +-> dw_edma_core_done_handle() (dw-edma-v0-core.c) +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c) +-> dw_edma_done_interrupt() (dw-edma-core.c) +-> dw_edma_core_clear_done_int() (dw-edma-v0-core.c) +-> dw_edma_core_abort_handle() (dw-edma-v0-core.c) +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c) +-> dw_edma_abort_interrupt() (dw-edma-core.c) +-> dw_edma_core_clear_abort_int() (dw-edma-v0-core.c) See the calls thread is more linear in my case and the reader won't need to get several times back and forth between the generic part of the driver and the IP-core-specific one. -Serge(y) > + .start = dw_edma_v0_core_start, > + .ch_config = dw_edma_v0_core_ch_config, > + .debugfs_on = dw_edma_v0_core_debugfs_on, > +}; > + > +void dw_edma_v0_core_register(struct dw_edma *dw) > +{ > + dw->core = &edma_core; > +} > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h > index ab96a1f48080..04a882222f99 100644 > --- a/drivers/dma/dw-edma/dw-edma-v0-core.h > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h > @@ -11,17 +11,7 @@ > > #include <linux/dma/edma.h> > > -/* eDMA management callbacks */ > -void dw_edma_v0_core_off(struct dw_edma *chan); > -u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir); > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan); > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan); > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan); > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir); > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir); > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); > -/* eDMA debug fs callbacks */ > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); > +/* eDMA core register */ > +void dw_edma_v0_core_register(struct dw_edma *dw); > > #endif /* _DW_EDMA_V0_CORE_H */ > -- > 2.34.1 >
On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote: > From: Cai huoqing <cai.huoqing@linux.dev> > > The structure dw_edma_core_ops has a set of the pointers > abstracting out the DW eDMA vX and DW HDMA Native controllers. > And use dw_edma_v0_core_register to set up operation. > > Signed-off-by: Cai huoqing <cai.huoqing@linux.dev> > --- > drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------ > drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++ > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++---- > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +--- > 4 files changed, 188 insertions(+), 69 deletions(-) > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c > index 1906a836f0aa..c171809beba1 100644 > --- a/drivers/dma/dw-edma/dw-edma-core.c > +++ b/drivers/dma/dw-edma/dw-edma-core.c > @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) > > static void dw_edma_start_transfer(struct dw_edma_chan *chan) > { > + struct dw_edma *dw = chan->dw; > struct dw_edma_chunk *child; > struct dw_edma_desc *desc; > struct virt_dma_desc *vd; > @@ -200,7 +201,7 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan) > if (!child) > return; > > - dw_edma_v0_core_start(child, !desc->xfer_sz); > + dw_edma_core_start(dw, child, !desc->xfer_sz); > desc->xfer_sz += child->ll_region.sz; > dw_edma_free_burst(child); > list_del(&child->list); > @@ -285,7 +286,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) > chan->configured = false; > } else if (chan->status == EDMA_ST_IDLE) { > chan->configured = false; > - } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { > + } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { > /* > * The channel is in a false BUSY state, probably didn't > * receive or lost an interrupt > @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, > return dw_edma_device_transfer(&xfer); > } > > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > +void dw_edma_done_interrupt(struct dw_edma_chan *chan) > { > struct dw_edma_desc *desc; > struct virt_dma_desc *vd; > unsigned long flags; > > - dw_edma_v0_core_clear_done_int(chan); > + dw_edma_core_clear_done_int(chan); > > spin_lock_irqsave(&chan->vc.lock, flags); > vd = vchan_next_desc(&chan->vc); > @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > spin_unlock_irqrestore(&chan->vc.lock, flags); > } > > -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > { > struct virt_dma_desc *vd; > unsigned long flags; > > - dw_edma_v0_core_clear_abort_int(chan); > + dw_edma_core_clear_abort_int(chan); > > spin_lock_irqsave(&chan->vc.lock, flags); > vd = vchan_next_desc(&chan->vc); > @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > { > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; > struct dw_edma_irq *dw_irq = data; > - struct dw_edma *dw = dw_irq->dw; > - unsigned long total, pos, val; > - unsigned long off; > - u32 mask; > - > - if (write) { > - total = dw->wr_ch_cnt; > - off = 0; > - mask = dw_irq->wr_mask; > - } else { > - total = dw->rd_ch_cnt; > - off = dw->wr_ch_cnt; > - mask = dw_irq->rd_mask; > - } > - > - val = dw_edma_v0_core_status_done_int(dw, write ? > - EDMA_DIR_WRITE : > - EDMA_DIR_READ); > - val &= mask; > - for_each_set_bit(pos, &val, total) { > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > - > - dw_edma_done_interrupt(chan); > - } > - > - val = dw_edma_v0_core_status_abort_int(dw, write ? > - EDMA_DIR_WRITE : > - EDMA_DIR_READ); > - val &= mask; > - for_each_set_bit(pos, &val, total) { > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > > - dw_edma_abort_interrupt(chan); > - } > + dw_edma_core_done_handle(dw_irq, dir); > + dw_edma_core_abort_handle(dw_irq, dir); > > return IRQ_HANDLED; > } > @@ -806,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) > > vchan_init(&chan->vc, dma); > > - dw_edma_v0_core_device_config(chan); > + dw_edma_core_ch_config(chan); > } > > /* Set DMA channel capabilities */ > @@ -951,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > dw->chip = chip; > > + dw_edma_v0_core_register(dw); > + > raw_spin_lock_init(&dw->lock); > > dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); > + dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); > dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); > > dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); > + dw_edma_core_ch_count(dw, EDMA_DIR_READ)); > dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); > > if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) > @@ -977,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > dev_name(chip->dev)); > > /* Disable eDMA, only to establish the ideal initial conditions */ > - dw_edma_v0_core_off(dw); > + dw_edma_core_off(dw); > > /* Request IRQs */ > err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); > @@ -990,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > goto err_irq_free; > > /* Turn debugfs on */ > - dw_edma_v0_core_debugfs_on(dw); > + dw_edma_core_debugfs_on(dw); > > chip->dw = dw; > > @@ -1016,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) > return -ENODEV; > > /* Disable eDMA */ > - dw_edma_v0_core_off(dw); > + dw_edma_core_off(dw); > > /* Free irqs */ > for (i = (dw->nr_irqs - 1); i >= 0; i--) > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h > index 0ab2b6dba880..2555ae0e1b8b 100644 > --- a/drivers/dma/dw-edma/dw-edma-core.h > +++ b/drivers/dma/dw-edma/dw-edma-core.h > @@ -111,6 +111,23 @@ struct dw_edma { > raw_spinlock_t lock; /* Only for legacy */ > > struct dw_edma_chip *chip; > + > + const struct dw_edma_core_ops *core; > +}; > + > +struct dw_edma_core_ops { > + void (*off)(struct dw_edma *dw); > + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); > + enum dma_status (*ch_status)(struct dw_edma_chan *chan); > + void (*clear_done_int)(struct dw_edma_chan *chan); > + void (*clear_abort_int)(struct dw_edma_chan *chan); > + void (*done_handle)(struct dw_edma_irq *dw_irq, > + enum dw_edma_dir dir); > + void (*abort_handle)(struct dw_edma_irq *dw_irq, > + enum dw_edma_dir dir); > + void (*start)(struct dw_edma_chunk *chunk, bool first); > + void (*ch_config)(struct dw_edma_chan *chan); > + void (*debugfs_on)(struct dw_edma *dw); > }; > > struct dw_edma_sg { > @@ -136,6 +153,9 @@ struct dw_edma_transfer { > enum dw_edma_xfer_type type; > }; > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan); > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan); > + > static inline > struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc) > { > @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan) > return vc2dw_edma_chan(to_virt_chan(dchan)); > } > > +static inline > +void dw_edma_core_off(struct dw_edma *dw) > +{ > + dw->core->off(dw); > +} > + > +static inline > +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > +{ > + return dw->core->ch_count(dw, dir); > +} > + > +static inline > +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + return dw->core->ch_status(chan); > +} > + > +static inline > +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + dw->core->clear_done_int(chan); > +} > + > +static inline > +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + dw->core->clear_abort_int(chan); > +} > + > +static inline > +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + > + dw->core->done_handle(dw_irq, dir); > +} > + > +static inline > +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + > + dw->core->abort_handle(dw_irq, dir); > +} > + > +static inline > +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) > +{ > + dw->core->start(chunk, first); > +} > + > +static inline > +void dw_edma_core_ch_config(struct dw_edma_chan *chan) > +{ > + struct dw_edma *dw = chan->dw; > + > + dw->core->ch_config(chan); > +} > + > +static inline > +void dw_edma_core_debugfs_on(struct dw_edma *dw) > +{ > + dw->core->debugfs_on(dw); > +} > + > #endif /* _DW_EDMA_CORE_H */ > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c > index 72e79a0c0a4e..73dcf72ba3a9 100644 > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c > @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, > readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) > > /* eDMA management callbacks */ > -void dw_edma_v0_core_off(struct dw_edma *dw) > +static void dw_edma_v0_core_off(struct dw_edma *dw) > { > SET_BOTH_32(dw, int_mask, > EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); > @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw) > SET_BOTH_32(dw, engine_en, 0); > } > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > { > u32 num_ch; > > @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > return (u16)num_ch; > } > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > u32 tmp; > @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > return DMA_ERROR; > } > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > > @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); > } > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > > @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); > } > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > { > return FIELD_GET(EDMA_V0_DONE_INT_MASK, > GET_RW_32(dw, dir, int_status)); > } > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > { > return FIELD_GET(EDMA_V0_ABORT_INT_MASK, > GET_RW_32(dw, dir, int_status)); > } > > +static > +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + unsigned long total, pos, val; > + unsigned long off; > + u32 mask; > + > + if (dir == EDMA_DIR_WRITE) { > + total = dw->wr_ch_cnt; > + off = 0; > + mask = dw_irq->wr_mask; > + } else { > + total = dw->rd_ch_cnt; > + off = dw->wr_ch_cnt; > + mask = dw_irq->rd_mask; > + } > + > + val = dw_edma_v0_core_status_done_int(dw, dir); > + val &= mask; > + for_each_set_bit(pos, &val, total) > + dw_edma_done_interrupt(&dw->chan[pos + off]); > +} > + > +static > +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > +{ > + struct dw_edma *dw = dw_irq->dw; > + unsigned long total, pos, val; > + unsigned long off; > + u32 mask; > + > + if (dir == EDMA_DIR_WRITE) { > + total = dw->wr_ch_cnt; > + off = 0; > + mask = dw_irq->wr_mask; > + } else { > + total = dw->rd_ch_cnt; > + off = dw->wr_ch_cnt; > + mask = dw_irq->rd_mask; > + } > + > + val = dw_edma_v0_core_status_abort_int(dw, dir); > + val &= mask; > + for_each_set_bit(pos, &val, total) > + dw_edma_abort_interrupt(&dw->chan[pos + off]); > +} > + > static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, > u32 control, u32 size, u64 sar, u64 dar) > { > @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) > dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); > } > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > { > struct dw_edma_chan *chan = chunk->chan; > struct dw_edma *dw = chan->dw; > @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); > } > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) > { > struct dw_edma *dw = chan->dw; > u32 tmp = 0; > @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp); > break; > } > - > - return 0; > } > > /* eDMA debugfs callbacks */ > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > { > dw_edma_v0_debugfs_on(dw); > } > + > +static const struct dw_edma_core_ops edma_core = { Please use the dw_edma_v0_core name for the structure instance in order to maintain the local naming convention. -Serge(y) > + .off = dw_edma_v0_core_off, > + .ch_count = dw_edma_v0_core_ch_count, > + .ch_status = dw_edma_v0_core_ch_status, > + .clear_done_int = dw_edma_v0_core_clear_done_int, > + .clear_abort_int = dw_edma_v0_core_clear_abort_int, > + .done_handle = dw_edma_v0_core_done_handle, > + .abort_handle = dw_edma_v0_core_abort_handle, > + .start = dw_edma_v0_core_start, > + .ch_config = dw_edma_v0_core_ch_config, > + .debugfs_on = dw_edma_v0_core_debugfs_on, > +}; > + > +void dw_edma_v0_core_register(struct dw_edma *dw) > +{ > + dw->core = &edma_core; > +} > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h > index ab96a1f48080..04a882222f99 100644 > --- a/drivers/dma/dw-edma/dw-edma-v0-core.h > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h > @@ -11,17 +11,7 @@ > > #include <linux/dma/edma.h> > > -/* eDMA management callbacks */ > -void dw_edma_v0_core_off(struct dw_edma *chan); > -u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir); > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan); > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan); > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan); > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir); > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir); > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); > -/* eDMA debug fs callbacks */ > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); > +/* eDMA core register */ > +void dw_edma_v0_core_register(struct dw_edma *dw); > > #endif /* _DW_EDMA_V0_CORE_H */ > -- > 2.34.1 >
Hi Serge, thanks for your reply. On 16 2ę 23 19:17:17, Serge Semin wrote: > On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote: > > From: Cai huoqing <cai.huoqing@linux.dev> > > > > The structure dw_edma_core_ops has a set of the pointers > > abstracting out the DW eDMA vX and DW HDMA Native controllers. > > And use dw_edma_v0_core_register to set up operation. > > > > Signed-off-by: Cai huoqing <cai.huoqing@linux.dev> > > --- > > drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------ > > drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++ > > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++---- > > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +--- > > 4 files changed, 188 insertions(+), 69 deletions(-) > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c > > index 1906a836f0aa..c171809beba1 100644 > > --- a/drivers/dma/dw-edma/dw-edma-core.c > > +++ b/drivers/dma/dw-edma/dw-edma-core.c > > @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) > > > > static void dw_edma_start_transfer(struct dw_edma_chan *chan) > > { > > + struct dw_edma *dw = chan->dw; > > struct dw_edma_chunk *child; > > struct dw_edma_desc *desc; > > struct virt_dma_desc *vd; > > @@ -200,7 +201,7 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan) > > if (!child) > > return; > > > > - dw_edma_v0_core_start(child, !desc->xfer_sz); > > + dw_edma_core_start(dw, child, !desc->xfer_sz); > > desc->xfer_sz += child->ll_region.sz; > > dw_edma_free_burst(child); > > list_del(&child->list); > > @@ -285,7 +286,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) > > chan->configured = false; > > } else if (chan->status == EDMA_ST_IDLE) { > > chan->configured = false; > > - } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { > > + } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { > > /* > > * The channel is in a false BUSY state, probably didn't > > * receive or lost an interrupt > > @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, > > return dw_edma_device_transfer(&xfer); > > } > > > > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan) > > { > > struct dw_edma_desc *desc; > > struct virt_dma_desc *vd; > > unsigned long flags; > > > > > - dw_edma_v0_core_clear_done_int(chan); > > + dw_edma_core_clear_done_int(chan); <+ > | > As I already said in v2 just call this method + in the IRQ-handle loop > within the IP-core-specific module (see further for details). Thus you'll > be able to drop the dw_edma_core_ops.clear_done_int pointer. The same > concerns the dw_edma_core_ops.clear_abort_int callback. > > > > > spin_lock_irqsave(&chan->vc.lock, flags); > > vd = vchan_next_desc(&chan->vc); > > @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > > spin_unlock_irqrestore(&chan->vc.lock, flags); > > } > > > > -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > { > > struct virt_dma_desc *vd; > > unsigned long flags; > > > > > - dw_edma_v0_core_clear_abort_int(chan); > > + dw_edma_core_clear_abort_int(chan); > > ditto > > > > > spin_lock_irqsave(&chan->vc.lock, flags); > > vd = vchan_next_desc(&chan->vc); > > @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > > > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > > { > > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; > > struct dw_edma_irq *dw_irq = data; > > - struct dw_edma *dw = dw_irq->dw; > > - unsigned long total, pos, val; > > - unsigned long off; > > - u32 mask; > > - > > - if (write) { > > - total = dw->wr_ch_cnt; > > - off = 0; > > - mask = dw_irq->wr_mask; > > - } else { > > - total = dw->rd_ch_cnt; > > - off = dw->wr_ch_cnt; > > - mask = dw_irq->rd_mask; > > - } > > - > > - val = dw_edma_v0_core_status_done_int(dw, write ? > > - EDMA_DIR_WRITE : > > - EDMA_DIR_READ); > > - val &= mask; > > - for_each_set_bit(pos, &val, total) { > > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > > - > > - dw_edma_done_interrupt(chan); > > - } > > - > > - val = dw_edma_v0_core_status_abort_int(dw, write ? > > - EDMA_DIR_WRITE : > > - EDMA_DIR_READ); > > - val &= mask; > > - for_each_set_bit(pos, &val, total) { > > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > > > > - dw_edma_abort_interrupt(chan); > > - } > > + dw_edma_core_done_handle(dw_irq, dir); > > + dw_edma_core_abort_handle(dw_irq, dir); > > > > return IRQ_HANDLED; > > } > > @@ -806,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) > > > > vchan_init(&chan->vc, dma); > > > > - dw_edma_v0_core_device_config(chan); > > + dw_edma_core_ch_config(chan); > > } > > > > /* Set DMA channel capabilities */ > > @@ -951,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > > > dw->chip = chip; > > > > + dw_edma_v0_core_register(dw); > > + > > raw_spin_lock_init(&dw->lock); > > > > dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, > > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); > > + dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); > > dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); > > > > dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, > > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); > > + dw_edma_core_ch_count(dw, EDMA_DIR_READ)); > > dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); > > > > if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) > > @@ -977,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > dev_name(chip->dev)); > > > > /* Disable eDMA, only to establish the ideal initial conditions */ > > - dw_edma_v0_core_off(dw); > > + dw_edma_core_off(dw); > > > > /* Request IRQs */ > > err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); > > @@ -990,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > goto err_irq_free; > > > > /* Turn debugfs on */ > > - dw_edma_v0_core_debugfs_on(dw); > > + dw_edma_core_debugfs_on(dw); > > > > chip->dw = dw; > > > > @@ -1016,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) > > return -ENODEV; > > > > /* Disable eDMA */ > > - dw_edma_v0_core_off(dw); > > + dw_edma_core_off(dw); > > > > /* Free irqs */ > > for (i = (dw->nr_irqs - 1); i >= 0; i--) > > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h > > index 0ab2b6dba880..2555ae0e1b8b 100644 > > --- a/drivers/dma/dw-edma/dw-edma-core.h > > +++ b/drivers/dma/dw-edma/dw-edma-core.h > > @@ -111,6 +111,23 @@ struct dw_edma { > > raw_spinlock_t lock; /* Only for legacy */ > > > > struct dw_edma_chip *chip; > > + > > + const struct dw_edma_core_ops *core; > > +}; > > + > > +struct dw_edma_core_ops { > > + void (*off)(struct dw_edma *dw); > > + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); > > + enum dma_status (*ch_status)(struct dw_edma_chan *chan); > > > + void (*clear_done_int)(struct dw_edma_chan *chan); > > + void (*clear_abort_int)(struct dw_edma_chan *chan); > > As I noted above these methods could be called locally from the > corresponding IP-core-specific module. Thus you won't need to define > the callbacks. It shall simplify the interface a bit. > > > + void (*done_handle)(struct dw_edma_irq *dw_irq, > > + enum dw_edma_dir dir); > > + void (*abort_handle)(struct dw_edma_irq *dw_irq, > > + enum dw_edma_dir dir); > > Seeing both of these callbacks (done,abort handlers) are called from > the same IRQ-handler what about converting them to a single callback: > irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir); > ? > The eDMA channels IRQ-handlers would look like this: > --- a/drivers/dma/dw-edma/dw-edma-core.c > +++ b/drivers/dma/dw-edma/dw-edma-core.c > @@ -692,12 +692,16 @@ static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) > { > - return dw_edma_interrupt(irq, data, true); > + struct dw_edma_irq *dw_irq = data; > + > + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE); > } > > static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) > { > - return dw_edma_interrupt(irq, data, false); > + struct dw_edma_irq *dw_irq = data; > + > + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ); > } > > static irqreturn_t dw_edma_interrupt_common(int irq, void *data) > > Therefore you'll be able to completely drop the > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > function from drivers/dma/dw-edma/dw-edma-core.c. The function body would > be almost fully moved to the handle_int() function of the eDMA/HDMA > core-specific ops. > > For instance in case of the DW eDMA it would look like this: > static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq, enum dw_edma_dir dir) > { > struct dw_edma *dw = dw_irq->dw; > unsigned long total, pos, val; > unsigned long off; > u32 mask; > > if (dir == EDMA_DIR_WRITE) { > total = dw->wr_ch_cnt; > off = 0; > mask = dw_irq->wr_mask; > } else { > total = dw->rd_ch_cnt; > off = dw->wr_ch_cnt; > mask = dw_irq->rd_mask; > } > > val = dw_edma_v0_core_status_done_int(dw, dir) & mask; > for_each_set_bit(pos, &val, total) { > struct dw_edma_chan *chan = &dw->chan[pos + off]; > > dw_edma_v0_core_clear_done_int(chan); > > dw_edma_done_interrupt(chan); > } > > val = dw_edma_v0_core_status_abort_int(dw, dir) & mask; > for_each_set_bit(pos, &val, total) { > struct dw_edma_chan *chan = &dw->chan[pos + off]; > > dw_edma_v0_core_clear_abort_int(chan); > > dw_edma_abort_interrupt(chan); > } > > return IRQ_HANDLED; > } > > What do you think? > > > + void (*start)(struct dw_edma_chunk *chunk, bool first); > > + void (*ch_config)(struct dw_edma_chan *chan); > > + void (*debugfs_on)(struct dw_edma *dw); > > }; > > > > struct dw_edma_sg { > > @@ -136,6 +153,9 @@ struct dw_edma_transfer { > > enum dw_edma_xfer_type type; > > }; > > > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan); > > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan); > > + > > static inline > > struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc) > > { > > @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan) > > return vc2dw_edma_chan(to_virt_chan(dchan)); > > } > > > > +static inline > > +void dw_edma_core_off(struct dw_edma *dw) > > +{ > > + dw->core->off(dw); > > +} > > + > > +static inline > > +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > +{ > > + return dw->core->ch_count(dw, dir); > > +} > > + > > +static inline > > +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan) > > +{ > > + struct dw_edma *dw = chan->dw; > > + > > + return dw->core->ch_status(chan); > > +} > > + > > +static inline > > +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan) > > +{ > > + struct dw_edma *dw = chan->dw; > > + > > + dw->core->clear_done_int(chan); > > +} > > + > > +static inline > > +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan) > > +{ > > + struct dw_edma *dw = chan->dw; > > + > > + dw->core->clear_abort_int(chan); > > +} > > + > > > +static inline > > +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > +{ > > + struct dw_edma *dw = dw_irq->dw; > > + > > + dw->core->done_handle(dw_irq, dir); > > +} > > + > > +static inline > > +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > +{ > > + struct dw_edma *dw = dw_irq->dw; > > + > > + dw->core->abort_handle(dw_irq, dir); > > +} > > As I suggested above these two methods can be replaced with a single > one: dw_edma_core_handle_int(...). > > > + > > +static inline > > +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) > > +{ > > + dw->core->start(chunk, first); > > +} > > + > > +static inline > > +void dw_edma_core_ch_config(struct dw_edma_chan *chan) > > +{ > > + struct dw_edma *dw = chan->dw; > > + > > + dw->core->ch_config(chan); > > +} > > + > > +static inline > > +void dw_edma_core_debugfs_on(struct dw_edma *dw) > > +{ > > + dw->core->debugfs_on(dw); > > +} > > + > > #endif /* _DW_EDMA_CORE_H */ > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c > > index 72e79a0c0a4e..73dcf72ba3a9 100644 > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c > > @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, > > readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) > > > > /* eDMA management callbacks */ > > -void dw_edma_v0_core_off(struct dw_edma *dw) > > +static void dw_edma_v0_core_off(struct dw_edma *dw) > > { > > SET_BOTH_32(dw, int_mask, > > EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); > > @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw) > > SET_BOTH_32(dw, engine_en, 0); > > } > > > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > { > > u32 num_ch; > > > > @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > return (u16)num_ch; > > } > > > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > > +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > > { > > struct dw_edma *dw = chan->dw; > > u32 tmp; > > @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > > return DMA_ERROR; > > } > > > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > > +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > > { > > struct dw_edma *dw = chan->dw; > > > > @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > > FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); > > } > > > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > > +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > > { > > struct dw_edma *dw = chan->dw; > > > > @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > > FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); > > } > > > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > > +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > > { > > return FIELD_GET(EDMA_V0_DONE_INT_MASK, > > GET_RW_32(dw, dir, int_status)); > > } > > > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > > +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > > { > > return FIELD_GET(EDMA_V0_ABORT_INT_MASK, > > GET_RW_32(dw, dir, int_status)); > > } > > > > +static > > +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > +{ > > + struct dw_edma *dw = dw_irq->dw; > > + unsigned long total, pos, val; > > + unsigned long off; > > + u32 mask; > > + > > + if (dir == EDMA_DIR_WRITE) { > > + total = dw->wr_ch_cnt; > > + off = 0; > > + mask = dw_irq->wr_mask; > > + } else { > > + total = dw->rd_ch_cnt; > > + off = dw->wr_ch_cnt; > > + mask = dw_irq->rd_mask; > > + } > > + > > + val = dw_edma_v0_core_status_done_int(dw, dir); > > + val &= mask; > > > + for_each_set_bit(pos, &val, total) > > + dw_edma_done_interrupt(&dw->chan[pos + off]); > > As I suggested above replace this with: > + for_each_set_bit(pos, &val, total) { > + struct dw_edma_chan *chan = &dw->chan[pos + off]; > + > + dw_edma_v0_core_clear_done_int(chan); > + > + dw_edma_done_interrupt(chan); > + } > > Therefore you'll be able to drop the clear_done_int callback and its > invocation from the dw_edma_done_interrupt() function. > > BTW Why did you change the dw_edma_v0_core_done_handle() semantics > from taking the function pointers as an argument to calling the global > version of dw_edma_done_interrupt() ? In my patch, 'dw_edma_core_clear_done_int' is called by 'dw_edma_done_interrupt', 'dw_edma_core_clear_done_int' point to 'dw_edma_v0_core_clear_done_int'. -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) +void dw_edma_done_interrupt(struct dw_edma_chan *chan) { struct dw_edma_desc *desc; struct virt_dma_desc *vd; unsigned long flags; - dw_edma_v0_core_clear_done_int(chan); + dw_edma_core_clear_done_int(chan); So, my patch only drop the callback params, like this in v2 patch "dw_irq, dw_edma_done_interrupt, dir)" - for_each_set_bit(pos, &val, total) { - struct dw_edma_chan *chan = &dw->chan[pos + off]; + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; - dw_edma_abort_interrupt(chan); - } + dw->core->dw_xdma_core_done_handle(dw_irq, dw_edma_done_interrupt, dir); + dw->core->dw_xdma_core_abort_handle(dw_irq, dw_edma_abort_interrupt, dir); And I will fix it in next version. Thanks, Cai- > > > +} > > + > > +static > > +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > +{ > > + struct dw_edma *dw = dw_irq->dw; > > + unsigned long total, pos, val; > > + unsigned long off; > > + u32 mask; > > + > > + if (dir == EDMA_DIR_WRITE) { > > + total = dw->wr_ch_cnt; > > + off = 0; > > + mask = dw_irq->wr_mask; > > + } else { > > + total = dw->rd_ch_cnt; > > + off = dw->wr_ch_cnt; > > + mask = dw_irq->rd_mask; > > + } > > + > > + val = dw_edma_v0_core_status_abort_int(dw, dir); > > + val &= mask; > > > + for_each_set_bit(pos, &val, total) > > + dw_edma_abort_interrupt(&dw->chan[pos + off]); > > ditto > > > +} > > + > > static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, > > u32 control, u32 size, u64 sar, u64 dar) > > { > > @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) > > dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); > > } > > > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > > +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > > { > > struct dw_edma_chan *chan = chunk->chan; > > struct dw_edma *dw = chan->dw; > > @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > > FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); > > } > > > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > > +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) > > { > > struct dw_edma *dw = chan->dw; > > u32 tmp = 0; > > @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > > SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp); > > break; > > } > > - > > - return 0; > > } > > > > /* eDMA debugfs callbacks */ > > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > > +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > > { > > dw_edma_v0_debugfs_on(dw); > > } > > + > > +static const struct dw_edma_core_ops edma_core = { > > + .off = dw_edma_v0_core_off, > > + .ch_count = dw_edma_v0_core_ch_count, > > + .ch_status = dw_edma_v0_core_ch_status, > > > + .clear_done_int = dw_edma_v0_core_clear_done_int, > > + .clear_abort_int = dw_edma_v0_core_clear_abort_int, > > + .done_handle = dw_edma_v0_core_done_handle, > > + .abort_handle = dw_edma_v0_core_abort_handle, > > If you take into account what I suggested above these four callbacks will > be replaced with a single one handle_int. Thus the interface will look > much simpler so will the whole IRQs-handling logic: > > irq: dw_edma_interrupt_{write,read}() > +-> dw_edma_core_handle_int() (dw-edma-v0-core.c) > +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c) > +-> dw_edma_v0_core_clear_done_int() (dw-edma-v0-core.c) > +-> dw_edma_done_interrupt() (dw-edma-core.c) > +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c) > +-> dw_edma_v0_core_clear_abort_int() (dw-edma-v0-core.c) > +-> dw_edma_abort_interrupt() (dw-edma-core.c) > > In your case the calls hierarchy looks like this: > irq: dw_edma_interrupt_{write,read}() > +-> dw_edma_interrupt() > +-> dw_edma_core_done_handle() (dw-edma-v0-core.c) > +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c) > +-> dw_edma_done_interrupt() (dw-edma-core.c) > +-> dw_edma_core_clear_done_int() (dw-edma-v0-core.c) > +-> dw_edma_core_abort_handle() (dw-edma-v0-core.c) > +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c) > +-> dw_edma_abort_interrupt() (dw-edma-core.c) > +-> dw_edma_core_clear_abort_int() (dw-edma-v0-core.c) > > See the calls thread is more linear in my case and the reader won't > need to get several times back and forth between the generic part of > the driver and the IP-core-specific one. > > -Serge(y) > > > + .start = dw_edma_v0_core_start, > > + .ch_config = dw_edma_v0_core_ch_config, > > + .debugfs_on = dw_edma_v0_core_debugfs_on, > > +}; > > + > > +void dw_edma_v0_core_register(struct dw_edma *dw) > > +{ > > + dw->core = &edma_core; > > +} > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h > > index ab96a1f48080..04a882222f99 100644 > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.h > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h > > @@ -11,17 +11,7 @@ > > > > #include <linux/dma/edma.h> > > > > -/* eDMA management callbacks */ > > -void dw_edma_v0_core_off(struct dw_edma *chan); > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir); > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan); > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan); > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan); > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir); > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir); > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); > > -/* eDMA debug fs callbacks */ > > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); > > +/* eDMA core register */ > > +void dw_edma_v0_core_register(struct dw_edma *dw); > > > > #endif /* _DW_EDMA_V0_CORE_H */ > > -- > > 2.34.1 > >
On Mon, Feb 20, 2023 at 02:01:50PM +0800, Cai Huoqing wrote: > Hi Serge, thanks for your reply. > > On 16 2ę 23 19:17:17, Serge Semin wrote: > > On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote: > > > From: Cai huoqing <cai.huoqing@linux.dev> > > > > > > The structure dw_edma_core_ops has a set of the pointers > > > abstracting out the DW eDMA vX and DW HDMA Native controllers. > > > And use dw_edma_v0_core_register to set up operation. > > > > > > Signed-off-by: Cai huoqing <cai.huoqing@linux.dev> > > > --- > > > drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------ > > > drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++ > > > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++---- > > > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +--- > > > 4 files changed, 188 insertions(+), 69 deletions(-) > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c > > > index 1906a836f0aa..c171809beba1 100644 > > > --- a/drivers/dma/dw-edma/dw-edma-core.c > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c > > > @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) > > > > > > static void dw_edma_start_transfer(struct dw_edma_chan *chan) > > > { > > > + struct dw_edma *dw = chan->dw; > > > struct dw_edma_chunk *child; > > > struct dw_edma_desc *desc; > > > struct virt_dma_desc *vd; > > > @@ -200,7 +201,7 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan) > > > if (!child) > > > return; > > > > > > - dw_edma_v0_core_start(child, !desc->xfer_sz); > > > + dw_edma_core_start(dw, child, !desc->xfer_sz); > > > desc->xfer_sz += child->ll_region.sz; > > > dw_edma_free_burst(child); > > > list_del(&child->list); > > > @@ -285,7 +286,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) > > > chan->configured = false; > > > } else if (chan->status == EDMA_ST_IDLE) { > > > chan->configured = false; > > > - } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { > > > + } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { > > > /* > > > * The channel is in a false BUSY state, probably didn't > > > * receive or lost an interrupt > > > @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, > > > return dw_edma_device_transfer(&xfer); > > > } > > > > > > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan) > > > { > > > struct dw_edma_desc *desc; > > > struct virt_dma_desc *vd; > > > unsigned long flags; > > > > > > > > - dw_edma_v0_core_clear_done_int(chan); > > > + dw_edma_core_clear_done_int(chan); <+ > > | > > As I already said in v2 just call this method + in the IRQ-handle loop > > within the IP-core-specific module (see further for details). Thus you'll > > be able to drop the dw_edma_core_ops.clear_done_int pointer. The same > > concerns the dw_edma_core_ops.clear_abort_int callback. > > > > > > > > spin_lock_irqsave(&chan->vc.lock, flags); > > > vd = vchan_next_desc(&chan->vc); > > > @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > > > spin_unlock_irqrestore(&chan->vc.lock, flags); > > > } > > > > > > -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > > { > > > struct virt_dma_desc *vd; > > > unsigned long flags; > > > > > > > > - dw_edma_v0_core_clear_abort_int(chan); > > > + dw_edma_core_clear_abort_int(chan); > > > > ditto > > > > > > > > spin_lock_irqsave(&chan->vc.lock, flags); > > > vd = vchan_next_desc(&chan->vc); > > > @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) > > > > > > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > > > { > > > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; > > > struct dw_edma_irq *dw_irq = data; > > > - struct dw_edma *dw = dw_irq->dw; > > > - unsigned long total, pos, val; > > > - unsigned long off; > > > - u32 mask; > > > - > > > - if (write) { > > > - total = dw->wr_ch_cnt; > > > - off = 0; > > > - mask = dw_irq->wr_mask; > > > - } else { > > > - total = dw->rd_ch_cnt; > > > - off = dw->wr_ch_cnt; > > > - mask = dw_irq->rd_mask; > > > - } > > > - > > > - val = dw_edma_v0_core_status_done_int(dw, write ? > > > - EDMA_DIR_WRITE : > > > - EDMA_DIR_READ); > > > - val &= mask; > > > - for_each_set_bit(pos, &val, total) { > > > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > > > - > > > - dw_edma_done_interrupt(chan); > > > - } > > > - > > > - val = dw_edma_v0_core_status_abort_int(dw, write ? > > > - EDMA_DIR_WRITE : > > > - EDMA_DIR_READ); > > > - val &= mask; > > > - for_each_set_bit(pos, &val, total) { > > > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > > > > > > - dw_edma_abort_interrupt(chan); > > > - } > > > + dw_edma_core_done_handle(dw_irq, dir); > > > + dw_edma_core_abort_handle(dw_irq, dir); > > > > > > return IRQ_HANDLED; > > > } > > > @@ -806,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) > > > > > > vchan_init(&chan->vc, dma); > > > > > > - dw_edma_v0_core_device_config(chan); > > > + dw_edma_core_ch_config(chan); > > > } > > > > > > /* Set DMA channel capabilities */ > > > @@ -951,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > > > > > dw->chip = chip; > > > > > > + dw_edma_v0_core_register(dw); > > > + > > > raw_spin_lock_init(&dw->lock); > > > > > > dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, > > > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); > > > + dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); > > > dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); > > > > > > dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, > > > - dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); > > > + dw_edma_core_ch_count(dw, EDMA_DIR_READ)); > > > dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); > > > > > > if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) > > > @@ -977,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > > dev_name(chip->dev)); > > > > > > /* Disable eDMA, only to establish the ideal initial conditions */ > > > - dw_edma_v0_core_off(dw); > > > + dw_edma_core_off(dw); > > > > > > /* Request IRQs */ > > > err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); > > > @@ -990,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) > > > goto err_irq_free; > > > > > > /* Turn debugfs on */ > > > - dw_edma_v0_core_debugfs_on(dw); > > > + dw_edma_core_debugfs_on(dw); > > > > > > chip->dw = dw; > > > > > > @@ -1016,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) > > > return -ENODEV; > > > > > > /* Disable eDMA */ > > > - dw_edma_v0_core_off(dw); > > > + dw_edma_core_off(dw); > > > > > > /* Free irqs */ > > > for (i = (dw->nr_irqs - 1); i >= 0; i--) > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h > > > index 0ab2b6dba880..2555ae0e1b8b 100644 > > > --- a/drivers/dma/dw-edma/dw-edma-core.h > > > +++ b/drivers/dma/dw-edma/dw-edma-core.h > > > @@ -111,6 +111,23 @@ struct dw_edma { > > > raw_spinlock_t lock; /* Only for legacy */ > > > > > > struct dw_edma_chip *chip; > > > + > > > + const struct dw_edma_core_ops *core; > > > +}; > > > + > > > +struct dw_edma_core_ops { > > > + void (*off)(struct dw_edma *dw); > > > + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); > > > + enum dma_status (*ch_status)(struct dw_edma_chan *chan); > > > > > + void (*clear_done_int)(struct dw_edma_chan *chan); > > > + void (*clear_abort_int)(struct dw_edma_chan *chan); > > > > As I noted above these methods could be called locally from the > > corresponding IP-core-specific module. Thus you won't need to define > > the callbacks. It shall simplify the interface a bit. > > > > > + void (*done_handle)(struct dw_edma_irq *dw_irq, > > > + enum dw_edma_dir dir); > > > + void (*abort_handle)(struct dw_edma_irq *dw_irq, > > > + enum dw_edma_dir dir); > > > > Seeing both of these callbacks (done,abort handlers) are called from > > the same IRQ-handler what about converting them to a single callback: > > irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir); > > ? > > The eDMA channels IRQ-handlers would look like this: > > --- a/drivers/dma/dw-edma/dw-edma-core.c > > +++ b/drivers/dma/dw-edma/dw-edma-core.c > > @@ -692,12 +692,16 @@ static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > > static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) > > { > > - return dw_edma_interrupt(irq, data, true); > > + struct dw_edma_irq *dw_irq = data; > > + > > + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE); > > } > > > > static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) > > { > > - return dw_edma_interrupt(irq, data, false); > > + struct dw_edma_irq *dw_irq = data; > > + > > + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ); > > } > > > > static irqreturn_t dw_edma_interrupt_common(int irq, void *data) > > > > Therefore you'll be able to completely drop the > > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) > > function from drivers/dma/dw-edma/dw-edma-core.c. The function body would > > be almost fully moved to the handle_int() function of the eDMA/HDMA > > core-specific ops. > > > > For instance in case of the DW eDMA it would look like this: > > static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq, enum dw_edma_dir dir) > > { > > struct dw_edma *dw = dw_irq->dw; > > unsigned long total, pos, val; > > unsigned long off; > > u32 mask; > > > > if (dir == EDMA_DIR_WRITE) { > > total = dw->wr_ch_cnt; > > off = 0; > > mask = dw_irq->wr_mask; > > } else { > > total = dw->rd_ch_cnt; > > off = dw->wr_ch_cnt; > > mask = dw_irq->rd_mask; > > } > > > > val = dw_edma_v0_core_status_done_int(dw, dir) & mask; > > for_each_set_bit(pos, &val, total) { > > struct dw_edma_chan *chan = &dw->chan[pos + off]; > > > > dw_edma_v0_core_clear_done_int(chan); > > > > dw_edma_done_interrupt(chan); > > } > > > > val = dw_edma_v0_core_status_abort_int(dw, dir) & mask; > > for_each_set_bit(pos, &val, total) { > > struct dw_edma_chan *chan = &dw->chan[pos + off]; > > > > dw_edma_v0_core_clear_abort_int(chan); > > > > dw_edma_abort_interrupt(chan); > > } > > > > return IRQ_HANDLED; > > } > > > > What do you think? > > > > > + void (*start)(struct dw_edma_chunk *chunk, bool first); > > > + void (*ch_config)(struct dw_edma_chan *chan); > > > + void (*debugfs_on)(struct dw_edma *dw); > > > }; > > > > > > struct dw_edma_sg { > > > @@ -136,6 +153,9 @@ struct dw_edma_transfer { > > > enum dw_edma_xfer_type type; > > > }; > > > > > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan); > > > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan); > > > + > > > static inline > > > struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc) > > > { > > > @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan) > > > return vc2dw_edma_chan(to_virt_chan(dchan)); > > > } > > > > > > +static inline > > > +void dw_edma_core_off(struct dw_edma *dw) > > > +{ > > > + dw->core->off(dw); > > > +} > > > + > > > +static inline > > > +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > > +{ > > > + return dw->core->ch_count(dw, dir); > > > +} > > > + > > > +static inline > > > +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan) > > > +{ > > > + struct dw_edma *dw = chan->dw; > > > + > > > + return dw->core->ch_status(chan); > > > +} > > > + > > > +static inline > > > +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan) > > > +{ > > > + struct dw_edma *dw = chan->dw; > > > + > > > + dw->core->clear_done_int(chan); > > > +} > > > + > > > +static inline > > > +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan) > > > +{ > > > + struct dw_edma *dw = chan->dw; > > > + > > > + dw->core->clear_abort_int(chan); > > > +} > > > + > > > > > +static inline > > > +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > > +{ > > > + struct dw_edma *dw = dw_irq->dw; > > > + > > > + dw->core->done_handle(dw_irq, dir); > > > +} > > > + > > > +static inline > > > +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > > +{ > > > + struct dw_edma *dw = dw_irq->dw; > > > + > > > + dw->core->abort_handle(dw_irq, dir); > > > +} > > > > As I suggested above these two methods can be replaced with a single > > one: dw_edma_core_handle_int(...). > > > > > + > > > +static inline > > > +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) > > > +{ > > > + dw->core->start(chunk, first); > > > +} > > > + > > > +static inline > > > +void dw_edma_core_ch_config(struct dw_edma_chan *chan) > > > +{ > > > + struct dw_edma *dw = chan->dw; > > > + > > > + dw->core->ch_config(chan); > > > +} > > > + > > > +static inline > > > +void dw_edma_core_debugfs_on(struct dw_edma *dw) > > > +{ > > > + dw->core->debugfs_on(dw); > > > +} > > > + > > > #endif /* _DW_EDMA_CORE_H */ > > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c > > > index 72e79a0c0a4e..73dcf72ba3a9 100644 > > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c > > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c > > > @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, > > > readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) > > > > > > /* eDMA management callbacks */ > > > -void dw_edma_v0_core_off(struct dw_edma *dw) > > > +static void dw_edma_v0_core_off(struct dw_edma *dw) > > > { > > > SET_BOTH_32(dw, int_mask, > > > EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); > > > @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw) > > > SET_BOTH_32(dw, engine_en, 0); > > > } > > > > > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > > +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > > { > > > u32 num_ch; > > > > > > @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) > > > return (u16)num_ch; > > > } > > > > > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > > > +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > > > { > > > struct dw_edma *dw = chan->dw; > > > u32 tmp; > > > @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) > > > return DMA_ERROR; > > > } > > > > > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > > > +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > > > { > > > struct dw_edma *dw = chan->dw; > > > > > > @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) > > > FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); > > > } > > > > > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > > > +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > > > { > > > struct dw_edma *dw = chan->dw; > > > > > > @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) > > > FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); > > > } > > > > > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > > > +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) > > > { > > > return FIELD_GET(EDMA_V0_DONE_INT_MASK, > > > GET_RW_32(dw, dir, int_status)); > > > } > > > > > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > > > +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) > > > { > > > return FIELD_GET(EDMA_V0_ABORT_INT_MASK, > > > GET_RW_32(dw, dir, int_status)); > > > } > > > > > > +static > > > +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > > +{ > > > + struct dw_edma *dw = dw_irq->dw; > > > + unsigned long total, pos, val; > > > + unsigned long off; > > > + u32 mask; > > > + > > > + if (dir == EDMA_DIR_WRITE) { > > > + total = dw->wr_ch_cnt; > > > + off = 0; > > > + mask = dw_irq->wr_mask; > > > + } else { > > > + total = dw->rd_ch_cnt; > > > + off = dw->wr_ch_cnt; > > > + mask = dw_irq->rd_mask; > > > + } > > > + > > > + val = dw_edma_v0_core_status_done_int(dw, dir); > > > + val &= mask; > > > > > + for_each_set_bit(pos, &val, total) > > > + dw_edma_done_interrupt(&dw->chan[pos + off]); > > > > As I suggested above replace this with: > > + for_each_set_bit(pos, &val, total) { > > + struct dw_edma_chan *chan = &dw->chan[pos + off]; > > + > > + dw_edma_v0_core_clear_done_int(chan); > > + > > + dw_edma_done_interrupt(chan); > > + } > > > > Therefore you'll be able to drop the clear_done_int callback and its > > invocation from the dw_edma_done_interrupt() function. > > > > BTW Why did you change the dw_edma_v0_core_done_handle() semantics > > from taking the function pointers as an argument to calling the global > > version of dw_edma_done_interrupt() ? > In my patch, 'dw_edma_core_clear_done_int' is called by 'dw_edma_done_interrupt', > > 'dw_edma_core_clear_done_int' point to 'dw_edma_v0_core_clear_done_int'. > > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) > +void dw_edma_done_interrupt(struct dw_edma_chan *chan) > { > struct dw_edma_desc *desc; > struct virt_dma_desc *vd; > unsigned long flags; > > - dw_edma_v0_core_clear_done_int(chan); > + dw_edma_core_clear_done_int(chan); > > > So, my patch only drop the callback params, like this in v2 patch > "dw_irq, dw_edma_done_interrupt, dir)" > > > - for_each_set_bit(pos, &val, total) { > - struct dw_edma_chan *chan = &dw->chan[pos + off]; > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; > > - dw_edma_abort_interrupt(chan); > - } > + dw->core->dw_xdma_core_done_handle(dw_irq, dw_edma_done_interrupt, dir); > + dw->core->dw_xdma_core_abort_handle(dw_irq, dw_edma_abort_interrupt, dir); > > > And I will fix it in next version. I was talking about this change: v1: dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, void (*done_cb)(struct dw_edma_chan *chan), enum dw_edma_dir dir) v2: dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) I don't have strong opinion about that but having a function pointer passed as parameter seemed a tiny bit more context-wise readable since you won't need to memories that dw_edma_done_interrupt() and dw_edma_abort_interrupt() are global, they are defined in the core part of the driver and they are supposed to be called from the IP-core abstraction layers. On the other hand the handle_int() callback prototype will look a bit more bulky. Note if you decide to get the previous semantics back please pass the callbacks as the last arguments: dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, void (*done)(struct dw_edma_chan *chan), void (*abort)(struct dw_edma_chan *chan)) -Serge(y) > > Thanks, > Cai- > > > > > +} > > > + > > > +static > > > +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) > > > +{ > > > + struct dw_edma *dw = dw_irq->dw; > > > + unsigned long total, pos, val; > > > + unsigned long off; > > > + u32 mask; > > > + > > > + if (dir == EDMA_DIR_WRITE) { > > > + total = dw->wr_ch_cnt; > > > + off = 0; > > > + mask = dw_irq->wr_mask; > > > + } else { > > > + total = dw->rd_ch_cnt; > > > + off = dw->wr_ch_cnt; > > > + mask = dw_irq->rd_mask; > > > + } > > > + > > > + val = dw_edma_v0_core_status_abort_int(dw, dir); > > > + val &= mask; > > > > > + for_each_set_bit(pos, &val, total) > > > + dw_edma_abort_interrupt(&dw->chan[pos + off]); > > > > ditto > > > > > +} > > > + > > > static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, > > > u32 control, u32 size, u64 sar, u64 dar) > > > { > > > @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) > > > dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); > > > } > > > > > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > > > +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > > > { > > > struct dw_edma_chan *chan = chunk->chan; > > > struct dw_edma *dw = chan->dw; > > > @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) > > > FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); > > > } > > > > > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > > > +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) > > > { > > > struct dw_edma *dw = chan->dw; > > > u32 tmp = 0; > > > @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) > > > SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp); > > > break; > > > } > > > - > > > - return 0; > > > } > > > > > > /* eDMA debugfs callbacks */ > > > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > > > +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) > > > { > > > dw_edma_v0_debugfs_on(dw); > > > } > > > + > > > +static const struct dw_edma_core_ops edma_core = { > > > + .off = dw_edma_v0_core_off, > > > + .ch_count = dw_edma_v0_core_ch_count, > > > + .ch_status = dw_edma_v0_core_ch_status, > > > > > + .clear_done_int = dw_edma_v0_core_clear_done_int, > > > + .clear_abort_int = dw_edma_v0_core_clear_abort_int, > > > + .done_handle = dw_edma_v0_core_done_handle, > > > + .abort_handle = dw_edma_v0_core_abort_handle, > > > > If you take into account what I suggested above these four callbacks will > > be replaced with a single one handle_int. Thus the interface will look > > much simpler so will the whole IRQs-handling logic: > > > > irq: dw_edma_interrupt_{write,read}() > > +-> dw_edma_core_handle_int() (dw-edma-v0-core.c) > > +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c) > > +-> dw_edma_v0_core_clear_done_int() (dw-edma-v0-core.c) > > +-> dw_edma_done_interrupt() (dw-edma-core.c) > > +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c) > > +-> dw_edma_v0_core_clear_abort_int() (dw-edma-v0-core.c) > > +-> dw_edma_abort_interrupt() (dw-edma-core.c) > > > > In your case the calls hierarchy looks like this: > > irq: dw_edma_interrupt_{write,read}() > > +-> dw_edma_interrupt() > > +-> dw_edma_core_done_handle() (dw-edma-v0-core.c) > > +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c) > > +-> dw_edma_done_interrupt() (dw-edma-core.c) > > +-> dw_edma_core_clear_done_int() (dw-edma-v0-core.c) > > +-> dw_edma_core_abort_handle() (dw-edma-v0-core.c) > > +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c) > > +-> dw_edma_abort_interrupt() (dw-edma-core.c) > > +-> dw_edma_core_clear_abort_int() (dw-edma-v0-core.c) > > > > See the calls thread is more linear in my case and the reader won't > > need to get several times back and forth between the generic part of > > the driver and the IP-core-specific one. > > > > -Serge(y) > > > > > + .start = dw_edma_v0_core_start, > > > + .ch_config = dw_edma_v0_core_ch_config, > > > + .debugfs_on = dw_edma_v0_core_debugfs_on, > > > +}; > > > + > > > +void dw_edma_v0_core_register(struct dw_edma *dw) > > > +{ > > > + dw->core = &edma_core; > > > +} > > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h > > > index ab96a1f48080..04a882222f99 100644 > > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.h > > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h > > > @@ -11,17 +11,7 @@ > > > > > > #include <linux/dma/edma.h> > > > > > > -/* eDMA management callbacks */ > > > -void dw_edma_v0_core_off(struct dw_edma *chan); > > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir); > > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan); > > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan); > > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan); > > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir); > > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir); > > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); > > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); > > > -/* eDMA debug fs callbacks */ > > > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); > > > +/* eDMA core register */ > > > +void dw_edma_v0_core_register(struct dw_edma *dw); > > > > > > #endif /* _DW_EDMA_V0_CORE_H */ > > > -- > > > 2.34.1 > > >
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 1906a836f0aa..c171809beba1 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) static void dw_edma_start_transfer(struct dw_edma_chan *chan) { + struct dw_edma *dw = chan->dw; struct dw_edma_chunk *child; struct dw_edma_desc *desc; struct virt_dma_desc *vd; @@ -200,7 +201,7 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan) if (!child) return; - dw_edma_v0_core_start(child, !desc->xfer_sz); + dw_edma_core_start(dw, child, !desc->xfer_sz); desc->xfer_sz += child->ll_region.sz; dw_edma_free_burst(child); list_del(&child->list); @@ -285,7 +286,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) chan->configured = false; } else if (chan->status == EDMA_ST_IDLE) { chan->configured = false; - } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { + } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { /* * The channel is in a false BUSY state, probably didn't * receive or lost an interrupt @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, return dw_edma_device_transfer(&xfer); } -static void dw_edma_done_interrupt(struct dw_edma_chan *chan) +void dw_edma_done_interrupt(struct dw_edma_chan *chan) { struct dw_edma_desc *desc; struct virt_dma_desc *vd; unsigned long flags; - dw_edma_v0_core_clear_done_int(chan); + dw_edma_core_clear_done_int(chan); spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) spin_unlock_irqrestore(&chan->vc.lock, flags); } -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) +void dw_edma_abort_interrupt(struct dw_edma_chan *chan) { struct virt_dma_desc *vd; unsigned long flags; - dw_edma_v0_core_clear_abort_int(chan); + dw_edma_core_clear_abort_int(chan); spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) { + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; struct dw_edma_irq *dw_irq = data; - struct dw_edma *dw = dw_irq->dw; - unsigned long total, pos, val; - unsigned long off; - u32 mask; - - if (write) { - total = dw->wr_ch_cnt; - off = 0; - mask = dw_irq->wr_mask; - } else { - total = dw->rd_ch_cnt; - off = dw->wr_ch_cnt; - mask = dw_irq->rd_mask; - } - - val = dw_edma_v0_core_status_done_int(dw, write ? - EDMA_DIR_WRITE : - EDMA_DIR_READ); - val &= mask; - for_each_set_bit(pos, &val, total) { - struct dw_edma_chan *chan = &dw->chan[pos + off]; - - dw_edma_done_interrupt(chan); - } - - val = dw_edma_v0_core_status_abort_int(dw, write ? - EDMA_DIR_WRITE : - EDMA_DIR_READ); - val &= mask; - for_each_set_bit(pos, &val, total) { - struct dw_edma_chan *chan = &dw->chan[pos + off]; - dw_edma_abort_interrupt(chan); - } + dw_edma_core_done_handle(dw_irq, dir); + dw_edma_core_abort_handle(dw_irq, dir); return IRQ_HANDLED; } @@ -806,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) vchan_init(&chan->vc, dma); - dw_edma_v0_core_device_config(chan); + dw_edma_core_ch_config(chan); } /* Set DMA channel capabilities */ @@ -951,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip) dw->chip = chip; + dw_edma_v0_core_register(dw); + raw_spin_lock_init(&dw->lock); dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, - dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); + dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, - dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); + dw_edma_core_ch_count(dw, EDMA_DIR_READ)); dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) @@ -977,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) dev_name(chip->dev)); /* Disable eDMA, only to establish the ideal initial conditions */ - dw_edma_v0_core_off(dw); + dw_edma_core_off(dw); /* Request IRQs */ err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); @@ -990,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) goto err_irq_free; /* Turn debugfs on */ - dw_edma_v0_core_debugfs_on(dw); + dw_edma_core_debugfs_on(dw); chip->dw = dw; @@ -1016,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) return -ENODEV; /* Disable eDMA */ - dw_edma_v0_core_off(dw); + dw_edma_core_off(dw); /* Free irqs */ for (i = (dw->nr_irqs - 1); i >= 0; i--) diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 0ab2b6dba880..2555ae0e1b8b 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -111,6 +111,23 @@ struct dw_edma { raw_spinlock_t lock; /* Only for legacy */ struct dw_edma_chip *chip; + + const struct dw_edma_core_ops *core; +}; + +struct dw_edma_core_ops { + void (*off)(struct dw_edma *dw); + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); + enum dma_status (*ch_status)(struct dw_edma_chan *chan); + void (*clear_done_int)(struct dw_edma_chan *chan); + void (*clear_abort_int)(struct dw_edma_chan *chan); + void (*done_handle)(struct dw_edma_irq *dw_irq, + enum dw_edma_dir dir); + void (*abort_handle)(struct dw_edma_irq *dw_irq, + enum dw_edma_dir dir); + void (*start)(struct dw_edma_chunk *chunk, bool first); + void (*ch_config)(struct dw_edma_chan *chan); + void (*debugfs_on)(struct dw_edma *dw); }; struct dw_edma_sg { @@ -136,6 +153,9 @@ struct dw_edma_transfer { enum dw_edma_xfer_type type; }; +void dw_edma_done_interrupt(struct dw_edma_chan *chan); +void dw_edma_abort_interrupt(struct dw_edma_chan *chan); + static inline struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc) { @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan) return vc2dw_edma_chan(to_virt_chan(dchan)); } +static inline +void dw_edma_core_off(struct dw_edma *dw) +{ + dw->core->off(dw); +} + +static inline +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) +{ + return dw->core->ch_count(dw, dir); +} + +static inline +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + return dw->core->ch_status(chan); +} + +static inline +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + dw->core->clear_done_int(chan); +} + +static inline +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + dw->core->clear_abort_int(chan); +} + +static inline +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) +{ + struct dw_edma *dw = dw_irq->dw; + + dw->core->done_handle(dw_irq, dir); +} + +static inline +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) +{ + struct dw_edma *dw = dw_irq->dw; + + dw->core->abort_handle(dw_irq, dir); +} + +static inline +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) +{ + dw->core->start(chunk, first); +} + +static inline +void dw_edma_core_ch_config(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + dw->core->ch_config(chan); +} + +static inline +void dw_edma_core_debugfs_on(struct dw_edma *dw) +{ + dw->core->debugfs_on(dw); +} + #endif /* _DW_EDMA_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 72e79a0c0a4e..73dcf72ba3a9 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) /* eDMA management callbacks */ -void dw_edma_v0_core_off(struct dw_edma *dw) +static void dw_edma_v0_core_off(struct dw_edma *dw) { SET_BOTH_32(dw, int_mask, EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw) SET_BOTH_32(dw, engine_en, 0); } -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) { u32 num_ch; @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) return (u16)num_ch; } -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; u32 tmp; @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) return DMA_ERROR; } -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); } -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); } -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) { return FIELD_GET(EDMA_V0_DONE_INT_MASK, GET_RW_32(dw, dir, int_status)); } -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) { return FIELD_GET(EDMA_V0_ABORT_INT_MASK, GET_RW_32(dw, dir, int_status)); } +static +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) +{ + struct dw_edma *dw = dw_irq->dw; + unsigned long total, pos, val; + unsigned long off; + u32 mask; + + if (dir == EDMA_DIR_WRITE) { + total = dw->wr_ch_cnt; + off = 0; + mask = dw_irq->wr_mask; + } else { + total = dw->rd_ch_cnt; + off = dw->wr_ch_cnt; + mask = dw_irq->rd_mask; + } + + val = dw_edma_v0_core_status_done_int(dw, dir); + val &= mask; + for_each_set_bit(pos, &val, total) + dw_edma_done_interrupt(&dw->chan[pos + off]); +} + +static +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir) +{ + struct dw_edma *dw = dw_irq->dw; + unsigned long total, pos, val; + unsigned long off; + u32 mask; + + if (dir == EDMA_DIR_WRITE) { + total = dw->wr_ch_cnt; + off = 0; + mask = dw_irq->wr_mask; + } else { + total = dw->rd_ch_cnt; + off = dw->wr_ch_cnt; + mask = dw_irq->rd_mask; + } + + val = dw_edma_v0_core_status_abort_int(dw, dir); + val &= mask; + for_each_set_bit(pos, &val, total) + dw_edma_abort_interrupt(&dw->chan[pos + off]); +} + static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, u32 control, u32 size, u64 sar, u64 dar) { @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); } -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); } -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; u32 tmp = 0; @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp); break; } - - return 0; } /* eDMA debugfs callbacks */ -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) { dw_edma_v0_debugfs_on(dw); } + +static const struct dw_edma_core_ops edma_core = { + .off = dw_edma_v0_core_off, + .ch_count = dw_edma_v0_core_ch_count, + .ch_status = dw_edma_v0_core_ch_status, + .clear_done_int = dw_edma_v0_core_clear_done_int, + .clear_abort_int = dw_edma_v0_core_clear_abort_int, + .done_handle = dw_edma_v0_core_done_handle, + .abort_handle = dw_edma_v0_core_abort_handle, + .start = dw_edma_v0_core_start, + .ch_config = dw_edma_v0_core_ch_config, + .debugfs_on = dw_edma_v0_core_debugfs_on, +}; + +void dw_edma_v0_core_register(struct dw_edma *dw) +{ + dw->core = &edma_core; +} diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h index ab96a1f48080..04a882222f99 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.h +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h @@ -11,17 +11,7 @@ #include <linux/dma/edma.h> -/* eDMA management callbacks */ -void dw_edma_v0_core_off(struct dw_edma *chan); -u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir); -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan); -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan); -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan); -u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir); -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir); -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); -/* eDMA debug fs callbacks */ -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); +/* eDMA core register */ +void dw_edma_v0_core_register(struct dw_edma *dw); #endif /* _DW_EDMA_V0_CORE_H */