|
@@ -171,8 +171,7 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static int shdma_setup_slave(struct shdma_chan *schan, int slave_id,
|
|
|
- dma_addr_t slave_addr)
|
|
|
+static int shdma_setup_slave(struct shdma_chan *schan, dma_addr_t slave_addr)
|
|
|
{
|
|
|
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
|
|
|
const struct shdma_ops *ops = sdev->ops;
|
|
@@ -183,25 +182,23 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id,
|
|
|
ret = ops->set_slave(schan, match, slave_addr, true);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
-
|
|
|
- slave_id = schan->slave_id;
|
|
|
} else {
|
|
|
- match = slave_id;
|
|
|
+ match = schan->real_slave_id;
|
|
|
}
|
|
|
|
|
|
- if (slave_id < 0 || slave_id >= slave_num)
|
|
|
+ if (schan->real_slave_id < 0 || schan->real_slave_id >= slave_num)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (test_and_set_bit(slave_id, shdma_slave_used))
|
|
|
+ if (test_and_set_bit(schan->real_slave_id, shdma_slave_used))
|
|
|
return -EBUSY;
|
|
|
|
|
|
ret = ops->set_slave(schan, match, slave_addr, false);
|
|
|
if (ret < 0) {
|
|
|
- clear_bit(slave_id, shdma_slave_used);
|
|
|
+ clear_bit(schan->real_slave_id, shdma_slave_used);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- schan->slave_id = slave_id;
|
|
|
+ schan->slave_id = schan->real_slave_id;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -221,10 +218,12 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan)
|
|
|
*/
|
|
|
if (slave) {
|
|
|
/* Legacy mode: .private is set in filter */
|
|
|
- ret = shdma_setup_slave(schan, slave->slave_id, 0);
|
|
|
+ schan->real_slave_id = slave->slave_id;
|
|
|
+ ret = shdma_setup_slave(schan, 0);
|
|
|
if (ret < 0)
|
|
|
goto esetslave;
|
|
|
} else {
|
|
|
+ /* Normal mode: real_slave_id was set by filter */
|
|
|
schan->slave_id = -EINVAL;
|
|
|
}
|
|
|
|
|
@@ -258,11 +257,14 @@ esetslave:
|
|
|
|
|
|
/*
|
|
|
* This is the standard shdma filter function to be used as a replacement to the
|
|
|
- * "old" method, using the .private pointer. If for some reason you allocate a
|
|
|
- * channel without slave data, use something like ERR_PTR(-EINVAL) as a filter
|
|
|
+ * "old" method, using the .private pointer.
|
|
|
+ * You always have to pass a valid slave id as the argument, old drivers that
|
|
|
+ * pass ERR_PTR(-EINVAL) as a filter parameter and set it up in dma_slave_config
|
|
|
+ * need to be updated so we can remove the slave_id field from dma_slave_config.
|
|
|
* parameter. If this filter is used, the slave driver, after calling
|
|
|
* dma_request_channel(), will also have to call dmaengine_slave_config() with
|
|
|
- * .slave_id, .direction, and either .src_addr or .dst_addr set.
|
|
|
+ * .direction, and either .src_addr or .dst_addr set.
|
|
|
+ *
|
|
|
* NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE
|
|
|
* capability! If this becomes a requirement, hardware glue drivers, using this
|
|
|
* services would have to provide their own filters, which first would check
|
|
@@ -276,7 +278,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg)
|
|
|
{
|
|
|
struct shdma_chan *schan;
|
|
|
struct shdma_dev *sdev;
|
|
|
- int match = (long)arg;
|
|
|
+ int slave_id = (long)arg;
|
|
|
int ret;
|
|
|
|
|
|
/* Only support channels handled by this driver. */
|
|
@@ -284,19 +286,39 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg)
|
|
|
shdma_alloc_chan_resources)
|
|
|
return false;
|
|
|
|
|
|
- if (match < 0)
|
|
|
+ schan = to_shdma_chan(chan);
|
|
|
+ sdev = to_shdma_dev(chan->device);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For DT, the schan->slave_id field is generated by the
|
|
|
+ * set_slave function from the slave ID that is passed in
|
|
|
+ * from xlate. For the non-DT case, the slave ID is
|
|
|
+ * directly passed into the filter function by the driver
|
|
|
+ */
|
|
|
+ if (schan->dev->of_node) {
|
|
|
+ ret = sdev->ops->set_slave(schan, slave_id, 0, true);
|
|
|
+ if (ret < 0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ schan->real_slave_id = schan->slave_id;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (slave_id < 0) {
|
|
|
/* No slave requested - arbitrary channel */
|
|
|
+ dev_warn(sdev->dma_dev.dev, "invalid slave ID passed to dma_request_slave\n");
|
|
|
return true;
|
|
|
+ }
|
|
|
|
|
|
- schan = to_shdma_chan(chan);
|
|
|
- if (!schan->dev->of_node && match >= slave_num)
|
|
|
+ if (slave_id >= slave_num)
|
|
|
return false;
|
|
|
|
|
|
- sdev = to_shdma_dev(schan->dma_chan.device);
|
|
|
- ret = sdev->ops->set_slave(schan, match, 0, true);
|
|
|
+ ret = sdev->ops->set_slave(schan, slave_id, 0, true);
|
|
|
if (ret < 0)
|
|
|
return false;
|
|
|
|
|
|
+ schan->real_slave_id = slave_id;
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
EXPORT_SYMBOL(shdma_chan_filter);
|
|
@@ -452,6 +474,8 @@ static void shdma_free_chan_resources(struct dma_chan *chan)
|
|
|
chan->private = NULL;
|
|
|
}
|
|
|
|
|
|
+ schan->real_slave_id = 0;
|
|
|
+
|
|
|
spin_lock_irq(&schan->chan_lock);
|
|
|
|
|
|
list_splice_init(&schan->ld_free, &list);
|
|
@@ -764,11 +788,20 @@ static int shdma_config(struct dma_chan *chan,
|
|
|
*/
|
|
|
if (!config)
|
|
|
return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * overriding the slave_id through dma_slave_config is deprecated,
|
|
|
+ * but possibly some out-of-tree drivers still do it.
|
|
|
+ */
|
|
|
+ if (WARN_ON_ONCE(config->slave_id &&
|
|
|
+ config->slave_id != schan->real_slave_id))
|
|
|
+ schan->real_slave_id = config->slave_id;
|
|
|
+
|
|
|
/*
|
|
|
* We could lock this, but you shouldn't be configuring the
|
|
|
* channel, while using it...
|
|
|
*/
|
|
|
- return shdma_setup_slave(schan, config->slave_id,
|
|
|
+ return shdma_setup_slave(schan,
|
|
|
config->direction == DMA_DEV_TO_MEM ?
|
|
|
config->src_addr : config->dst_addr);
|
|
|
}
|