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