Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at c9a28fa7b9ac19b676deefa0a171ce7df8755c08 535 lines 14 kB view raw
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 "sram.h" 27#include "bestcomm_priv.h" 28#include "bestcomm.h" 29 30#define DRIVER_NAME "bestcomm-core" 31 32/* MPC5200 device tree match tables */ 33static struct of_device_id mpc52xx_sram_ids[] __devinitdata = { 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 /* Get and reserve a task num */ 56 spin_lock(&bcom_eng->lock); 57 58 for (i=0; i<BCOM_MAX_TASKS; i++) 59 if (!bcom_eng->tdt[i].stop) { /* we use stop as a marker */ 60 bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */ 61 tasknum = i; 62 break; 63 } 64 65 spin_unlock(&bcom_eng->lock); 66 67 if (tasknum < 0) 68 return NULL; 69 70 /* Allocate our structure */ 71 tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL); 72 if (!tsk) 73 goto error; 74 75 tsk->tasknum = tasknum; 76 if (priv_size) 77 tsk->priv = (void*)tsk + sizeof(struct bcom_task); 78 79 /* Get IRQ of that task */ 80 tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum); 81 if (tsk->irq == NO_IRQ) 82 goto error; 83 84 /* Init the BDs, if needed */ 85 if (bd_count) { 86 tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL); 87 if (!tsk->cookie) 88 goto error; 89 90 tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa); 91 if (!tsk->bd) 92 goto error; 93 memset(tsk->bd, 0x00, bd_count * bd_size); 94 95 tsk->num_bd = bd_count; 96 tsk->bd_size = bd_size; 97 } 98 99 return tsk; 100 101error: 102 if (tsk) { 103 if (tsk->irq != NO_IRQ) 104 irq_dispose_mapping(tsk->irq); 105 bcom_sram_free(tsk->bd); 106 kfree(tsk->cookie); 107 kfree(tsk); 108 } 109 110 bcom_eng->tdt[tasknum].stop = 0; 111 112 return NULL; 113} 114EXPORT_SYMBOL_GPL(bcom_task_alloc); 115 116void 117bcom_task_free(struct bcom_task *tsk) 118{ 119 /* Stop the task */ 120 bcom_disable_task(tsk->tasknum); 121 122 /* Clear TDT */ 123 bcom_eng->tdt[tsk->tasknum].start = 0; 124 bcom_eng->tdt[tsk->tasknum].stop = 0; 125 126 /* Free everything */ 127 irq_dispose_mapping(tsk->irq); 128 bcom_sram_free(tsk->bd); 129 kfree(tsk->cookie); 130 kfree(tsk); 131} 132EXPORT_SYMBOL_GPL(bcom_task_free); 133 134int 135bcom_load_image(int task, u32 *task_image) 136{ 137 struct bcom_task_header *hdr = (struct bcom_task_header *)task_image; 138 struct bcom_tdt *tdt; 139 u32 *desc, *var, *inc; 140 u32 *desc_src, *var_src, *inc_src; 141 142 /* Safety checks */ 143 if (hdr->magic != BCOM_TASK_MAGIC) { 144 printk(KERN_ERR DRIVER_NAME 145 ": Trying to load invalid microcode\n"); 146 return -EINVAL; 147 } 148 149 if ((task < 0) || (task >= BCOM_MAX_TASKS)) { 150 printk(KERN_ERR DRIVER_NAME 151 ": Trying to load invalid task %d\n", task); 152 return -EINVAL; 153 } 154 155 /* Initial load or reload */ 156 tdt = &bcom_eng->tdt[task]; 157 158 if (tdt->start) { 159 desc = bcom_task_desc(task); 160 if (hdr->desc_size != bcom_task_num_descs(task)) { 161 printk(KERN_ERR DRIVER_NAME 162 ": Trying to reload wrong task image " 163 "(%d size %d/%d)!\n", 164 task, 165 hdr->desc_size, 166 bcom_task_num_descs(task)); 167 return -EINVAL; 168 } 169 } else { 170 phys_addr_t start_pa; 171 172 desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa); 173 if (!desc) 174 return -ENOMEM; 175 176 tdt->start = start_pa; 177 tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32)); 178 } 179 180 var = bcom_task_var(task); 181 inc = bcom_task_inc(task); 182 183 /* Clear & copy */ 184 memset(var, 0x00, BCOM_VAR_SIZE); 185 memset(inc, 0x00, BCOM_INC_SIZE); 186 187 desc_src = (u32 *)(hdr + 1); 188 var_src = desc_src + hdr->desc_size; 189 inc_src = var_src + hdr->var_size; 190 191 memcpy(desc, desc_src, hdr->desc_size * sizeof(u32)); 192 memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32)); 193 memcpy(inc, inc_src, hdr->inc_size * sizeof(u32)); 194 195 return 0; 196} 197EXPORT_SYMBOL_GPL(bcom_load_image); 198 199void 200bcom_set_initiator(int task, int initiator) 201{ 202 int i; 203 int num_descs; 204 u32 *desc; 205 int next_drd_has_initiator; 206 207 bcom_set_tcr_initiator(task, initiator); 208 209 /* Just setting tcr is apparently not enough due to some problem */ 210 /* with it. So we just go thru all the microcode and replace in */ 211 /* the DRD directly */ 212 213 desc = bcom_task_desc(task); 214 next_drd_has_initiator = 1; 215 num_descs = bcom_task_num_descs(task); 216 217 for (i=0; i<num_descs; i++, desc++) { 218 if (!bcom_desc_is_drd(*desc)) 219 continue; 220 if (next_drd_has_initiator) 221 if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS) 222 bcom_set_desc_initiator(desc, initiator); 223 next_drd_has_initiator = !bcom_drd_is_extended(*desc); 224 } 225} 226EXPORT_SYMBOL_GPL(bcom_set_initiator); 227 228 229/* Public API */ 230 231void 232bcom_enable(struct bcom_task *tsk) 233{ 234 bcom_enable_task(tsk->tasknum); 235} 236EXPORT_SYMBOL_GPL(bcom_enable); 237 238void 239bcom_disable(struct bcom_task *tsk) 240{ 241 bcom_disable_task(tsk->tasknum); 242} 243EXPORT_SYMBOL_GPL(bcom_disable); 244 245 246/* ======================================================================== */ 247/* Engine init/cleanup */ 248/* ======================================================================== */ 249 250/* Function Descriptor table */ 251/* this will need to be updated if Freescale changes their task code FDT */ 252static u32 fdt_ops[] = { 253 0xa0045670, /* FDT[48] - load_acc() */ 254 0x80045670, /* FDT[49] - unload_acc() */ 255 0x21800000, /* FDT[50] - and() */ 256 0x21e00000, /* FDT[51] - or() */ 257 0x21500000, /* FDT[52] - xor() */ 258 0x21400000, /* FDT[53] - andn() */ 259 0x21500000, /* FDT[54] - not() */ 260 0x20400000, /* FDT[55] - add() */ 261 0x20500000, /* FDT[56] - sub() */ 262 0x20800000, /* FDT[57] - lsh() */ 263 0x20a00000, /* FDT[58] - rsh() */ 264 0xc0170000, /* FDT[59] - crc8() */ 265 0xc0145670, /* FDT[60] - crc16() */ 266 0xc0345670, /* FDT[61] - crc32() */ 267 0xa0076540, /* FDT[62] - endian32() */ 268 0xa0000760, /* FDT[63] - endian16() */ 269}; 270 271 272static int __devinit 273bcom_engine_init(void) 274{ 275 int task; 276 phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa; 277 unsigned int tdt_size, ctx_size, var_size, fdt_size; 278 u16 regval; 279 280 /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */ 281 tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt); 282 ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE; 283 var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE); 284 fdt_size = BCOM_FDT_SIZE; 285 286 bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa); 287 bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa); 288 bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa); 289 bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa); 290 291 if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) { 292 printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n"); 293 294 bcom_sram_free(bcom_eng->tdt); 295 bcom_sram_free(bcom_eng->ctx); 296 bcom_sram_free(bcom_eng->var); 297 bcom_sram_free(bcom_eng->fdt); 298 299 return -ENOMEM; 300 } 301 302 memset(bcom_eng->tdt, 0x00, tdt_size); 303 memset(bcom_eng->ctx, 0x00, ctx_size); 304 memset(bcom_eng->var, 0x00, var_size); 305 memset(bcom_eng->fdt, 0x00, fdt_size); 306 307 /* Copy the FDT for the EU#3 */ 308 memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops)); 309 310 /* Initialize Task base structure */ 311 for (task=0; task<BCOM_MAX_TASKS; task++) 312 { 313 out_be16(&bcom_eng->regs->tcr[task], 0); 314 out_8(&bcom_eng->regs->ipr[task], 0); 315 316 bcom_eng->tdt[task].context = ctx_pa; 317 bcom_eng->tdt[task].var = var_pa; 318 bcom_eng->tdt[task].fdt = fdt_pa; 319 320 var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE; 321 ctx_pa += BCOM_CTX_SIZE; 322 } 323 324 out_be32(&bcom_eng->regs->taskBar, tdt_pa); 325 326 /* Init 'always' initiator */ 327 out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS); 328 329 /* Disable COMM Bus Prefetch on the original 5200; it's broken */ 330 if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) { 331 regval = in_be16(&bcom_eng->regs->PtdCntrl); 332 out_be16(&bcom_eng->regs->PtdCntrl, regval | 1); 333 } 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 __devinit 368mpc52xx_bcom_probe(struct of_device *op, const struct of_device_id *match) 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->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 printk(KERN_ERR DRIVER_NAME ": " 402 "Can't allocate state structure\n"); 403 rv = -ENOMEM; 404 goto error_sramclean; 405 } 406 407 /* Save the node */ 408 bcom_eng->ofnode = op->node; 409 410 /* Get, reserve & map io */ 411 if (of_address_to_resource(op->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, sizeof(struct mpc52xx_sdma), 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 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->node); 456 457 printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n"); 458 459 return rv; 460} 461 462 463static int 464mpc52xx_bcom_remove(struct of_device *op) 465{ 466 /* Clean up the engine */ 467 bcom_engine_cleanup(); 468 469 /* Cleanup SRAM */ 470 bcom_sram_cleanup(); 471 472 /* Release regs */ 473 iounmap(bcom_eng->regs); 474 release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma)); 475 476 /* Release the node */ 477 of_node_put(bcom_eng->ofnode); 478 479 /* Release memory */ 480 kfree(bcom_eng); 481 bcom_eng = NULL; 482 483 return 0; 484} 485 486static struct of_device_id mpc52xx_bcom_of_match[] = { 487 { .type = "dma-controller", .compatible = "fsl,mpc5200-bestcomm", }, 488 { .type = "dma-controller", .compatible = "mpc5200-bestcomm", }, 489 {}, 490}; 491 492MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match); 493 494 495static struct of_platform_driver mpc52xx_bcom_of_platform_driver = { 496 .owner = THIS_MODULE, 497 .name = DRIVER_NAME, 498 .match_table = mpc52xx_bcom_of_match, 499 .probe = mpc52xx_bcom_probe, 500 .remove = mpc52xx_bcom_remove, 501 .driver = { 502 .name = DRIVER_NAME, 503 .owner = THIS_MODULE, 504 }, 505}; 506 507 508/* ======================================================================== */ 509/* Module */ 510/* ======================================================================== */ 511 512static int __init 513mpc52xx_bcom_init(void) 514{ 515 return of_register_platform_driver(&mpc52xx_bcom_of_platform_driver); 516} 517 518static void __exit 519mpc52xx_bcom_exit(void) 520{ 521 of_unregister_platform_driver(&mpc52xx_bcom_of_platform_driver); 522} 523 524/* If we're not a module, we must make sure everything is setup before */ 525/* anyone tries to use us ... that's why we use subsys_initcall instead */ 526/* of module_init. */ 527subsys_initcall(mpc52xx_bcom_init); 528module_exit(mpc52xx_bcom_exit); 529 530MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA"); 531MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>"); 532MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>"); 533MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>"); 534MODULE_LICENSE("GPL v2"); 535