From patchwork Mon Feb 12 16:38:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= X-Patchwork-Id: 872148 X-Patchwork-Delegate: daniel.schwierzeck@googlemail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Fm/Y53du"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3zgBKq2t90z9sRW for ; Tue, 13 Feb 2018 03:42:39 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 15CB6C21F18; Mon, 12 Feb 2018 16:40:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 872C6C21EBF; Mon, 12 Feb 2018 16:39:10 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 89A3BC21E76; Mon, 12 Feb 2018 16:39:04 +0000 (UTC) Received: from mail-wm0-f67.google.com (mail-wm0-f67.google.com [74.125.82.67]) by lists.denx.de (Postfix) with ESMTPS id D4129C21D8A for ; Mon, 12 Feb 2018 16:39:03 +0000 (UTC) Received: by mail-wm0-f67.google.com with SMTP id v71so11106818wmv.2 for ; Mon, 12 Feb 2018 08:39:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=GUEEMPLJ9LrQI9S5350rAxQYPbVYL4jpgR4CjsZFKqA=; b=Fm/Y53duxt5FD5B8pGdeNiuPzqmzyBvkE/OhOBq7+VJSLho5fwMkQuCNYW2nTzcOwd depu+uOFkjXpDfwrikEJX9n9YAa7CPSWVe55mJT2oDWRSdrYtkDwHq6AACPw56uNhf2I xvKEZeNd7n208n2AVFBttAQ63VLArvTN+ctjVphP7owk9G1ZpYr/JNqh6FDParfcqNBG 2eqQZCU7fqPNJJoaPsW2C7nJ4+pwd5lvpUAfy3HPdMSbNB9sqJsSe+uldbI94tITAMr4 JTCnk7gIvjY8oU62kF3XGdZeTIm7OR2mqeqAqeqktwvavIUqWtOijKInmUfu1WtWkna3 JJhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=GUEEMPLJ9LrQI9S5350rAxQYPbVYL4jpgR4CjsZFKqA=; b=rbI/K7MKhQNouqxf5EPY3RRw6bb96gBGLFswIdJarQMdnLn8ydUukPY/B1TTYiQn4T CMxDbh107GcNb4JfS4JgmE7t4khYdefjshBJ36TnQbqEiFDvT2rsmLpZJj+nPE7V7cOy Gh/SP3Vp9WSTGKkkLdKzME0EdGWSdOro4VCZSav1daxBOOWtCYzEGrJr+ia+lgUmBERM cWypllMvPuBXjZklXd+QiE95MJQ5sp7UI64oAicnOfxzCDkqbu1DwQ+3L17d2L2iIybn wBAuw3z3c6NAL2wRZfKcJsuNToW45c+Yz1T6FedwF89PXaVUQyOlZKQ8jW/jbeaRbC0v N0pg== X-Gm-Message-State: APf1xPC3BnVk9DmxLQElMonmjmk/u+u08vnzdf6k9j2O7gyqXyJOTfkH ybXqP3wLOgiOTXMdFlQuadDwH+sj X-Google-Smtp-Source: AH8x224d6lm7c5/ma/8ydPp3HsTInPnsYusN9jlhhvHVS1+Rnwvr+THz6NujPpyi7pxReSAy9GtoZA== X-Received: by 10.28.125.19 with SMTP id y19mr3788505wmc.101.1518453542938; Mon, 12 Feb 2018 08:39:02 -0800 (PST) Received: from skynet.lan (175.red-2-137-31.dynamicip.rima-tde.net. [2.137.31.175]) by smtp.gmail.com with ESMTPSA id j144sm5750202wmj.45.2018.02.12.08.39.01 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 12 Feb 2018 08:39:02 -0800 (PST) From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= To: u-boot@lists.denx.de, sjg@chromium.org, daniel.schwierzeck@gmail.com, mugunthanvnm@ti.com, jteki@openedev.com Date: Mon, 12 Feb 2018 17:38:46 +0100 Message-Id: <20180212163858.25601-3-noltari@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180212163858.25601-1-noltari@gmail.com> References: <20180212163858.25601-1-noltari@gmail.com> MIME-Version: 1.0 Subject: [U-Boot] [RFC 02/14] dma: add bcm6348-iudma support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Signed-off-by: Álvaro Fernández Rojas --- drivers/dma/Kconfig | 8 + drivers/dma/Makefile | 1 + drivers/dma/bcm6348-iudma.c | 498 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+) create mode 100644 drivers/dma/bcm6348-iudma.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 1b92c7789d..f58323f6c0 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -12,6 +12,14 @@ config DMA buses that is used to transfer data to and from memory. The uclass interface is defined in include/dma.h. +config BCM6348_IUDMA + bool "BCM6348 IUDMA driver" + depends on ARCH_BMIPS + help + Enable the BCM6348 IUDMA driver. + This driver support data transfer from devices to + memory and from memory to devices. + config TI_EDMA3 bool "TI EDMA3 driver" help diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 39b78b2a3d..b2b4147349 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DMA) += dma-uclass.o obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o obj-$(CONFIG_APBH_DMA) += apbh_dma.o +obj-$(CONFIG_BCM6348_IUDMA) += bcm6348-iudma.o obj-$(CONFIG_FSL_DMA) += fsl_dma.o obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o obj-$(CONFIG_TI_EDMA3) += ti-edma3.o diff --git a/drivers/dma/bcm6348-iudma.c b/drivers/dma/bcm6348-iudma.c new file mode 100644 index 0000000000..22d81cc990 --- /dev/null +++ b/drivers/dma/bcm6348-iudma.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2018 Álvaro Fernández Rojas + * + * Derived from linux/drivers/dma/bcm63xx-iudma.c: + * Copyright (C) 2015 Simon Arlott + * + * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c: + * Copyright (C) 2008 Maxime Bizon + * + * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h: + * Copyright (C) 2000-2010 Broadcom Corporation + * + * Derived from bcm963xx_4.12L.06B_consumer/bcmdrivers/opensource/net/enet/impl4/bcmenet.c: + * Copyright (C) 2010 Broadcom Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define ALIGN_END_ADDR(type, ptr, size) \ + ((unsigned long)(ptr) + roundup((size) * sizeof(type), \ + ARCH_DMA_MINALIGN)) + +#define DMA_MAX_BURST_LENGTH 16 + +/* DMA Channels */ +#define DMA_CHAN_FLOWC(x) ((x) >> 1) +#define DMA_CHAN_FLOWC_MAX 8 +#define DMA_CHAN_MAX 16 +#define DMA_CHAN_SIZE 0x10 +#define DMA_CHAN_TOUT 500 + +/* DMA Global Configuration register */ +#define DMA_CFG_REG 0x00 +#define DMA_CFG_ENABLE_SHIFT 0 +#define DMA_CFG_ENABLE_MASK (1 << DMA_CFG_ENABLE_SHIFT) +#define DMA_CFG_FLOWC_ENABLE(x) BIT(DMA_CHAN_FLOWC(x) + 1) +#define DMA_CFG_NCHANS_SHIFT 24 +#define DMA_CFG_NCHANS_MASK (0xf << DMA_CFG_NCHANS_SHIFT) + +/* DMA Global Flow Control Threshold registers */ +#define DMA_FLOWC_THR_LO_REG(x) (0x04 + DMA_CHAN_FLOWC(x) * 0x0c) +#define DMA_FLOWC_THR_LO_SHIFT 0 +#define DMA_FLOWC_THR_LO_MASK (5 << DMA_FLOWC_THR_LO_SHIFT) + +#define DMA_FLOWC_THR_HI_REG(x) (0x08 + DMA_CHAN_FLOWC(x) * 0x0c) +#define DMA_FLOWC_THR_HI_SHIFT 0 +#define DMA_FLOWC_THR_HI_MASK (10 << DMA_FLOWC_THR_HI_SHIFT) + +/* DMA Global Flow Control Buffer Allocation registers */ +#define DMA_FLOWC_ALLOC_REG(x) (0x0c + DMA_CHAN_FLOWC(x) * 0x0c) +#define DMA_FLOWC_ALLOC_FORCE_SHIFT 31 +#define DMA_FLOWC_ALLOC_FORCE_MASK (1 << DMA_FLOWC_ALLOC_FORCE_SHIFT) + +/* DMA Global Reset register */ +#define DMA_RST_REG 0x34 +#define DMA_RST_CHAN_SHIFT 0 +#define DMA_RST_CHAN_MASK(x) (1 << x) + +/* DMA Channel Configuration register */ +#define DMAC_CFG_REG(x) (DMA_CHAN_SIZE * (x) + 0x00) +#define DMAC_CFG_ENABLE_SHIFT 0 +#define DMAC_CFG_ENABLE_MASK (1 << DMAC_CFG_ENABLE_SHIFT) +#define DMAC_CFG_PKT_HALT_SHIFT 1 +#define DMAC_CFG_PKT_HALT_MASK (1 << DMAC_CFG_PKT_HALT_SHIFT) +#define DMAC_CFG_BRST_HALT_SHIFT 2 +#define DMAC_CFG_BRST_HALT_MASK (1 << DMAC_CFG_BRST_HALT_SHIFT) + +/* DMA Channel Interrupts registers */ +#define DMAC_IR_ST_REG(x) (DMA_CHAN_SIZE * (x) + 0x04) +#define DMAC_IR_EN_REG(x) (DMA_CHAN_SIZE * (x) + 0x08) + +#define DMAC_IR_DONE_SHIFT 2 +#define DMAC_IR_DONE_MASK (1 << DMAC_IR_DONE_SHIFT) + +/* DMA Channel Max Burst Length register */ +#define DMAC_BURST_REG(x) (DMA_CHAN_SIZE * (x) + 0x0c) +#define DMAC_BURST_MAX_SHIFT 0 +#define DMAC_BURST_MAX_MASK (16 << DMAC_BURST_MAX_SHIFT) + +/* DMA SRAM Descriptor Ring Start register */ +#define DMAS_RSTART_REG(x) (DMA_CHAN_SIZE * (x) + 0x00) + +/* DMA SRAM State/Bytes done/ring offset register */ +#define DMAS_STATE_DATA_REG(x) (DMA_CHAN_SIZE * (x) + 0x04) + +/* DMA SRAM Buffer Descriptor status and length register */ +#define DMAS_DESC_LEN_STATUS_REG(x) (DMA_CHAN_SIZE * (x) + 0x08) + +/* DMA SRAM Buffer Descriptor status and length register */ +#define DMAS_DESC_BASE_BUFPTR_REG(x) (DMA_CHAN_SIZE * (x) + 0x0c) + +struct bcm6348_dma_desc { + uint16_t length; + + uint16_t status; +#define DMAD_ST_CRC_SHIFT 8 +#define DMAD_ST_CRC_MASK (1 << DMAD_ST_CRC_SHIFT) +#define DMAD_ST_WRAP_SHIFT 12 +#define DMAD_ST_WRAP_MASK (1 << DMAD_ST_WRAP_SHIFT) +#define DMAD_ST_SOP_SHIFT 13 +#define DMAD_ST_SOP_MASK (1 << DMAD_ST_SOP_SHIFT) +#define DMAD_ST_EOP_SHIFT 14 +#define DMAD_ST_EOP_MASK (1 << DMAD_ST_EOP_SHIFT) +#define DMAD_ST_OWN_SHIFT 15 +#define DMAD_ST_OWN_MASK (1 << DMAD_ST_OWN_SHIFT) + + uint32_t address; +} __attribute__((aligned(1))); + +struct bcm6348_chan_priv { + void __iomem *dma_ring; + uint8_t dma_ring_size; + uint8_t desc_id; +}; + +struct bcm6348_iudma_priv { + void __iomem *base; + void __iomem *chan; + void __iomem *sram; + struct bcm6348_chan_priv **ch_priv; + uint8_t n_channels; +}; + +static bool bcm6348_iudma_chan_is_rx(uint8_t ch) +{ + return !(ch & 1); +} + +static void bcm6348_iudma_chan_stop(struct bcm6348_iudma_priv *priv, + uint8_t ch) +{ + unsigned int timeout = DMA_CHAN_TOUT; + + /* disable dma channel interrupts */ + writel_be(0, priv->chan + DMAC_IR_EN_REG(ch)); + + do { + uint32_t cfg, halt; + + if (timeout > DMA_CHAN_TOUT / 2) + halt = DMAC_CFG_PKT_HALT_MASK; + else + halt = DMAC_CFG_BRST_HALT_MASK; + + /* try to stop dma channel */ + writel_be(halt, priv->chan + DMAC_CFG_REG(ch)); + mb(); + + /* check if channel was stopped */ + cfg = readl_be(priv->chan + DMAC_CFG_REG(ch)); + if (!(cfg & DMAC_CFG_ENABLE_MASK)) + break; + + udelay(1); + } while (--timeout); + + if (!timeout) + pr_err("unable to stop channel %u\n", ch); + + /* reset dma channel */ + setbits_be32(priv->base + DMA_RST_REG, DMA_RST_CHAN_MASK(ch)); + clrbits_be32(priv->base + DMA_RST_REG, DMA_RST_CHAN_MASK(ch)); +} + +static int bcm6348_iudma_disable(struct dma *dma) +{ + struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev); + + bcm6348_iudma_chan_stop(priv, dma->id); + + if (bcm6348_iudma_chan_is_rx(dma->id)) + writel_be(DMA_FLOWC_ALLOC_FORCE_MASK, + DMA_FLOWC_ALLOC_REG(dma->id)); + + return 0; +} + +static int bcm6348_iudma_enable(struct dma *dma) +{ + struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev); + struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id]; + struct bcm6348_dma_desc *dma_desc; + uint8_t i; + + /* init dma rings */ + dma_desc = ch_priv->dma_ring; + for (i = 0; i < ch_priv->dma_ring_size; i++) { + if (bcm6348_iudma_chan_is_rx(dma->id)) { + dma_desc->status = DMAD_ST_OWN_MASK; + dma_desc->length = PKTSIZE_ALIGN; + dma_desc->address = virt_to_phys(net_rx_packets[i]); + } else { + dma_desc->status = 0; + dma_desc->length = 0; + dma_desc->address = 0; + } + + if (i == ch_priv->dma_ring_size - 1) + dma_desc->status |= DMAD_ST_WRAP_MASK; + + if (bcm6348_iudma_chan_is_rx(dma->id)) + writel_be(1, + priv->base + DMA_FLOWC_ALLOC_REG(dma->id)); + + dma_desc++; + } + + /* init to first descriptor */ + ch_priv->desc_id = 0; + + /* force cache writeback */ + flush_dcache_range((ulong)ch_priv->dma_ring, + ALIGN_END_ADDR(struct bcm6348_dma_desc, ch_priv->dma_ring, + ch_priv->dma_ring_size)); + + /* clear sram */ + writel_be(0, priv->sram + DMAS_STATE_DATA_REG(dma->id)); + writel_be(0, priv->sram + DMAS_DESC_LEN_STATUS_REG(dma->id)); + writel_be(0, priv->sram + DMAS_DESC_BASE_BUFPTR_REG(dma->id)); + + /* set dma ring start */ + writel_be(virt_to_phys(ch_priv->dma_ring), + priv->sram + DMAS_RSTART_REG(dma->id)); + + /* set flow control */ + if (bcm6348_iudma_chan_is_rx(dma->id)) { + setbits_be32(priv->base + DMA_CFG_REG, + DMA_CFG_FLOWC_ENABLE(dma->id)); + writel_be(DMA_FLOWC_THR_LO_MASK, + priv->base + DMA_FLOWC_THR_LO_REG(dma->id)); + writel_be(DMA_FLOWC_THR_HI_MASK, + priv->base + DMA_FLOWC_THR_HI_REG(dma->id)); + } + + /* set dma max burst */ + writel_be(DMA_MAX_BURST_LENGTH, priv->chan + DMAC_BURST_REG(dma->id)); + + /* clear interrupts */ + writel_be(DMAC_IR_DONE_MASK, priv->chan + DMAC_IR_ST_REG(dma->id)); + writel_be(0, priv->chan + DMAC_IR_EN_REG(dma->id)); + + setbits_be32(priv->chan + DMAC_CFG_REG(dma->id), DMAC_CFG_ENABLE_MASK); + + return 0; +} + +static int bcm6348_iudma_request(struct dma *dma) +{ + struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev); + struct bcm6348_chan_priv *ch_priv; + + /* check if channel is valid */ + if (dma->id >= priv->n_channels) + return -ENODEV; + + /* alloc channel private data */ + priv->ch_priv[dma->id] = calloc(1, sizeof(struct bcm6348_chan_priv)); + if (!priv->ch_priv[dma->id]) + return -ENOMEM; + ch_priv = priv->ch_priv[dma->id]; + + /* alloc dma ring */ + if (bcm6348_iudma_chan_is_rx(dma->id)) + ch_priv->dma_ring_size = PKTBUFSRX; + else + ch_priv->dma_ring_size = 1; + ch_priv->dma_ring = + malloc_cache_aligned(sizeof(struct bcm6348_dma_desc) * + ch_priv->dma_ring_size); + if (!ch_priv->dma_ring) + return -ENOMEM; + + return 0; +} + +static int bcm6348_iudma_receive(struct dma *dma, void **dst) +{ + struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev); + struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id]; + struct bcm6348_dma_desc *dma_desc; + void __iomem *dma_buff; + uint16_t status; + int ret; + + /* get dma ring descriptor address */ + dma_desc = ch_priv->dma_ring; + dma_desc += ch_priv->desc_id; + + /* invalidate cache data */ + invalidate_dcache_range((ulong)dma_desc, + ALIGN_END_ADDR(struct bcm6348_dma_desc, dma_desc, 1)); + + /* check dma own */ + if (dma_desc->status & DMAD_ST_OWN_MASK) + return 0; + + /* check dma end */ + if (!(dma_desc->status & DMAD_ST_EOP_MASK)) + return -EINVAL; + + /* get dma buff descriptor address */ + dma_buff = phys_to_virt(dma_desc->address); + + /* invalidate cache data */ + invalidate_dcache_range((ulong)dma_buff, + (ulong)(dma_buff + PKTSIZE_ALIGN)); + + /* get dma data */ + *dst = dma_buff; + ret = dma_desc->length; + + /* reinit dma descriptor */ + status = dma_desc->status & DMAD_ST_WRAP_MASK; + status |= DMAD_ST_OWN_MASK; + + dma_desc->length = PKTSIZE_ALIGN; + dma_desc->status = status; + + /* flush cache */ + flush_dcache_range((ulong)dma_desc, + ALIGN_END_ADDR(struct bcm6348_dma_desc, dma_desc, 1)); + + /* set flow control buffer alloc */ + writel_be(1, priv->base + DMA_FLOWC_ALLOC_REG(dma->id)); + + /* enable dma */ + setbits_be32(priv->chan + DMAC_CFG_REG(dma->id), DMAC_CFG_ENABLE_MASK); + + /* set interrupt */ + writel_be(DMAC_IR_DONE_MASK, priv->chan + DMAC_IR_EN_REG(dma->id)); + + /* increment dma descriptor */ + ch_priv->desc_id = (ch_priv->desc_id + 1) % ch_priv->dma_ring_size; + + return ret - 4; +} + +static int bcm6348_iudma_send(struct dma *dma, void *src, size_t len) +{ + struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev); + struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id]; + struct bcm6348_dma_desc *dma_desc; + uint16_t val; + + /* get dma ring descriptor address */ + dma_desc = ch_priv->dma_ring; + dma_desc += ch_priv->desc_id; + + dma_desc->address = virt_to_phys(src); + + /* config dma descriptor */ + val = (DMAD_ST_OWN_MASK | + DMAD_ST_EOP_MASK | + DMAD_ST_CRC_MASK | + DMAD_ST_SOP_MASK); + if (ch_priv->desc_id == ch_priv->dma_ring_size - 1) + val |= DMAD_ST_WRAP_MASK; + + dma_desc->length = len; + dma_desc->status = val; + + /* flush cache */ + flush_dcache_range((ulong)src, (ulong)src + PKTSIZE_ALIGN); + + /* flush cache */ + flush_dcache_range((ulong)dma_desc, + ALIGN_END_ADDR(struct bcm6348_dma_desc, dma_desc, 1)); + + /* enable dma */ + setbits_be32(priv->chan + DMAC_CFG_REG(dma->id), DMAC_CFG_ENABLE_MASK); + + /* set interrupt */ + writel_be(DMAC_IR_DONE_MASK, priv->chan + DMAC_IR_EN_REG(dma->id)); + + /* poll dma status */ + do { + /* invalidate cache */ + invalidate_dcache_range((ulong)dma_desc, + ALIGN_END_ADDR(struct bcm6348_dma_desc, dma_desc, 1)); + + if (!(dma_desc->status & DMAD_ST_OWN_MASK)) + break; + } while(1); + + /* increment dma descriptor */ + ch_priv->desc_id = (ch_priv->desc_id + 1) % ch_priv->dma_ring_size; + + return 0; +} + +static const struct dma_ops bcm6348_iudma_ops = { + .disable = bcm6348_iudma_disable, + .enable = bcm6348_iudma_enable, + .request = bcm6348_iudma_request, + .receive = bcm6348_iudma_receive, + .send = bcm6348_iudma_send, +}; + +static const struct udevice_id bcm6348_iudma_ids[] = { + { .compatible = "brcm,bcm6348-iudma", }, + { /* sentinel */ } +}; + +static int bcm6348_iudma_probe(struct udevice *dev) +{ + struct bcm6348_iudma_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + uint8_t ch; + int i; + + /* try to enable clocks */ + for (i = 0; ; i++) { + struct clk clk; + int ret; + + ret = clk_get_by_index(dev, i, &clk); + if (ret < 0) + break; + if (clk_enable(&clk)) + pr_err("failed to enable clock %d\n", i); + clk_free(&clk); + } + + /* try to perform resets */ + for (i = 0; ; i++) { + struct reset_ctl reset; + int ret; + + ret = reset_get_by_index(dev, i, &reset); + if (ret < 0) + break; + if (reset_deassert(&reset)) + pr_err("failed to deassert reset %d\n", i); + reset_free(&reset); + } + + /* dma global base address */ + addr = devfdt_get_addr_name(dev, "dma"); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->base = ioremap(addr, 0); + + /* dma channels base address */ + addr = devfdt_get_addr_name(dev, "dma-channels"); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->chan = ioremap(addr, 0); + + /* dma sram base address */ + addr = devfdt_get_addr_name(dev, "dma-sram"); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->sram = ioremap(addr, 0); + + /* disable dma controller */ + clrbits_be32(priv->base + DMA_CFG_REG, DMA_CFG_ENABLE_MASK); + + /* get number of channels */ + priv->n_channels = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "dma-channels", 8); + if (priv->n_channels > DMA_CHAN_MAX) + return -EINVAL; + + /* alloc channel private data pointers */ + priv->ch_priv = calloc(priv->n_channels, + sizeof(struct bcm6348_chan_priv*)); + if (!priv->ch_priv) + return -ENOMEM; + + /* stop dma channels */ + for (ch = 0; ch < priv->n_channels; ch++) + bcm6348_iudma_chan_stop(priv, ch); + + /* enable dma controller */ + setbits_be32(priv->base + DMA_CFG_REG, DMA_CFG_ENABLE_MASK); + + return 0; +} + +U_BOOT_DRIVER(bcm6348_iudma) = { + .name = "bcm6348_iudma", + .id = UCLASS_DMA, + .of_match = bcm6348_iudma_ids, + .ops = &bcm6348_iudma_ops, + .priv_auto_alloc_size = sizeof(struct bcm6348_iudma_priv), + .probe = bcm6348_iudma_probe, +};