1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * OMAP1/OMAP7xx - specific DMA driver |
4 | * |
5 | * Copyright (C) 2003 - 2008 Nokia Corporation |
6 | * Author: Juha Yrjölä <juha.yrjola@nokia.com> |
7 | * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> |
8 | * Graphics DMA and LCD DMA graphics tranformations |
9 | * by Imre Deak <imre.deak@nokia.com> |
10 | * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc. |
11 | * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. |
12 | * |
13 | * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ |
14 | * Converted DMA library into platform driver |
15 | * - G, Manjunath Kondaiah <manjugk@ti.com> |
16 | */ |
17 | |
18 | #include <linux/err.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/module.h> |
21 | #include <linux/init.h> |
22 | #include <linux/device.h> |
23 | #include <linux/io.h> |
24 | #include <linux/dma-mapping.h> |
25 | #include <linux/dmaengine.h> |
26 | #include <linux/omap-dma.h> |
27 | #include "tc.h" |
28 | |
29 | #include "soc.h" |
30 | |
31 | #define OMAP1_DMA_BASE (0xfffed800) |
32 | |
33 | static u32 enable_1510_mode; |
34 | |
35 | static const struct omap_dma_reg reg_map[] = { |
36 | [GCR] = { 0x0400, 0x00, OMAP_DMA_REG_16BIT }, |
37 | [GSCR] = { .offset: 0x0404, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
38 | [GRST1] = { .offset: 0x0408, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
39 | [HW_ID] = { .offset: 0x0442, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
40 | [PCH2_ID] = { .offset: 0x0444, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
41 | [PCH0_ID] = { .offset: 0x0446, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
42 | [PCH1_ID] = { .offset: 0x0448, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
43 | [PCHG_ID] = { .offset: 0x044a, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
44 | [PCHD_ID] = { .offset: 0x044c, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
45 | [CAPS_0] = { .offset: 0x044e, .stride: 0x00, .type: OMAP_DMA_REG_2X16BIT }, |
46 | [CAPS_1] = { .offset: 0x0452, .stride: 0x00, .type: OMAP_DMA_REG_2X16BIT }, |
47 | [CAPS_2] = { .offset: 0x0456, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
48 | [CAPS_3] = { .offset: 0x0458, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
49 | [CAPS_4] = { .offset: 0x045a, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
50 | [PCH2_SR] = { .offset: 0x0460, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
51 | [PCH0_SR] = { .offset: 0x0480, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
52 | [PCH1_SR] = { .offset: 0x0482, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
53 | [PCHD_SR] = { .offset: 0x04c0, .stride: 0x00, .type: OMAP_DMA_REG_16BIT }, |
54 | |
55 | /* Common Registers */ |
56 | [CSDP] = { .offset: 0x0000, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
57 | [CCR] = { .offset: 0x0002, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
58 | [CICR] = { .offset: 0x0004, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
59 | [CSR] = { .offset: 0x0006, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
60 | [CEN] = { .offset: 0x0010, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
61 | [CFN] = { .offset: 0x0012, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
62 | [CSFI] = { .offset: 0x0014, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
63 | [CSEI] = { .offset: 0x0016, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
64 | [CPC] = { .offset: 0x0018, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, /* 15xx only */ |
65 | [CSAC] = { .offset: 0x0018, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
66 | [CDAC] = { .offset: 0x001a, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
67 | [CDEI] = { .offset: 0x001c, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
68 | [CDFI] = { .offset: 0x001e, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
69 | [CLNK_CTRL] = { .offset: 0x0028, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
70 | |
71 | /* Channel specific register offsets */ |
72 | [CSSA] = { .offset: 0x0008, .stride: 0x40, .type: OMAP_DMA_REG_2X16BIT }, |
73 | [CDSA] = { .offset: 0x000c, .stride: 0x40, .type: OMAP_DMA_REG_2X16BIT }, |
74 | [COLOR] = { .offset: 0x0020, .stride: 0x40, .type: OMAP_DMA_REG_2X16BIT }, |
75 | [CCR2] = { .offset: 0x0024, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
76 | [LCH_CTRL] = { .offset: 0x002a, .stride: 0x40, .type: OMAP_DMA_REG_16BIT }, |
77 | }; |
78 | |
79 | static struct resource res[] __initdata = { |
80 | [0] = { |
81 | .start = OMAP1_DMA_BASE, |
82 | .end = OMAP1_DMA_BASE + SZ_2K - 1, |
83 | .flags = IORESOURCE_MEM, |
84 | }, |
85 | [1] = { |
86 | .name = "0" , |
87 | .start = INT_DMA_CH0_6, |
88 | .flags = IORESOURCE_IRQ, |
89 | }, |
90 | [2] = { |
91 | .name = "1" , |
92 | .start = INT_DMA_CH1_7, |
93 | .flags = IORESOURCE_IRQ, |
94 | }, |
95 | [3] = { |
96 | .name = "2" , |
97 | .start = INT_DMA_CH2_8, |
98 | .flags = IORESOURCE_IRQ, |
99 | }, |
100 | [4] = { |
101 | .name = "3" , |
102 | .start = INT_DMA_CH3, |
103 | .flags = IORESOURCE_IRQ, |
104 | }, |
105 | [5] = { |
106 | .name = "4" , |
107 | .start = INT_DMA_CH4, |
108 | .flags = IORESOURCE_IRQ, |
109 | }, |
110 | [6] = { |
111 | .name = "5" , |
112 | .start = INT_DMA_CH5, |
113 | .flags = IORESOURCE_IRQ, |
114 | }, |
115 | /* Handled in lcd_dma.c */ |
116 | [7] = { |
117 | .name = "6" , |
118 | .start = INT_1610_DMA_CH6, |
119 | .flags = IORESOURCE_IRQ, |
120 | }, |
121 | /* irq's for omap16xx and omap7xx */ |
122 | [8] = { |
123 | .name = "7" , |
124 | .start = INT_1610_DMA_CH7, |
125 | .flags = IORESOURCE_IRQ, |
126 | }, |
127 | [9] = { |
128 | .name = "8" , |
129 | .start = INT_1610_DMA_CH8, |
130 | .flags = IORESOURCE_IRQ, |
131 | }, |
132 | [10] = { |
133 | .name = "9" , |
134 | .start = INT_1610_DMA_CH9, |
135 | .flags = IORESOURCE_IRQ, |
136 | }, |
137 | [11] = { |
138 | .name = "10" , |
139 | .start = INT_1610_DMA_CH10, |
140 | .flags = IORESOURCE_IRQ, |
141 | }, |
142 | [12] = { |
143 | .name = "11" , |
144 | .start = INT_1610_DMA_CH11, |
145 | .flags = IORESOURCE_IRQ, |
146 | }, |
147 | [13] = { |
148 | .name = "12" , |
149 | .start = INT_1610_DMA_CH12, |
150 | .flags = IORESOURCE_IRQ, |
151 | }, |
152 | [14] = { |
153 | .name = "13" , |
154 | .start = INT_1610_DMA_CH13, |
155 | .flags = IORESOURCE_IRQ, |
156 | }, |
157 | [15] = { |
158 | .name = "14" , |
159 | .start = INT_1610_DMA_CH14, |
160 | .flags = IORESOURCE_IRQ, |
161 | }, |
162 | [16] = { |
163 | .name = "15" , |
164 | .start = INT_1610_DMA_CH15, |
165 | .flags = IORESOURCE_IRQ, |
166 | }, |
167 | [17] = { |
168 | .name = "16" , |
169 | .start = INT_DMA_LCD, |
170 | .flags = IORESOURCE_IRQ, |
171 | }, |
172 | }; |
173 | |
174 | static void __iomem *dma_base; |
175 | static inline void dma_write(u32 val, int reg, int lch) |
176 | { |
177 | void __iomem *addr = dma_base; |
178 | |
179 | addr += reg_map[reg].offset; |
180 | addr += reg_map[reg].stride * lch; |
181 | |
182 | __raw_writew(val, addr); |
183 | if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT) |
184 | __raw_writew(val: val >> 16, addr: addr + 2); |
185 | } |
186 | |
187 | static inline u32 dma_read(int reg, int lch) |
188 | { |
189 | void __iomem *addr = dma_base; |
190 | uint32_t val; |
191 | |
192 | addr += reg_map[reg].offset; |
193 | addr += reg_map[reg].stride * lch; |
194 | |
195 | val = __raw_readw(addr); |
196 | if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT) |
197 | val |= __raw_readw(addr: addr + 2) << 16; |
198 | |
199 | return val; |
200 | } |
201 | |
202 | static void omap1_clear_lch_regs(int lch) |
203 | { |
204 | int i; |
205 | |
206 | for (i = CPC; i <= COLOR; i += 1) |
207 | dma_write(val: 0, reg: i, lch); |
208 | } |
209 | |
210 | static void omap1_clear_dma(int lch) |
211 | { |
212 | u32 l; |
213 | |
214 | l = dma_read(reg: CCR, lch); |
215 | l &= ~OMAP_DMA_CCR_EN; |
216 | dma_write(val: l, reg: CCR, lch); |
217 | |
218 | /* Clear pending interrupts */ |
219 | l = dma_read(reg: CSR, lch); |
220 | } |
221 | |
222 | static void omap1_show_dma_caps(void) |
223 | { |
224 | if (enable_1510_mode) { |
225 | printk(KERN_INFO "DMA support for OMAP15xx initialized\n" ); |
226 | } else { |
227 | u16 w; |
228 | printk(KERN_INFO "OMAP DMA hardware version %d\n" , |
229 | dma_read(HW_ID, 0)); |
230 | printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n" , |
231 | dma_read(CAPS_0, 0), dma_read(CAPS_1, 0), |
232 | dma_read(CAPS_2, 0), dma_read(CAPS_3, 0), |
233 | dma_read(CAPS_4, 0)); |
234 | |
235 | /* Disable OMAP 3.0/3.1 compatibility mode. */ |
236 | w = dma_read(reg: GSCR, lch: 0); |
237 | w |= 1 << 3; |
238 | dma_write(val: w, reg: GSCR, lch: 0); |
239 | } |
240 | } |
241 | |
242 | static unsigned configure_dma_errata(void) |
243 | { |
244 | unsigned errata = 0; |
245 | |
246 | /* |
247 | * Erratum 3.2/3.3: sometimes 0 is returned if CSAC/CDAC is |
248 | * read before the DMA controller finished disabling the channel. |
249 | */ |
250 | if (!cpu_is_omap15xx()) |
251 | SET_DMA_ERRATA(DMA_ERRATA_3_3); |
252 | |
253 | return errata; |
254 | } |
255 | |
256 | static const struct platform_device_info omap_dma_dev_info = { |
257 | .name = "omap-dma-engine" , |
258 | .id = -1, |
259 | .dma_mask = DMA_BIT_MASK(32), |
260 | .res = res, |
261 | .num_res = 1, |
262 | }; |
263 | |
264 | /* OMAP1510, OMAP1610*/ |
265 | static const struct dma_slave_map omap1xxx_sdma_map[] = { |
266 | { "omap-mcbsp.1" , "tx" , SDMA_FILTER_PARAM(8) }, |
267 | { "omap-mcbsp.1" , "rx" , SDMA_FILTER_PARAM(9) }, |
268 | { "omap-mcbsp.3" , "tx" , SDMA_FILTER_PARAM(10) }, |
269 | { "omap-mcbsp.3" , "rx" , SDMA_FILTER_PARAM(11) }, |
270 | { "omap-mcbsp.2" , "tx" , SDMA_FILTER_PARAM(16) }, |
271 | { "omap-mcbsp.2" , "rx" , SDMA_FILTER_PARAM(17) }, |
272 | { "mmci-omap.0" , "tx" , SDMA_FILTER_PARAM(21) }, |
273 | { "mmci-omap.0" , "rx" , SDMA_FILTER_PARAM(22) }, |
274 | { "omap_udc" , "rx0" , SDMA_FILTER_PARAM(26) }, |
275 | { "omap_udc" , "rx1" , SDMA_FILTER_PARAM(27) }, |
276 | { "omap_udc" , "rx2" , SDMA_FILTER_PARAM(28) }, |
277 | { "omap_udc" , "tx0" , SDMA_FILTER_PARAM(29) }, |
278 | { "omap_udc" , "tx1" , SDMA_FILTER_PARAM(30) }, |
279 | { "omap_udc" , "tx2" , SDMA_FILTER_PARAM(31) }, |
280 | { "mmci-omap.1" , "tx" , SDMA_FILTER_PARAM(54) }, |
281 | { "mmci-omap.1" , "rx" , SDMA_FILTER_PARAM(55) }, |
282 | }; |
283 | |
284 | static struct omap_system_dma_plat_info dma_plat_info __initdata = { |
285 | .reg_map = reg_map, |
286 | .channel_stride = 0x40, |
287 | .show_dma_caps = omap1_show_dma_caps, |
288 | .clear_lch_regs = omap1_clear_lch_regs, |
289 | .clear_dma = omap1_clear_dma, |
290 | .dma_write = dma_write, |
291 | .dma_read = dma_read, |
292 | }; |
293 | |
294 | static int __init omap1_system_dma_init(void) |
295 | { |
296 | struct omap_system_dma_plat_info p; |
297 | struct omap_dma_dev_attr *d; |
298 | struct platform_device *pdev, *dma_pdev; |
299 | int ret; |
300 | |
301 | pdev = platform_device_alloc(name: "omap_dma_system" , id: 0); |
302 | if (!pdev) { |
303 | pr_err("%s: Unable to device alloc for dma\n" , |
304 | __func__); |
305 | return -ENOMEM; |
306 | } |
307 | |
308 | dma_base = ioremap(offset: res[0].start, size: resource_size(res: &res[0])); |
309 | if (!dma_base) { |
310 | pr_err("%s: Unable to ioremap\n" , __func__); |
311 | ret = -ENODEV; |
312 | goto exit_device_put; |
313 | } |
314 | |
315 | ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); |
316 | if (ret) { |
317 | dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n" , |
318 | __func__, pdev->name, pdev->id); |
319 | goto exit_iounmap; |
320 | } |
321 | |
322 | d = kzalloc(size: sizeof(*d), GFP_KERNEL); |
323 | if (!d) { |
324 | ret = -ENOMEM; |
325 | goto exit_iounmap; |
326 | } |
327 | |
328 | /* Valid attributes for omap1 plus processors */ |
329 | if (cpu_is_omap15xx()) |
330 | d->dev_caps = ENABLE_1510_MODE; |
331 | enable_1510_mode = d->dev_caps & ENABLE_1510_MODE; |
332 | |
333 | if (cpu_is_omap16xx()) |
334 | d->dev_caps = ENABLE_16XX_MODE; |
335 | |
336 | d->dev_caps |= SRC_PORT; |
337 | d->dev_caps |= DST_PORT; |
338 | d->dev_caps |= SRC_INDEX; |
339 | d->dev_caps |= DST_INDEX; |
340 | d->dev_caps |= IS_BURST_ONLY4; |
341 | d->dev_caps |= CLEAR_CSR_ON_READ; |
342 | d->dev_caps |= IS_WORD_16; |
343 | |
344 | /* available logical channels */ |
345 | if (cpu_is_omap15xx()) { |
346 | d->lch_count = 9; |
347 | } else { |
348 | if (d->dev_caps & ENABLE_1510_MODE) |
349 | d->lch_count = 9; |
350 | else |
351 | d->lch_count = 16; |
352 | } |
353 | |
354 | p = dma_plat_info; |
355 | p.dma_attr = d; |
356 | p.errata = configure_dma_errata(); |
357 | |
358 | p.slave_map = omap1xxx_sdma_map; |
359 | p.slavecnt = ARRAY_SIZE(omap1xxx_sdma_map); |
360 | |
361 | ret = platform_device_add_data(pdev, data: &p, size: sizeof(p)); |
362 | if (ret) { |
363 | dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n" , |
364 | __func__, pdev->name, pdev->id); |
365 | goto exit_release_d; |
366 | } |
367 | |
368 | ret = platform_device_add(pdev); |
369 | if (ret) { |
370 | dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n" , |
371 | __func__, pdev->name, pdev->id); |
372 | goto exit_release_d; |
373 | } |
374 | |
375 | dma_pdev = platform_device_register_full(pdevinfo: &omap_dma_dev_info); |
376 | if (IS_ERR(ptr: dma_pdev)) { |
377 | ret = PTR_ERR(ptr: dma_pdev); |
378 | goto exit_release_pdev; |
379 | } |
380 | |
381 | return ret; |
382 | |
383 | exit_release_pdev: |
384 | platform_device_del(pdev); |
385 | exit_release_d: |
386 | kfree(objp: d); |
387 | exit_iounmap: |
388 | iounmap(addr: dma_base); |
389 | exit_device_put: |
390 | platform_device_put(pdev); |
391 | |
392 | return ret; |
393 | } |
394 | arch_initcall(omap1_system_dma_init); |
395 | |