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