Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for MPC52xx processor BestComm peripheral controller
0004  *
0005  * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
0006  * Copyright (C) 2005      Varma Electronics Oy,
0007  *                         ( by Andrey Volkov <avolkov@varma-el.com> )
0008  * Copyright (C) 2003-2004 MontaVista, Software, Inc.
0009  *                         ( by Dale Farnsworth <dfarnsworth@mvista.com> )
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/kernel.h>
0014 #include <linux/slab.h>
0015 #include <linux/of.h>
0016 #include <linux/of_address.h>
0017 #include <linux/of_device.h>
0018 #include <linux/of_irq.h>
0019 #include <linux/of_platform.h>
0020 #include <asm/io.h>
0021 #include <asm/irq.h>
0022 #include <asm/mpc52xx.h>
0023 
0024 #include <linux/fsl/bestcomm/sram.h>
0025 #include <linux/fsl/bestcomm/bestcomm_priv.h>
0026 #include "linux/fsl/bestcomm/bestcomm.h"
0027 
0028 #define DRIVER_NAME "bestcomm-core"
0029 
0030 /* MPC5200 device tree match tables */
0031 static const struct of_device_id mpc52xx_sram_ids[] = {
0032     { .compatible = "fsl,mpc5200-sram", },
0033     { .compatible = "mpc5200-sram", },
0034     {}
0035 };
0036 
0037 
0038 struct bcom_engine *bcom_eng = NULL;
0039 EXPORT_SYMBOL_GPL(bcom_eng);    /* needed for inline functions */
0040 
0041 /* ======================================================================== */
0042 /* Public and private API                                                   */
0043 /* ======================================================================== */
0044 
0045 /* Private API */
0046 
0047 struct bcom_task *
0048 bcom_task_alloc(int bd_count, int bd_size, int priv_size)
0049 {
0050     int i, tasknum = -1;
0051     struct bcom_task *tsk;
0052 
0053     /* Don't try to do anything if bestcomm init failed */
0054     if (!bcom_eng)
0055         return NULL;
0056 
0057     /* Get and reserve a task num */
0058     spin_lock(&bcom_eng->lock);
0059 
0060     for (i=0; i<BCOM_MAX_TASKS; i++)
0061         if (!bcom_eng->tdt[i].stop) {   /* we use stop as a marker */
0062             bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */
0063             tasknum = i;
0064             break;
0065         }
0066 
0067     spin_unlock(&bcom_eng->lock);
0068 
0069     if (tasknum < 0)
0070         return NULL;
0071 
0072     /* Allocate our structure */
0073     tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL);
0074     if (!tsk)
0075         goto error;
0076 
0077     tsk->tasknum = tasknum;
0078     if (priv_size)
0079         tsk->priv = (void*)tsk + sizeof(struct bcom_task);
0080 
0081     /* Get IRQ of that task */
0082     tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum);
0083     if (!tsk->irq)
0084         goto error;
0085 
0086     /* Init the BDs, if needed */
0087     if (bd_count) {
0088         tsk->cookie = kmalloc_array(bd_count, sizeof(void *),
0089                         GFP_KERNEL);
0090         if (!tsk->cookie)
0091             goto error;
0092 
0093         tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa);
0094         if (!tsk->bd)
0095             goto error;
0096         memset_io(tsk->bd, 0x00, bd_count * bd_size);
0097 
0098         tsk->num_bd = bd_count;
0099         tsk->bd_size = bd_size;
0100     }
0101 
0102     return tsk;
0103 
0104 error:
0105     if (tsk) {
0106         if (tsk->irq)
0107             irq_dispose_mapping(tsk->irq);
0108         bcom_sram_free(tsk->bd);
0109         kfree(tsk->cookie);
0110         kfree(tsk);
0111     }
0112 
0113     bcom_eng->tdt[tasknum].stop = 0;
0114 
0115     return NULL;
0116 }
0117 EXPORT_SYMBOL_GPL(bcom_task_alloc);
0118 
0119 void
0120 bcom_task_free(struct bcom_task *tsk)
0121 {
0122     /* Stop the task */
0123     bcom_disable_task(tsk->tasknum);
0124 
0125     /* Clear TDT */
0126     bcom_eng->tdt[tsk->tasknum].start = 0;
0127     bcom_eng->tdt[tsk->tasknum].stop  = 0;
0128 
0129     /* Free everything */
0130     irq_dispose_mapping(tsk->irq);
0131     bcom_sram_free(tsk->bd);
0132     kfree(tsk->cookie);
0133     kfree(tsk);
0134 }
0135 EXPORT_SYMBOL_GPL(bcom_task_free);
0136 
0137 int
0138 bcom_load_image(int task, u32 *task_image)
0139 {
0140     struct bcom_task_header *hdr = (struct bcom_task_header *)task_image;
0141     struct bcom_tdt *tdt;
0142     u32 *desc, *var, *inc;
0143     u32 *desc_src, *var_src, *inc_src;
0144 
0145     /* Safety checks */
0146     if (hdr->magic != BCOM_TASK_MAGIC) {
0147         printk(KERN_ERR DRIVER_NAME
0148             ": Trying to load invalid microcode\n");
0149         return -EINVAL;
0150     }
0151 
0152     if ((task < 0) || (task >= BCOM_MAX_TASKS)) {
0153         printk(KERN_ERR DRIVER_NAME
0154             ": Trying to load invalid task %d\n", task);
0155         return -EINVAL;
0156     }
0157 
0158     /* Initial load or reload */
0159     tdt = &bcom_eng->tdt[task];
0160 
0161     if (tdt->start) {
0162         desc = bcom_task_desc(task);
0163         if (hdr->desc_size != bcom_task_num_descs(task)) {
0164             printk(KERN_ERR DRIVER_NAME
0165                 ": Trying to reload wrong task image "
0166                 "(%d size %d/%d)!\n",
0167                 task,
0168                 hdr->desc_size,
0169                 bcom_task_num_descs(task));
0170             return -EINVAL;
0171         }
0172     } else {
0173         phys_addr_t start_pa;
0174 
0175         desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa);
0176         if (!desc)
0177             return -ENOMEM;
0178 
0179         tdt->start = start_pa;
0180         tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32));
0181     }
0182 
0183     var = bcom_task_var(task);
0184     inc = bcom_task_inc(task);
0185 
0186     /* Clear & copy */
0187     memset_io(var, 0x00, BCOM_VAR_SIZE);
0188     memset_io(inc, 0x00, BCOM_INC_SIZE);
0189 
0190     desc_src = (u32 *)(hdr + 1);
0191     var_src = desc_src + hdr->desc_size;
0192     inc_src = var_src + hdr->var_size;
0193 
0194     memcpy_toio(desc, desc_src, hdr->desc_size * sizeof(u32));
0195     memcpy_toio(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32));
0196     memcpy_toio(inc, inc_src, hdr->inc_size * sizeof(u32));
0197 
0198     return 0;
0199 }
0200 EXPORT_SYMBOL_GPL(bcom_load_image);
0201 
0202 void
0203 bcom_set_initiator(int task, int initiator)
0204 {
0205     int i;
0206     int num_descs;
0207     u32 *desc;
0208     int next_drd_has_initiator;
0209 
0210     bcom_set_tcr_initiator(task, initiator);
0211 
0212     /* Just setting tcr is apparently not enough due to some problem */
0213     /* with it. So we just go thru all the microcode and replace in  */
0214     /* the DRD directly */
0215 
0216     desc = bcom_task_desc(task);
0217     next_drd_has_initiator = 1;
0218     num_descs = bcom_task_num_descs(task);
0219 
0220     for (i=0; i<num_descs; i++, desc++) {
0221         if (!bcom_desc_is_drd(*desc))
0222             continue;
0223         if (next_drd_has_initiator)
0224             if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS)
0225                 bcom_set_desc_initiator(desc, initiator);
0226         next_drd_has_initiator = !bcom_drd_is_extended(*desc);
0227     }
0228 }
0229 EXPORT_SYMBOL_GPL(bcom_set_initiator);
0230 
0231 
0232 /* Public API */
0233 
0234 void
0235 bcom_enable(struct bcom_task *tsk)
0236 {
0237     bcom_enable_task(tsk->tasknum);
0238 }
0239 EXPORT_SYMBOL_GPL(bcom_enable);
0240 
0241 void
0242 bcom_disable(struct bcom_task *tsk)
0243 {
0244     bcom_disable_task(tsk->tasknum);
0245 }
0246 EXPORT_SYMBOL_GPL(bcom_disable);
0247 
0248 
0249 /* ======================================================================== */
0250 /* Engine init/cleanup                                                      */
0251 /* ======================================================================== */
0252 
0253 /* Function Descriptor table */
0254 /* this will need to be updated if Freescale changes their task code FDT */
0255 static u32 fdt_ops[] = {
0256     0xa0045670, /* FDT[48] - load_acc()   */
0257     0x80045670, /* FDT[49] - unload_acc() */
0258     0x21800000, /* FDT[50] - and()        */
0259     0x21e00000, /* FDT[51] - or()         */
0260     0x21500000, /* FDT[52] - xor()        */
0261     0x21400000, /* FDT[53] - andn()       */
0262     0x21500000, /* FDT[54] - not()        */
0263     0x20400000, /* FDT[55] - add()        */
0264     0x20500000, /* FDT[56] - sub()        */
0265     0x20800000, /* FDT[57] - lsh()        */
0266     0x20a00000, /* FDT[58] - rsh()        */
0267     0xc0170000, /* FDT[59] - crc8()       */
0268     0xc0145670, /* FDT[60] - crc16()      */
0269     0xc0345670, /* FDT[61] - crc32()      */
0270     0xa0076540, /* FDT[62] - endian32()   */
0271     0xa0000760, /* FDT[63] - endian16()   */
0272 };
0273 
0274 
0275 static int bcom_engine_init(void)
0276 {
0277     int task;
0278     phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
0279     unsigned int tdt_size, ctx_size, var_size, fdt_size;
0280 
0281     /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
0282     tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
0283     ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;
0284     var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);
0285     fdt_size = BCOM_FDT_SIZE;
0286 
0287     bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);
0288     bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);
0289     bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);
0290     bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);
0291 
0292     if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) {
0293         printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");
0294 
0295         bcom_sram_free(bcom_eng->tdt);
0296         bcom_sram_free(bcom_eng->ctx);
0297         bcom_sram_free(bcom_eng->var);
0298         bcom_sram_free(bcom_eng->fdt);
0299 
0300         return -ENOMEM;
0301     }
0302 
0303     memset_io(bcom_eng->tdt, 0x00, tdt_size);
0304     memset_io(bcom_eng->ctx, 0x00, ctx_size);
0305     memset_io(bcom_eng->var, 0x00, var_size);
0306     memset_io(bcom_eng->fdt, 0x00, fdt_size);
0307 
0308     /* Copy the FDT for the EU#3 */
0309     memcpy_toio(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops));
0310 
0311     /* Initialize Task base structure */
0312     for (task=0; task<BCOM_MAX_TASKS; task++)
0313     {
0314         out_be16(&bcom_eng->regs->tcr[task], 0);
0315         out_8(&bcom_eng->regs->ipr[task], 0);
0316 
0317         bcom_eng->tdt[task].context = ctx_pa;
0318         bcom_eng->tdt[task].var = var_pa;
0319         bcom_eng->tdt[task].fdt = fdt_pa;
0320 
0321         var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;
0322         ctx_pa += BCOM_CTX_SIZE;
0323     }
0324 
0325     out_be32(&bcom_eng->regs->taskBar, tdt_pa);
0326 
0327     /* Init 'always' initiator */
0328     out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
0329 
0330     /* Disable COMM Bus Prefetch on the original 5200; it's broken */
0331     if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
0332         bcom_disable_prefetch();
0333 
0334     /* Init lock */
0335     spin_lock_init(&bcom_eng->lock);
0336 
0337     return 0;
0338 }
0339 
0340 static void
0341 bcom_engine_cleanup(void)
0342 {
0343     int task;
0344 
0345     /* Stop all tasks */
0346     for (task=0; task<BCOM_MAX_TASKS; task++)
0347     {
0348         out_be16(&bcom_eng->regs->tcr[task], 0);
0349         out_8(&bcom_eng->regs->ipr[task], 0);
0350     }
0351 
0352     out_be32(&bcom_eng->regs->taskBar, 0ul);
0353 
0354     /* Release the SRAM zones */
0355     bcom_sram_free(bcom_eng->tdt);
0356     bcom_sram_free(bcom_eng->ctx);
0357     bcom_sram_free(bcom_eng->var);
0358     bcom_sram_free(bcom_eng->fdt);
0359 }
0360 
0361 
0362 /* ======================================================================== */
0363 /* OF platform driver                                                       */
0364 /* ======================================================================== */
0365 
0366 static int mpc52xx_bcom_probe(struct platform_device *op)
0367 {
0368     struct device_node *ofn_sram;
0369     struct resource res_bcom;
0370 
0371     int rv;
0372 
0373     /* Inform user we're ok so far */
0374     printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
0375 
0376     /* Get the bestcomm node */
0377     of_node_get(op->dev.of_node);
0378 
0379     /* Prepare SRAM */
0380     ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids);
0381     if (!ofn_sram) {
0382         printk(KERN_ERR DRIVER_NAME ": "
0383             "No SRAM found in device tree\n");
0384         rv = -ENODEV;
0385         goto error_ofput;
0386     }
0387     rv = bcom_sram_init(ofn_sram, DRIVER_NAME);
0388     of_node_put(ofn_sram);
0389 
0390     if (rv) {
0391         printk(KERN_ERR DRIVER_NAME ": "
0392             "Error in SRAM init\n");
0393         goto error_ofput;
0394     }
0395 
0396     /* Get a clean struct */
0397     bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);
0398     if (!bcom_eng) {
0399         rv = -ENOMEM;
0400         goto error_sramclean;
0401     }
0402 
0403     /* Save the node */
0404     bcom_eng->ofnode = op->dev.of_node;
0405 
0406     /* Get, reserve & map io */
0407     if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) {
0408         printk(KERN_ERR DRIVER_NAME ": "
0409             "Can't get resource\n");
0410         rv = -EINVAL;
0411         goto error_sramclean;
0412     }
0413 
0414     if (!request_mem_region(res_bcom.start, resource_size(&res_bcom),
0415                 DRIVER_NAME)) {
0416         printk(KERN_ERR DRIVER_NAME ": "
0417             "Can't request registers region\n");
0418         rv = -EBUSY;
0419         goto error_sramclean;
0420     }
0421 
0422     bcom_eng->regs_base = res_bcom.start;
0423     bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));
0424     if (!bcom_eng->regs) {
0425         printk(KERN_ERR DRIVER_NAME ": "
0426             "Can't map registers\n");
0427         rv = -ENOMEM;
0428         goto error_release;
0429     }
0430 
0431     /* Now, do the real init */
0432     rv = bcom_engine_init();
0433     if (rv)
0434         goto error_unmap;
0435 
0436     /* Done ! */
0437     printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
0438         (long)bcom_eng->regs_base);
0439 
0440     return 0;
0441 
0442     /* Error path */
0443 error_unmap:
0444     iounmap(bcom_eng->regs);
0445 error_release:
0446     release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
0447 error_sramclean:
0448     kfree(bcom_eng);
0449     bcom_sram_cleanup();
0450 error_ofput:
0451     of_node_put(op->dev.of_node);
0452 
0453     printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
0454 
0455     return rv;
0456 }
0457 
0458 
0459 static int mpc52xx_bcom_remove(struct platform_device *op)
0460 {
0461     /* Clean up the engine */
0462     bcom_engine_cleanup();
0463 
0464     /* Cleanup SRAM */
0465     bcom_sram_cleanup();
0466 
0467     /* Release regs */
0468     iounmap(bcom_eng->regs);
0469     release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma));
0470 
0471     /* Release the node */
0472     of_node_put(bcom_eng->ofnode);
0473 
0474     /* Release memory */
0475     kfree(bcom_eng);
0476     bcom_eng = NULL;
0477 
0478     return 0;
0479 }
0480 
0481 static const struct of_device_id mpc52xx_bcom_of_match[] = {
0482     { .compatible = "fsl,mpc5200-bestcomm", },
0483     { .compatible = "mpc5200-bestcomm", },
0484     {},
0485 };
0486 
0487 MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);
0488 
0489 
0490 static struct platform_driver mpc52xx_bcom_of_platform_driver = {
0491     .probe      = mpc52xx_bcom_probe,
0492     .remove     = mpc52xx_bcom_remove,
0493     .driver = {
0494         .name = DRIVER_NAME,
0495         .of_match_table = mpc52xx_bcom_of_match,
0496     },
0497 };
0498 
0499 
0500 /* ======================================================================== */
0501 /* Module                                                                   */
0502 /* ======================================================================== */
0503 
0504 static int __init
0505 mpc52xx_bcom_init(void)
0506 {
0507     return platform_driver_register(&mpc52xx_bcom_of_platform_driver);
0508 }
0509 
0510 static void __exit
0511 mpc52xx_bcom_exit(void)
0512 {
0513     platform_driver_unregister(&mpc52xx_bcom_of_platform_driver);
0514 }
0515 
0516 /* If we're not a module, we must make sure everything is setup before  */
0517 /* anyone tries to use us ... that's why we use subsys_initcall instead */
0518 /* of module_init. */
0519 subsys_initcall(mpc52xx_bcom_init);
0520 module_exit(mpc52xx_bcom_exit);
0521 
0522 MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
0523 MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
0524 MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
0525 MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
0526 MODULE_LICENSE("GPL v2");