This patch adds support for the core of the BestComm API for the Freescale MPC5200(b). The BestComm engine is a microcode-controlled / tasks-based DMA used by several of the onchip devices.
Setting up the tasks / memory allocation and all common low level functions are handled by this patch. The specifics details of each tasks and their microcode are split-out in separate patches. This is not the official API, but a much cleaner one. (hopefully) Signed-off-by: Sylvain Munaut <[EMAIL PROTECTED]> --- arch/powerpc/platforms/Kconfig | 2 + arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/bestcomm/Kconfig | 18 + arch/powerpc/sysdev/bestcomm/Makefile | 8 + arch/powerpc/sysdev/bestcomm/bestcomm.c | 657 ++++++++++++++++++++++++++ arch/powerpc/sysdev/bestcomm/bestcomm.h | 136 ++++++ arch/powerpc/sysdev/bestcomm/bestcomm_priv.h | 325 +++++++++++++ arch/powerpc/sysdev/bestcomm/sram.c | 177 +++++++ arch/powerpc/sysdev/bestcomm/sram.h | 54 +++ 9 files changed, 1378 insertions(+), 0 deletions(-) create mode 100644 arch/powerpc/sysdev/bestcomm/Kconfig create mode 100644 arch/powerpc/sysdev/bestcomm/Makefile create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.c create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm.h create mode 100644 arch/powerpc/sysdev/bestcomm/bestcomm_priv.h create mode 100644 arch/powerpc/sysdev/bestcomm/sram.c create mode 100644 arch/powerpc/sysdev/bestcomm/sram.h diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 9189ba5..99d48be 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -293,4 +293,6 @@ config FSL_ULI1575 Freescale reference boards. The boards all use the ULI in pretty much the same way. +source "arch/powerpc/sysdev/bestcomm/Kconfig" + endmenu diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 08ce31e..0c8a29d 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o obj-$(CONFIG_FSL_PCI) += fsl_pci.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc_cmos_setup.o diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig new file mode 100644 index 0000000..3366e24 --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/Kconfig @@ -0,0 +1,18 @@ +# +# Kconfig options for Bestcomm +# + +config PPC_BESTCOMM + tristate "Bestcomm DMA engine support" + depends on PPC_MPC52xx + default n + select PPC_LIB_RHEAP + help + BestComm is the name of the communication coprocessor found + on the Freescale MPC5200 family of processor. It's usage is + optionnal for some drivers (like ATA), but required for + others (like FEC). + + If you want to use drivers that require DMA operations, + answer Y or M. Otherwise say N. + diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile new file mode 100644 index 0000000..a24aa06 --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for BestComm & co +# + +bestcomm-core-objs := bestcomm.o sram.o + +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o + diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c new file mode 100644 index 0000000..10fb476 --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c @@ -0,0 +1,657 @@ +/* + * Driver for MPC52xx processor BestComm peripheral controller + * + * + * Copyright (C) 2006-2007 Sylvain Munaut <[EMAIL PROTECTED]> + * Copyright (C) 2005 Varma Electronics Oy, + * ( by Andrey Volkov <[EMAIL PROTECTED]> ) + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth <[EMAIL PROTECTED]> ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/mpc52xx.h> +#include <asm/of_device.h> +#include <asm/of_platform.h> + +#include "sram.h" +#include "bestcomm_priv.h" +#include "bestcomm.h" + +#define DRIVER_NAME "bestcomm-core" + + +struct bcom_engine *bcom_eng = NULL; +EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */ + + +/* ======================================================================== */ +/* Public and private API */ +/* ======================================================================== */ + +/* Debug Dump */ + +#define BCOM_DPRINTK(a,b...) printk(KERN_DEBUG DRIVER_NAME ": " a, ## b) + +void +bcom_dump_status(void) +{ + int i; + struct mpc52xx_sdma __iomem *r = bcom_eng->regs; + + BCOM_DPRINTK("BestComm status dump (pa=%08lx, va=%p)\n", + bcom_eng->regs_base, bcom_eng->regs); + BCOM_DPRINTK(" taskBar = %08x\n", in_be32(&r->taskBar)); + BCOM_DPRINTK(" currentPointer = %08x\n", in_be32(&r->currentPointer)); + BCOM_DPRINTK(" endPointer = %08x\n", in_be32(&r->endPointer)); + BCOM_DPRINTK(" variablePointer = %08x\n", in_be32(&r->variablePointer)); + BCOM_DPRINTK(" IntVect1 = %08x\n", (u32)in_8(&r->IntVect1)); + BCOM_DPRINTK(" IntVect2 = %08x\n", (u32)in_8(&r->IntVect2)); + BCOM_DPRINTK(" PtdCntrl = %08hx\n", in_be16(&r->PtdCntrl)); + BCOM_DPRINTK(" IntPend = %08x\n", in_be32(&r->IntPend)); + BCOM_DPRINTK(" IntMask = %08x\n", in_be32(&r->IntMask)); + + BCOM_DPRINTK(" TCR dump :\n"); + + for (i=0; i<16; i++) { + printk("%s%04hx%s", + (i&0x7) == 0x0 ? KERN_DEBUG "\t" : "", + in_be16(&r->tcr[i]), + (i&0x7) == 0x7 ? "\n" : " "); + } + + BCOM_DPRINTK(" IPR dump :\n"); + + for (i=0; i<32; i++) { + printk("%s%02x%s", + (i&0x7) == 0x0 ? KERN_DEBUG "\t" : "", + (u32)in_8(&r->ipr[i]), + (i&0x7) == 0x7 ? "\n" : " "); + } + + BCOM_DPRINTK(" cReqSelect = %08x\n", in_be32(&r->cReqSelect)); + BCOM_DPRINTK(" task_size0 = %08x\n", in_be32(&r->task_size0)); + BCOM_DPRINTK(" task_size1 = %08x\n", in_be32(&r->task_size1)); + BCOM_DPRINTK(" MDEDebug = %08x\n", in_be32(&r->MDEDebug)); + BCOM_DPRINTK(" ADSDebug = %08x\n", in_be32(&r->ADSDebug)); + BCOM_DPRINTK(" Value1 = %08x\n", in_be32(&r->Value1)); + BCOM_DPRINTK(" Value2 = %08x\n", in_be32(&r->Value2)); + BCOM_DPRINTK(" Control = %08x\n", in_be32(&r->Control)); + BCOM_DPRINTK(" Status = %08x\n", in_be32(&r->Status)); + BCOM_DPRINTK(" PTDDebug = %08x\n", in_be32(&r->PTDDebug)); +} +EXPORT_SYMBOL_GPL(bcom_dump_status); + +void +bcom_dump_task(int task) +{ + int i; + u32 *p; + struct bcom_tdt *tdt = &bcom_eng->tdt[task]; + + BCOM_DPRINTK("Task dump %d\n", task); + BCOM_DPRINTK(" tcr = %04hx\n", bcom_eng->regs->tcr[task]); + BCOM_DPRINTK(" tdt = %p\n", &bcom_eng->tdt[task]); + BCOM_DPRINTK(" tdt->start = %08x\n", tdt->start); + BCOM_DPRINTK(" tdt->stop = %08x\n", tdt->stop); + BCOM_DPRINTK(" tdt->var = %08x\n", tdt->var); + BCOM_DPRINTK(" tdt->fdt = %08x\n", tdt->fdt); + BCOM_DPRINTK(" tdt->status = %08x\n", tdt->exec_status); + BCOM_DPRINTK(" tdt->mvtp = %08x\n", tdt->mvtp); + BCOM_DPRINTK(" tdt->context = %08x\n", tdt->context); + BCOM_DPRINTK(" tdt->litbase = %08x\n", tdt->litbase); + + BCOM_DPRINTK(" code :\n"); + + p = bcom_task_desc(task); + for (i=0; i<bcom_task_num_descs(task); i++) + printk(KERN_DEBUG "\t%p %08x\n", &p[i], p[i]); + + BCOM_DPRINTK(" var/inc :\n"); + + p = bcom_task_var(task); + for (i=0; i<BCOM_MAX_VAR+BCOM_MAX_INC; i++) + printk(KERN_DEBUG "\t%p %08x\n", &p[i], p[i]); +} +EXPORT_SYMBOL_GPL(bcom_dump_task); + +void +bcom_dump_bdring(struct bcom_task *tsk) +{ + int i, j; + + BCOM_DPRINTK("BD ring dump %d\n", tsk->tasknum); + + for (i=0; i<tsk->num_bd; i++) { + BCOM_DPRINTK(" BD[%02d] :\n", i); + BCOM_DPRINTK(" cookie : %p\n", tsk->cookie[i]); + BCOM_DPRINTK(" status : %08x\n", tsk->bd[i].status); + for (j=0; j<(tsk->bd_size/sizeof(u32))-1; j++) + BCOM_DPRINTK(" data[%02d] : %08x\n", + j, tsk->bd[i].data[j]); + } +} +EXPORT_SYMBOL_GPL(bcom_dump_bdring); + + +/* Private API */ + +struct bcom_task * +bcom_task_alloc(int bd_count, int bd_size, int priv_size) +{ + int i, tasknum = -1; + struct bcom_task *tsk; + + /* Get and reserve a task num */ + spin_lock(&bcom_eng->lock); + + for (i=0; i<BCOM_MAX_TASKS; i++) + if (!bcom_eng->tdt[i].stop) { /* we use stop as a marker */ + bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */ + tasknum = i; + break; + } + + spin_unlock(&bcom_eng->lock); + + if (tasknum < 0) + return NULL; + + /* Allocate our structure */ + tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL); + if (!tsk) + goto error; + + tsk->tasknum = tasknum; + if (priv_size) + tsk->priv = (void*)tsk + sizeof(struct bcom_task); + + /* Get IRQ of that task */ + tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum); + if (tsk->irq == NO_IRQ) + goto error; + + /* Init the BDs, if needed */ + if (bd_count) { + tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL); + if (!tsk->cookie) + goto error; + + tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa); + if (!tsk->bd) + goto error; + memset(tsk->bd, 0x00, bd_count * bd_size); + + tsk->num_bd = bd_count; + tsk->bd_size = bd_size; + } + + return tsk; + +error: + if (tsk) { + if (tsk->irq != NO_IRQ) + irq_dispose_mapping(tsk->irq); + bcom_sram_free(tsk->bd); + kfree(tsk->cookie); + kfree(tsk); + } + + bcom_eng->tdt[tasknum].stop = 0; + + return NULL; +} +EXPORT_SYMBOL_GPL(bcom_task_alloc); + +void +bcom_task_release(struct bcom_task *tsk) +{ + /* Stop the task */ + bcom_disable_task(tsk->tasknum); + + /* Clear TDT */ + bcom_eng->tdt[tsk->tasknum].start = 0; + bcom_eng->tdt[tsk->tasknum].stop = 0; + + /* Free everything */ + irq_dispose_mapping(tsk->irq); + bcom_sram_free(tsk->bd); + kfree(tsk->cookie); + kfree(tsk); +} +EXPORT_SYMBOL_GPL(bcom_task_release); + +int +bcom_load_image(int task, u32 *task_image) +{ + struct bcom_task_header *hdr = (struct bcom_task_header *)task_image; + struct bcom_tdt *tdt; + u32 *desc, *var, *inc; + u32 *desc_src, *var_src, *inc_src; + + /* Safety checks */ + if (hdr->magic != BCOM_TASK_MAGIC) { + printk(KERN_ERR DRIVER_NAME + ": Trying to load invalid microcode\n"); + return -EINVAL; + } + + if ((task < 0) || (task >= BCOM_MAX_TASKS)) { + printk(KERN_ERR DRIVER_NAME + ": Trying to load invalid task %d\n", task); + return -EINVAL; + } + + /* Initial load or reload */ + tdt = &bcom_eng->tdt[task]; + + if (tdt->start) { + desc = bcom_task_desc(task); + if (hdr->desc_size != bcom_task_num_descs(task)) { + printk(KERN_ERR DRIVER_NAME + ": Trying to reload wrong task image " + "(%d size %d/%d)!\n", + task, + hdr->desc_size, + bcom_task_num_descs(task)); + return -EINVAL; + } + } else { + phys_addr_t start_pa; + + desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa); + if (!desc) + return -ENOMEM; + + tdt->start = start_pa; + tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32)); + } + + var = bcom_task_var(task); + inc = bcom_task_inc(task); + + /* Clear & copy */ + memset(var, 0x00, BCOM_VAR_SIZE); + memset(inc, 0x00, BCOM_INC_SIZE); + + desc_src = (u32 *)(hdr + 1); + var_src = desc_src + hdr->desc_size; + inc_src = var_src + hdr->var_size; + + memcpy(desc, desc_src, hdr->desc_size * sizeof(u32)); + memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32)); + memcpy(inc, inc_src, hdr->inc_size * sizeof(u32)); + + return 0; +} +EXPORT_SYMBOL_GPL(bcom_load_image); + +void +bcom_set_initiator(int task, int initiator) +{ + int i; + int num_descs; + u32 *desc; + int next_drd_has_initiator; + + bcom_set_tcr_initiator(task, initiator); + + /* Just setting tcr is apparently not enough due to some problem */ + /* with it. So we just go thru all the microcode and replace in */ + /* the DRD directly */ + + desc = bcom_task_desc(task); + next_drd_has_initiator = 1; + num_descs = bcom_task_num_descs(task); + + for (i=0; i<num_descs; i++, desc++) { + if (!bcom_desc_is_drd(*desc)) + continue; + if (next_drd_has_initiator) + if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS) + bcom_set_desc_initiator(desc, initiator); + next_drd_has_initiator = !bcom_drd_is_extended(*desc); + } +} +EXPORT_SYMBOL_GPL(bcom_set_initiator); + + +/* Public API */ + +void +bcom_enable(struct bcom_task *tsk) +{ + bcom_enable_task(tsk->tasknum); +} +EXPORT_SYMBOL_GPL(bcom_enable); + +void +bcom_disable(struct bcom_task *tsk) +{ + bcom_disable_task(tsk->tasknum); +} +EXPORT_SYMBOL_GPL(bcom_disable); + + +/* ======================================================================== */ +/* Engine init/cleanup */ +/* ======================================================================== */ + +/* Function Descriptor table */ +/* this will need to be updated if Freescale changes their task code FDT */ +static u32 fdt_ops[] = { + 0xa0045670, /* FDT[48] - load_acc() */ + 0x80045670, /* FDT[49] - unload_acc() */ + 0x21800000, /* FDT[50] - and() */ + 0x21e00000, /* FDT[51] - or() */ + 0x21500000, /* FDT[52] - xor() */ + 0x21400000, /* FDT[53] - andn() */ + 0x21500000, /* FDT[54] - not() */ + 0x20400000, /* FDT[55] - add() */ + 0x20500000, /* FDT[56] - sub() */ + 0x20800000, /* FDT[57] - lsh() */ + 0x20a00000, /* FDT[58] - rsh() */ + 0xc0170000, /* FDT[59] - crc8() */ + 0xc0145670, /* FDT[60] - crc16() */ + 0xc0345670, /* FDT[61] - crc32() */ + 0xa0076540, /* FDT[62] - endian32() */ + 0xa0000760, /* FDT[63] - endian16() */ +}; + + +static int __devinit +bcom_engine_init(void) +{ + int task; + phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa; + unsigned int tdt_size, ctx_size, var_size, fdt_size; + + /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */ + tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt); + ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE; + var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE); + fdt_size = BCOM_FDT_SIZE; + + bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa); + bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa); + bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa); + bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa); + + if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) { + printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n"); + + bcom_sram_free(bcom_eng->tdt); + bcom_sram_free(bcom_eng->ctx); + bcom_sram_free(bcom_eng->var); + bcom_sram_free(bcom_eng->fdt); + + return -ENOMEM; + } + + memset(bcom_eng->tdt, 0x00, tdt_size); + memset(bcom_eng->ctx, 0x00, ctx_size); + memset(bcom_eng->var, 0x00, var_size); + memset(bcom_eng->fdt, 0x00, fdt_size); + + /* Copy the FDT for the EU#3 */ + memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops)); + + /* Initialize Task base structure */ + for (task=0; task<BCOM_MAX_TASKS; task++) + { + out_be16(&bcom_eng->regs->tcr[task], 0); + out_8(&bcom_eng->regs->ipr[task], 0); + + bcom_eng->tdt[task].context = ctx_pa; + bcom_eng->tdt[task].var = var_pa; + bcom_eng->tdt[task].fdt = fdt_pa; + + var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE; + ctx_pa += BCOM_CTX_SIZE; + } + + out_be32(&bcom_eng->regs->taskBar, tdt_pa); + + /* Init 'always' initiator */ + out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS); + + /* Disable COMM Bus Prefetch, apparently it's not reliable yet */ + /* FIXME: This should be done on 5200 and not 5200B ... */ + out_be16(&bcom_eng->regs->PtdCntrl, in_be16(&bcom_eng->regs->PtdCntrl) | 1); + + /* Init lock */ + spin_lock_init(&bcom_eng->lock); + + return 0; +} + +static void +bcom_engine_cleanup(void) +{ + int task; + + /* Stop all tasks */ + for (task=0; task<BCOM_MAX_TASKS; task++) + { + out_be16(&bcom_eng->regs->tcr[task], 0); + out_8(&bcom_eng->regs->ipr[task], 0); + } + + out_be32(&bcom_eng->regs->taskBar, 0ul); + + /* Release the SRAM zones */ + bcom_sram_free(bcom_eng->tdt); + bcom_sram_free(bcom_eng->ctx); + bcom_sram_free(bcom_eng->var); + bcom_sram_free(bcom_eng->fdt); +} + + +/* ======================================================================== */ +/* OF platform driver */ +/* ======================================================================== */ + +static int __devinit +mpc52xx_bcom_probe(struct of_device *op, const struct of_device_id *match) +{ + struct device_node *ofn_bcom, *ofn_sram; + struct resource res_bcom; + + int rv; + + /* Inform user we're ok so far */ + printk(KERN_INFO "DMA: MPC52xx BestComm driver\n"); + + /* Get the bestcomm node */ + ofn_bcom = op->node; + of_node_get(ofn_bcom); + + /* Prepare SRAM */ + ofn_sram = of_find_compatible_node(NULL, "sram", "mpc5200-sram"); + if (!ofn_sram) { + printk(KERN_ERR DRIVER_NAME ": " + "No SRAM found in device tree\n"); + rv = -ENODEV; + goto error_ofput; + } + + rv = bcom_sram_init(ofn_sram, DRIVER_NAME); + + of_node_put(ofn_sram); + + if (rv) { + printk(KERN_ERR DRIVER_NAME ": " + "Error in SRAM init\n"); + goto error_ofput; + } + + /* Get a clean struct */ + bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL); + if (!bcom_eng) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't allocate state structure\n"); + rv = -ENOMEM; + goto error_sramclean; + } + + /* Save the node */ + bcom_eng->ofnode = ofn_bcom; + + /* Get, reserve & map io */ + if (of_address_to_resource(bcom_eng->ofnode, 0, &res_bcom)) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't get resource\n"); + rv = -EINVAL; + goto error_sramclean; + } + + if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma), + DRIVER_NAME)) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't request registers region\n"); + rv = -EBUSY; + goto error_sramclean; + } + + bcom_eng->regs_base = res_bcom.start; + bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma)); + if (!bcom_eng->regs) { + printk(KERN_ERR DRIVER_NAME ": " + "Can't map registers\n"); + rv = -ENOMEM; + goto error_release; + } + + /* Now, do the real init */ + rv = bcom_engine_init(); + if (rv) + goto error_unmap; + + /* Done ! */ + printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n", + bcom_eng->regs_base); + + return 0; + + /* Error path */ +error_unmap: + iounmap(bcom_eng->regs); +error_release: + release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma)); +error_sramclean: + bcom_sram_cleanup(); +error_ofput: + of_node_put(bcom_eng->ofnode); + + printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n"); + + return rv; +} + + +static int +mpc52xx_bcom_remove(struct of_device *op) +{ + /* Clean up the engine */ + bcom_engine_cleanup(); + + /* Cleanup SRAM */ + bcom_sram_cleanup(); + + /* Release regs */ + iounmap(bcom_eng->regs); + release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma)); + + /* Release the node */ + of_node_put(bcom_eng->ofnode); + + /* Release memory */ + kfree(bcom_eng); + bcom_eng = NULL; + + return 0; +} + + +#ifdef CONFIG_PM + +static int +mpc52xx_bcom_suspend(struct of_device *op, pm_message_t state) +{ + return 0; /* FIXME : What to do here ? */ +} + +static int +mpc52xx_bcom_resume(struct of_device *op) +{ + return 0; +} + +#endif + +static struct of_device_id mpc52xx_bcom_of_match[] = { + { + .type = "dma-controller", + .compatible = "mpc5200-bestcomm", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match); + + +static struct of_platform_driver mpc52xx_bcom_of_platform_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = mpc52xx_bcom_of_match, + .probe = mpc52xx_bcom_probe, + .remove = mpc52xx_bcom_remove, +#ifdef CONFIG_PM + .suspend = mpc52xx_bcom_suspend, + .resume = mpc52xx_bcom_resume, +#endif + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_bcom_init(void) +{ + return of_register_platform_driver(&mpc52xx_bcom_of_platform_driver); +} + +static void __exit +mpc52xx_bcom_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_bcom_of_platform_driver); +} + +/* If we're not a module, we must make sure everything is setup before */ +/* anyone tries to use us ... that's why we use subsys_initcall instead */ +/* of module_init. */ +subsys_initcall(mpc52xx_bcom_init); +module_exit(mpc52xx_bcom_exit); + +MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA"); +MODULE_AUTHOR("Sylvain Munaut <[EMAIL PROTECTED]>"); +MODULE_AUTHOR("Andrey Volkov <[EMAIL PROTECTED]>"); +MODULE_AUTHOR("Dale Farnsworth <[EMAIL PROTECTED]>"); +MODULE_LICENSE("GPL v2"); + diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h new file mode 100644 index 0000000..eac3eec --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h @@ -0,0 +1,136 @@ +/* + * Public header for the MPC52xx processor BestComm driver + * + * + * Copyright (C) 2006 Sylvain Munaut <[EMAIL PROTECTED]> + * Copyright (C) 2005 Varma Electronics Oy, + * ( by Andrey Volkov <[EMAIL PROTECTED]> ) + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth <[EMAIL PROTECTED]> ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_H__ +#define __BESTCOMM_H__ + +struct bcom_bd; /* defined later on ... */ + + +/* ======================================================================== */ +/* Generic task managment */ +/* ======================================================================== */ + +struct bcom_task { + unsigned int tasknum; + unsigned int flags; + int irq; + + struct bcom_bd *bd; + phys_addr_t bd_pa; + void **cookie; + unsigned short index; + unsigned short outdex; + unsigned int num_bd; + unsigned int bd_size; + + void* priv; +}; + +#define BCOM_FLAGS_NONE 0x00000000ul +#define BCOM_FLAGS_ENABLE_TASK (1ul << 0) + + +extern void bcom_enable(struct bcom_task *tsk); +extern void bcom_disable(struct bcom_task *tsk); + +static inline int +bcom_get_task_irq(struct bcom_task *tsk) { + return tsk->irq; +} + + +/* Debug dumps */ +extern void bcom_dump_status(void); +extern void bcom_dump_task(int task); +extern void bcom_dump_bdring(struct bcom_task *tsk); + + +/* ======================================================================== */ +/* BD based tasks helpers */ +/* ======================================================================== */ + +struct bcom_bd { + u32 status; + u32 data[1]; /* variable, but at least 1 */ +}; + +#define BCOM_BD_READY 0x40000000ul + +static inline int /* user shouldn't use this ! */ +_bcom_next_index(struct bcom_task *tsk) +{ + return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1; +} + +static inline int /* user shouldn't use this ! */ +_bcom_next_outdex(struct bcom_task *tsk) +{ + return ((tsk->outdex + 1) == tsk->num_bd) ? 0 : tsk->outdex + 1; +} + +static inline int +bcom_queue_empty(struct bcom_task *tsk) +{ + return tsk->index == tsk->outdex; +} + +static inline int +bcom_queue_full(struct bcom_task *tsk) +{ + return tsk->outdex == _bcom_next_index(tsk); +} + +static inline int +bcom_buffer_done(struct bcom_task *tsk) +{ + if (bcom_queue_empty(tsk)) + return 0; + return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY); +} + +static inline struct bcom_bd * +bcom_prepare_next_buffer(struct bcom_task *tsk) +{ + tsk->bd[tsk->index].status = 0; /* cleanup last status */ + return &tsk->bd[tsk->index]; +} + +static inline void +bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie) +{ + tsk->cookie[tsk->index] = cookie; + mb(); /* ensure the bd is really up-to-date */ + tsk->bd[tsk->index].status |= BCOM_BD_READY; + tsk->index = _bcom_next_index(tsk); + if (tsk->flags & BCOM_FLAGS_ENABLE_TASK) + bcom_enable(tsk); +} + +static inline void * +bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd) +{ + void *cookie = tsk->cookie[tsk->outdex]; + if (p_status) + *p_status = tsk->bd[tsk->outdex].status; + if (p_bd) + *p_bd = &tsk->bd[tsk->outdex]; + tsk->outdex = _bcom_next_outdex(tsk); + return cookie; +} + + +#endif /* __BESTCOMM_H__ */ + diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h new file mode 100644 index 0000000..6f33f0c --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h @@ -0,0 +1,325 @@ +/* + * Private header for the MPC52xx processor BestComm driver + * + * + * Copyright (C) 2006 Sylvain Munaut <[EMAIL PROTECTED]> + * Copyright (C) 2005 Varma Electronics Oy, + * ( by Andrey Volkov <[EMAIL PROTECTED]> ) + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * ( by Dale Farnsworth <[EMAIL PROTECTED]> ) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_PRIV_H__ +#define __BESTCOMM_PRIV_H__ + +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/mpc52xx.h> + +#include "sram.h" + + +/* ======================================================================== */ +/* Engine related stuff */ +/* ======================================================================== */ + +/* Zones sizes and needed alignments */ +#define BCOM_MAX_TASKS 16 +#define BCOM_MAX_VAR 24 +#define BCOM_MAX_INC 8 +#define BCOM_MAX_FDT 64 +#define BCOM_MAX_CTX 20 +#define BCOM_CTX_SIZE (BCOM_MAX_CTX * sizeof(u32)) +#define BCOM_CTX_ALIGN 0x100 +#define BCOM_VAR_SIZE (BCOM_MAX_VAR * sizeof(u32)) +#define BCOM_INC_SIZE (BCOM_MAX_INC * sizeof(u32)) +#define BCOM_VAR_ALIGN 0x80 +#define BCOM_FDT_SIZE (BCOM_MAX_FDT * sizeof(u32)) +#define BCOM_FDT_ALIGN 0x100 + +/* Task Descriptor Table Entry */ +struct bcom_tdt { + u32 start; + u32 stop; + u32 var; + u32 fdt; + u32 exec_status; /* used internally by BestComm engine */ + u32 mvtp; /* used internally by BestComm engine */ + u32 context; + u32 litbase; +}; + +/* This holds all info needed globaly to handle the engine */ +struct bcom_engine { + struct device_node *ofnode; + struct mpc52xx_sdma __iomem *regs; + phys_addr_t regs_base; + + struct bcom_tdt *tdt; + u32 *ctx; + u32 *var; + u32 *fdt; + + spinlock_t lock; +}; + +extern struct bcom_engine *bcom_eng; + + +/* ======================================================================== */ +/* Tasks related stuff */ +/* ======================================================================== */ + +/* Tasks image header */ +#define BCOM_TASK_MAGIC 0x4243544B /* 'BCTK' */ + +struct bcom_task_header { + u32 magic; + u8 desc_size; /* the size fields */ + u8 var_size; /* are given in number */ + u8 inc_size; /* of 32-bits words */ + u8 first_var; + u8 reserved[8]; +}; + +/* Descriptors stucture & co */ +#define BCOM_DESC_NOP 0x000001f8 +#define BCOM_LCD_MASK 0x80000000 +#define BCOM_DRD_EXTENDED 0x40000000 +#define BCOM_DRD_INITIATOR_SHIFT 21 + +/* Tasks pragma */ +#define BCOM_PRAGMA_BIT_RSV 7 /* reserved pragma bit */ +#define BCOM_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when possible, */ + /* 1=iter end */ +#define BCOM_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */ + /* task enable */ +#define BCOM_PRAGMA_BIT_PACK 4 /* pack data enable */ +#define BCOM_PRAGMA_BIT_INTEGER 3 /* data alignment */ + /* 0=frac(msb), 1=int(lsb) */ +#define BCOM_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */ +#define BCOM_PRAGMA_BIT_CW 1 /* write line buffer enable */ +#define BCOM_PRAGMA_BIT_RL 0 /* read line buffer enable */ + + /* Looks like XLB speculative read generates XLB errors when a buffer + * is at the end of the physical memory. i.e. when accessing the + * lasts words, the engine tries to prefetch the next but there is no + * next ... + */ +#define BCOM_STD_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \ + (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \ + (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \ + (0 << BCOM_PRAGMA_BIT_PACK) | \ + (0 << BCOM_PRAGMA_BIT_INTEGER) | \ + (0 << BCOM_PRAGMA_BIT_SPECREAD) | \ + (1 << BCOM_PRAGMA_BIT_CW) | \ + (1 << BCOM_PRAGMA_BIT_RL)) + +#define BCOM_PCI_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \ + (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \ + (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \ + (0 << BCOM_PRAGMA_BIT_PACK) | \ + (1 << BCOM_PRAGMA_BIT_INTEGER) | \ + (0 << BCOM_PRAGMA_BIT_SPECREAD) | \ + (1 << BCOM_PRAGMA_BIT_CW) | \ + (1 << BCOM_PRAGMA_BIT_RL)) + +#define BCOM_ATA_PRAGMA BCOM_STD_PRAGMA +#define BCOM_CRC16_DP_0_PRAGMA BCOM_STD_PRAGMA +#define BCOM_CRC16_DP_1_PRAGMA BCOM_STD_PRAGMA +#define BCOM_FEC_RX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_FEC_TX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_0_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_1_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_2_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_3_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_BD_0_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_DP_BD_1_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_RX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_TX_BD_PRAGMA BCOM_STD_PRAGMA +#define BCOM_GEN_LPC_PRAGMA BCOM_STD_PRAGMA +#define BCOM_PCI_RX_PRAGMA BCOM_PCI_PRAGMA +#define BCOM_PCI_TX_PRAGMA BCOM_PCI_PRAGMA + +/* Initiators number */ +#define BCOM_INITIATOR_ALWAYS 0 +#define BCOM_INITIATOR_SCTMR_0 1 +#define BCOM_INITIATOR_SCTMR_1 2 +#define BCOM_INITIATOR_FEC_RX 3 +#define BCOM_INITIATOR_FEC_TX 4 +#define BCOM_INITIATOR_ATA_RX 5 +#define BCOM_INITIATOR_ATA_TX 6 +#define BCOM_INITIATOR_SCPCI_RX 7 +#define BCOM_INITIATOR_SCPCI_TX 8 +#define BCOM_INITIATOR_PSC3_RX 9 +#define BCOM_INITIATOR_PSC3_TX 10 +#define BCOM_INITIATOR_PSC2_RX 11 +#define BCOM_INITIATOR_PSC2_TX 12 +#define BCOM_INITIATOR_PSC1_RX 13 +#define BCOM_INITIATOR_PSC1_TX 14 +#define BCOM_INITIATOR_SCTMR_2 15 +#define BCOM_INITIATOR_SCLPC 16 +#define BCOM_INITIATOR_PSC5_RX 17 +#define BCOM_INITIATOR_PSC5_TX 18 +#define BCOM_INITIATOR_PSC4_RX 19 +#define BCOM_INITIATOR_PSC4_TX 20 +#define BCOM_INITIATOR_I2C2_RX 21 +#define BCOM_INITIATOR_I2C2_TX 22 +#define BCOM_INITIATOR_I2C1_RX 23 +#define BCOM_INITIATOR_I2C1_TX 24 +#define BCOM_INITIATOR_PSC6_RX 25 +#define BCOM_INITIATOR_PSC6_TX 26 +#define BCOM_INITIATOR_IRDA_RX 25 +#define BCOM_INITIATOR_IRDA_TX 26 +#define BCOM_INITIATOR_SCTMR_3 27 +#define BCOM_INITIATOR_SCTMR_4 28 +#define BCOM_INITIATOR_SCTMR_5 29 +#define BCOM_INITIATOR_SCTMR_6 30 +#define BCOM_INITIATOR_SCTMR_7 31 + +/* Initiators priorities */ +#define BCOM_IPR_ALWAYS 7 +#define BCOM_IPR_SCTMR_0 2 +#define BCOM_IPR_SCTMR_1 2 +#define BCOM_IPR_FEC_RX 6 +#define BCOM_IPR_FEC_TX 5 +#define BCOM_IPR_ATA_RX 4 +#define BCOM_IPR_ATA_TX 3 +#define BCOM_IPR_SCPCI_RX 2 +#define BCOM_IPR_SCPCI_TX 2 +#define BCOM_IPR_PSC3_RX 2 +#define BCOM_IPR_PSC3_TX 2 +#define BCOM_IPR_PSC2_RX 2 +#define BCOM_IPR_PSC2_TX 2 +#define BCOM_IPR_PSC1_RX 2 +#define BCOM_IPR_PSC1_TX 2 +#define BCOM_IPR_SCTMR_2 2 +#define BCOM_IPR_SCLPC 2 +#define BCOM_IPR_PSC5_RX 2 +#define BCOM_IPR_PSC5_TX 2 +#define BCOM_IPR_PSC4_RX 2 +#define BCOM_IPR_PSC4_TX 2 +#define BCOM_IPR_I2C2_RX 2 +#define BCOM_IPR_I2C2_TX 2 +#define BCOM_IPR_I2C1_RX 2 +#define BCOM_IPR_I2C1_TX 2 +#define BCOM_IPR_PSC6_RX 2 +#define BCOM_IPR_PSC6_TX 2 +#define BCOM_IPR_IRDA_RX 2 +#define BCOM_IPR_IRDA_TX 2 +#define BCOM_IPR_SCTMR_3 2 +#define BCOM_IPR_SCTMR_4 2 +#define BCOM_IPR_SCTMR_5 2 +#define BCOM_IPR_SCTMR_6 2 +#define BCOM_IPR_SCTMR_7 2 + + +/* ======================================================================== */ +/* API */ +/* ======================================================================== */ + +extern struct bcom_task *bcom_task_alloc(int bd_count, int bd_size, int priv_size); +extern void bcom_task_release(struct bcom_task *tsk); + +extern int bcom_load_image(int task, u32 *task_image); +extern void bcom_set_initiator(int task, int initiator); + + +#define TASK_ENABLE 0x8000 + +static inline void +bcom_enable_task(int task) +{ + u16 reg; + reg = in_be16(&bcom_eng->regs->tcr[task]); + out_be16(&bcom_eng->regs->tcr[task], reg | TASK_ENABLE); +} + +static inline void +bcom_disable_task(int task) +{ + u16 reg = in_be16(&bcom_eng->regs->tcr[task]); + out_be16(&bcom_eng->regs->tcr[task], reg & ~TASK_ENABLE); +} + + +static inline u32 * +bcom_task_desc(int task) +{ + return bcom_sram_pa2va(bcom_eng->tdt[task].start); +} + +static inline int +bcom_task_num_descs(int task) +{ + return (bcom_eng->tdt[task].stop - bcom_eng->tdt[task].start)/sizeof(u32) + 1; +} + +static inline u32 * +bcom_task_var(int task) +{ + return bcom_sram_pa2va(bcom_eng->tdt[task].var); +} + +static inline u32 * +bcom_task_inc(int task) +{ + return &bcom_task_var(task)[BCOM_MAX_VAR]; +} + + +static inline int +bcom_drd_is_extended(u32 desc) +{ + return (desc) & BCOM_DRD_EXTENDED; +} + +static inline int +bcom_desc_is_drd(u32 desc) +{ + return !(desc & BCOM_LCD_MASK) && desc != BCOM_DESC_NOP; +} + +static inline int +bcom_desc_initiator(u32 desc) +{ + return (desc >> BCOM_DRD_INITIATOR_SHIFT) & 0x1f; +} + +static inline void +bcom_set_desc_initiator(u32 *desc, int initiator) +{ + *desc = (*desc & ~(0x1f << BCOM_DRD_INITIATOR_SHIFT)) | + ((initiator & 0x1f) << BCOM_DRD_INITIATOR_SHIFT); +} + + +static inline void +bcom_set_task_pragma(int task, int pragma) +{ + u32 *fdt = &bcom_eng->tdt[task].fdt; + *fdt = (*fdt & ~0xff) | pragma; +} + +static inline void +bcom_set_task_auto_start(int task, int next_task) +{ + u16 __iomem *tcr = &bcom_eng->regs->tcr[task]; + out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task); +} + +static inline void +bcom_set_tcr_initiator(int task, int initiator) +{ + u16 __iomem *tcr = &bcom_eng->regs->tcr[task]; + out_be16(tcr, (in_be16(tcr) & ~0x1f00) | ((initiator & 0x1f) << 8)); +} + + +#endif /* __BESTCOMM_PRIV_H__ */ + diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/arch/powerpc/sysdev/bestcomm/sram.c new file mode 100644 index 0000000..a5094d6 --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/sram.c @@ -0,0 +1,177 @@ +/* + * Simple memory allocator for on-board SRAM + * + * + * Maintainer : Sylvain Munaut <[EMAIL PROTECTED]> + * + * Copyright (C) 2005 Sylvain Munaut <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/ioport.h> + +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/prom.h> + +#include "sram.h" + + +/* Struct keeping our 'state' */ +struct bcom_sram *bcom_sram = NULL; +EXPORT_SYMBOL_GPL(bcom_sram); /* needed for inline functions */ + + +/* ======================================================================== */ +/* Public API */ +/* ======================================================================== */ +/* DO NOT USE in interrupts, if needed in irq handler, we should use the + _irqsave version of the spin_locks */ + +int bcom_sram_init(struct device_node *sram_node, char *owner) +{ + int rv; + const u32 *regaddr_p; + u64 regaddr64, size64; + unsigned int psize; + + /* Create our state struct */ + if (bcom_sram) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Already initialiwed !\n", owner); + return -EBUSY; + } + + bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL); + if (!bcom_sram) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Couldn't allocate internal state !\n", owner); + return -ENOMEM; + } + + /* Get address and size of the sram */ + regaddr_p = of_get_address(sram_node, 0, &size64, NULL); + if (!regaddr_p) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Invalid device node !\n", owner); + rv = -EINVAL; + goto error_free; + } + + regaddr64 = of_translate_address(sram_node, regaddr_p); + + bcom_sram->base_phys = (phys_addr_t) regaddr64; + bcom_sram->size = (unsigned int) size64; + + /* Request region */ + if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Couln't request region !\n", owner); + rv = -EBUSY; + goto error_free; + } + + /* Map SRAM */ + /* sram is not really __iomem */ + bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size); + + if (!bcom_sram->base_virt) { + printk(KERN_ERR "%s: bcom_sram_init: " + "Map error SRAM zone 0x%08lx (0x%0x)!\n", + owner, bcom_sram->base_phys, bcom_sram->size ); + rv = -ENOMEM; + goto error_release; + } + + /* Create an rheap (defaults to 32 bits word alignment) */ + bcom_sram->rh = rh_create(4); + + /* Attach the free zones */ +#if 0 + /* Currently disabled ... for future use only */ + reg_addr_p = of_get_property(sram_node, "available", &psize); +#else + regaddr_p = NULL; + psize = 0; +#endif + + if (!regaddr_p || !psize) { + /* Attach the whole zone */ + rh_attach_region(bcom_sram->rh, 0, bcom_sram->size); + } else { + /* Attach each zone independently */ + while (psize >= 2 * sizeof(u32)) { + phys_addr_t zbase = of_translate_address(sram_node, regaddr_p); + rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys, regaddr_p[1]); + regaddr_p += 2; + psize -= 2 * sizeof(u32); + } + } + + /* Init our spinlock */ + spin_lock_init(&bcom_sram->lock); + + return 0; + +error_release: + release_mem_region(bcom_sram->base_phys, bcom_sram->size); +error_free: + kfree(bcom_sram); + bcom_sram = NULL; + + return rv; +} +EXPORT_SYMBOL_GPL(bcom_sram_init); + +void bcom_sram_cleanup(void) +{ + /* Free resources */ + if (bcom_sram) { + rh_destroy(bcom_sram->rh); + iounmap((void __iomem *)bcom_sram->base_virt); + release_mem_region(bcom_sram->base_phys, bcom_sram->size); + kfree(bcom_sram); + bcom_sram = NULL; + } +} +EXPORT_SYMBOL_GPL(bcom_sram_cleanup); + +void* bcom_sram_alloc(int size, int align, phys_addr_t *phys) +{ + unsigned long offset; + + spin_lock(&bcom_sram->lock); + offset = rh_alloc_align(bcom_sram->rh, size, align, NULL); + spin_unlock(&bcom_sram->lock); + + if (IS_ERR_VALUE(offset)) + return NULL; + + *phys = bcom_sram->base_phys + offset; + return bcom_sram->base_virt + offset; +} +EXPORT_SYMBOL_GPL(bcom_sram_alloc); + +void bcom_sram_free(void *ptr) +{ + unsigned long offset; + + if (!ptr) + return; + + offset = ptr - bcom_sram->base_virt; + + spin_lock(&bcom_sram->lock); + rh_free(bcom_sram->rh, offset); + spin_unlock(&bcom_sram->lock); +} +EXPORT_SYMBOL_GPL(bcom_sram_free); + diff --git a/arch/powerpc/sysdev/bestcomm/sram.h b/arch/powerpc/sysdev/bestcomm/sram.h new file mode 100644 index 0000000..b6d6689 --- /dev/null +++ b/arch/powerpc/sysdev/bestcomm/sram.h @@ -0,0 +1,54 @@ +/* + * Handling of a sram zone for bestcomm + * + * + * Copyright (C) 2007 Sylvain Munaut <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __BESTCOMM_SRAM_H__ +#define __BESTCOMM_SRAM_H__ + +#include <asm/rheap.h> +#include <asm/mmu.h> +#include <linux/spinlock.h> + + +/* Structure used internally */ + /* The internals are here for the inline functions + * sake, certainly not for the user to mess with ! + */ +struct bcom_sram { + phys_addr_t base_phys; + void *base_virt; + unsigned int size; + rh_info_t *rh; + spinlock_t lock; +}; + +extern struct bcom_sram *bcom_sram; + + +/* Public API */ +extern int bcom_sram_init(struct device_node *sram_node, char *owner); +extern void bcom_sram_cleanup(void); + +extern void* bcom_sram_alloc(int size, int align, phys_addr_t *phys); +extern void bcom_sram_free(void *ptr); + +static inline phys_addr_t bcom_sram_va2pa(void *va) { + return bcom_sram->base_phys + + (unsigned long)(va - bcom_sram->base_virt); +} + +static inline void *bcom_sram_pa2va(phys_addr_t pa) { + return bcom_sram->base_virt + + (unsigned long)(pa - bcom_sram->base_phys); +} + + +#endif /* __BESTCOMM_SRAM_H__ */ + -- 1.5.3 _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-dev