diff options
author | Vinod Koul <vinod.koul@intel.com> | 2016-10-02 23:48:20 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-10-02 23:48:20 -0400 |
commit | 4dfc9afe0d46caa309c5eaede841551d4056fefc (patch) | |
tree | e069fef612dc52417683ccb4a0ecbd52179361d1 | |
parent | 850e0448a6db0650a6c41adacbc92f106094d1db (diff) | |
parent | 77ff7a706f014a56d38f07acf220f381a8fe0fd8 (diff) |
Merge branch 'topic/mv_xor' into for-linus
-rw-r--r-- | drivers/dma/mv_xor.c | 95 | ||||
-rw-r--r-- | drivers/dma/mv_xor.h | 7 | ||||
-rw-r--r-- | include/linux/mbus.h | 18 |
3 files changed, 117 insertions, 3 deletions
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index f8b5e7424b3a..a4f0eb1d1d9c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c | |||
@@ -467,12 +467,90 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan) | |||
467 | return mv_chan->slots_allocated ? : -ENOMEM; | 467 | return mv_chan->slots_allocated ? : -ENOMEM; |
468 | } | 468 | } |
469 | 469 | ||
470 | /* | ||
471 | * Check if source or destination is an PCIe/IO address (non-SDRAM) and add | ||
472 | * a new MBus window if necessary. Use a cache for these check so that | ||
473 | * the MMIO mapped registers don't have to be accessed for this check | ||
474 | * to speed up this process. | ||
475 | */ | ||
476 | static int mv_xor_add_io_win(struct mv_xor_chan *mv_chan, u32 addr) | ||
477 | { | ||
478 | struct mv_xor_device *xordev = mv_chan->xordev; | ||
479 | void __iomem *base = mv_chan->mmr_high_base; | ||
480 | u32 win_enable; | ||
481 | u32 size; | ||
482 | u8 target, attr; | ||
483 | int ret; | ||
484 | int i; | ||
485 | |||
486 | /* Nothing needs to get done for the Armada 3700 */ | ||
487 | if (xordev->xor_type == XOR_ARMADA_37XX) | ||
488 | return 0; | ||
489 | |||
490 | /* | ||
491 | * Loop over the cached windows to check, if the requested area | ||
492 | * is already mapped. If this the case, nothing needs to be done | ||
493 | * and we can return. | ||
494 | */ | ||
495 | for (i = 0; i < WINDOW_COUNT; i++) { | ||
496 | if (addr >= xordev->win_start[i] && | ||
497 | addr <= xordev->win_end[i]) { | ||
498 | /* Window is already mapped */ | ||
499 | return 0; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * The window is not mapped, so we need to create the new mapping | ||
505 | */ | ||
506 | |||
507 | /* If no IO window is found that addr has to be located in SDRAM */ | ||
508 | ret = mvebu_mbus_get_io_win_info(addr, &size, &target, &attr); | ||
509 | if (ret < 0) | ||
510 | return 0; | ||
511 | |||
512 | /* | ||
513 | * Mask the base addr 'addr' according to 'size' read back from the | ||
514 | * MBus window. Otherwise we might end up with an address located | ||
515 | * somewhere in the middle of this area here. | ||
516 | */ | ||
517 | size -= 1; | ||
518 | addr &= ~size; | ||
519 | |||
520 | /* | ||
521 | * Reading one of both enabled register is enough, as they are always | ||
522 | * programmed to the identical values | ||
523 | */ | ||
524 | win_enable = readl(base + WINDOW_BAR_ENABLE(0)); | ||
525 | |||
526 | /* Set 'i' to the first free window to write the new values to */ | ||
527 | i = ffs(~win_enable) - 1; | ||
528 | if (i >= WINDOW_COUNT) | ||
529 | return -ENOMEM; | ||
530 | |||
531 | writel((addr & 0xffff0000) | (attr << 8) | target, | ||
532 | base + WINDOW_BASE(i)); | ||
533 | writel(size & 0xffff0000, base + WINDOW_SIZE(i)); | ||
534 | |||
535 | /* Fill the caching variables for later use */ | ||
536 | xordev->win_start[i] = addr; | ||
537 | xordev->win_end[i] = addr + size; | ||
538 | |||
539 | win_enable |= (1 << i); | ||
540 | win_enable |= 3 << (16 + (2 * i)); | ||
541 | writel(win_enable, base + WINDOW_BAR_ENABLE(0)); | ||
542 | writel(win_enable, base + WINDOW_BAR_ENABLE(1)); | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
470 | static struct dma_async_tx_descriptor * | 547 | static struct dma_async_tx_descriptor * |
471 | mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, | 548 | mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, |
472 | unsigned int src_cnt, size_t len, unsigned long flags) | 549 | unsigned int src_cnt, size_t len, unsigned long flags) |
473 | { | 550 | { |
474 | struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); | 551 | struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); |
475 | struct mv_xor_desc_slot *sw_desc; | 552 | struct mv_xor_desc_slot *sw_desc; |
553 | int ret; | ||
476 | 554 | ||
477 | if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) | 555 | if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) |
478 | return NULL; | 556 | return NULL; |
@@ -483,6 +561,11 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, | |||
483 | "%s src_cnt: %d len: %zu dest %pad flags: %ld\n", | 561 | "%s src_cnt: %d len: %zu dest %pad flags: %ld\n", |
484 | __func__, src_cnt, len, &dest, flags); | 562 | __func__, src_cnt, len, &dest, flags); |
485 | 563 | ||
564 | /* Check if a new window needs to get added for 'dest' */ | ||
565 | ret = mv_xor_add_io_win(mv_chan, dest); | ||
566 | if (ret) | ||
567 | return NULL; | ||
568 | |||
486 | sw_desc = mv_chan_alloc_slot(mv_chan); | 569 | sw_desc = mv_chan_alloc_slot(mv_chan); |
487 | if (sw_desc) { | 570 | if (sw_desc) { |
488 | sw_desc->type = DMA_XOR; | 571 | sw_desc->type = DMA_XOR; |
@@ -490,8 +573,13 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, | |||
490 | mv_desc_init(sw_desc, dest, len, flags); | 573 | mv_desc_init(sw_desc, dest, len, flags); |
491 | if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) | 574 | if (mv_chan->op_in_desc == XOR_MODE_IN_DESC) |
492 | mv_desc_set_mode(sw_desc); | 575 | mv_desc_set_mode(sw_desc); |
493 | while (src_cnt--) | 576 | while (src_cnt--) { |
577 | /* Check if a new window needs to get added for 'src' */ | ||
578 | ret = mv_xor_add_io_win(mv_chan, src[src_cnt]); | ||
579 | if (ret) | ||
580 | return NULL; | ||
494 | mv_desc_set_src_addr(sw_desc, src_cnt, src[src_cnt]); | 581 | mv_desc_set_src_addr(sw_desc, src_cnt, src[src_cnt]); |
582 | } | ||
495 | } | 583 | } |
496 | 584 | ||
497 | dev_dbg(mv_chan_to_devp(mv_chan), | 585 | dev_dbg(mv_chan_to_devp(mv_chan), |
@@ -956,6 +1044,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev, | |||
956 | mv_chan->op_in_desc = XOR_MODE_IN_DESC; | 1044 | mv_chan->op_in_desc = XOR_MODE_IN_DESC; |
957 | 1045 | ||
958 | dma_dev = &mv_chan->dmadev; | 1046 | dma_dev = &mv_chan->dmadev; |
1047 | mv_chan->xordev = xordev; | ||
959 | 1048 | ||
960 | /* | 1049 | /* |
961 | * These source and destination dummy buffers are used to implement | 1050 | * These source and destination dummy buffers are used to implement |
@@ -1083,6 +1172,10 @@ mv_xor_conf_mbus_windows(struct mv_xor_device *xordev, | |||
1083 | dram->mbus_dram_target_id, base + WINDOW_BASE(i)); | 1172 | dram->mbus_dram_target_id, base + WINDOW_BASE(i)); |
1084 | writel((cs->size - 1) & 0xffff0000, base + WINDOW_SIZE(i)); | 1173 | writel((cs->size - 1) & 0xffff0000, base + WINDOW_SIZE(i)); |
1085 | 1174 | ||
1175 | /* Fill the caching variables for later use */ | ||
1176 | xordev->win_start[i] = cs->base; | ||
1177 | xordev->win_end[i] = cs->base + cs->size - 1; | ||
1178 | |||
1086 | win_enable |= (1 << i); | 1179 | win_enable |= (1 << i); |
1087 | win_enable |= 3 << (16 + (2 * i)); | 1180 | win_enable |= 3 << (16 + (2 * i)); |
1088 | } | 1181 | } |
diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index bf56e082e7cd..88eeab222a23 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h | |||
@@ -80,12 +80,17 @@ | |||
80 | #define WINDOW_BAR_ENABLE(chan) (0x40 + ((chan) << 2)) | 80 | #define WINDOW_BAR_ENABLE(chan) (0x40 + ((chan) << 2)) |
81 | #define WINDOW_OVERRIDE_CTRL(chan) (0xA0 + ((chan) << 2)) | 81 | #define WINDOW_OVERRIDE_CTRL(chan) (0xA0 + ((chan) << 2)) |
82 | 82 | ||
83 | #define WINDOW_COUNT 8 | ||
84 | |||
83 | struct mv_xor_device { | 85 | struct mv_xor_device { |
84 | void __iomem *xor_base; | 86 | void __iomem *xor_base; |
85 | void __iomem *xor_high_base; | 87 | void __iomem *xor_high_base; |
86 | struct clk *clk; | 88 | struct clk *clk; |
87 | struct mv_xor_chan *channels[MV_XOR_MAX_CHANNELS]; | 89 | struct mv_xor_chan *channels[MV_XOR_MAX_CHANNELS]; |
88 | int xor_type; | 90 | int xor_type; |
91 | |||
92 | u32 win_start[WINDOW_COUNT]; | ||
93 | u32 win_end[WINDOW_COUNT]; | ||
89 | }; | 94 | }; |
90 | 95 | ||
91 | /** | 96 | /** |
@@ -127,6 +132,8 @@ struct mv_xor_chan { | |||
127 | char dummy_dst[MV_XOR_MIN_BYTE_COUNT]; | 132 | char dummy_dst[MV_XOR_MIN_BYTE_COUNT]; |
128 | dma_addr_t dummy_src_addr, dummy_dst_addr; | 133 | dma_addr_t dummy_src_addr, dummy_dst_addr; |
129 | u32 saved_config_reg, saved_int_mask_reg; | 134 | u32 saved_config_reg, saved_int_mask_reg; |
135 | |||
136 | struct mv_xor_device *xordev; | ||
130 | }; | 137 | }; |
131 | 138 | ||
132 | /** | 139 | /** |
diff --git a/include/linux/mbus.h b/include/linux/mbus.h index d610232762e3..2931aa43dab1 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h | |||
@@ -11,6 +11,8 @@ | |||
11 | #ifndef __LINUX_MBUS_H | 11 | #ifndef __LINUX_MBUS_H |
12 | #define __LINUX_MBUS_H | 12 | #define __LINUX_MBUS_H |
13 | 13 | ||
14 | #include <linux/errno.h> | ||
15 | |||
14 | struct resource; | 16 | struct resource; |
15 | 17 | ||
16 | struct mbus_dram_target_info | 18 | struct mbus_dram_target_info |
@@ -55,6 +57,8 @@ struct mbus_dram_target_info | |||
55 | #ifdef CONFIG_PLAT_ORION | 57 | #ifdef CONFIG_PLAT_ORION |
56 | extern const struct mbus_dram_target_info *mv_mbus_dram_info(void); | 58 | extern const struct mbus_dram_target_info *mv_mbus_dram_info(void); |
57 | extern const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void); | 59 | extern const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void); |
60 | int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target, | ||
61 | u8 *attr); | ||
58 | #else | 62 | #else |
59 | static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void) | 63 | static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void) |
60 | { | 64 | { |
@@ -64,14 +68,24 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(vo | |||
64 | { | 68 | { |
65 | return NULL; | 69 | return NULL; |
66 | } | 70 | } |
71 | static inline int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, | ||
72 | u8 *target, u8 *attr) | ||
73 | { | ||
74 | /* | ||
75 | * On all ARM32 MVEBU platforms with MBus support, this stub | ||
76 | * function will not get called. The real function from the | ||
77 | * MBus driver is called instead. ARM64 MVEBU platforms like | ||
78 | * the Armada 3700 could use the mv_xor device driver which calls | ||
79 | * into this function | ||
80 | */ | ||
81 | return -EINVAL; | ||
82 | } | ||
67 | #endif | 83 | #endif |
68 | 84 | ||
69 | int mvebu_mbus_save_cpu_target(u32 __iomem *store_addr); | 85 | int mvebu_mbus_save_cpu_target(u32 __iomem *store_addr); |
70 | void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); | 86 | void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); |
71 | void mvebu_mbus_get_pcie_io_aperture(struct resource *res); | 87 | void mvebu_mbus_get_pcie_io_aperture(struct resource *res); |
72 | int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr); | 88 | int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr); |
73 | int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target, | ||
74 | u8 *attr); | ||
75 | int mvebu_mbus_add_window_remap_by_id(unsigned int target, | 89 | int mvebu_mbus_add_window_remap_by_id(unsigned int target, |
76 | unsigned int attribute, | 90 | unsigned int attribute, |
77 | phys_addr_t base, size_t size, | 91 | phys_addr_t base, size_t size, |