cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bestcomm.c (13423B)


      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 */
     35static const struct of_device_id mpc52xx_sram_ids[] = {
     36	{ .compatible = "fsl,mpc5200-sram", },
     37	{ .compatible = "mpc5200-sram", },
     38	{}
     39};
     40
     41
     42struct bcom_engine *bcom_eng = NULL;
     43EXPORT_SYMBOL_GPL(bcom_eng);	/* needed for inline functions */
     44
     45/* ======================================================================== */
     46/* Public and private API                                                   */
     47/* ======================================================================== */
     48
     49/* Private API */
     50
     51struct bcom_task *
     52bcom_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
    108error:
    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}
    121EXPORT_SYMBOL_GPL(bcom_task_alloc);
    122
    123void
    124bcom_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}
    139EXPORT_SYMBOL_GPL(bcom_task_free);
    140
    141int
    142bcom_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}
    204EXPORT_SYMBOL_GPL(bcom_load_image);
    205
    206void
    207bcom_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}
    233EXPORT_SYMBOL_GPL(bcom_set_initiator);
    234
    235
    236/* Public API */
    237
    238void
    239bcom_enable(struct bcom_task *tsk)
    240{
    241	bcom_enable_task(tsk->tasknum);
    242}
    243EXPORT_SYMBOL_GPL(bcom_enable);
    244
    245void
    246bcom_disable(struct bcom_task *tsk)
    247{
    248	bcom_disable_task(tsk->tasknum);
    249}
    250EXPORT_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 */
    259static 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
    279static 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
    344static void
    345bcom_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
    370static 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 */
    447error_unmap:
    448	iounmap(bcom_eng->regs);
    449error_release:
    450	release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
    451error_sramclean:
    452	kfree(bcom_eng);
    453	bcom_sram_cleanup();
    454error_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
    463static 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
    485static const struct of_device_id mpc52xx_bcom_of_match[] = {
    486	{ .compatible = "fsl,mpc5200-bestcomm", },
    487	{ .compatible = "mpc5200-bestcomm", },
    488	{},
    489};
    490
    491MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);
    492
    493
    494static 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
    508static int __init
    509mpc52xx_bcom_init(void)
    510{
    511	return platform_driver_register(&mpc52xx_bcom_of_platform_driver);
    512}
    513
    514static void __exit
    515mpc52xx_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. */
    523subsys_initcall(mpc52xx_bcom_init);
    524module_exit(mpc52xx_bcom_exit);
    525
    526MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
    527MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
    528MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
    529MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
    530MODULE_LICENSE("GPL v2");
    531