xref: /linux/drivers/dma/bestcomm/bestcomm.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Driver for MPC52xx processor BestComm peripheral controller
4  *
5  * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
6  * Copyright (C) 2005      Varma Electronics Oy,
7  *                         ( by Andrey Volkov <avolkov@varma-el.com> )
8  * Copyright (C) 2003-2004 MontaVista, Software, Inc.
9  *                         ( by Dale Farnsworth <dfarnsworth@mvista.com> )
10  */
11 
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/slab.h>
15 #include <linux/of.h>
16 #include <linux/of_address.h>
17 #include <linux/of_device.h>
18 #include <linux/of_irq.h>
19 #include <linux/of_platform.h>
20 #include <asm/io.h>
21 #include <asm/irq.h>
22 #include <asm/mpc52xx.h>
23 
24 #include <linux/fsl/bestcomm/sram.h>
25 #include <linux/fsl/bestcomm/bestcomm_priv.h>
26 #include "linux/fsl/bestcomm/bestcomm.h"
27 
28 #define DRIVER_NAME "bestcomm-core"
29 
30 /* MPC5200 device tree match tables */
31 static const struct of_device_id mpc52xx_sram_ids[] = {
32 	{ .compatible = "fsl,mpc5200-sram", },
33 	{ .compatible = "mpc5200-sram", },
34 	{}
35 };
36 
37 
38 struct bcom_engine *bcom_eng = NULL;
39 EXPORT_SYMBOL_GPL(bcom_eng);	/* needed for inline functions */
40 
41 /* ======================================================================== */
42 /* Public and private API                                                   */
43 /* ======================================================================== */
44 
45 /* Private API */
46 
47 struct bcom_task *
48 bcom_task_alloc(int bd_count, int bd_size, int priv_size)
49 {
50 	int i, tasknum = -1;
51 	struct bcom_task *tsk;
52 
53 	/* Don't try to do anything if bestcomm init failed */
54 	if (!bcom_eng)
55 		return NULL;
56 
57 	/* Get and reserve a task num */
58 	spin_lock(&bcom_eng->lock);
59 
60 	for (i=0; i<BCOM_MAX_TASKS; i++)
61 		if (!bcom_eng->tdt[i].stop) {	/* we use stop as a marker */
62 			bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */
63 			tasknum = i;
64 			break;
65 		}
66 
67 	spin_unlock(&bcom_eng->lock);
68 
69 	if (tasknum < 0)
70 		return NULL;
71 
72 	/* Allocate our structure */
73 	tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL);
74 	if (!tsk)
75 		goto error;
76 
77 	tsk->tasknum = tasknum;
78 	if (priv_size)
79 		tsk->priv = (void*)tsk + sizeof(struct bcom_task);
80 
81 	/* Get IRQ of that task */
82 	tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum);
83 	if (!tsk->irq)
84 		goto error;
85 
86 	/* Init the BDs, if needed */
87 	if (bd_count) {
88 		tsk->cookie = kmalloc_array(bd_count, sizeof(void *),
89 					    GFP_KERNEL);
90 		if (!tsk->cookie)
91 			goto error;
92 
93 		tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa);
94 		if (!tsk->bd)
95 			goto error;
96 		memset_io(tsk->bd, 0x00, bd_count * bd_size);
97 
98 		tsk->num_bd = bd_count;
99 		tsk->bd_size = bd_size;
100 	}
101 
102 	return tsk;
103 
104 error:
105 	if (tsk) {
106 		if (tsk->irq)
107 			irq_dispose_mapping(tsk->irq);
108 		bcom_sram_free(tsk->bd);
109 		kfree(tsk->cookie);
110 		kfree(tsk);
111 	}
112 
113 	bcom_eng->tdt[tasknum].stop = 0;
114 
115 	return NULL;
116 }
117 EXPORT_SYMBOL_GPL(bcom_task_alloc);
118 
119 void
120 bcom_task_free(struct bcom_task *tsk)
121 {
122 	/* Stop the task */
123 	bcom_disable_task(tsk->tasknum);
124 
125 	/* Clear TDT */
126 	bcom_eng->tdt[tsk->tasknum].start = 0;
127 	bcom_eng->tdt[tsk->tasknum].stop  = 0;
128 
129 	/* Free everything */
130 	irq_dispose_mapping(tsk->irq);
131 	bcom_sram_free(tsk->bd);
132 	kfree(tsk->cookie);
133 	kfree(tsk);
134 }
135 EXPORT_SYMBOL_GPL(bcom_task_free);
136 
137 int
138 bcom_load_image(int task, u32 *task_image)
139 {
140 	struct bcom_task_header *hdr = (struct bcom_task_header *)task_image;
141 	struct bcom_tdt *tdt;
142 	u32 *desc, *var, *inc;
143 	u32 *desc_src, *var_src, *inc_src;
144 
145 	/* Safety checks */
146 	if (hdr->magic != BCOM_TASK_MAGIC) {
147 		printk(KERN_ERR DRIVER_NAME
148 			": Trying to load invalid microcode\n");
149 		return -EINVAL;
150 	}
151 
152 	if ((task < 0) || (task >= BCOM_MAX_TASKS)) {
153 		printk(KERN_ERR DRIVER_NAME
154 			": Trying to load invalid task %d\n", task);
155 		return -EINVAL;
156 	}
157 
158 	/* Initial load or reload */
159 	tdt = &bcom_eng->tdt[task];
160 
161 	if (tdt->start) {
162 		desc = bcom_task_desc(task);
163 		if (hdr->desc_size != bcom_task_num_descs(task)) {
164 			printk(KERN_ERR DRIVER_NAME
165 				": Trying to reload wrong task image "
166 				"(%d size %d/%d)!\n",
167 				task,
168 				hdr->desc_size,
169 				bcom_task_num_descs(task));
170 			return -EINVAL;
171 		}
172 	} else {
173 		phys_addr_t start_pa;
174 
175 		desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa);
176 		if (!desc)
177 			return -ENOMEM;
178 
179 		tdt->start = start_pa;
180 		tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32));
181 	}
182 
183 	var = bcom_task_var(task);
184 	inc = bcom_task_inc(task);
185 
186 	/* Clear & copy */
187 	memset_io(var, 0x00, BCOM_VAR_SIZE);
188 	memset_io(inc, 0x00, BCOM_INC_SIZE);
189 
190 	desc_src = (u32 *)(hdr + 1);
191 	var_src = desc_src + hdr->desc_size;
192 	inc_src = var_src + hdr->var_size;
193 
194 	memcpy_toio(desc, desc_src, hdr->desc_size * sizeof(u32));
195 	memcpy_toio(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32));
196 	memcpy_toio(inc, inc_src, hdr->inc_size * sizeof(u32));
197 
198 	return 0;
199 }
200 EXPORT_SYMBOL_GPL(bcom_load_image);
201 
202 void
203 bcom_set_initiator(int task, int initiator)
204 {
205 	int i;
206 	int num_descs;
207 	u32 *desc;
208 	int next_drd_has_initiator;
209 
210 	bcom_set_tcr_initiator(task, initiator);
211 
212 	/* Just setting tcr is apparently not enough due to some problem */
213 	/* with it. So we just go thru all the microcode and replace in  */
214 	/* the DRD directly */
215 
216 	desc = bcom_task_desc(task);
217 	next_drd_has_initiator = 1;
218 	num_descs = bcom_task_num_descs(task);
219 
220 	for (i=0; i<num_descs; i++, desc++) {
221 		if (!bcom_desc_is_drd(*desc))
222 			continue;
223 		if (next_drd_has_initiator)
224 			if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS)
225 				bcom_set_desc_initiator(desc, initiator);
226 		next_drd_has_initiator = !bcom_drd_is_extended(*desc);
227 	}
228 }
229 EXPORT_SYMBOL_GPL(bcom_set_initiator);
230 
231 
232 /* Public API */
233 
234 void
235 bcom_enable(struct bcom_task *tsk)
236 {
237 	bcom_enable_task(tsk->tasknum);
238 }
239 EXPORT_SYMBOL_GPL(bcom_enable);
240 
241 void
242 bcom_disable(struct bcom_task *tsk)
243 {
244 	bcom_disable_task(tsk->tasknum);
245 }
246 EXPORT_SYMBOL_GPL(bcom_disable);
247 
248 
249 /* ======================================================================== */
250 /* Engine init/cleanup                                                      */
251 /* ======================================================================== */
252 
253 /* Function Descriptor table */
254 /* this will need to be updated if Freescale changes their task code FDT */
255 static u32 fdt_ops[] = {
256 	0xa0045670,	/* FDT[48] - load_acc()	  */
257 	0x80045670,	/* FDT[49] - unload_acc() */
258 	0x21800000,	/* FDT[50] - and()        */
259 	0x21e00000,	/* FDT[51] - or()         */
260 	0x21500000,	/* FDT[52] - xor()        */
261 	0x21400000,	/* FDT[53] - andn()       */
262 	0x21500000,	/* FDT[54] - not()        */
263 	0x20400000,	/* FDT[55] - add()        */
264 	0x20500000,	/* FDT[56] - sub()        */
265 	0x20800000,	/* FDT[57] - lsh()        */
266 	0x20a00000,	/* FDT[58] - rsh()        */
267 	0xc0170000,	/* FDT[59] - crc8()       */
268 	0xc0145670,	/* FDT[60] - crc16()      */
269 	0xc0345670,	/* FDT[61] - crc32()      */
270 	0xa0076540,	/* FDT[62] - endian32()   */
271 	0xa0000760,	/* FDT[63] - endian16()   */
272 };
273 
274 
275 static int bcom_engine_init(void)
276 {
277 	int task;
278 	phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
279 	unsigned int tdt_size, ctx_size, var_size, fdt_size;
280 
281 	/* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
282 	tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
283 	ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;
284 	var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);
285 	fdt_size = BCOM_FDT_SIZE;
286 
287 	bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);
288 	bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);
289 	bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);
290 	bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);
291 
292 	if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) {
293 		printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");
294 
295 		bcom_sram_free(bcom_eng->tdt);
296 		bcom_sram_free(bcom_eng->ctx);
297 		bcom_sram_free(bcom_eng->var);
298 		bcom_sram_free(bcom_eng->fdt);
299 
300 		return -ENOMEM;
301 	}
302 
303 	memset_io(bcom_eng->tdt, 0x00, tdt_size);
304 	memset_io(bcom_eng->ctx, 0x00, ctx_size);
305 	memset_io(bcom_eng->var, 0x00, var_size);
306 	memset_io(bcom_eng->fdt, 0x00, fdt_size);
307 
308 	/* Copy the FDT for the EU#3 */
309 	memcpy_toio(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops));
310 
311 	/* Initialize Task base structure */
312 	for (task=0; task<BCOM_MAX_TASKS; task++)
313 	{
314 		out_be16(&bcom_eng->regs->tcr[task], 0);
315 		out_8(&bcom_eng->regs->ipr[task], 0);
316 
317 		bcom_eng->tdt[task].context	= ctx_pa;
318 		bcom_eng->tdt[task].var	= var_pa;
319 		bcom_eng->tdt[task].fdt	= fdt_pa;
320 
321 		var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;
322 		ctx_pa += BCOM_CTX_SIZE;
323 	}
324 
325 	out_be32(&bcom_eng->regs->taskBar, tdt_pa);
326 
327 	/* Init 'always' initiator */
328 	out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
329 
330 	/* Disable COMM Bus Prefetch on the original 5200; it's broken */
331 	if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
332 		bcom_disable_prefetch();
333 
334 	/* Init lock */
335 	spin_lock_init(&bcom_eng->lock);
336 
337 	return 0;
338 }
339 
340 static void
341 bcom_engine_cleanup(void)
342 {
343 	int task;
344 
345 	/* Stop all tasks */
346 	for (task=0; task<BCOM_MAX_TASKS; task++)
347 	{
348 		out_be16(&bcom_eng->regs->tcr[task], 0);
349 		out_8(&bcom_eng->regs->ipr[task], 0);
350 	}
351 
352 	out_be32(&bcom_eng->regs->taskBar, 0ul);
353 
354 	/* Release the SRAM zones */
355 	bcom_sram_free(bcom_eng->tdt);
356 	bcom_sram_free(bcom_eng->ctx);
357 	bcom_sram_free(bcom_eng->var);
358 	bcom_sram_free(bcom_eng->fdt);
359 }
360 
361 
362 /* ======================================================================== */
363 /* OF platform driver                                                       */
364 /* ======================================================================== */
365 
366 static int mpc52xx_bcom_probe(struct platform_device *op)
367 {
368 	struct device_node *ofn_sram;
369 	struct resource res_bcom;
370 
371 	int rv;
372 
373 	/* Inform user we're ok so far */
374 	printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
375 
376 	/* Get the bestcomm node */
377 	of_node_get(op->dev.of_node);
378 
379 	/* Prepare SRAM */
380 	ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids);
381 	if (!ofn_sram) {
382 		printk(KERN_ERR DRIVER_NAME ": "
383 			"No SRAM found in device tree\n");
384 		rv = -ENODEV;
385 		goto error_ofput;
386 	}
387 	rv = bcom_sram_init(ofn_sram, DRIVER_NAME);
388 	of_node_put(ofn_sram);
389 
390 	if (rv) {
391 		printk(KERN_ERR DRIVER_NAME ": "
392 			"Error in SRAM init\n");
393 		goto error_ofput;
394 	}
395 
396 	/* Get a clean struct */
397 	bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);
398 	if (!bcom_eng) {
399 		rv = -ENOMEM;
400 		goto error_sramclean;
401 	}
402 
403 	/* Save the node */
404 	bcom_eng->ofnode = op->dev.of_node;
405 
406 	/* Get, reserve & map io */
407 	if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) {
408 		printk(KERN_ERR DRIVER_NAME ": "
409 			"Can't get resource\n");
410 		rv = -EINVAL;
411 		goto error_sramclean;
412 	}
413 
414 	if (!request_mem_region(res_bcom.start, resource_size(&res_bcom),
415 				DRIVER_NAME)) {
416 		printk(KERN_ERR DRIVER_NAME ": "
417 			"Can't request registers region\n");
418 		rv = -EBUSY;
419 		goto error_sramclean;
420 	}
421 
422 	bcom_eng->regs_base = res_bcom.start;
423 	bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));
424 	if (!bcom_eng->regs) {
425 		printk(KERN_ERR DRIVER_NAME ": "
426 			"Can't map registers\n");
427 		rv = -ENOMEM;
428 		goto error_release;
429 	}
430 
431 	/* Now, do the real init */
432 	rv = bcom_engine_init();
433 	if (rv)
434 		goto error_unmap;
435 
436 	/* Done ! */
437 	printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
438 		(long)bcom_eng->regs_base);
439 
440 	return 0;
441 
442 	/* Error path */
443 error_unmap:
444 	iounmap(bcom_eng->regs);
445 error_release:
446 	release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
447 error_sramclean:
448 	kfree(bcom_eng);
449 	bcom_sram_cleanup();
450 error_ofput:
451 	of_node_put(op->dev.of_node);
452 
453 	printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
454 
455 	return rv;
456 }
457 
458 
459 static int mpc52xx_bcom_remove(struct platform_device *op)
460 {
461 	/* Clean up the engine */
462 	bcom_engine_cleanup();
463 
464 	/* Cleanup SRAM */
465 	bcom_sram_cleanup();
466 
467 	/* Release regs */
468 	iounmap(bcom_eng->regs);
469 	release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma));
470 
471 	/* Release the node */
472 	of_node_put(bcom_eng->ofnode);
473 
474 	/* Release memory */
475 	kfree(bcom_eng);
476 	bcom_eng = NULL;
477 
478 	return 0;
479 }
480 
481 static const struct of_device_id mpc52xx_bcom_of_match[] = {
482 	{ .compatible = "fsl,mpc5200-bestcomm", },
483 	{ .compatible = "mpc5200-bestcomm", },
484 	{},
485 };
486 
487 MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);
488 
489 
490 static struct platform_driver mpc52xx_bcom_of_platform_driver = {
491 	.probe		= mpc52xx_bcom_probe,
492 	.remove		= mpc52xx_bcom_remove,
493 	.driver = {
494 		.name = DRIVER_NAME,
495 		.of_match_table = mpc52xx_bcom_of_match,
496 	},
497 };
498 
499 
500 /* ======================================================================== */
501 /* Module                                                                   */
502 /* ======================================================================== */
503 
504 static int __init
505 mpc52xx_bcom_init(void)
506 {
507 	return platform_driver_register(&mpc52xx_bcom_of_platform_driver);
508 }
509 
510 static void __exit
511 mpc52xx_bcom_exit(void)
512 {
513 	platform_driver_unregister(&mpc52xx_bcom_of_platform_driver);
514 }
515 
516 /* If we're not a module, we must make sure everything is setup before  */
517 /* anyone tries to use us ... that's why we use subsys_initcall instead */
518 /* of module_init. */
519 subsys_initcall(mpc52xx_bcom_init);
520 module_exit(mpc52xx_bcom_exit);
521 
522 MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
523 MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
524 MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
525 MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
526 MODULE_LICENSE("GPL v2");
527