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 */ 33 static const struct of_device_id mpc52xx_sram_ids[] = { 34 { .compatible = "fsl,mpc5200-sram", }, 35 { .compatible = "mpc5200-sram", }, 36 {} 37 }; 38 39 40 struct bcom_engine *bcom_eng = NULL; 41 EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */ 42 43 /* ======================================================================== */ 44 /* Public and private API */ 45 /* ======================================================================== */ 46 47 /* Private API */ 48 49 struct bcom_task * 50 bcom_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 105 error: 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 } 118 EXPORT_SYMBOL_GPL(bcom_task_alloc); 119 120 void 121 bcom_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 } 136 EXPORT_SYMBOL_GPL(bcom_task_free); 137 138 int 139 bcom_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 } 201 EXPORT_SYMBOL_GPL(bcom_load_image); 202 203 void 204 bcom_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 } 230 EXPORT_SYMBOL_GPL(bcom_set_initiator); 231 232 233 /* Public API */ 234 235 void 236 bcom_enable(struct bcom_task *tsk) 237 { 238 bcom_enable_task(tsk->tasknum); 239 } 240 EXPORT_SYMBOL_GPL(bcom_enable); 241 242 void 243 bcom_disable(struct bcom_task *tsk) 244 { 245 bcom_disable_task(tsk->tasknum); 246 } 247 EXPORT_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 */ 256 static 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 276 static 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 341 static void 342 bcom_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 367 static 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 rv = -ENOMEM; 401 goto error_sramclean; 402 } 403 404 /* Save the node */ 405 bcom_eng->ofnode = op->dev.of_node; 406 407 /* Get, reserve & map io */ 408 if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) { 409 printk(KERN_ERR DRIVER_NAME ": " 410 "Can't get resource\n"); 411 rv = -EINVAL; 412 goto error_sramclean; 413 } 414 415 if (!request_mem_region(res_bcom.start, resource_size(&res_bcom), 416 DRIVER_NAME)) { 417 printk(KERN_ERR DRIVER_NAME ": " 418 "Can't request registers region\n"); 419 rv = -EBUSY; 420 goto error_sramclean; 421 } 422 423 bcom_eng->regs_base = res_bcom.start; 424 bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma)); 425 if (!bcom_eng->regs) { 426 printk(KERN_ERR DRIVER_NAME ": " 427 "Can't map registers\n"); 428 rv = -ENOMEM; 429 goto error_release; 430 } 431 432 /* Now, do the real init */ 433 rv = bcom_engine_init(); 434 if (rv) 435 goto error_unmap; 436 437 /* Done ! */ 438 printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n", 439 (long)bcom_eng->regs_base); 440 441 return 0; 442 443 /* Error path */ 444 error_unmap: 445 iounmap(bcom_eng->regs); 446 error_release: 447 release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma)); 448 error_sramclean: 449 kfree(bcom_eng); 450 bcom_sram_cleanup(); 451 error_ofput: 452 of_node_put(op->dev.of_node); 453 454 printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n"); 455 456 return rv; 457 } 458 459 460 static int mpc52xx_bcom_remove(struct platform_device *op) 461 { 462 /* Clean up the engine */ 463 bcom_engine_cleanup(); 464 465 /* Cleanup SRAM */ 466 bcom_sram_cleanup(); 467 468 /* Release regs */ 469 iounmap(bcom_eng->regs); 470 release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma)); 471 472 /* Release the node */ 473 of_node_put(bcom_eng->ofnode); 474 475 /* Release memory */ 476 kfree(bcom_eng); 477 bcom_eng = NULL; 478 479 return 0; 480 } 481 482 static const struct of_device_id mpc52xx_bcom_of_match[] = { 483 { .compatible = "fsl,mpc5200-bestcomm", }, 484 { .compatible = "mpc5200-bestcomm", }, 485 {}, 486 }; 487 488 MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match); 489 490 491 static struct platform_driver mpc52xx_bcom_of_platform_driver = { 492 .probe = mpc52xx_bcom_probe, 493 .remove = mpc52xx_bcom_remove, 494 .driver = { 495 .name = DRIVER_NAME, 496 .of_match_table = mpc52xx_bcom_of_match, 497 }, 498 }; 499 500 501 /* ======================================================================== */ 502 /* Module */ 503 /* ======================================================================== */ 504 505 static int __init 506 mpc52xx_bcom_init(void) 507 { 508 return platform_driver_register(&mpc52xx_bcom_of_platform_driver); 509 } 510 511 static void __exit 512 mpc52xx_bcom_exit(void) 513 { 514 platform_driver_unregister(&mpc52xx_bcom_of_platform_driver); 515 } 516 517 /* If we're not a module, we must make sure everything is setup before */ 518 /* anyone tries to use us ... that's why we use subsys_initcall instead */ 519 /* of module_init. */ 520 subsys_initcall(mpc52xx_bcom_init); 521 module_exit(mpc52xx_bcom_exit); 522 523 MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA"); 524 MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>"); 525 MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>"); 526 MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>"); 527 MODULE_LICENSE("GPL v2"); 528 529