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 v2.6.38-rc3 533 lines 13 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 /* 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 105error: 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} 118EXPORT_SYMBOL_GPL(bcom_task_alloc); 119 120void 121bcom_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} 136EXPORT_SYMBOL_GPL(bcom_task_free); 137 138int 139bcom_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} 201EXPORT_SYMBOL_GPL(bcom_load_image); 202 203void 204bcom_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} 230EXPORT_SYMBOL_GPL(bcom_set_initiator); 231 232 233/* Public API */ 234 235void 236bcom_enable(struct bcom_task *tsk) 237{ 238 bcom_enable_task(tsk->tasknum); 239} 240EXPORT_SYMBOL_GPL(bcom_enable); 241 242void 243bcom_disable(struct bcom_task *tsk) 244{ 245 bcom_disable_task(tsk->tasknum); 246} 247EXPORT_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 */ 256static 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 276static int __devinit 277bcom_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 342static void 343bcom_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 368static int __devinit mpc52xx_bcom_probe(struct platform_device *op, 369 const struct of_device_id *match) 370{ 371 struct device_node *ofn_sram; 372 struct resource res_bcom; 373 374 int rv; 375 376 /* Inform user we're ok so far */ 377 printk(KERN_INFO "DMA: MPC52xx BestComm driver\n"); 378 379 /* Get the bestcomm node */ 380 of_node_get(op->dev.of_node); 381 382 /* Prepare SRAM */ 383 ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids); 384 if (!ofn_sram) { 385 printk(KERN_ERR DRIVER_NAME ": " 386 "No SRAM found in device tree\n"); 387 rv = -ENODEV; 388 goto error_ofput; 389 } 390 rv = bcom_sram_init(ofn_sram, DRIVER_NAME); 391 of_node_put(ofn_sram); 392 393 if (rv) { 394 printk(KERN_ERR DRIVER_NAME ": " 395 "Error in SRAM init\n"); 396 goto error_ofput; 397 } 398 399 /* Get a clean struct */ 400 bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL); 401 if (!bcom_eng) { 402 printk(KERN_ERR DRIVER_NAME ": " 403 "Can't allocate state structure\n"); 404 rv = -ENOMEM; 405 goto error_sramclean; 406 } 407 408 /* Save the node */ 409 bcom_eng->ofnode = op->dev.of_node; 410 411 /* Get, reserve & map io */ 412 if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) { 413 printk(KERN_ERR DRIVER_NAME ": " 414 "Can't get resource\n"); 415 rv = -EINVAL; 416 goto error_sramclean; 417 } 418 419 if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma), 420 DRIVER_NAME)) { 421 printk(KERN_ERR DRIVER_NAME ": " 422 "Can't request registers region\n"); 423 rv = -EBUSY; 424 goto error_sramclean; 425 } 426 427 bcom_eng->regs_base = res_bcom.start; 428 bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma)); 429 if (!bcom_eng->regs) { 430 printk(KERN_ERR DRIVER_NAME ": " 431 "Can't map registers\n"); 432 rv = -ENOMEM; 433 goto error_release; 434 } 435 436 /* Now, do the real init */ 437 rv = bcom_engine_init(); 438 if (rv) 439 goto error_unmap; 440 441 /* Done ! */ 442 printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n", 443 (long)bcom_eng->regs_base); 444 445 return 0; 446 447 /* Error path */ 448error_unmap: 449 iounmap(bcom_eng->regs); 450error_release: 451 release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma)); 452error_sramclean: 453 kfree(bcom_eng); 454 bcom_sram_cleanup(); 455error_ofput: 456 of_node_put(op->dev.of_node); 457 458 printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n"); 459 460 return rv; 461} 462 463 464static int mpc52xx_bcom_remove(struct platform_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 { .compatible = "fsl,mpc5200-bestcomm", }, 488 { .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 .probe = mpc52xx_bcom_probe, 497 .remove = mpc52xx_bcom_remove, 498 .driver = { 499 .name = DRIVER_NAME, 500 .owner = THIS_MODULE, 501 .of_match_table = mpc52xx_bcom_of_match, 502 }, 503}; 504 505 506/* ======================================================================== */ 507/* Module */ 508/* ======================================================================== */ 509 510static int __init 511mpc52xx_bcom_init(void) 512{ 513 return of_register_platform_driver(&mpc52xx_bcom_of_platform_driver); 514} 515 516static void __exit 517mpc52xx_bcom_exit(void) 518{ 519 of_unregister_platform_driver(&mpc52xx_bcom_of_platform_driver); 520} 521 522/* If we're not a module, we must make sure everything is setup before */ 523/* anyone tries to use us ... that's why we use subsys_initcall instead */ 524/* of module_init. */ 525subsys_initcall(mpc52xx_bcom_init); 526module_exit(mpc52xx_bcom_exit); 527 528MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA"); 529MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>"); 530MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>"); 531MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>"); 532MODULE_LICENSE("GPL v2"); 533