Path: blob/master/arch/powerpc/sysdev/bestcomm/bestcomm.c
10818 views
/*1* Driver for MPC52xx processor BestComm peripheral controller2*3*4* Copyright (C) 2006-2007 Sylvain Munaut <[email protected]>5* Copyright (C) 2005 Varma Electronics Oy,6* ( by Andrey Volkov <[email protected]> )7* Copyright (C) 2003-2004 MontaVista, Software, Inc.8* ( by Dale Farnsworth <[email protected]> )9*10* This file is licensed under the terms of the GNU General Public License11* version 2. This program is licensed "as is" without any warranty of any12* kind, whether express or implied.13*/1415#include <linux/module.h>16#include <linux/kernel.h>17#include <linux/slab.h>18#include <linux/of.h>19#include <linux/of_device.h>20#include <linux/of_platform.h>21#include <asm/io.h>22#include <asm/irq.h>23#include <asm/mpc52xx.h>2425#include "sram.h"26#include "bestcomm_priv.h"27#include "bestcomm.h"2829#define DRIVER_NAME "bestcomm-core"3031/* MPC5200 device tree match tables */32static struct of_device_id mpc52xx_sram_ids[] __devinitdata = {33{ .compatible = "fsl,mpc5200-sram", },34{ .compatible = "mpc5200-sram", },35{}36};373839struct bcom_engine *bcom_eng = NULL;40EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */4142/* ======================================================================== */43/* Public and private API */44/* ======================================================================== */4546/* Private API */4748struct bcom_task *49bcom_task_alloc(int bd_count, int bd_size, int priv_size)50{51int i, tasknum = -1;52struct bcom_task *tsk;5354/* Don't try to do anything if bestcomm init failed */55if (!bcom_eng)56return NULL;5758/* Get and reserve a task num */59spin_lock(&bcom_eng->lock);6061for (i=0; i<BCOM_MAX_TASKS; i++)62if (!bcom_eng->tdt[i].stop) { /* we use stop as a marker */63bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */64tasknum = i;65break;66}6768spin_unlock(&bcom_eng->lock);6970if (tasknum < 0)71return NULL;7273/* Allocate our structure */74tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL);75if (!tsk)76goto error;7778tsk->tasknum = tasknum;79if (priv_size)80tsk->priv = (void*)tsk + sizeof(struct bcom_task);8182/* Get IRQ of that task */83tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum);84if (tsk->irq == NO_IRQ)85goto error;8687/* Init the BDs, if needed */88if (bd_count) {89tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL);90if (!tsk->cookie)91goto error;9293tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa);94if (!tsk->bd)95goto error;96memset(tsk->bd, 0x00, bd_count * bd_size);9798tsk->num_bd = bd_count;99tsk->bd_size = bd_size;100}101102return tsk;103104error:105if (tsk) {106if (tsk->irq != NO_IRQ)107irq_dispose_mapping(tsk->irq);108bcom_sram_free(tsk->bd);109kfree(tsk->cookie);110kfree(tsk);111}112113bcom_eng->tdt[tasknum].stop = 0;114115return NULL;116}117EXPORT_SYMBOL_GPL(bcom_task_alloc);118119void120bcom_task_free(struct bcom_task *tsk)121{122/* Stop the task */123bcom_disable_task(tsk->tasknum);124125/* Clear TDT */126bcom_eng->tdt[tsk->tasknum].start = 0;127bcom_eng->tdt[tsk->tasknum].stop = 0;128129/* Free everything */130irq_dispose_mapping(tsk->irq);131bcom_sram_free(tsk->bd);132kfree(tsk->cookie);133kfree(tsk);134}135EXPORT_SYMBOL_GPL(bcom_task_free);136137int138bcom_load_image(int task, u32 *task_image)139{140struct bcom_task_header *hdr = (struct bcom_task_header *)task_image;141struct bcom_tdt *tdt;142u32 *desc, *var, *inc;143u32 *desc_src, *var_src, *inc_src;144145/* Safety checks */146if (hdr->magic != BCOM_TASK_MAGIC) {147printk(KERN_ERR DRIVER_NAME148": Trying to load invalid microcode\n");149return -EINVAL;150}151152if ((task < 0) || (task >= BCOM_MAX_TASKS)) {153printk(KERN_ERR DRIVER_NAME154": Trying to load invalid task %d\n", task);155return -EINVAL;156}157158/* Initial load or reload */159tdt = &bcom_eng->tdt[task];160161if (tdt->start) {162desc = bcom_task_desc(task);163if (hdr->desc_size != bcom_task_num_descs(task)) {164printk(KERN_ERR DRIVER_NAME165": Trying to reload wrong task image "166"(%d size %d/%d)!\n",167task,168hdr->desc_size,169bcom_task_num_descs(task));170return -EINVAL;171}172} else {173phys_addr_t start_pa;174175desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa);176if (!desc)177return -ENOMEM;178179tdt->start = start_pa;180tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32));181}182183var = bcom_task_var(task);184inc = bcom_task_inc(task);185186/* Clear & copy */187memset(var, 0x00, BCOM_VAR_SIZE);188memset(inc, 0x00, BCOM_INC_SIZE);189190desc_src = (u32 *)(hdr + 1);191var_src = desc_src + hdr->desc_size;192inc_src = var_src + hdr->var_size;193194memcpy(desc, desc_src, hdr->desc_size * sizeof(u32));195memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32));196memcpy(inc, inc_src, hdr->inc_size * sizeof(u32));197198return 0;199}200EXPORT_SYMBOL_GPL(bcom_load_image);201202void203bcom_set_initiator(int task, int initiator)204{205int i;206int num_descs;207u32 *desc;208int next_drd_has_initiator;209210bcom_set_tcr_initiator(task, initiator);211212/* Just setting tcr is apparently not enough due to some problem */213/* with it. So we just go thru all the microcode and replace in */214/* the DRD directly */215216desc = bcom_task_desc(task);217next_drd_has_initiator = 1;218num_descs = bcom_task_num_descs(task);219220for (i=0; i<num_descs; i++, desc++) {221if (!bcom_desc_is_drd(*desc))222continue;223if (next_drd_has_initiator)224if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS)225bcom_set_desc_initiator(desc, initiator);226next_drd_has_initiator = !bcom_drd_is_extended(*desc);227}228}229EXPORT_SYMBOL_GPL(bcom_set_initiator);230231232/* Public API */233234void235bcom_enable(struct bcom_task *tsk)236{237bcom_enable_task(tsk->tasknum);238}239EXPORT_SYMBOL_GPL(bcom_enable);240241void242bcom_disable(struct bcom_task *tsk)243{244bcom_disable_task(tsk->tasknum);245}246EXPORT_SYMBOL_GPL(bcom_disable);247248249/* ======================================================================== */250/* Engine init/cleanup */251/* ======================================================================== */252253/* Function Descriptor table */254/* this will need to be updated if Freescale changes their task code FDT */255static u32 fdt_ops[] = {2560xa0045670, /* FDT[48] - load_acc() */2570x80045670, /* FDT[49] - unload_acc() */2580x21800000, /* FDT[50] - and() */2590x21e00000, /* FDT[51] - or() */2600x21500000, /* FDT[52] - xor() */2610x21400000, /* FDT[53] - andn() */2620x21500000, /* FDT[54] - not() */2630x20400000, /* FDT[55] - add() */2640x20500000, /* FDT[56] - sub() */2650x20800000, /* FDT[57] - lsh() */2660x20a00000, /* FDT[58] - rsh() */2670xc0170000, /* FDT[59] - crc8() */2680xc0145670, /* FDT[60] - crc16() */2690xc0345670, /* FDT[61] - crc32() */2700xa0076540, /* FDT[62] - endian32() */2710xa0000760, /* FDT[63] - endian16() */272};273274275static int __devinit276bcom_engine_init(void)277{278int task;279phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;280unsigned int tdt_size, ctx_size, var_size, fdt_size;281282/* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */283tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);284ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;285var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);286fdt_size = BCOM_FDT_SIZE;287288bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);289bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);290bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);291bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);292293if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) {294printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");295296bcom_sram_free(bcom_eng->tdt);297bcom_sram_free(bcom_eng->ctx);298bcom_sram_free(bcom_eng->var);299bcom_sram_free(bcom_eng->fdt);300301return -ENOMEM;302}303304memset(bcom_eng->tdt, 0x00, tdt_size);305memset(bcom_eng->ctx, 0x00, ctx_size);306memset(bcom_eng->var, 0x00, var_size);307memset(bcom_eng->fdt, 0x00, fdt_size);308309/* Copy the FDT for the EU#3 */310memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops));311312/* Initialize Task base structure */313for (task=0; task<BCOM_MAX_TASKS; task++)314{315out_be16(&bcom_eng->regs->tcr[task], 0);316out_8(&bcom_eng->regs->ipr[task], 0);317318bcom_eng->tdt[task].context = ctx_pa;319bcom_eng->tdt[task].var = var_pa;320bcom_eng->tdt[task].fdt = fdt_pa;321322var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;323ctx_pa += BCOM_CTX_SIZE;324}325326out_be32(&bcom_eng->regs->taskBar, tdt_pa);327328/* Init 'always' initiator */329out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);330331/* Disable COMM Bus Prefetch on the original 5200; it's broken */332if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)333bcom_disable_prefetch();334335/* Init lock */336spin_lock_init(&bcom_eng->lock);337338return 0;339}340341static void342bcom_engine_cleanup(void)343{344int task;345346/* Stop all tasks */347for (task=0; task<BCOM_MAX_TASKS; task++)348{349out_be16(&bcom_eng->regs->tcr[task], 0);350out_8(&bcom_eng->regs->ipr[task], 0);351}352353out_be32(&bcom_eng->regs->taskBar, 0ul);354355/* Release the SRAM zones */356bcom_sram_free(bcom_eng->tdt);357bcom_sram_free(bcom_eng->ctx);358bcom_sram_free(bcom_eng->var);359bcom_sram_free(bcom_eng->fdt);360}361362363/* ======================================================================== */364/* OF platform driver */365/* ======================================================================== */366367static int __devinit mpc52xx_bcom_probe(struct platform_device *op)368{369struct device_node *ofn_sram;370struct resource res_bcom;371372int rv;373374/* Inform user we're ok so far */375printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");376377/* Get the bestcomm node */378of_node_get(op->dev.of_node);379380/* Prepare SRAM */381ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids);382if (!ofn_sram) {383printk(KERN_ERR DRIVER_NAME ": "384"No SRAM found in device tree\n");385rv = -ENODEV;386goto error_ofput;387}388rv = bcom_sram_init(ofn_sram, DRIVER_NAME);389of_node_put(ofn_sram);390391if (rv) {392printk(KERN_ERR DRIVER_NAME ": "393"Error in SRAM init\n");394goto error_ofput;395}396397/* Get a clean struct */398bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);399if (!bcom_eng) {400printk(KERN_ERR DRIVER_NAME ": "401"Can't allocate state structure\n");402rv = -ENOMEM;403goto error_sramclean;404}405406/* Save the node */407bcom_eng->ofnode = op->dev.of_node;408409/* Get, reserve & map io */410if (of_address_to_resource(op->dev.of_node, 0, &res_bcom)) {411printk(KERN_ERR DRIVER_NAME ": "412"Can't get resource\n");413rv = -EINVAL;414goto error_sramclean;415}416417if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma),418DRIVER_NAME)) {419printk(KERN_ERR DRIVER_NAME ": "420"Can't request registers region\n");421rv = -EBUSY;422goto error_sramclean;423}424425bcom_eng->regs_base = res_bcom.start;426bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));427if (!bcom_eng->regs) {428printk(KERN_ERR DRIVER_NAME ": "429"Can't map registers\n");430rv = -ENOMEM;431goto error_release;432}433434/* Now, do the real init */435rv = bcom_engine_init();436if (rv)437goto error_unmap;438439/* Done ! */440printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",441(long)bcom_eng->regs_base);442443return 0;444445/* Error path */446error_unmap:447iounmap(bcom_eng->regs);448error_release:449release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));450error_sramclean:451kfree(bcom_eng);452bcom_sram_cleanup();453error_ofput:454of_node_put(op->dev.of_node);455456printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");457458return rv;459}460461462static int mpc52xx_bcom_remove(struct platform_device *op)463{464/* Clean up the engine */465bcom_engine_cleanup();466467/* Cleanup SRAM */468bcom_sram_cleanup();469470/* Release regs */471iounmap(bcom_eng->regs);472release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma));473474/* Release the node */475of_node_put(bcom_eng->ofnode);476477/* Release memory */478kfree(bcom_eng);479bcom_eng = NULL;480481return 0;482}483484static struct of_device_id mpc52xx_bcom_of_match[] = {485{ .compatible = "fsl,mpc5200-bestcomm", },486{ .compatible = "mpc5200-bestcomm", },487{},488};489490MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);491492493static 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};502503504/* ======================================================================== */505/* Module */506/* ======================================================================== */507508static int __init509mpc52xx_bcom_init(void)510{511return platform_driver_register(&mpc52xx_bcom_of_platform_driver);512}513514static void __exit515mpc52xx_bcom_exit(void)516{517platform_driver_unregister(&mpc52xx_bcom_of_platform_driver);518}519520/* 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);525526MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");527MODULE_AUTHOR("Sylvain Munaut <[email protected]>");528MODULE_AUTHOR("Andrey Volkov <[email protected]>");529MODULE_AUTHOR("Dale Farnsworth <[email protected]>");530MODULE_LICENSE("GPL v2");531532533534