linux/arch/powerpc/sysdev/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 "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
 369mpc52xx_bcom_probe(struct of_device *op, 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->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->node;
 410
 411        /* Get, reserve & map io */
 412        if (of_address_to_resource(op->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->node);
 457
 458        printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
 459
 460        return rv;
 461}
 462
 463
 464static int
 465mpc52xx_bcom_remove(struct of_device *op)
 466{
 467        /* Clean up the engine */
 468        bcom_engine_cleanup();
 469
 470        /* Cleanup SRAM */
 471        bcom_sram_cleanup();
 472
 473        /* Release regs */
 474        iounmap(bcom_eng->regs);
 475        release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma));
 476
 477        /* Release the node */
 478        of_node_put(bcom_eng->ofnode);
 479
 480        /* Release memory */
 481        kfree(bcom_eng);
 482        bcom_eng = NULL;
 483
 484        return 0;
 485}
 486
 487static struct of_device_id mpc52xx_bcom_of_match[] = {
 488        { .compatible = "fsl,mpc5200-bestcomm", },
 489        { .compatible = "mpc5200-bestcomm", },
 490        {},
 491};
 492
 493MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);
 494
 495
 496static struct of_platform_driver mpc52xx_bcom_of_platform_driver = {
 497        .owner          = THIS_MODULE,
 498        .name           = DRIVER_NAME,
 499        .match_table    = mpc52xx_bcom_of_match,
 500        .probe          = mpc52xx_bcom_probe,
 501        .remove         = mpc52xx_bcom_remove,
 502        .driver         = {
 503                .name   = DRIVER_NAME,
 504                .owner  = THIS_MODULE,
 505        },
 506};
 507
 508
 509/* ======================================================================== */
 510/* Module                                                                   */
 511/* ======================================================================== */
 512
 513static int __init
 514mpc52xx_bcom_init(void)
 515{
 516        return of_register_platform_driver(&mpc52xx_bcom_of_platform_driver);
 517}
 518
 519static void __exit
 520mpc52xx_bcom_exit(void)
 521{
 522        of_unregister_platform_driver(&mpc52xx_bcom_of_platform_driver);
 523}
 524
 525/* If we're not a module, we must make sure everything is setup before  */
 526/* anyone tries to use us ... that's why we use subsys_initcall instead */
 527/* of module_init. */
 528subsys_initcall(mpc52xx_bcom_init);
 529module_exit(mpc52xx_bcom_exit);
 530
 531MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
 532MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
 533MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
 534MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
 535MODULE_LICENSE("GPL v2");
 536
 537