Skip to content

Commit

Permalink
dma: axi-dmac: Discover length alignment requirement
Browse files Browse the repository at this point in the history
Starting with version 4.1.a the AXI-DMAC is capable of reporting the
required length alignment.

The LSBs that are required to be set for alignment will always read back as
set from the transfer length register. It is not possible to clear them by
writing a 0. This means the driver can discover the length alignment
requirement by writing 0 to that register and reading back the value.

Since the DMA will support length alignment requirements that are different
from the address alignment requirement track both of them independently.

For older versions of the peripheral assume that the length alignment
requirement is equal to the address alignment requirement.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
  • Loading branch information
larsclausen authored and commodo committed Jul 4, 2018
1 parent 9eb75c4 commit d95bc0b
Showing 1 changed file with 19 additions and 6 deletions.
25 changes: 19 additions & 6 deletions drivers/dma/dma-axi-dmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
* there is no address than can or needs to be configured for the device side.
*/

#define AXI_DMAC_REG_VERSION 0x00

#define AXI_DMAC_REG_IRQ_MASK 0x80
#define AXI_DMAC_REG_IRQ_PENDING 0x84
#define AXI_DMAC_REG_IRQ_SOURCE 0x88
Expand Down Expand Up @@ -117,7 +119,8 @@ struct axi_dmac_chan {
unsigned int dest_type;

unsigned int max_length;
unsigned int align_mask;
unsigned int address_align_mask;
unsigned int length_align_mask;

bool hw_cyclic;
bool hw_2d;
Expand Down Expand Up @@ -181,14 +184,14 @@ static bool axi_dmac_check_len(struct axi_dmac_chan *chan, unsigned int len)
{
if (len == 0)
return false;
if ((len & chan->align_mask) != 0) /* Not aligned */
if ((len & chan->length_align_mask) != 0) /* Not aligned */
return false;
return true;
}

static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr)
{
if ((addr & chan->align_mask) != 0) /* Not aligned */
if ((addr & chan->address_align_mask) != 0) /* Not aligned */
return false;
return true;
}
Expand Down Expand Up @@ -442,7 +445,7 @@ static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan,
num_segments = DIV_ROUND_UP(period_len, chan->max_length);
segment_size = DIV_ROUND_UP(period_len, num_segments);
/* Take care of alignment */
segment_size = ((segment_size - 1) | chan->align_mask) + 1;
segment_size = ((segment_size - 1) | chan->length_align_mask) + 1;

for (i = 0; i < num_periods; i++) {
len = period_len;
Expand Down Expand Up @@ -705,7 +708,7 @@ static int axi_dmac_parse_chan_dt(struct device_node *of_chan,
return ret;
chan->dest_width = val / 8;

chan->align_mask = max(chan->dest_width, chan->src_width) - 1;
chan->address_align_mask = max(chan->dest_width, chan->src_width) - 1;

if (axi_dmac_dest_is_mem(chan) && axi_dmac_src_is_mem(chan))
chan->direction = DMA_MEM_TO_MEM;
Expand Down Expand Up @@ -766,14 +769,17 @@ static int axi_dmac_parse_chan_dt_compat(struct device_node *of_node,
of_property_read_u32(of_chan, "adi,destination-bus-width", &tmp);
chan->dest_width = tmp / 8;

chan->align_mask = max(chan->dest_width, chan->src_width) - 1;
chan->address_align_mask = max(chan->dest_width, chan->src_width) - 1;

return 0;
}

static int axi_dmac_detect_caps(struct axi_dmac *dmac)
{
struct axi_dmac_chan *chan = &dmac->chan;
unsigned int version;

version = axi_dmac_read(dmac, AXI_DMAC_REG_VERSION);

axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, AXI_DMAC_FLAG_CYCLIC);
if (axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS) == AXI_DMAC_FLAG_CYCLIC)
Expand Down Expand Up @@ -804,6 +810,13 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac)
return -ENODEV;
}

if ((version & 0xff00) >= 0x0100) {
axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, 0x00);
chan->length_align_mask = axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH);
} else {
chan->length_align_mask = chan->address_align_mask;
}

return 0;
}

Expand Down

0 comments on commit d95bc0b

Please sign in to comment.