.. | .. |
---|
18 | 18 | #include <linux/completion.h> |
---|
19 | 19 | #include <linux/delay.h> |
---|
20 | 20 | #include <linux/dma-mapping.h> |
---|
| 21 | +#include <linux/dmaengine.h> |
---|
21 | 22 | #include <linux/errno.h> |
---|
22 | 23 | #include <linux/init.h> |
---|
23 | 24 | #include <linux/interrupt.h> |
---|
.. | .. |
---|
79 | 80 | DMM_PAT_DESCR__2, DMM_PAT_DESCR__3}, |
---|
80 | 81 | }; |
---|
81 | 82 | |
---|
| 83 | +static int dmm_dma_copy(struct dmm *dmm, dma_addr_t src, dma_addr_t dst) |
---|
| 84 | +{ |
---|
| 85 | + struct dma_async_tx_descriptor *tx; |
---|
| 86 | + enum dma_status status; |
---|
| 87 | + dma_cookie_t cookie; |
---|
| 88 | + |
---|
| 89 | + tx = dmaengine_prep_dma_memcpy(dmm->wa_dma_chan, dst, src, 4, 0); |
---|
| 90 | + if (!tx) { |
---|
| 91 | + dev_err(dmm->dev, "Failed to prepare DMA memcpy\n"); |
---|
| 92 | + return -EIO; |
---|
| 93 | + } |
---|
| 94 | + |
---|
| 95 | + cookie = tx->tx_submit(tx); |
---|
| 96 | + if (dma_submit_error(cookie)) { |
---|
| 97 | + dev_err(dmm->dev, "Failed to do DMA tx_submit\n"); |
---|
| 98 | + return -EIO; |
---|
| 99 | + } |
---|
| 100 | + |
---|
| 101 | + status = dma_sync_wait(dmm->wa_dma_chan, cookie); |
---|
| 102 | + if (status != DMA_COMPLETE) |
---|
| 103 | + dev_err(dmm->dev, "i878 wa DMA copy failure\n"); |
---|
| 104 | + |
---|
| 105 | + dmaengine_terminate_all(dmm->wa_dma_chan); |
---|
| 106 | + return 0; |
---|
| 107 | +} |
---|
| 108 | + |
---|
| 109 | +static u32 dmm_read_wa(struct dmm *dmm, u32 reg) |
---|
| 110 | +{ |
---|
| 111 | + dma_addr_t src, dst; |
---|
| 112 | + int r; |
---|
| 113 | + |
---|
| 114 | + src = dmm->phys_base + reg; |
---|
| 115 | + dst = dmm->wa_dma_handle; |
---|
| 116 | + |
---|
| 117 | + r = dmm_dma_copy(dmm, src, dst); |
---|
| 118 | + if (r) { |
---|
| 119 | + dev_err(dmm->dev, "sDMA read transfer timeout\n"); |
---|
| 120 | + return readl(dmm->base + reg); |
---|
| 121 | + } |
---|
| 122 | + |
---|
| 123 | + /* |
---|
| 124 | + * As per i878 workaround, the DMA is used to access the DMM registers. |
---|
| 125 | + * Make sure that the readl is not moved by the compiler or the CPU |
---|
| 126 | + * earlier than the DMA finished writing the value to memory. |
---|
| 127 | + */ |
---|
| 128 | + rmb(); |
---|
| 129 | + return readl(dmm->wa_dma_data); |
---|
| 130 | +} |
---|
| 131 | + |
---|
| 132 | +static void dmm_write_wa(struct dmm *dmm, u32 val, u32 reg) |
---|
| 133 | +{ |
---|
| 134 | + dma_addr_t src, dst; |
---|
| 135 | + int r; |
---|
| 136 | + |
---|
| 137 | + writel(val, dmm->wa_dma_data); |
---|
| 138 | + /* |
---|
| 139 | + * As per i878 workaround, the DMA is used to access the DMM registers. |
---|
| 140 | + * Make sure that the writel is not moved by the compiler or the CPU, so |
---|
| 141 | + * the data will be in place before we start the DMA to do the actual |
---|
| 142 | + * register write. |
---|
| 143 | + */ |
---|
| 144 | + wmb(); |
---|
| 145 | + |
---|
| 146 | + src = dmm->wa_dma_handle; |
---|
| 147 | + dst = dmm->phys_base + reg; |
---|
| 148 | + |
---|
| 149 | + r = dmm_dma_copy(dmm, src, dst); |
---|
| 150 | + if (r) { |
---|
| 151 | + dev_err(dmm->dev, "sDMA write transfer timeout\n"); |
---|
| 152 | + writel(val, dmm->base + reg); |
---|
| 153 | + } |
---|
| 154 | +} |
---|
| 155 | + |
---|
82 | 156 | static u32 dmm_read(struct dmm *dmm, u32 reg) |
---|
83 | 157 | { |
---|
84 | | - return readl(dmm->base + reg); |
---|
| 158 | + if (dmm->dmm_workaround) { |
---|
| 159 | + u32 v; |
---|
| 160 | + unsigned long flags; |
---|
| 161 | + |
---|
| 162 | + spin_lock_irqsave(&dmm->wa_lock, flags); |
---|
| 163 | + v = dmm_read_wa(dmm, reg); |
---|
| 164 | + spin_unlock_irqrestore(&dmm->wa_lock, flags); |
---|
| 165 | + |
---|
| 166 | + return v; |
---|
| 167 | + } else { |
---|
| 168 | + return readl(dmm->base + reg); |
---|
| 169 | + } |
---|
85 | 170 | } |
---|
86 | 171 | |
---|
87 | 172 | static void dmm_write(struct dmm *dmm, u32 val, u32 reg) |
---|
88 | 173 | { |
---|
89 | | - writel(val, dmm->base + reg); |
---|
| 174 | + if (dmm->dmm_workaround) { |
---|
| 175 | + unsigned long flags; |
---|
| 176 | + |
---|
| 177 | + spin_lock_irqsave(&dmm->wa_lock, flags); |
---|
| 178 | + dmm_write_wa(dmm, val, reg); |
---|
| 179 | + spin_unlock_irqrestore(&dmm->wa_lock, flags); |
---|
| 180 | + } else { |
---|
| 181 | + writel(val, dmm->base + reg); |
---|
| 182 | + } |
---|
| 183 | +} |
---|
| 184 | + |
---|
| 185 | +static int dmm_workaround_init(struct dmm *dmm) |
---|
| 186 | +{ |
---|
| 187 | + dma_cap_mask_t mask; |
---|
| 188 | + |
---|
| 189 | + spin_lock_init(&dmm->wa_lock); |
---|
| 190 | + |
---|
| 191 | + dmm->wa_dma_data = dma_alloc_coherent(dmm->dev, sizeof(u32), |
---|
| 192 | + &dmm->wa_dma_handle, GFP_KERNEL); |
---|
| 193 | + if (!dmm->wa_dma_data) |
---|
| 194 | + return -ENOMEM; |
---|
| 195 | + |
---|
| 196 | + dma_cap_zero(mask); |
---|
| 197 | + dma_cap_set(DMA_MEMCPY, mask); |
---|
| 198 | + |
---|
| 199 | + dmm->wa_dma_chan = dma_request_channel(mask, NULL, NULL); |
---|
| 200 | + if (!dmm->wa_dma_chan) { |
---|
| 201 | + dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle); |
---|
| 202 | + return -ENODEV; |
---|
| 203 | + } |
---|
| 204 | + |
---|
| 205 | + return 0; |
---|
| 206 | +} |
---|
| 207 | + |
---|
| 208 | +static void dmm_workaround_uninit(struct dmm *dmm) |
---|
| 209 | +{ |
---|
| 210 | + dma_release_channel(dmm->wa_dma_chan); |
---|
| 211 | + |
---|
| 212 | + dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle); |
---|
90 | 213 | } |
---|
91 | 214 | |
---|
92 | 215 | /* simple allocator to grab next 16 byte aligned memory from txn */ |
---|
.. | .. |
---|
614 | 737 | unsigned long flags; |
---|
615 | 738 | |
---|
616 | 739 | if (omap_dmm) { |
---|
| 740 | + /* Disable all enabled interrupts */ |
---|
| 741 | + dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_CLR); |
---|
| 742 | + free_irq(omap_dmm->irq, omap_dmm); |
---|
| 743 | + |
---|
617 | 744 | /* free all area regions */ |
---|
618 | 745 | spin_lock_irqsave(&list_lock, flags); |
---|
619 | 746 | list_for_each_entry_safe(block, _block, &omap_dmm->alloc_head, |
---|
.. | .. |
---|
636 | 763 | if (omap_dmm->dummy_page) |
---|
637 | 764 | __free_page(omap_dmm->dummy_page); |
---|
638 | 765 | |
---|
639 | | - if (omap_dmm->irq > 0) |
---|
640 | | - free_irq(omap_dmm->irq, omap_dmm); |
---|
| 766 | + if (omap_dmm->dmm_workaround) |
---|
| 767 | + dmm_workaround_uninit(omap_dmm); |
---|
641 | 768 | |
---|
642 | 769 | iounmap(omap_dmm->base); |
---|
643 | 770 | kfree(omap_dmm); |
---|
.. | .. |
---|
684 | 811 | goto fail; |
---|
685 | 812 | } |
---|
686 | 813 | |
---|
| 814 | + omap_dmm->phys_base = mem->start; |
---|
687 | 815 | omap_dmm->base = ioremap(mem->start, SZ_2K); |
---|
688 | 816 | |
---|
689 | 817 | if (!omap_dmm->base) { |
---|
.. | .. |
---|
698 | 826 | } |
---|
699 | 827 | |
---|
700 | 828 | omap_dmm->dev = &dev->dev; |
---|
| 829 | + |
---|
| 830 | + if (of_machine_is_compatible("ti,dra7")) { |
---|
| 831 | + /* |
---|
| 832 | + * DRA7 Errata i878 says that MPU should not be used to access |
---|
| 833 | + * RAM and DMM at the same time. As it's not possible to prevent |
---|
| 834 | + * MPU accessing RAM, we need to access DMM via a proxy. |
---|
| 835 | + */ |
---|
| 836 | + if (!dmm_workaround_init(omap_dmm)) { |
---|
| 837 | + omap_dmm->dmm_workaround = true; |
---|
| 838 | + dev_info(&dev->dev, |
---|
| 839 | + "workaround for errata i878 in use\n"); |
---|
| 840 | + } else { |
---|
| 841 | + dev_warn(&dev->dev, |
---|
| 842 | + "failed to initialize work-around for i878\n"); |
---|
| 843 | + } |
---|
| 844 | + } |
---|
701 | 845 | |
---|
702 | 846 | hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO); |
---|
703 | 847 | omap_dmm->num_engines = (hwinfo >> 24) & 0x1F; |
---|
.. | .. |
---|
724 | 868 | dmm_write(omap_dmm, 0x80000000, DMM_PAT_VIEW_MAP_BASE); |
---|
725 | 869 | dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__0); |
---|
726 | 870 | dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__1); |
---|
727 | | - |
---|
728 | | - ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED, |
---|
729 | | - "omap_dmm_irq_handler", omap_dmm); |
---|
730 | | - |
---|
731 | | - if (ret) { |
---|
732 | | - dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n", |
---|
733 | | - omap_dmm->irq, ret); |
---|
734 | | - omap_dmm->irq = -1; |
---|
735 | | - goto fail; |
---|
736 | | - } |
---|
737 | | - |
---|
738 | | - /* Enable all interrupts for each refill engine except |
---|
739 | | - * ERR_LUT_MISS<n> (which is just advisory, and we don't care |
---|
740 | | - * about because we want to be able to refill live scanout |
---|
741 | | - * buffers for accelerated pan/scroll) and FILL_DSC<n> which |
---|
742 | | - * we just generally don't care about. |
---|
743 | | - */ |
---|
744 | | - dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_SET); |
---|
745 | 871 | |
---|
746 | 872 | omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32); |
---|
747 | 873 | if (!omap_dmm->dummy_page) { |
---|
.. | .. |
---|
835 | 961 | .p1.y = omap_dmm->container_height - 1, |
---|
836 | 962 | }; |
---|
837 | 963 | |
---|
| 964 | + ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED, |
---|
| 965 | + "omap_dmm_irq_handler", omap_dmm); |
---|
| 966 | + |
---|
| 967 | + if (ret) { |
---|
| 968 | + dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n", |
---|
| 969 | + omap_dmm->irq, ret); |
---|
| 970 | + omap_dmm->irq = -1; |
---|
| 971 | + goto fail; |
---|
| 972 | + } |
---|
| 973 | + |
---|
| 974 | + /* Enable all interrupts for each refill engine except |
---|
| 975 | + * ERR_LUT_MISS<n> (which is just advisory, and we don't care |
---|
| 976 | + * about because we want to be able to refill live scanout |
---|
| 977 | + * buffers for accelerated pan/scroll) and FILL_DSC<n> which |
---|
| 978 | + * we just generally don't care about. |
---|
| 979 | + */ |
---|
| 980 | + dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_SET); |
---|
| 981 | + |
---|
838 | 982 | /* initialize all LUTs to dummy page entries */ |
---|
839 | 983 | for (i = 0; i < omap_dmm->num_lut; i++) { |
---|
840 | 984 | area.tcm = omap_dmm->tcm[i]; |
---|