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