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