aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/sh
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2012-07-05 06:29:42 -0400
committerVinod Koul <vinod.koul@linux.intel.com>2012-07-20 01:58:20 -0400
commit1ff8df4f5388ad66bd7d0199b5839a2e3345c055 (patch)
treec6d80a09a4722c8c39cea58b80a771e34594b74b /drivers/dma/sh
parentc2cdb7e4d16394fc51dc5c2c5b3e7c3733bdfaac (diff)
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>
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}