diff options
Diffstat (limited to 'drivers/dma/sh')
-rw-r--r-- | drivers/dma/sh/shdma-base.c | 114 | ||||
-rw-r--r-- | drivers/dma/sh/shdma.c | 5 |
2 files changed, 92 insertions, 27 deletions
diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 73db282a1436..27f5c781fd73 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c | |||
@@ -171,6 +171,65 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan) | |||
171 | return NULL; | 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 | static int shdma_alloc_chan_resources(struct dma_chan *chan) | 233 | static int shdma_alloc_chan_resources(struct dma_chan *chan) |
175 | { | 234 | { |
176 | struct shdma_chan *schan = to_shdma_chan(chan); | 235 | struct shdma_chan *schan = to_shdma_chan(chan); |
@@ -185,21 +244,10 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) | |||
185 | * never runs concurrently with itself or free_chan_resources. | 244 | * never runs concurrently with itself or free_chan_resources. |
186 | */ | 245 | */ |
187 | if (slave) { | 246 | if (slave) { |
188 | if (slave->slave_id < 0 || slave->slave_id >= slave_num) { | 247 | /* Legacy mode: .private is set in filter */ |
189 | ret = -EINVAL; | 248 | ret = shdma_setup_slave(schan, slave->slave_id); |
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); | ||
199 | if (ret < 0) | 249 | if (ret < 0) |
200 | goto esetslave; | 250 | goto esetslave; |
201 | |||
202 | schan->slave_id = slave->slave_id; | ||
203 | } else { | 251 | } else { |
204 | schan->slave_id = -EINVAL; | 252 | schan->slave_id = -EINVAL; |
205 | } | 253 | } |
@@ -228,8 +276,6 @@ edescalloc: | |||
228 | if (slave) | 276 | if (slave) |
229 | esetslave: | 277 | esetslave: |
230 | clear_bit(slave->slave_id, shdma_slave_used); | 278 | clear_bit(slave->slave_id, shdma_slave_used); |
231 | etestused: | ||
232 | evalid: | ||
233 | chan->private = NULL; | 279 | chan->private = NULL; |
234 | return ret; | 280 | return ret; |
235 | } | 281 | } |
@@ -587,22 +633,40 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
587 | struct shdma_chan *schan = to_shdma_chan(chan); | 633 | struct shdma_chan *schan = to_shdma_chan(chan); |
588 | struct shdma_dev *sdev = to_shdma_dev(chan->device); | 634 | struct shdma_dev *sdev = to_shdma_dev(chan->device); |
589 | const struct shdma_ops *ops = sdev->ops; | 635 | const struct shdma_ops *ops = sdev->ops; |
636 | struct dma_slave_config *config; | ||
590 | unsigned long flags; | 637 | unsigned long flags; |
591 | 638 | int ret; | |
592 | /* Only supports DMA_TERMINATE_ALL */ | ||
593 | if (cmd != DMA_TERMINATE_ALL) | ||
594 | return -ENXIO; | ||
595 | 639 | ||
596 | if (!chan) | 640 | if (!chan) |
597 | return -EINVAL; | 641 | return -EINVAL; |
598 | 642 | ||
599 | spin_lock_irqsave(&schan->chan_lock, flags); | 643 | switch (cmd) { |
600 | 644 | case DMA_TERMINATE_ALL: | |
601 | ops->halt_channel(schan); | 645 | spin_lock_irqsave(&schan->chan_lock, flags); |
602 | 646 | ops->halt_channel(schan); | |
603 | spin_unlock_irqrestore(&schan->chan_lock, flags); | 647 | spin_unlock_irqrestore(&schan->chan_lock, flags); |
604 | 648 | ||
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 | return 0; | 671 | return 0; |
608 | } | 672 | } |
diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 9a10d8bbdef2..027c9be97654 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c | |||
@@ -320,7 +320,7 @@ static const struct sh_dmae_slave_config *dmae_find_slave( | |||
320 | } | 320 | } |
321 | 321 | ||
322 | static int sh_dmae_set_slave(struct shdma_chan *schan, | 322 | static int sh_dmae_set_slave(struct shdma_chan *schan, |
323 | int slave_id) | 323 | int slave_id, bool try) |
324 | { | 324 | { |
325 | struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, | 325 | struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, |
326 | shdma_chan); | 326 | shdma_chan); |
@@ -328,7 +328,8 @@ static int sh_dmae_set_slave(struct shdma_chan *schan, | |||
328 | if (!cfg) | 328 | if (!cfg) |
329 | return -ENODEV; | 329 | return -ENODEV; |
330 | 330 | ||
331 | sh_chan->config = cfg; | 331 | if (!try) |
332 | sh_chan->config = cfg; | ||
332 | 333 | ||
333 | return 0; | 334 | return 0; |
334 | } | 335 | } |