| .. | .. |
|---|
| 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]; |
|---|