/*
* Marvell MV64x60 Memory Controller kernel module for PPC platforms
*
* Author: Dave Jiang <djiang@mvista.com>
*
* 2006-2007 (c) MontaVista Software, Inc. 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/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/edac.h>
#include <linux/gfp.h>
#include "edac_module.h"
#include "mv64x60_edac.h"
static const char *mv64x60_ctl_name = "MV64x60";
static int edac_dev_idx;
static int edac_pci_idx;
static int edac_mc_idx;
/*********************** PCI err device **********************************/
#ifdef CONFIG_PCI
static void mv64x60_pci_check(struct edac_pci_ctl_info *pci)
{
struct mv64x60_pci_pdata *pdata = pci->pvt_info;
u32 cause;
cause = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE);
if (!cause)
return;
printk(KERN_ERR "Error in PCI %d Interface\n", pdata->pci_hose);
printk(KERN_ERR "Cause register: 0x%08x\n", cause);
printk(KERN_ERR "Address Low: 0x%08x\n",
in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_LO));
printk(KERN_ERR "Address High: 0x%08x\n",
in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_HI));
printk(KERN_ERR "Attribute: 0x%08x\n",
in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ATTR));
printk(KERN_ERR "Command: 0x%08x\n",
in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CMD));
out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, ~cause);
if (cause & MV64X60_PCI_PE_MASK)
edac_pci_handle_pe(pci, pci->ctl_name);
if (!(cause & MV64X60_PCI_PE_MASK))
edac_pci_handle_npe(pci, pci->ctl_name);
}
static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id)
{
struct edac_pci_ctl_info *pci = dev_id;
struct mv64x60_pci_pdata *pdata = pci->pvt_info;
u32 val;
val = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE);
if (!val)
return IRQ_NONE;
mv64x60_pci_check(pci);
return IRQ_HANDLED;
}
/*
* Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of
* errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as
* well. IOW, don't set bit 0.
*/
/* Erratum FEr PCI-#16: clear bit 0 of PCI SERRn Mask reg. */
static int __init mv64x60_pci_fixup(struct platform_device *pdev)
{
struct resource *r;
void __iomem *pci_serr;
r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!r) {
printk(KERN_ERR "%s: Unable to get resource for "
"PCI err regs\n", __func__);
return -ENOENT;
}
pci_serr = ioremap(r->start, resource_size(r));
if (!pci_serr)
return -ENOMEM;
out_le32(pci_serr, in_le32(pci_serr) & ~0x1);
iounmap(pci_serr);
return 0;
}
static int mv64x60_pci_err_probe(struct platform_device *pdev)
{
struct edac_pci_ctl_info *pci;
struct mv64x60_pci_pdata *pdata;
struct resource *r;
int res = 0;
if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL))
return -ENOMEM;
pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err");
if (!pci)
return -ENOMEM;
pdata = pci->pvt_info;
pdata->pci_hose = pdev->id;
pdata->name = "mpc85xx_pci_err";
platform_set_drvdata(pdev, pci);
pci->dev = &pdev->dev;
pci->dev_name = dev_name(&pdev->dev);
pci->mod_name = EDAC_MOD_STR;
pci->ctl_name = pdata->name;
if (edac_op_state == EDAC_OPSTATE_POLL)
pci->edac_check = mv64x60_pci_check;
pdata->edac_idx = edac_pci_idx++;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
printk(KERN_ERR "%s: Unable to get resource for "
"PCI err regs\n", __func__);
res = -ENOENT;
goto err;
}
if (!devm_request_mem_region(&pdev->dev,
r->start,
resource_size(r),
pdata->name)) {
printk(KERN_ERR "%s: Error while requesting mem region\n",
__func__);
res = -EBUSY;
goto err;
}
pdata->pci_vbase = devm_ioremap(&pdev->dev,
r->start,
resource_size(r));
if (!pdata->pci_vbase) {
printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__);
res = -ENOMEM;
goto err;
}
res = mv64x60_pci_fixup(pdev);
if (res < 0) {
printk(KERN_ERR "%s: PCI fixup failed\n", __func__);
goto err;
}
out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0);
out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK,