aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/sh
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/sh')
-rw-r--r--drivers/dma/sh/shdma-base.c114
-rw-r--r--drivers/dma/sh/shdma.c5
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
174static 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 */
210bool 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}
231EXPORT_SYMBOL(shdma_chan_filter);
232
174static int shdma_alloc_chan_resources(struct dma_chan *chan) 233static 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)
229esetslave: 277esetslave:
230 clear_bit(slave->slave_id, shdma_slave_used); 278 clear_bit(slave->slave_id, shdma_slave_used);
231etestused:
232evalid:
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
322static int sh_dmae_set_slave(struct shdma_chan *schan, 322static 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}