Skip to content

Commit 2efed8d

Browse files
committed
fixup! dmaengine: dw-axi-dmac: Add DMA channel selection
Simplify and improve the channel selection scheme by removing the concept of excluded channels. Requesting more channels than are available is a configuration error, but otherwise it is better to get a non-ideal channel than no channel at all. Give each channel a score based on how well it matches the client's requirements, then attempt to claim them in order of decreasing score. A channel which possesses a required flag is scored more highly than one that doesn't, but not having a trait that isn't wanted also contributes to a channel's score, just less so. By walking the list until the end, even channels that don't match at all will be allocated if there is enough demand. Signed-off-by: Phil Elwell <phil@raspberrypi.com>
1 parent 06a89f0 commit 2efed8d

File tree

2 files changed

+54
-29
lines changed

2 files changed

+54
-29
lines changed

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

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,33 +1499,62 @@ static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec,
14991499
{
15001500
struct dw_axi_dma *dw = ofdma->of_dma_data;
15011501
struct axi_dma_chan *chan;
1502+
uint32_t chan_flags_all;
1503+
uint32_t busy_channels;
15021504
struct dma_chan *dchan;
1503-
uint32_t chan_mask = 0;
1504-
uint32_t chan_sel;
15051505
dma_cap_mask_t mask;
1506+
uint32_t chan_mask;
1507+
uint32_t chan_sel;
1508+
int max_score;
1509+
int score;
15061510
int i;
15071511

1508-
/*
1509-
* Walk through all channels looking for the best match.
1510-
* Starting from 0, choose the first available slave channel which isn't precluded.
1511-
*/
1512-
chan_sel = dma_spec->args[0];
1513-
1514-
for (i = 0; i < dw->hdata->nr_channels; i++) {
1515-
if (((dw->sel_precluded[i] & chan_sel) == 0) &&
1516-
((dw->sel_required[i] & chan_sel) == dw->sel_required[i]))
1517-
chan_mask |= (1 << i);
1518-
}
1512+
for (i = 0; i < dw->hdata->nr_channels; i++)
1513+
chan_flags_all |= dw->chan_flags[i];
15191514

15201515
dma_cap_zero(mask);
15211516
dma_cap_set(DMA_SLAVE, mask);
15221517

1523-
dchan = __dma_request_channel(&mask, dw_axi_dma_filter_fn, &chan_mask, ofdma->of_node);
1524-
if (!dchan)
1525-
return NULL;
1518+
chan_sel = dma_spec->args[0];
1519+
busy_channels = 0;
1520+
dchan = NULL;
1521+
1522+
while (1) {
1523+
max_score = 0;
1524+
chan_mask = 0;
1525+
1526+
for (i = 0; i < dw->hdata->nr_channels; i++) {
1527+
if (busy_channels & (1 << i))
1528+
continue;
1529+
/*
1530+
* Positive matches (wanted flags that match) score twice that of
1531+
* negetive matches (not wanted flags that are not present).
1532+
*/
1533+
score = 2 * hweight32(chan_sel & dw->chan_flags[i]) +
1534+
1 * hweight32(~chan_sel & ~dw->chan_flags[i] & chan_flags_all);
1535+
if (score > max_score) {
1536+
max_score = score;
1537+
chan_mask = (1 << i);
1538+
} else if (score == max_score) {
1539+
chan_mask |= (1 << i);
1540+
}
1541+
}
1542+
1543+
if (!chan_mask)
1544+
return NULL;
1545+
1546+
dchan = __dma_request_channel(&mask, dw_axi_dma_filter_fn,
1547+
&chan_mask, ofdma->of_node);
1548+
if (dchan)
1549+
break;
1550+
1551+
/* Repeat, after first marking this group of channels as busy */
1552+
busy_channels |= chan_mask;
1553+
}
15261554

15271555
chan = dchan_to_axi_dma_chan(dchan);
15281556
chan->hw_handshake_num = (u8)chan_sel;
1557+
15291558
return dchan;
15301559
}
15311560

@@ -1607,17 +1636,14 @@ static int parse_device_properties(struct axi_dma_chip *chip)
16071636
}
16081637
}
16091638

1610-
/* sel-require is optional */
1611-
memset(chip->dw->sel_required, 0, sizeof(chip->dw->sel_required));
1612-
device_property_read_u32_array(dev, "snps,sel-require",
1613-
chip->dw->sel_required,
1614-
chip->dw->hdata->nr_channels);
1615-
1616-
/* sel-preclude is optional */
1617-
memset(chip->dw->sel_precluded, 0, sizeof(chip->dw->sel_precluded));
1618-
device_property_read_u32_array(dev, "snps,sel-preclude",
1619-
chip->dw->sel_precluded,
1620-
chip->dw->hdata->nr_channels);
1639+
/* snps,chan-flags is optional */
1640+
memset(chip->dw->chan_flags, 0, sizeof(chip->dw->chan_flags));
1641+
if (device_property_read_u32_array(dev, "snps,chan-flags",
1642+
chip->dw->chan_flags,
1643+
chip->dw->hdata->nr_channels) < 0)
1644+
device_property_read_u32_array(dev, "snps,sel-require",
1645+
chip->dw->chan_flags,
1646+
chip->dw->hdata->nr_channels);
16211647

16221648
return 0;
16231649
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +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 sel_required[DMAC_MAX_CHANNELS];
62-
u32 sel_precluded[DMAC_MAX_CHANNELS];
61+
u32 chan_flags[DMAC_MAX_CHANNELS];
6362

6463
/* channels */
6564
struct axi_dma_chan *chan;

0 commit comments

Comments
 (0)