[PATCH RFC 10/11] dmaengine: bcm2835: add BCM2711 40-bit DMA support

Vinod Koul vkoul at kernel.org
Tue Feb 15 03:19:55 PST 2022


On 27-12-21, 13:06, Stefan Wahren wrote:
> BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
> to access the full 4GB of memory on a Pi 4. Assume every channel is capable
> of 40-bit address range.
> 
> Signed-off-by: Stefan Wahren <stefan.wahren at i2se.com>
> ---
>  drivers/dma/bcm2835-dma.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 243 insertions(+)
> 
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 7159fa2..83343f9 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -36,6 +36,7 @@
>  
>  #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
>  #define BCM2835_DMA_CHAN_NAME_SIZE 8
> +#define BCM2711_DMA40_PHYS_ADDR 0x400000000ULL
>  
>  /**
>   * struct bcm2835_dmadev - BCM2835 DMA controller
> @@ -65,6 +66,17 @@ struct bcm2835_dma_cb {
>  	uint32_t pad[2];
>  };
>  
> +struct bcm2711_dma40_scb {
> +	uint32_t ti;
> +	uint32_t src;
> +	uint32_t srci;
> +	uint32_t dst;
> +	uint32_t dsti;
> +	uint32_t len;
> +	uint32_t next_cb;
> +	uint32_t rsvd;
> +};
> +
>  struct bcm2835_cb_entry {
>  	struct bcm_dma_cb *cb;
>  	dma_addr_t paddr;
> @@ -205,6 +217,49 @@ struct bcm2835_desc {
>  #define BCM2835_DMA_CHAN(n)	((n) << 8) /* Base address */
>  #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
>  
> +/* 40-bit DMA support */
> +#define BCM2711_DMA40_CS	0x00
> +#define BCM2711_DMA40_CB	0x04
> +#define BCM2711_DMA40_DEBUG	0x0c
> +#define BCM2711_DMA40_TI	0x10
> +#define BCM2711_DMA40_SRC	0x14
> +#define BCM2711_DMA40_SRCI	0x18
> +#define BCM2711_DMA40_DEST	0x1c
> +#define BCM2711_DMA40_DESTI	0x20
> +#define BCM2711_DMA40_LEN	0x24
> +#define BCM2711_DMA40_NEXT_CB	0x28
> +#define BCM2711_DMA40_DEBUG2	0x2c
> +
> +#define BCM2711_DMA40_ACTIVE		BIT(0)
> +#define BCM2711_DMA40_END		BIT(1)
> +#define BCM2711_DMA40_INT		BIT(2)
> +#define BCM2711_DMA40_DREQ		BIT(3)  /* DREQ state */
> +#define BCM2711_DMA40_RD_PAUSED		BIT(4)  /* Reading is paused */
> +#define BCM2711_DMA40_WR_PAUSED		BIT(5)  /* Writing is paused */
> +#define BCM2711_DMA40_DREQ_PAUSED	BIT(6)  /* Is paused by DREQ flow control */
> +#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7)  /* Waiting for last write */
> +#define BCM2711_DMA40_ERR		BIT(10)
> +#define BCM2711_DMA40_QOS(x)		(((x) & 0x1f) << 16)
> +#define BCM2711_DMA40_PANIC_QOS(x)	(((x) & 0x1f) << 20)

Use FIELD_PREP() for this ?

> +#define BCM2711_DMA40_WAIT_FOR_WRITES	BIT(28)
> +#define BCM2711_DMA40_DISDEBUG		BIT(29)
> +#define BCM2711_DMA40_ABORT		BIT(30)
> +#define BCM2711_DMA40_HALT		BIT(31)
> +
> +/* Transfer information bits */
> +#define BCM2711_DMA40_INTEN		BIT(0)
> +#define BCM2711_DMA40_TDMODE		BIT(1) /* 2D-Mode */
> +#define BCM2711_DMA40_WAIT_RESP		BIT(2) /* wait for AXI write to be acked */
> +#define BCM2711_DMA40_WAIT_RD_RESP	BIT(3) /* wait for AXI read to complete */
> +#define BCM2711_DMA40_PER_MAP(x)	((x & 31) << 9) /* REQ source */
> +#define BCM2711_DMA40_S_DREQ		BIT(14) /* enable SREQ for source */
> +#define BCM2711_DMA40_D_DREQ		BIT(15) /* enable DREQ for destination */
> +#define BCM2711_DMA40_S_WAIT(x)		((x & 0xff) << 16) /* add DMA read-wait cycles */
> +#define BCM2711_DMA40_D_WAIT(x)		((x & 0xff) << 24) /* add DMA write-wait cycles */

Use FIELD_PREP() for this ?

> +
> +#define BCM2711_DMA40_INC		BIT(12)
> +#define BCM2711_DMA40_IGNORE		BIT(15)
> +
>  /* the max dma length for different channels */
>  #define MAX_DMA_LEN SZ_1G
>  #define MAX_LITE_DMA_LEN (SZ_64K - 4)
> @@ -297,6 +352,53 @@ static u32 bcm2835_dma_prepare_cb_extra(struct bcm2835_chan *c,
>  	return result;
>  }
>  
> +static u32 bcm2711_dma_prepare_cb_info(struct bcm2835_chan *c,
> +				       enum dma_transfer_direction direction,
> +				       bool zero_page)
> +{
> +	u32 result;
> +
> +	if (direction == DMA_MEM_TO_MEM)
> +		return 0;
> +
> +	result = BCM2711_DMA40_WAIT_RESP;
> +
> +	/* Setup DREQ channel */
> +	if (c->dreq != 0)
> +		result |= BCM2711_DMA40_PER_MAP(c->dreq);
> +
> +	if (direction == DMA_DEV_TO_MEM) {
> +		result |= BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP;
> +	} else {
> +		result |= BCM2711_DMA40_D_DREQ;
> +	}
> +
> +	return result;
> +}
> +
> +static u32 bcm2711_dma_prepare_cb_extra(struct bcm2835_chan *c,
> +					enum dma_transfer_direction direction,
> +					bool cyclic, bool final,
> +					unsigned long flags)
> +{
> +	u32 result = 0;
> +
> +	if (cyclic) {
> +		if (flags & DMA_PREP_INTERRUPT)
> +			result |= BCM2711_DMA40_INTEN;
> +	} else {
> +		if (!final)
> +			return 0;
> +
> +		result |= BCM2711_DMA40_INTEN;
> +
> +		if (direction == DMA_MEM_TO_MEM)
> +			result |= BCM2711_DMA40_WAIT_RESP;
> +	}
> +
> +	return result;
> +}
> +
>  static inline bool need_src_incr(enum dma_transfer_direction direction)
>  {
>  	return direction != DMA_DEV_TO_MEM;
> @@ -413,6 +515,120 @@ static dma_addr_t bcm2835_dma_read_addr(struct bcm2835_chan *c,
>  	return 0;
>  }
>  
> +static inline u32 bcm2711_dma_cb_get_length(void *data)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	return scb->len;
> +}
> +
> +static inline dma_addr_t
> +bcm2711_dma_cb_get_addr(void *data, enum dma_transfer_direction direction)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	if (direction == DMA_DEV_TO_MEM)
> +		return scb->dst + ((scb->dsti & 0xff) << 8);
> +
> +	return scb->src + ((scb->srci & 0xff) << 8);
> +}
> +
> +static inline void
> +bcm2711_dma_cb_init(void *data, struct bcm2835_chan *c,
> +		    enum dma_transfer_direction direction, u32 src, u32 dst,
> +		    bool zero_page)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	scb->ti = bcm2711_dma_prepare_cb_info(c, direction, zero_page);
> +	scb->src = lower_32_bits(src);
> +	scb->srci = upper_32_bits(src);
> +
> +	if (need_src_incr(direction))
> +		scb->srci |= BCM2711_DMA40_INC;
> +
> +	scb->dst = lower_32_bits(dst);
> +	scb->dsti = upper_32_bits(dst);
> +
> +	if (need_dst_incr(direction))
> +		scb->dsti |= BCM2711_DMA40_INC;
> +
> +	scb->next_cb = 0;
> +}
> +
> +static inline void
> +bcm2711_dma_cb_set_src(void *data, enum dma_transfer_direction direction,
> +		       u32 src)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	scb->src = lower_32_bits(src);
> +	scb->srci = upper_32_bits(src);
> +
> +	if (need_src_incr(direction))
> +		scb->srci |= BCM2711_DMA40_INC;
> +}
> +
> +static inline void
> +bcm2711_dma_cb_set_dst(void *data, enum dma_transfer_direction direction,
> +		       u32 dst)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	scb->dst = lower_32_bits(dst);
> +	scb->dsti = upper_32_bits(dst);
> +
> +	if (need_dst_incr(direction))
> +		scb->dsti |= BCM2711_DMA40_INC;
> +}
> +
> +static inline void bcm2711_dma_cb_set_next(void *data, u32 next)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	scb->next_cb = next;
> +}
> +
> +static inline void bcm2711_dma_cb_set_length(void *data, u32 length)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	scb->len = length;
> +}
> +
> +static inline void
> +bcm2711_dma_cb_append_extra(void *data, struct bcm2835_chan *c,
> +			    enum dma_transfer_direction direction,
> +			    bool cyclic, bool final, unsigned long flags)
> +{
> +	struct bcm2711_dma40_scb *scb = data;
> +
> +	scb->ti |= bcm2711_dma_prepare_cb_extra(c, direction, cyclic, final,
> +						flags);
> +}

this helper is just a wrapper over bcm2711_dma_prepare_cb_extra() do
consider getting rid of extra layer...

> +
> +static inline dma_addr_t bcm2711_dma_to_cb_addr(dma_addr_t addr)
> +{
> +	return (addr >> 5);
> +}
> +
> +static void bcm2711_dma_chan_plat_init(struct bcm2835_chan *c)
> +{
> +}
> +
> +static dma_addr_t bcm2711_dma_read_addr(struct bcm2835_chan *c,
> +					enum dma_transfer_direction direction)
> +{
> +	if (direction == DMA_MEM_TO_DEV)
> +		return readl(c->chan_base + BCM2711_DMA40_SRC) +
> +			((readl(c->chan_base + BCM2711_DMA40_SRCI) & 0xff) << 8);
> +	else if (direction == DMA_DEV_TO_MEM)
> +		return readl(c->chan_base + BCM2711_DMA40_DEST) +
> +			((readl(c->chan_base + BCM2711_DMA40_DESTI) & 0xff) << 8);
> +
> +	return 0;
> +}
> +
>  static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
>  {
>  	size_t i;
> @@ -1070,8 +1286,35 @@ static const struct bcm2835_dma_cfg bcm2835_data = {
>  	.read_addr = bcm2835_dma_read_addr,
>  };
>  
> +static const struct bcm2835_dma_cfg bcm2711_data = {
> +	.addr_offset = BCM2711_DMA40_PHYS_ADDR,
> +
> +	.cs_reg = BCM2711_DMA40_CS,
> +	.cb_reg = BCM2711_DMA40_CB,
> +
> +	.wait_mask = BCM2711_DMA40_WAITING_FOR_WRITES,
> +	.reset_mask = BCM2711_DMA40_HALT,
> +	.int_mask = BCM2711_DMA40_INTEN,
> +	.active_mask = BCM2711_DMA40_ACTIVE,
> +
> +	.cb_get_length = bcm2711_dma_cb_get_length,
> +	.cb_get_addr = bcm2711_dma_cb_get_addr,
> +	.cb_init = bcm2711_dma_cb_init,
> +	.cb_set_src = bcm2711_dma_cb_set_src,
> +	.cb_set_dst = bcm2711_dma_cb_set_dst,
> +	.cb_set_next = bcm2711_dma_cb_set_next,
> +	.cb_set_length = bcm2711_dma_cb_set_length,
> +	.cb_append_extra = bcm2711_dma_cb_append_extra,
> +
> +	.to_cb_addr = bcm2711_dma_to_cb_addr,
> +
> +	.chan_plat_init = bcm2711_dma_chan_plat_init,
> +	.read_addr = bcm2711_dma_read_addr,
> +};
> +
>  static const struct of_device_id bcm2835_dma_of_match[] = {
>  	{ .compatible = "brcm,bcm2835-dma", .data = &bcm2835_data },
> +	{ .compatible = "brcm,bcm2711-dma", .data = &bcm2711_data },
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
> -- 
> 2.7.4

-- 
~Vinod



More information about the linux-arm-kernel mailing list