brintos

brintos / linux-shallow public Read only

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