diff options
| -rw-r--r-- | drivers/dma/sh/shdma-base.c | 114 | ||||
| -rw-r--r-- | drivers/dma/sh/shdma.c | 5 | ||||
| -rw-r--r-- | include/linux/sh_dma.h | 2 | ||||
| -rw-r--r-- | include/linux/shdma-base.h | 2 |
4 files changed, 95 insertions, 28 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 | } |
diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 4e83f3e034f3..b64d6bec6f90 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h | |||
| @@ -99,4 +99,6 @@ struct sh_dmae_pdata { | |||
| 99 | #define CHCR_TE 0x00000002 | 99 | #define CHCR_TE 0x00000002 |
| 100 | #define CHCR_IE 0x00000004 | 100 | #define CHCR_IE 0x00000004 |
| 101 | 101 | ||
| 102 | bool shdma_chan_filter(struct dma_chan *chan, void *arg); | ||
| 103 | |||
| 102 | #endif | 104 | #endif |
diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 6263ad2e7426..93f9821554b6 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h | |||
| @@ -93,7 +93,7 @@ struct shdma_ops { | |||
| 93 | dma_addr_t (*slave_addr)(struct shdma_chan *); | 93 | dma_addr_t (*slave_addr)(struct shdma_chan *); |
| 94 | int (*desc_setup)(struct shdma_chan *, struct shdma_desc *, | 94 | int (*desc_setup)(struct shdma_chan *, struct shdma_desc *, |
| 95 | dma_addr_t, dma_addr_t, size_t *); | 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 | void (*setup_xfer)(struct shdma_chan *, int); | 97 | void (*setup_xfer)(struct shdma_chan *, int); |
| 98 | void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); | 98 | void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); |
| 99 | struct shdma_desc *(*embedded_desc)(void *, int); | 99 | struct shdma_desc *(*embedded_desc)(void *, int); |
