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