linux/drivers/dma/bestcomm/bestcomm.c
<<
>>
Prefs
   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 */
  33static struct of_device_id mpc52xx_sram_ids[] = {
  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 bcom_engine_init(void)
 277{
 278        int task;
 279        phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
 280        unsigned int tdt_size, ctx_size, var_size, fdt_size;
 281
 282        /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
 283        tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
 284        ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;
 285        var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);
 286        fdt_size = BCOM_FDT_SIZE;
 287
 288        bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);
 289        bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);
 290        bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);
 291        bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);
 292
 293        if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) {
 294                printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");
 295
 296                bcom_sram_free(bcom_eng->tdt);
 297                bcom_sram_free(bcom_eng->ctx);
 298                bcom_sram_free(bcom_eng->var);
 299                bcom_sram_free(bcom_eng->fdt);
 300
 301                return -ENOMEM;
 302        }
 303
 304        memset(bcom_eng->tdt, 0x00, tdt_size);
 305        memset(bcom_eng->ctx, 0x00, ctx_size);
 306        memset(bcom_eng->var, 0x00, var_size);
 307        memset(bcom_eng->fdt, 0x00, fdt_size);
 308
 309        /* Copy the FDT for the EU#3 */
 310        memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops));
 311
 312        /* Initialize Task base structure */
 313        for (task=0; task<BCOM_MAX_TASKS; task++)
 314        {
 315                out_be16(&bcom_eng->regs->tcr[task], 0);
 316                out_8(&bcom_eng->regs->ipr[task], 0);
 317
 318                bcom_eng->tdt[task].context     = ctx_pa;
 319                bcom_eng->tdt[task].var = var_pa;
 320                bcom_eng->tdt[task].fdt = fdt_pa;
 321
 322                var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;
 323                ctx_pa += BCOM_CTX_SIZE;
 324        }
 325
 326        out_be32(&bcom_eng->regs->taskBar, tdt_pa);
 327
 328        /* Init 'always' initiator */
 329        out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
 330
 331        /* Disable COMM Bus Prefetch on the original 5200; it's broken */
 332        if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
 333                bcom_disable_prefetch();
 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 mpc52xx_bcom_probe(struct platform_device *op)
 368{
 369        struct device_node *ofn_sram;
 370        struct resource res_bcom;
 371
 372        int rv;
 373
 374        /* Inform user we're ok so far */
 375        printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
 376
 377        /* Get the bestcomm node */
 378        of_node_get(op->dev.of_node);
 379
 380        /* Prepare SRAM */
 381        ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids);
 382        if (!ofn_sram) {
 383                printk(KERN_ERR DRIVER_NAME ": "
 384                        "No SRAM found in device tree\n");
 385                rv = -ENODEV;
 386                goto error_ofput;
 387        }
 388        rv = bcom_sram_init(ofn_sram, DRIVER_NAME);
 389        of_node_put(ofn_sram);
 390
 391        if (rv) {
 392                printk(KERN_ERR DRIVER_NAME ": "
 393                        "Error in SRAM init\n");
 394                goto error_ofput;
 395        }
 396
 397        /* Get a clean struct */
 398        bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);
 399        if (!bcom_eng) {
 400                printk(KERN_ERR DRIVER_NAME ": "
 401                        "Can't allocate state structure\n");
 402                rv = -ENOMEM;
 403                goto error_sramclean;
 404        }
 405
 406        /* Save the node */
 407        bcom_eng->ofnode = op->dev.of_node;
 408
 409        /* Get, reserve & map io */
 410        if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) {
 411                printk(KERN_ERR DRIVER_NAME ": "
 412                        "Can't get resource\n");
 413                rv = -EINVAL;
 414                goto error_sramclean;
 415        }
 416
 417        if (!request_mem_region(res_bcom.start, resource_size(&res_bcom),
 418                                DRIVER_NAME)) {
 419                printk(KERN_ERR DRIVER_NAME ": "
 420                        "Can't request registers region\n");
 421                rv = -EBUSY;
 422                goto error_sramclean;
 423        }
 424
 425        bcom_eng->regs_base = res_bcom.start;
 426        bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));
 427        if (!bcom_eng->regs) {
 428                printk(KERN_ERR DRIVER_NAME ": "
 429                        "Can't map registers\n");
 430                rv = -ENOMEM;
 431                goto error_release;
 432        }
 433
 434        /* Now, do the real init */
 435        rv = bcom_engine_init();
 436        if (rv)
 437                goto error_unmap;
 438
 439        /* Done ! */
 440        printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
 441                (long)bcom_eng->regs_base);
 442
 443        return 0;
 444
 445        /* Error path */
 446error_unmap:
 447        iounmap(bcom_eng->regs);
 448error_release:
 449        release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
 450error_sramclean:
 451        kfree(bcom_eng);
 452        bcom_sram_cleanup();
 453error_ofput:
 454        of_node_put(op->dev.of_node);
 455
 456        printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
 457
 458        return rv;
 459}
 460
 461
 462static int mpc52xx_bcom_remove(struct platform_device *op)
 463{
 464        /* Clean up the engine */
 465        bcom_engine_cleanup();
 466
 467        /* Cleanup SRAM */
 468        bcom_sram_cleanup();
 469
 470        /* Release regs */
 471        iounmap(bcom_eng->regs);
 472        release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma));
 473
 474        /* Release the node */
 475        of_node_put(bcom_eng->ofnode);
 476
 477        /* Release memory */
 478        kfree(bcom_eng);
 479        bcom_eng = NULL;
 480
 481        return 0;
 482}
 483
 484static struct of_device_id mpc52xx_bcom_of_match[] = {
 485        { .compatible = "fsl,mpc5200-bestcomm", },
 486        { .compatible = "mpc5200-bestcomm", },
 487        {},
 488};
 489
 490MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);
 491
 492
 493static struct platform_driver mpc52xx_bcom_of_platform_driver = {
 494        .probe          = mpc52xx_bcom_probe,
 495        .remove         = mpc52xx_bcom_remove,
 496        .driver = {
 497                .name = DRIVER_NAME,
 498                .owner = THIS_MODULE,
 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
 532