Commit 1ff8df4f5388ad66bd7d0199b5839a2e3345c055
Committed by
Vinod Koul
1 parent
c2cdb7e4d1
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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
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); |