aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2018-09-26 05:11:30 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2018-10-02 02:36:56 -0400
commitf5b9930b85dc6319fd6bcc259e447eff62fc691c (patch)
tree374c62c7c7f398320ab64be23983b0290e483ba6
parent176c866d40551565e78893fc0cb2bc8eed7b3c80 (diff)
drm/omap: partial workaround for DRA7xx DMM errata i878
Errata i878 says that MPU should not be used to access RAM and DMM at the same time. As it's not possible to prevent MPU accessing RAM, we need to access DMM via a proxy. This patch changes DMM driver to access DMM registers via sDMA. Instead of doing a normal readl/writel call to read/write a register, we use sDMA to copy 4 bytes from/to the DMM registers. This patch provides only a partial workaround for i878, as not only DMM register reads/writes are affected, but also accesses to the DMM mapped buffers (framebuffers, usually). Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_priv.h7
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c149
2 files changed, 154 insertions, 2 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
index c2785cc98dc9..60bb3f9297bc 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
@@ -159,6 +159,7 @@ struct dmm_platform_data {
159 159
160struct dmm { 160struct dmm {
161 struct device *dev; 161 struct device *dev;
162 dma_addr_t phys_base;
162 void __iomem *base; 163 void __iomem *base;
163 int irq; 164 int irq;
164 165
@@ -189,6 +190,12 @@ struct dmm {
189 struct list_head alloc_head; 190 struct list_head alloc_head;
190 191
191 const struct dmm_platform_data *plat_data; 192 const struct dmm_platform_data *plat_data;
193
194 bool dmm_workaround;
195 spinlock_t wa_lock;
196 u32 *wa_dma_data;
197 dma_addr_t wa_dma_handle;
198 struct dma_chan *wa_dma_chan;
192}; 199};
193 200
194#endif 201#endif
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 624d2023dd6b..252f5ebb1acc 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -18,6 +18,7 @@
18#include <linux/completion.h> 18#include <linux/completion.h>
19#include <linux/delay.h> 19#include <linux/delay.h>
20#include <linux/dma-mapping.h> 20#include <linux/dma-mapping.h>
21#include <linux/dmaengine.h>
21#include <linux/errno.h> 22#include <linux/errno.h>
22#include <linux/init.h> 23#include <linux/init.h>
23#include <linux/interrupt.h> 24#include <linux/interrupt.h>
@@ -79,14 +80,138 @@ static const u32 reg[][4] = {
79 DMM_PAT_DESCR__2, DMM_PAT_DESCR__3}, 80 DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
80}; 81};
81 82
83static int dmm_dma_copy(struct dmm *dmm, dma_addr_t src, dma_addr_t dst)
84{
85 struct dma_device *dma_dev = dmm->wa_dma_chan->device;
86 struct dma_async_tx_descriptor *tx;
87 enum dma_status status;
88 dma_cookie_t cookie;
89
90 tx = dma_dev->device_prep_dma_memcpy(dmm->wa_dma_chan, dst, src, 4, 0);
91 if (!tx) {
92 dev_err(dmm->dev, "Failed to prepare DMA memcpy\n");
93 return -EIO;
94 }
95
96 cookie = tx->tx_submit(tx);
97 if (dma_submit_error(cookie)) {
98 dev_err(dmm->dev, "Failed to do DMA tx_submit\n");
99 return -EIO;
100 }
101
102 dma_async_issue_pending(dmm->wa_dma_chan);
103 status = dma_sync_wait(dmm->wa_dma_chan, cookie);
104 if (status != DMA_COMPLETE)
105 dev_err(dmm->dev, "i878 wa DMA copy failure\n");
106
107 dmaengine_terminate_all(dmm->wa_dma_chan);
108 return 0;
109}
110
111static u32 dmm_read_wa(struct dmm *dmm, u32 reg)
112{
113 dma_addr_t src, dst;
114 int r;
115
116 src = dmm->phys_base + reg;
117 dst = dmm->wa_dma_handle;
118
119 r = dmm_dma_copy(dmm, src, dst);
120 if (r) {
121 dev_err(dmm->dev, "sDMA read transfer timeout\n");
122 return readl(dmm->base + reg);
123 }
124
125 /*
126 * As per i878 workaround, the DMA is used to access the DMM registers.
127 * Make sure that the readl is not moved by the compiler or the CPU
128 * earlier than the DMA finished writing the value to memory.
129 */
130 rmb();
131 return readl(dmm->wa_dma_data);
132}
133
134static void dmm_write_wa(struct dmm *dmm, u32 val, u32 reg)
135{
136 dma_addr_t src, dst;
137 int r;
138
139 writel(val, dmm->wa_dma_data);
140 /*
141 * As per i878 workaround, the DMA is used to access the DMM registers.
142 * Make sure that the writel is not moved by the compiler or the CPU, so
143 * the data will be in place before we start the DMA to do the actual
144 * register write.
145 */
146 wmb();
147
148 src = dmm->wa_dma_handle;
149 dst = dmm->phys_base + reg;
150
151 r = dmm_dma_copy(dmm, src, dst);
152 if (r) {
153 dev_err(dmm->dev, "sDMA write transfer timeout\n");
154 writel(val, dmm->base + reg);
155 }
156}
157
82static u32 dmm_read(struct dmm *dmm, u32 reg) 158static u32 dmm_read(struct dmm *dmm, u32 reg)
83{ 159{
84 return readl(dmm->base + reg); 160 if (dmm->dmm_workaround) {
161 u32 v;
162 unsigned long flags;
163
164 spin_lock_irqsave(&dmm->wa_lock, flags);
165 v = dmm_read_wa(dmm, reg);
166 spin_unlock_irqrestore(&dmm->wa_lock, flags);
167
168 return v;
169 } else {
170 return readl(dmm->base + reg);
171 }
85} 172}
86 173
87static void dmm_write(struct dmm *dmm, u32 val, u32 reg) 174static void dmm_write(struct dmm *dmm, u32 val, u32 reg)
88{ 175{
89 writel(val, dmm->base + reg); 176 if (dmm->dmm_workaround) {
177 unsigned long flags;
178
179 spin_lock_irqsave(&dmm->wa_lock, flags);
180 dmm_write_wa(dmm, val, reg);
181 spin_unlock_irqrestore(&dmm->wa_lock, flags);
182 } else {
183 writel(val, dmm->base + reg);
184 }
185}
186
187static int dmm_workaround_init(struct dmm *dmm)
188{
189 dma_cap_mask_t mask;
190
191 spin_lock_init(&dmm->wa_lock);
192
193 dmm->wa_dma_data = dma_alloc_coherent(dmm->dev, sizeof(u32),
194 &dmm->wa_dma_handle, GFP_KERNEL);
195 if (!dmm->wa_dma_data)
196 return -ENOMEM;
197
198 dma_cap_zero(mask);
199 dma_cap_set(DMA_MEMCPY, mask);
200
201 dmm->wa_dma_chan = dma_request_channel(mask, NULL, NULL);
202 if (!dmm->wa_dma_chan) {
203 dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle);
204 return -ENODEV;
205 }
206
207 return 0;
208}
209
210static void dmm_workaround_uninit(struct dmm *dmm)
211{
212 dma_release_channel(dmm->wa_dma_chan);
213
214 dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle);
90} 215}
91 216
92/* simple allocator to grab next 16 byte aligned memory from txn */ 217/* simple allocator to grab next 16 byte aligned memory from txn */
@@ -640,6 +765,9 @@ static int omap_dmm_remove(struct platform_device *dev)
640 if (omap_dmm->dummy_page) 765 if (omap_dmm->dummy_page)
641 __free_page(omap_dmm->dummy_page); 766 __free_page(omap_dmm->dummy_page);
642 767
768 if (omap_dmm->dmm_workaround)
769 dmm_workaround_uninit(omap_dmm);
770
643 iounmap(omap_dmm->base); 771 iounmap(omap_dmm->base);
644 kfree(omap_dmm); 772 kfree(omap_dmm);
645 omap_dmm = NULL; 773 omap_dmm = NULL;
@@ -685,6 +813,7 @@ static int omap_dmm_probe(struct platform_device *dev)
685 goto fail; 813 goto fail;
686 } 814 }
687 815
816 omap_dmm->phys_base = mem->start;
688 omap_dmm->base = ioremap(mem->start, SZ_2K); 817 omap_dmm->base = ioremap(mem->start, SZ_2K);
689 818
690 if (!omap_dmm->base) { 819 if (!omap_dmm->base) {
@@ -700,6 +829,22 @@ static int omap_dmm_probe(struct platform_device *dev)
700 829
701 omap_dmm->dev = &dev->dev; 830 omap_dmm->dev = &dev->dev;
702 831
832 if (of_machine_is_compatible("ti,dra7")) {
833 /*
834 * DRA7 Errata i878 says that MPU should not be used to access
835 * RAM and DMM at the same time. As it's not possible to prevent
836 * MPU accessing RAM, we need to access DMM via a proxy.
837 */
838 if (!dmm_workaround_init(omap_dmm)) {
839 omap_dmm->dmm_workaround = true;
840 dev_info(&dev->dev,
841 "workaround for errata i878 in use\n");
842 } else {
843 dev_warn(&dev->dev,
844 "failed to initialize work-around for i878\n");
845 }
846 }
847
703 hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO); 848 hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO);
704 omap_dmm->num_engines = (hwinfo >> 24) & 0x1F; 849 omap_dmm->num_engines = (hwinfo >> 24) & 0x1F;
705 omap_dmm->num_lut = (hwinfo >> 16) & 0x1F; 850 omap_dmm->num_lut = (hwinfo >> 16) & 0x1F;