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