Skip to content

Commit

Permalink
edac/85xx: Add PCIe error interrupt edac support
Browse files Browse the repository at this point in the history
Add pcie error interrupt edac support for mpc85xx, p3041, p4080, and
p5020. The mpc85xx uses the legacy interrupt report mechanism - the
error interrupts are reported directly to mpic. While the p3041/
p4080/p5020 attaches the most of error interrupts to interrupt zero. And
report error interrupts to mpic via interrupt 0.

This patch can handle both of them.

Signed-off-by: Chunhe Lan <Chunhe.Lan@freescale.com>
Link: http://lkml.kernel.org/r/1384712714-8826-3-git-send-email-morbidrsa@gmail.com
Cc: Doug Thompson <dougthompson@xmission.com>
Cc: Dave Jiang <dave.jiang@gmail.com>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
  • Loading branch information
Chunhe Lan authored and Borislav Petkov committed Nov 25, 2013
1 parent 6ce4eac commit c92132f
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 11 deletions.
98 changes: 87 additions & 11 deletions drivers/edac/mpc85xx_edac.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/*
* Freescale MPC85xx Memory Controller kenel module
*
* Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc.
*
* Author: Dave Jiang <djiang@mvista.com>
*
* 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
Expand Down Expand Up @@ -196,6 +198,42 @@ static void mpc85xx_pci_check(struct edac_pci_ctl_info *pci)
edac_pci_handle_npe(pci, pci->ctl_name);
}

static void mpc85xx_pcie_check(struct edac_pci_ctl_info *pci)
{
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
u32 err_detect;

err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);

pr_err("PCIe error(s) detected\n");
pr_err("PCIe ERR_DR register: 0x%08x\n", err_detect);
pr_err("PCIe ERR_CAP_STAT register: 0x%08x\n",
in_be32(pdata->pci_vbase + MPC85XX_PCI_GAS_TIMR));
pr_err("PCIe ERR_CAP_R0 register: 0x%08x\n",
in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R0));
pr_err("PCIe ERR_CAP_R1 register: 0x%08x\n",
in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R1));
pr_err("PCIe ERR_CAP_R2 register: 0x%08x\n",
in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R2));
pr_err("PCIe ERR_CAP_R3 register: 0x%08x\n",
in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R3));

/* clear error bits */
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
}

static int mpc85xx_pcie_find_capability(struct device_node *np)
{
struct pci_controller *hose;

if (!np)
return -EINVAL;

hose = pci_find_hose_for_OF_device(np);

return early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP);
}

static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
{
struct edac_pci_ctl_info *pci = dev_id;
Expand All @@ -207,7 +245,10 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
if (!err_detect)
return IRQ_NONE;

mpc85xx_pci_check(pci);
if (pdata->is_pcie)
mpc85xx_pcie_check(pci);
else
mpc85xx_pci_check(pci);

return IRQ_HANDLED;
}
Expand Down Expand Up @@ -239,14 +280,22 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
pdata = pci->pvt_info;
pdata->name = "mpc85xx_pci_err";
pdata->irq = NO_IRQ;

if (mpc85xx_pcie_find_capability(op->dev.of_node) > 0)
pdata->is_pcie = true;

dev_set_drvdata(&op->dev, pci);
pci->dev = &op->dev;
pci->mod_name = EDAC_MOD_STR;
pci->ctl_name = pdata->name;
pci->dev_name = dev_name(&op->dev);

if (edac_op_state == EDAC_OPSTATE_POLL)
pci->edac_check = mpc85xx_pci_check;
if (edac_op_state == EDAC_OPSTATE_POLL) {
if (pdata->is_pcie)
pci->edac_check = mpc85xx_pcie_check;
else
pci->edac_check = mpc85xx_pci_check;
}

pdata->edac_idx = edac_pci_idx++;

Expand Down Expand Up @@ -275,16 +324,26 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
goto err;
}

orig_pci_err_cap_dr =
in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR);
if (pdata->is_pcie) {
orig_pci_err_cap_dr =
in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR);
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, ~0);
orig_pci_err_en =
in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, 0);
} else {
orig_pci_err_cap_dr =
in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR);

/* PCI master abort is expected during config cycles */
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40);
/* PCI master abort is expected during config cycles */
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40);

orig_pci_err_en = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);
orig_pci_err_en =
in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);

/* disable master abort reporting */
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40);
/* disable master abort reporting */
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40);
}

/* clear error bits */
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);
Expand All @@ -297,7 +356,8 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
if (edac_op_state == EDAC_OPSTATE_INT) {
pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0);
res = devm_request_irq(&op->dev, pdata->irq,
mpc85xx_pci_isr, IRQF_DISABLED,
mpc85xx_pci_isr,
IRQF_DISABLED | IRQF_SHARED,
"[EDAC] PCI err", pci);
if (res < 0) {
printk(KERN_ERR
Expand All @@ -312,6 +372,22 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
pdata->irq);
}

if (pdata->is_pcie) {
/*
* Enable all PCIe error interrupt & error detect except invalid
* PEX_CONFIG_ADDR/PEX_CONFIG_DATA access interrupt generation
* enable bit and invalid PEX_CONFIG_ADDR/PEX_CONFIG_DATA access
* detection enable bit. Because PCIe bus code to initialize and
* configure these PCIe devices on booting will use some invalid
* PEX_CONFIG_ADDR/PEX_CONFIG_DATA, edac driver prints the much
* notice information. So disable this detect to fix ugly print.
*/
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0
& ~PEX_ERR_ICCAIE_EN_BIT);
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, 0
| PEX_ERR_ICCAD_DISR_BIT);
}

devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
edac_dbg(3, "success\n");
printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n");
Expand Down
7 changes: 7 additions & 0 deletions drivers/edac/mpc85xx_edac.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,19 @@
#define MPC85XX_PCI_ERR_DR 0x0000
#define MPC85XX_PCI_ERR_CAP_DR 0x0004
#define MPC85XX_PCI_ERR_EN 0x0008
#define PEX_ERR_ICCAIE_EN_BIT 0x00020000
#define MPC85XX_PCI_ERR_ATTRIB 0x000c
#define MPC85XX_PCI_ERR_ADDR 0x0010
#define PEX_ERR_ICCAD_DISR_BIT 0x00020000
#define MPC85XX_PCI_ERR_EXT_ADDR 0x0014
#define MPC85XX_PCI_ERR_DL 0x0018
#define MPC85XX_PCI_ERR_DH 0x001c
#define MPC85XX_PCI_GAS_TIMR 0x0020
#define MPC85XX_PCI_PCIX_TIMR 0x0024
#define MPC85XX_PCIE_ERR_CAP_R0 0x0028
#define MPC85XX_PCIE_ERR_CAP_R1 0x002c
#define MPC85XX_PCIE_ERR_CAP_R2 0x0030
#define MPC85XX_PCIE_ERR_CAP_R3 0x0034

struct mpc85xx_mc_pdata {
char *name;
Expand All @@ -158,6 +164,7 @@ struct mpc85xx_l2_pdata {

struct mpc85xx_pci_pdata {
char *name;
bool is_pcie;
int edac_idx;
void __iomem *pci_vbase;
int irq;
Expand Down

0 comments on commit c92132f

Please sign in to comment.