Skip to content

Commit fc6dc37

Browse files
pelwellpopcornmix
authored andcommitted
dmaengine: dw-axi-dmac: Add DMA channel selection
Add a mechanism to allow clients to prefer some DMA channels over others. This is required to allow high-bandwidth clients to request one of the two "heavy" channels, but could also be used to prevent some clients from hogging all channels. Signed-off-by: Phil Elwell <phil@raspberrypi.com>
1 parent 30784f1 commit fc6dc37

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,19 +1476,75 @@ static int __maybe_unused axi_dma_runtime_resume(struct device *dev)
14761476
return axi_dma_resume(chip);
14771477
}
14781478

1479+
static bool dw_axi_dma_filter_fn(struct dma_chan *dchan, void *filter_param)
1480+
{
1481+
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
1482+
uint32_t selector = *(const uint32_t *)filter_param;
1483+
1484+
return !!(selector & (1 << chan->id));
1485+
}
1486+
14791487
static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec,
14801488
struct of_dma *ofdma)
14811489
{
14821490
struct dw_axi_dma *dw = ofdma->of_dma_data;
14831491
struct axi_dma_chan *chan;
1492+
uint32_t chan_flags_all;
1493+
uint32_t busy_channels;
14841494
struct dma_chan *dchan;
1495+
dma_cap_mask_t mask;
1496+
uint32_t chan_mask;
1497+
uint32_t chan_sel;
1498+
int max_score;
1499+
int score;
1500+
int i;
14851501

1486-
dchan = dma_get_any_slave_channel(&dw->dma);
1487-
if (!dchan)
1488-
return NULL;
1502+
for (i = 0; i < dw->hdata->nr_channels; i++)
1503+
chan_flags_all |= dw->chan_flags[i];
1504+
1505+
dma_cap_zero(mask);
1506+
dma_cap_set(DMA_SLAVE, mask);
1507+
1508+
chan_sel = dma_spec->args[0];
1509+
busy_channels = 0;
1510+
dchan = NULL;
1511+
1512+
while (1) {
1513+
max_score = 0;
1514+
chan_mask = 0;
1515+
1516+
for (i = 0; i < dw->hdata->nr_channels; i++) {
1517+
if (busy_channels & (1 << i))
1518+
continue;
1519+
/*
1520+
* Positive matches (wanted flags that match) score twice that of
1521+
* negetive matches (not wanted flags that are not present).
1522+
*/
1523+
score = 2 * hweight32(chan_sel & dw->chan_flags[i]) +
1524+
1 * hweight32(~chan_sel & ~dw->chan_flags[i] & chan_flags_all);
1525+
if (score > max_score) {
1526+
max_score = score;
1527+
chan_mask = (1 << i);
1528+
} else if (score == max_score) {
1529+
chan_mask |= (1 << i);
1530+
}
1531+
}
1532+
1533+
if (!chan_mask)
1534+
return NULL;
1535+
1536+
dchan = __dma_request_channel(&mask, dw_axi_dma_filter_fn,
1537+
&chan_mask, ofdma->of_node);
1538+
if (dchan)
1539+
break;
1540+
1541+
/* Repeat, after first marking this group of channels as busy */
1542+
busy_channels |= chan_mask;
1543+
}
14891544

14901545
chan = dchan_to_axi_dma_chan(dchan);
1491-
chan->hw_handshake_num = dma_spec->args[0];
1546+
chan->hw_handshake_num = (u8)chan_sel;
1547+
14921548
return dchan;
14931549
}
14941550

@@ -1570,6 +1626,15 @@ static int parse_device_properties(struct axi_dma_chip *chip)
15701626
}
15711627
}
15721628

1629+
/* snps,chan-flags is optional */
1630+
memset(chip->dw->chan_flags, 0, sizeof(chip->dw->chan_flags));
1631+
if (device_property_read_u32_array(dev, "snps,chan-flags",
1632+
chip->dw->chan_flags,
1633+
chip->dw->hdata->nr_channels) < 0)
1634+
device_property_read_u32_array(dev, "snps,sel-require",
1635+
chip->dw->chan_flags,
1636+
chip->dw->hdata->nr_channels);
1637+
15731638
return 0;
15741639
}
15751640

drivers/dma/dw-axi-dmac/dw-axi-dmac.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct dw_axi_dma {
5858
struct dma_device dma;
5959
struct dw_axi_dma_hcfg *hdata;
6060
struct device_dma_parameters dma_parms;
61+
u32 chan_flags[DMAC_MAX_CHANNELS];
6162

6263
/* channels */
6364
struct axi_dma_chan *chan;

0 commit comments

Comments
 (0)