Commit 1ff8df4f5388ad66bd7d0199b5839a2e3345c055

Authored by Guennadi Liakhovetski
Committed by Vinod Koul
1 parent c2cdb7e4d1

dma: sh: provide a migration path for slave drivers to stop using .private

This patch extends the sh dmaengine driver to support the preferred channel
selection and configuration method, instead of using the "private" field
from struct dma_chan. We add a standard filter function to be used by
slave drivers instead of implementing their own ones, and add support for
the DMA_SLAVE_CONFIG control operation, which must accompany the new
channel selection method. We still support the legacy .private channel
allocation method to cater for a smooth driver migration.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
[applied a trvial checkpath fix]
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>

Showing 4 changed files with 95 additions and 28 deletions Side-by-side Diff

drivers/dma/sh/shdma-base.c
... ... @@ -171,6 +171,65 @@
171 171 return NULL;
172 172 }
173 173  
  174 +static int shdma_setup_slave(struct shdma_chan *schan, int slave_id)
  175 +{
  176 + struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
  177 + const struct shdma_ops *ops = sdev->ops;
  178 + int ret;
  179 +
  180 + if (slave_id < 0 || slave_id >= slave_num)
  181 + return -EINVAL;
  182 +
  183 + if (test_and_set_bit(slave_id, shdma_slave_used))
  184 + return -EBUSY;
  185 +
  186 + ret = ops->set_slave(schan, slave_id, false);
  187 + if (ret < 0) {
  188 + clear_bit(slave_id, shdma_slave_used);
  189 + return ret;
  190 + }
  191 +
  192 + schan->slave_id = slave_id;
  193 +
  194 + return 0;
  195 +}
  196 +
  197 +/*
  198 + * This is the standard shdma filter function to be used as a replacement to the
  199 + * "old" method, using the .private pointer. If for some reason you allocate a
  200 + * channel without slave data, use something like ERR_PTR(-EINVAL) as a filter
  201 + * parameter. If this filter is used, the slave driver, after calling
  202 + * dma_request_channel(), will also have to call dmaengine_slave_config() with
  203 + * .slave_id, .direction, and either .src_addr or .dst_addr set.
  204 + * NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE
  205 + * capability! If this becomes a requirement, hardware glue drivers, using this
  206 + * services would have to provide their own filters, which first would check
  207 + * the device driver, similar to how other DMAC drivers, e.g., sa11x0-dma.c, do
  208 + * this, and only then, in case of a match, call this common filter.
  209 + */
  210 +bool shdma_chan_filter(struct dma_chan *chan, void *arg)
  211 +{
  212 + struct shdma_chan *schan = to_shdma_chan(chan);
  213 + struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
  214 + const struct shdma_ops *ops = sdev->ops;
  215 + int slave_id = (int)arg;
  216 + int ret;
  217 +
  218 + if (slave_id < 0)
  219 + /* No slave requested - arbitrary channel */
  220 + return true;
  221 +
  222 + if (slave_id >= slave_num)
  223 + return false;
  224 +
  225 + ret = ops->set_slave(schan, slave_id, true);
  226 + if (ret < 0)
  227 + return false;
  228 +
  229 + return true;
  230 +}
  231 +EXPORT_SYMBOL(shdma_chan_filter);
  232 +
174 233 static int shdma_alloc_chan_resources(struct dma_chan *chan)
175 234 {
176 235 struct shdma_chan *schan = to_shdma_chan(chan);
177 236  
... ... @@ -185,21 +244,10 @@
185 244 * never runs concurrently with itself or free_chan_resources.
186 245 */
187 246 if (slave) {
188   - if (slave->slave_id < 0 || slave->slave_id >= slave_num) {
189   - ret = -EINVAL;
190   - goto evalid;
191   - }
192   -
193   - if (test_and_set_bit(slave->slave_id, shdma_slave_used)) {
194   - ret = -EBUSY;
195   - goto etestused;
196   - }
197   -
198   - ret = ops->set_slave(schan, slave->slave_id);
  247 + /* Legacy mode: .private is set in filter */
  248 + ret = shdma_setup_slave(schan, slave->slave_id);
199 249 if (ret < 0)
200 250 goto esetslave;
201   -
202   - schan->slave_id = slave->slave_id;
203 251 } else {
204 252 schan->slave_id = -EINVAL;
205 253 }
... ... @@ -228,8 +276,6 @@
228 276 if (slave)
229 277 esetslave:
230 278 clear_bit(slave->slave_id, shdma_slave_used);
231   -etestused:
232   -evalid:
233 279 chan->private = NULL;
234 280 return ret;
235 281 }
236 282  
237 283  
238 284  
239 285  
... ... @@ -587,22 +633,40 @@
587 633 struct shdma_chan *schan = to_shdma_chan(chan);
588 634 struct shdma_dev *sdev = to_shdma_dev(chan->device);
589 635 const struct shdma_ops *ops = sdev->ops;
  636 + struct dma_slave_config *config;
590 637 unsigned long flags;
  638 + int ret;
591 639  
592   - /* Only supports DMA_TERMINATE_ALL */
593   - if (cmd != DMA_TERMINATE_ALL)
594   - return -ENXIO;
595   -
596 640 if (!chan)
597 641 return -EINVAL;
598 642  
599   - spin_lock_irqsave(&schan->chan_lock, flags);
  643 + switch (cmd) {
  644 + case DMA_TERMINATE_ALL:
  645 + spin_lock_irqsave(&schan->chan_lock, flags);
  646 + ops->halt_channel(schan);
  647 + spin_unlock_irqrestore(&schan->chan_lock, flags);
600 648  
601   - ops->halt_channel(schan);
602   -
603   - spin_unlock_irqrestore(&schan->chan_lock, flags);
604   -
605   - shdma_chan_ld_cleanup(schan, true);
  649 + shdma_chan_ld_cleanup(schan, true);
  650 + break;
  651 + case DMA_SLAVE_CONFIG:
  652 + /*
  653 + * So far only .slave_id is used, but the slave drivers are
  654 + * encouraged to also set a transfer direction and an address.
  655 + */
  656 + if (!arg)
  657 + return -EINVAL;
  658 + /*
  659 + * We could lock this, but you shouldn't be configuring the
  660 + * channel, while using it...
  661 + */
  662 + config = (struct dma_slave_config *)arg;
  663 + ret = shdma_setup_slave(schan, config->slave_id);
  664 + if (ret < 0)
  665 + return ret;
  666 + break;
  667 + default:
  668 + return -ENXIO;
  669 + }
606 670  
607 671 return 0;
608 672 }
drivers/dma/sh/shdma.c
... ... @@ -320,7 +320,7 @@
320 320 }
321 321  
322 322 static int sh_dmae_set_slave(struct shdma_chan *schan,
323   - int slave_id)
  323 + int slave_id, bool try)
324 324 {
325 325 struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
326 326 shdma_chan);
... ... @@ -328,7 +328,8 @@
328 328 if (!cfg)
329 329 return -ENODEV;
330 330  
331   - sh_chan->config = cfg;
  331 + if (!try)
  332 + sh_chan->config = cfg;
332 333  
333 334 return 0;
334 335 }
include/linux/sh_dma.h
... ... @@ -99,5 +99,7 @@
99 99 #define CHCR_TE 0x00000002
100 100 #define CHCR_IE 0x00000004
101 101  
  102 +bool shdma_chan_filter(struct dma_chan *chan, void *arg);
  103 +
102 104 #endif
include/linux/shdma-base.h
... ... @@ -93,7 +93,7 @@
93 93 dma_addr_t (*slave_addr)(struct shdma_chan *);
94 94 int (*desc_setup)(struct shdma_chan *, struct shdma_desc *,
95 95 dma_addr_t, dma_addr_t, size_t *);
96   - int (*set_slave)(struct shdma_chan *, int);
  96 + int (*set_slave)(struct shdma_chan *, int, bool);
97 97 void (*setup_xfer)(struct shdma_chan *, int);
98 98 void (*start_xfer)(struct shdma_chan *, struct shdma_desc *);
99 99 struct shdma_desc *(*embedded_desc)(void *, int);