diff options
| author | Nicolas Ferre <nicolas.ferre@atmel.com> | 2011-04-30 10:57:46 -0400 |
|---|---|---|
| committer | Vinod Koul <vinod.koul@intel.com> | 2011-05-02 06:12:09 -0400 |
| commit | 53830cc75974a199b6b654c062ff8c54c58caa0b (patch) | |
| tree | 2aec11425a76ffe996297c25e71f3dc0eb6ef662 /drivers/dma | |
| parent | 9b3aa589eaa1366200062ce1f9cc7ddca8d1d578 (diff) | |
dmaengine: at_hdmac: add cyclic DMA operation support
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
| -rw-r--r-- | drivers/dma/at_hdmac.c | 231 | ||||
| -rw-r--r-- | drivers/dma/at_hdmac_regs.h | 14 |
2 files changed, 229 insertions, 16 deletions
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 13050e646f8..ed9d92429de 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c | |||
| @@ -165,6 +165,29 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) | |||
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | /** | 167 | /** |
| 168 | * atc_desc_chain - build chain adding a descripor | ||
| 169 | * @first: address of first descripor of the chain | ||
| 170 | * @prev: address of previous descripor of the chain | ||
| 171 | * @desc: descriptor to queue | ||
| 172 | * | ||
| 173 | * Called from prep_* functions | ||
| 174 | */ | ||
| 175 | static void atc_desc_chain(struct at_desc **first, struct at_desc **prev, | ||
| 176 | struct at_desc *desc) | ||
| 177 | { | ||
| 178 | if (!(*first)) { | ||
| 179 | *first = desc; | ||
| 180 | } else { | ||
| 181 | /* inform the HW lli about chaining */ | ||
| 182 | (*prev)->lli.dscr = desc->txd.phys; | ||
| 183 | /* insert the link descriptor to the LD ring */ | ||
| 184 | list_add_tail(&desc->desc_node, | ||
| 185 | &(*first)->tx_list); | ||
| 186 | } | ||
| 187 | *prev = desc; | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 168 | * atc_assign_cookie - compute and assign new cookie | 191 | * atc_assign_cookie - compute and assign new cookie |
| 169 | * @atchan: channel we work on | 192 | * @atchan: channel we work on |
| 170 | * @desc: descriptor to asign cookie for | 193 | * @desc: descriptor to asign cookie for |
| @@ -237,16 +260,12 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) | |||
| 237 | static void | 260 | static void |
| 238 | atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) | 261 | atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) |
| 239 | { | 262 | { |
| 240 | dma_async_tx_callback callback; | ||
| 241 | void *param; | ||
| 242 | struct dma_async_tx_descriptor *txd = &desc->txd; | 263 | struct dma_async_tx_descriptor *txd = &desc->txd; |
| 243 | 264 | ||
| 244 | dev_vdbg(chan2dev(&atchan->chan_common), | 265 | dev_vdbg(chan2dev(&atchan->chan_common), |
| 245 | "descriptor %u complete\n", txd->cookie); | 266 | "descriptor %u complete\n", txd->cookie); |
| 246 | 267 | ||
| 247 | atchan->completed_cookie = txd->cookie; | 268 | atchan->completed_cookie = txd->cookie; |
| 248 | callback = txd->callback; | ||
| 249 | param = txd->callback_param; | ||
| 250 | 269 | ||
| 251 | /* move children to free_list */ | 270 | /* move children to free_list */ |
| 252 | list_splice_init(&desc->tx_list, &atchan->free_list); | 271 | list_splice_init(&desc->tx_list, &atchan->free_list); |
| @@ -278,12 +297,19 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) | |||
| 278 | } | 297 | } |
| 279 | } | 298 | } |
| 280 | 299 | ||
| 281 | /* | 300 | /* for cyclic transfers, |
| 282 | * The API requires that no submissions are done from a | 301 | * no need to replay callback function while stopping */ |
| 283 | * callback, so we don't need to drop the lock here | 302 | if (!test_bit(ATC_IS_CYCLIC, &atchan->status)) { |
| 284 | */ | 303 | dma_async_tx_callback callback = txd->callback; |
| 285 | if (callback) | 304 | void *param = txd->callback_param; |
| 286 | callback(param); | 305 | |
| 306 | /* | ||
| 307 | * The API requires that no submissions are done from a | ||
| 308 | * callback, so we don't need to drop the lock here | ||
| 309 | */ | ||
| 310 | if (callback) | ||
| 311 | callback(param); | ||
| 312 | } | ||
| 287 | 313 | ||
| 288 | dma_run_dependencies(txd); | 314 | dma_run_dependencies(txd); |
| 289 | } | 315 | } |
| @@ -419,6 +445,26 @@ static void atc_handle_error(struct at_dma_chan *atchan) | |||
| 419 | atc_chain_complete(atchan, bad_desc); | 445 | atc_chain_complete(atchan, bad_desc); |
| 420 | } | 446 | } |
| 421 | 447 | ||
| 448 | /** | ||
| 449 | * atc_handle_cyclic - at the end of a period, run callback function | ||
| 450 | * @atchan: channel used for cyclic operations | ||
| 451 | * | ||
| 452 | * Called with atchan->lock held and bh disabled | ||
| 453 | */ | ||
| 454 | static void atc_handle_cyclic(struct at_dma_chan *atchan) | ||
| 455 | { | ||
| 456 | struct at_desc *first = atc_first_active(atchan); | ||
| 457 | struct dma_async_tx_descriptor *txd = &first->txd; | ||
| 458 | dma_async_tx_callback callback = txd->callback; | ||
| 459 | void *param = txd->callback_param; | ||
| 460 | |||
| 461 | dev_vdbg(chan2dev(&atchan->chan_common), | ||
| 462 | "new cyclic period llp 0x%08x\n", | ||
| 463 | channel_readl(atchan, DSCR)); | ||
| 464 | |||
| 465 | if (callback) | ||
| 466 | callback(param); | ||
| 467 | } | ||
| 422 | 468 | ||
| 423 | /*-- IRQ & Tasklet ---------------------------------------------------*/ | 469 | /*-- IRQ & Tasklet ---------------------------------------------------*/ |
| 424 | 470 | ||
| @@ -434,8 +480,10 @@ static void atc_tasklet(unsigned long data) | |||
| 434 | } | 480 | } |
| 435 | 481 | ||
| 436 | spin_lock(&atchan->lock); | 482 | spin_lock(&atchan->lock); |
| 437 | if (test_and_clear_bit(0, &atchan->error_status)) | 483 | if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status)) |
| 438 | atc_handle_error(atchan); | 484 | atc_handle_error(atchan); |
| 485 | else if (test_bit(ATC_IS_CYCLIC, &atchan->status)) | ||
| 486 | atc_handle_cyclic(atchan); | ||
| 439 | else | 487 | else |
| 440 | atc_advance_work(atchan); | 488 | atc_advance_work(atchan); |
| 441 | 489 | ||
| @@ -469,7 +517,7 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id) | |||
| 469 | /* Disable channel on AHB error */ | 517 | /* Disable channel on AHB error */ |
| 470 | dma_writel(atdma, CHDR, atchan->mask); | 518 | dma_writel(atdma, CHDR, atchan->mask); |
| 471 | /* Give information to tasklet */ | 519 | /* Give information to tasklet */ |
| 472 | set_bit(0, &atchan->error_status); | 520 | set_bit(ATC_IS_ERROR, &atchan->status); |
| 473 | } | 521 | } |
| 474 | tasklet_schedule(&atchan->tasklet); | 522 | tasklet_schedule(&atchan->tasklet); |
| 475 | ret = IRQ_HANDLED; | 523 | ret = IRQ_HANDLED; |
| @@ -759,6 +807,148 @@ err_desc_get: | |||
| 759 | return NULL; | 807 | return NULL; |
| 760 | } | 808 | } |
| 761 | 809 | ||
| 810 | /** | ||
| 811 | * atc_dma_cyclic_check_values | ||
| 812 | * Check for too big/unaligned periods and unaligned DMA buffer | ||
| 813 | */ | ||
| 814 | static int | ||
| 815 | atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, | ||
| 816 | size_t period_len, enum dma_data_direction direction) | ||
| 817 | { | ||
| 818 | if (period_len > (ATC_BTSIZE_MAX << reg_width)) | ||
| 819 | goto err_out; | ||
| 820 | if (unlikely(period_len & ((1 << reg_width) - 1))) | ||
| 821 | goto err_out; | ||
| 822 | if (unlikely(buf_addr & ((1 << reg_width) - 1))) | ||
| 823 | goto err_out; | ||
| 824 | if (unlikely(!(direction & (DMA_TO_DEVICE | DMA_FROM_DEVICE)))) | ||
| 825 | goto err_out; | ||
| 826 | |||
| 827 | return 0; | ||
| 828 | |||
| 829 | err_out: | ||
| 830 | return -EINVAL; | ||
| 831 | } | ||
| 832 | |||
| 833 | /** | ||
| 834 | * atc_dma_cyclic_fill_desc - Fill one period decriptor | ||
| 835 | */ | ||
| 836 | static int | ||
| 837 | atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, | ||
| 838 | unsigned int period_index, dma_addr_t buf_addr, | ||
| 839 | size_t period_len, enum dma_data_direction direction) | ||
| 840 | { | ||
| 841 | u32 ctrla; | ||
| 842 | unsigned int reg_width = atslave->reg_width; | ||
| 843 | |||
| 844 | /* prepare common CRTLA value */ | ||
| 845 | ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla | ||
| 846 | | ATC_DST_WIDTH(reg_width) | ||
| 847 | | ATC_SRC_WIDTH(reg_width) | ||
| 848 | | period_len >> reg_width; | ||
| 849 | |||
| 850 | switch (direction) { | ||
| 851 | case DMA_TO_DEVICE: | ||
| 852 | desc->lli.saddr = buf_addr + (period_len * period_index); | ||
| 853 | desc->lli.daddr = atslave->tx_reg; | ||
| 854 | desc->lli.ctrla = ctrla; | ||
| 855 | desc->lli.ctrlb = ATC_DEFAULT_CTRLB | ||
| 856 | | ATC_DST_ADDR_MODE_FIXED | ||
| 857 | | ATC_SRC_ADDR_MODE_INCR | ||
| 858 | | ATC_FC_MEM2PER; | ||
| 859 | break; | ||
| 860 | |||
| 861 | case DMA_FROM_DEVICE: | ||
| 862 | desc->lli.saddr = atslave->rx_reg; | ||
| 863 | desc->lli.daddr = buf_addr + (period_len * period_index); | ||
| 864 | desc->lli.ctrla = ctrla; | ||
| 865 | desc->lli.ctrlb = ATC_DEFAULT_CTRLB | ||
| 866 | | ATC_DST_ADDR_MODE_INCR | ||
| 867 | | ATC_SRC_ADDR_MODE_FIXED | ||
| 868 | | ATC_FC_PER2MEM; | ||
| 869 | break; | ||
| 870 | |||
| 871 | default: | ||
| 872 | return -EINVAL; | ||
| 873 | } | ||
| 874 | |||
| 875 | return 0; | ||
| 876 | } | ||
| 877 | |||
| 878 | /** | ||
| 879 | * atc_prep_dma_cyclic - prepare the cyclic DMA transfer | ||
| 880 | * @chan: the DMA channel to prepare | ||
| 881 | * @buf_addr: physical DMA address where the buffer starts | ||
| 882 | * @buf_len: total number of bytes for the entire buffer | ||
| 883 | * @period_len: number of bytes for each period | ||
| 884 | * @direction: transfer direction, to or from device | ||
| 885 | */ | ||
| 886 | static struct dma_async_tx_descriptor * | ||
| 887 | atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, | ||
| 888 | size_t period_len, enum dma_data_direction direction) | ||
| 889 | { | ||
| 890 | struct at_dma_chan *atchan = to_at_dma_chan(chan); | ||
| 891 | struct at_dma_slave *atslave = chan->private; | ||
| 892 | struct at_desc *first = NULL; | ||
| 893 | struct at_desc *prev = NULL; | ||
| 894 | unsigned long was_cyclic; | ||
| 895 | unsigned int periods = buf_len / period_len; | ||
| 896 | unsigned int i; | ||
| 897 | |||
| 898 | dev_vdbg(chan2dev(chan), "prep_dma_cyclic: %s buf@0x%08x - %d (%d/%d)\n", | ||
| 899 | direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", | ||
| 900 | buf_addr, | ||
| 901 | periods, buf_len, period_len); | ||
| 902 | |||
| 903 | if (unlikely(!atslave || !buf_len || !period_len)) { | ||
| 904 | dev_dbg(chan2dev(chan), "prep_dma_cyclic: length is zero!\n"); | ||
| 905 | return NULL; | ||
| 906 | } | ||
| 907 | |||
| 908 | was_cyclic = test_and_set_bit(ATC_IS_CYCLIC, &atchan->status); | ||
| 909 | if (was_cyclic) { | ||
| 910 | dev_dbg(chan2dev(chan), "prep_dma_cyclic: channel in use!\n"); | ||
| 911 | return NULL; | ||
| 912 | } | ||
| 913 | |||
| 914 | /* Check for too big/unaligned periods and unaligned DMA buffer */ | ||
| 915 | if (atc_dma_cyclic_check_values(atslave->reg_width, buf_addr, | ||
| 916 | period_len, direction)) | ||
| 917 | goto err_out; | ||
| 918 | |||
| 919 | /* build cyclic linked list */ | ||
| 920 | for (i = 0; i < periods; i++) { | ||
| 921 | struct at_desc *desc; | ||
| 922 | |||
| 923 | desc = atc_desc_get(atchan); | ||
| 924 | if (!desc) | ||
| 925 | goto err_desc_get; | ||
| 926 | |||
| 927 | if (atc_dma_cyclic_fill_desc(atslave, desc, i, buf_addr, | ||
| 928 | period_len, direction)) | ||
| 929 | goto err_desc_get; | ||
| 930 | |||
| 931 | atc_desc_chain(&first, &prev, desc); | ||
| 932 | } | ||
| 933 | |||
| 934 | /* lets make a cyclic list */ | ||
| 935 | prev->lli.dscr = first->txd.phys; | ||
| 936 | |||
| 937 | /* First descriptor of the chain embedds additional information */ | ||
| 938 | first->txd.cookie = -EBUSY; | ||
| 939 | first->len = buf_len; | ||
| 940 | |||
| 941 | return &first->txd; | ||
| 942 | |||
| 943 | err_desc_get: | ||
| 944 | dev_err(chan2dev(chan), "not enough descriptors available\n"); | ||
| 945 | atc_desc_put(atchan, first); | ||
| 946 | err_out: | ||
| 947 | clear_bit(ATC_IS_CYCLIC, &atchan->status); | ||
| 948 | return NULL; | ||
| 949 | } | ||
| 950 | |||
| 951 | |||
| 762 | static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | 952 | static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, |
| 763 | unsigned long arg) | 953 | unsigned long arg) |
| 764 | { | 954 | { |
| @@ -793,6 +983,9 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
| 793 | list_for_each_entry_safe(desc, _desc, &list, desc_node) | 983 | list_for_each_entry_safe(desc, _desc, &list, desc_node) |
| 794 | atc_chain_complete(atchan, desc); | 984 | atc_chain_complete(atchan, desc); |
| 795 | 985 | ||
| 986 | /* if channel dedicated to cyclic operations, free it */ | ||
| 987 | clear_bit(ATC_IS_CYCLIC, &atchan->status); | ||
| 988 | |||
| 796 | spin_unlock_bh(&atchan->lock); | 989 | spin_unlock_bh(&atchan->lock); |
| 797 | 990 | ||
| 798 | return 0; | 991 | return 0; |
| @@ -853,6 +1046,10 @@ static void atc_issue_pending(struct dma_chan *chan) | |||
| 853 | 1046 | ||
| 854 | dev_vdbg(chan2dev(chan), "issue_pending\n"); | 1047 | dev_vdbg(chan2dev(chan), "issue_pending\n"); |
| 855 | 1048 | ||
| 1049 | /* Not needed for cyclic transfers */ | ||
| 1050 | if (test_bit(ATC_IS_CYCLIC, &atchan->status)) | ||
| 1051 | return; | ||
| 1052 | |||
| 856 | spin_lock_bh(&atchan->lock); | 1053 | spin_lock_bh(&atchan->lock); |
| 857 | if (!atc_chan_is_enabled(atchan)) { | 1054 | if (!atc_chan_is_enabled(atchan)) { |
| 858 | atc_advance_work(atchan); | 1055 | atc_advance_work(atchan); |
| @@ -959,6 +1156,7 @@ static void atc_free_chan_resources(struct dma_chan *chan) | |||
| 959 | } | 1156 | } |
| 960 | list_splice_init(&atchan->free_list, &list); | 1157 | list_splice_init(&atchan->free_list, &list); |
| 961 | atchan->descs_allocated = 0; | 1158 | atchan->descs_allocated = 0; |
| 1159 | atchan->status = 0; | ||
| 962 | 1160 | ||
| 963 | dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); | 1161 | dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); |
| 964 | } | 1162 | } |
| @@ -1092,10 +1290,15 @@ static int __init at_dma_probe(struct platform_device *pdev) | |||
| 1092 | if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask)) | 1290 | if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask)) |
| 1093 | atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy; | 1291 | atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy; |
| 1094 | 1292 | ||
| 1095 | if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) { | 1293 | if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) |
| 1096 | atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; | 1294 | atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; |
| 1295 | |||
| 1296 | if (dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask)) | ||
| 1297 | atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic; | ||
| 1298 | |||
| 1299 | if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) || | ||
| 1300 | dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask)) | ||
| 1097 | atdma->dma_common.device_control = atc_control; | 1301 | atdma->dma_common.device_control = atc_control; |
| 1098 | } | ||
| 1099 | 1302 | ||
| 1100 | dma_writel(atdma, EN, AT_DMA_ENABLE); | 1303 | dma_writel(atdma, EN, AT_DMA_ENABLE); |
| 1101 | 1304 | ||
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 8303306ea82..c79a9e07f7b 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h | |||
| @@ -181,12 +181,22 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd) | |||
| 181 | /*-- Channels --------------------------------------------------------*/ | 181 | /*-- Channels --------------------------------------------------------*/ |
| 182 | 182 | ||
| 183 | /** | 183 | /** |
| 184 | * atc_status - information bits stored in channel status flag | ||
| 185 | * | ||
| 186 | * Manipulated with atomic operations. | ||
| 187 | */ | ||
| 188 | enum atc_status { | ||
| 189 | ATC_IS_ERROR = 0, | ||
| 190 | ATC_IS_CYCLIC = 24, | ||
| 191 | }; | ||
| 192 | |||
| 193 | /** | ||
| 184 | * struct at_dma_chan - internal representation of an Atmel HDMAC channel | 194 | * struct at_dma_chan - internal representation of an Atmel HDMAC channel |
| 185 | * @chan_common: common dmaengine channel object members | 195 | * @chan_common: common dmaengine channel object members |
| 186 | * @device: parent device | 196 | * @device: parent device |
| 187 | * @ch_regs: memory mapped register base | 197 | * @ch_regs: memory mapped register base |
| 188 | * @mask: channel index in a mask | 198 | * @mask: channel index in a mask |
| 189 | * @error_status: transmit error status information from irq handler | 199 | * @status: transmit status information from irq/prep* functions |
| 190 | * to tasklet (use atomic operations) | 200 | * to tasklet (use atomic operations) |
| 191 | * @tasklet: bottom half to finish transaction work | 201 | * @tasklet: bottom half to finish transaction work |
| 192 | * @lock: serializes enqueue/dequeue operations to descriptors lists | 202 | * @lock: serializes enqueue/dequeue operations to descriptors lists |
| @@ -201,7 +211,7 @@ struct at_dma_chan { | |||
| 201 | struct at_dma *device; | 211 | struct at_dma *device; |
| 202 | void __iomem *ch_regs; | 212 | void __iomem *ch_regs; |
| 203 | u8 mask; | 213 | u8 mask; |
| 204 | unsigned long error_status; | 214 | unsigned long status; |
| 205 | struct tasklet_struct tasklet; | 215 | struct tasklet_struct tasklet; |
| 206 | 216 | ||
| 207 | spinlock_t lock; | 217 | spinlock_t lock; |
