kernel-fxtec-pro1x/arch/powerpc/sysdev/bestcomm/bestcomm.c
Anatolij Gustschin 7ea6fd7e2d [POWERPC] Fix Oops with TQM5200 on TQM5200
The "bestcomm-core" driver defines its of_match table as follows

static struct of_device_id mpc52xx_bcom_of_match[] = {
	{ .type = "dma-controller", .compatible = "fsl,mpc5200-bestcomm", },
	{ .type = "dma-controller", .compatible = "mpc5200-bestcomm", },
	{},
};

so while registering the driver, the driver's probe function won't be
called, because the device tree node doesn't have a device_type
property.  Thus the driver's bcom_engine structure won't be allocated.
Referencing this structure later causes observed Oops.

Checking bcom_eng pointer for NULL before referencing data pointed
by it prevents oopsing, but fec driver still doesn't work (because
of the lost bestcomm match and resulted task allocation failure).
Actually the compatible property exists and should match and so
the fec driver should work.

This removes .type = "dma-controller" from the bestcomm driver's
mpc52xx_bcom_of_match table to solve the problem.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2008-03-24 17:55:51 +11:00

539 lines
13 KiB
C

/*
* Driver for MPC52xx processor BestComm peripheral controller
*
*
* Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
* Copyright (C) 2005 Varma Electronics Oy,
* ( by Andrey Volkov <avolkov@varma-el.com> )
* Copyright (C) 2003-2004 MontaVista, Software, Inc.
* ( by Dale Farnsworth <dfarnsworth@mvista.com> )
*
* 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 <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mpc52xx.h>
#include "sram.h"
#include "bestcomm_priv.h"
#include "bestcomm.h"
#define DRIVER_NAME "bestcomm-core"
/* MPC5200 device tree match tables */
static struct of_device_id mpc52xx_sram_ids[] __devinitdata = {
{ .compatible = "fsl,mpc5200-sram", },
{ .compatible = "mpc5200-sram", },
{}
};
struct bcom_engine *bcom_eng = NULL;
EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */
/* ======================================================================== */
/* Public and private API */
/* ======================================================================== */
/* 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;
/* Don't try to do anything if bestcomm init failed */
if (!bcom_eng)
return NULL;
/* 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_free(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_free);
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;
u16 regval;
/* 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 on the original 5200; it's broken */
if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
regval = in_be16(&bcom_eng->regs->PtdCntrl);
out_be16(&bcom_eng->regs->PtdCntrl, regval | 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_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 */
of_node_get(op->node);
/* Prepare SRAM */
ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids);
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 = op->node;
/* Get, reserve & map io */
if (of_address_to_resource(op->node, 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:
kfree(bcom_eng);
bcom_sram_cleanup();
error_ofput:
of_node_put(op->node);
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;
}
static struct of_device_id mpc52xx_bcom_of_match[] = {
{ .compatible = "fsl,mpc5200-bestcomm", },
{ .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,
.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 <tnt@246tNt.com>");
MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
MODULE_LICENSE("GPL v2");