14f4aeeabc061826376c9a72b4714d062664999eaDave Jiang/*
24f4aeeabc061826376c9a72b4714d062664999eaDave Jiang * Marvell MV64x60 Memory Controller kernel module for PPC platforms
34f4aeeabc061826376c9a72b4714d062664999eaDave Jiang *
44f4aeeabc061826376c9a72b4714d062664999eaDave Jiang * Author: Dave Jiang <djiang@mvista.com>
54f4aeeabc061826376c9a72b4714d062664999eaDave Jiang *
64f4aeeabc061826376c9a72b4714d062664999eaDave Jiang * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
74f4aeeabc061826376c9a72b4714d062664999eaDave Jiang * the terms of the GNU General Public License version 2. This program
84f4aeeabc061826376c9a72b4714d062664999eaDave Jiang * is licensed "as is" without any warranty of any kind, whether express
94f4aeeabc061826376c9a72b4714d062664999eaDave Jiang * or implied.
104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang *
114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang */
124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include <linux/module.h>
144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include <linux/init.h>
154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include <linux/interrupt.h>
164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include <linux/io.h>
174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include <linux/edac.h>
185a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include "edac_core.h"
214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include "edac_module.h"
224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#include "mv64x60_edac.h"
234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
244f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic const char *mv64x60_ctl_name = "MV64x60";
254f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int edac_dev_idx;
264f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int edac_pci_idx;
274f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int edac_mc_idx;
284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang/*********************** PCI err device **********************************/
304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#ifdef CONFIG_PCI
314f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void mv64x60_pci_check(struct edac_pci_ctl_info *pci)
324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_pci_pdata *pdata = pci->pvt_info;
344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 cause;
354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	cause = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE);
374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!cause)
384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return;
394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Error in PCI %d Interface\n", pdata->pci_hose);
414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Cause register: 0x%08x\n", cause);
424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Address Low: 0x%08x\n",
434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_LO));
444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Address High: 0x%08x\n",
454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_HI));
464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Attribute: 0x%08x\n",
474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ATTR));
484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Command: 0x%08x\n",
494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CMD));
504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, ~cause);
514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (cause & MV64X60_PCI_PE_MASK)
534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_pci_handle_pe(pci, pci->ctl_name);
544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!(cause & MV64X60_PCI_PE_MASK))
564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_pci_handle_npe(pci, pci->ctl_name);
574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
594f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic irqreturn_t mv64x60_pci_isr(int irq, void *dev_id)
604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_pci_ctl_info *pci = dev_id;
624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_pci_pdata *pdata = pci->pvt_info;
634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 val;
644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
654f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	val = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE);
664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!val)
674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return IRQ_NONE;
684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mv64x60_pci_check(pci);
704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return IRQ_HANDLED;
724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
74fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang/*
75fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of
76fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as
77fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang * well.  IOW, don't set bit 0.
78fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang */
79fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
80fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang/* Erratum FEr PCI-#16: clear bit 0 of PCI SERRn Mask reg. */
81fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiangstatic int __init mv64x60_pci_fixup(struct platform_device *pdev)
82fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang{
83fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	struct resource *r;
84fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	void __iomem *pci_serr;
85fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
86fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
87fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	if (!r) {
88fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang		printk(KERN_ERR "%s: Unable to get resource for "
89fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang		       "PCI err regs\n", __func__);
90fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang		return -ENOENT;
91fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	}
92fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
9330a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall	pci_serr = ioremap(r->start, resource_size(r));
94fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	if (!pci_serr)
95fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang		return -ENOMEM;
96fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
97fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	out_le32(pci_serr, in_le32(pci_serr) & ~0x1);
98fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	iounmap(pci_serr);
99fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
100fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	return 0;
101fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang}
102fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
1034f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
1044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
1054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_pci_ctl_info *pci;
1064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_pci_pdata *pdata;
1074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct resource *r;
1084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	int res = 0;
1094f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL))
1114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
1124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err");
1144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!pci)
1154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
1164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata = pci->pvt_info;
1184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->pci_hose = pdev->id;
1204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->name = "mpc85xx_pci_err";
1214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->irq = NO_IRQ;
1224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_set_drvdata(pdev, pci);
1234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pci->dev = &pdev->dev;
124031d5518591006efd13a33a86909b9477b22917bKay Sievers	pci->dev_name = dev_name(&pdev->dev);
1254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pci->mod_name = EDAC_MOD_STR;
1264f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pci->ctl_name = pdata->name;
1274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_POLL)
1294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		pci->edac_check = mv64x60_pci_check;
1304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->edac_idx = edac_pci_idx++;
1324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!r) {
1354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to get resource for "
1364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       "PCI err regs\n", __func__);
1374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOENT;
1384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
1394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
1404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devm_request_mem_region(&pdev->dev,
1424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     r->start,
14330a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall				     resource_size(r),
1444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     pdata->name)) {
1454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Error while requesting mem region\n",
1464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       __func__);
1474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -EBUSY;
1484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
1494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
1504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->pci_vbase = devm_ioremap(&pdev->dev,
1524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					r->start,
15330a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall					resource_size(r));
1544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!pdata->pci_vbase) {
1554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__);
1564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOMEM;
1574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
1584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
1594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
160fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	res = mv64x60_pci_fixup(pdev);
161fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	if (res < 0) {
162fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang		printk(KERN_ERR "%s: PCI fixup failed\n", __func__);
163fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang		goto err;
164fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang	}
165fcb19171d196172a4f57e056f7a60e6d1e2e8c85Dave Jiang
1664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0);
1674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0);
1684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK,
1694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		 MV64X60_PCIx_ERR_MASK_VAL);
1704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
1724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		debugf3("%s(): failed edac_pci_add_device()\n", __func__);
1734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
1744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
1754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_INT) {
1774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		pdata->irq = platform_get_irq(pdev, 0);
1784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = devm_request_irq(&pdev->dev,
1794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       pdata->irq,
1804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       mv64x60_pci_isr,
1814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       IRQF_DISABLED,
1824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       "[EDAC] PCI err",
1834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       pci);
1844f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		if (res < 0) {
1854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			printk(KERN_ERR "%s: Unable to request irq %d for "
1864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			       "MV64x60 PCI ERR\n", __func__, pdata->irq);
1874f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			res = -ENODEV;
1884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			goto err2;
1894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		}
1904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n",
1914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       pdata->irq);
1924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
1934f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1944f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_remove_group(&pdev->dev, mv64x60_pci_err_probe);
1954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* get this far and it's successful */
1974f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf3("%s(): success\n", __func__);
1984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
1994f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
2004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2014f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr2:
2024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_pci_del_device(&pdev->dev);
2034f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr:
2044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_pci_free_ctl_info(pci);
2054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_release_group(&pdev->dev, mv64x60_pci_err_probe);
2064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return res;
2074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
2084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2094f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int mv64x60_pci_err_remove(struct platform_device *pdev)
2104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
2114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
2124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf0("%s()\n", __func__);
2144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_pci_del_device(&pdev->dev);
2164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_pci_free_ctl_info(pci);
2184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
2204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
2214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2224f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic struct platform_driver mv64x60_pci_err_driver = {
2234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.probe = mv64x60_pci_err_probe,
2244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.remove = __devexit_p(mv64x60_pci_err_remove),
2254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.driver = {
2264f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		   .name = "mv64x60_pci_err",
2274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
2284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang};
2294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#endif /* CONFIG_PCI */
2314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang/*********************** SRAM err device **********************************/
2334f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void mv64x60_sram_check(struct edac_device_ctl_info *edac_dev)
2344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
2354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info;
2364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 cause;
2374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE);
2394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!cause)
2404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return;
2414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Error in internal SRAM\n");
2434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Cause register: 0x%08x\n", cause);
2444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Address Low: 0x%08x\n",
2454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_LO));
2464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Address High: 0x%08x\n",
2474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_HI));
2484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Data Low: 0x%08x\n",
2494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_LO));
2504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Data High: 0x%08x\n",
2514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_HI));
2524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Parity: 0x%08x\n",
2534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_PARITY));
2544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0);
2554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
2574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
2584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2594f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic irqreturn_t mv64x60_sram_isr(int irq, void *dev_id)
2604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
2614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_device_ctl_info *edac_dev = dev_id;
2624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info;
2634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 cause;
2644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2654f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE);
2664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!cause)
2674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return IRQ_NONE;
2684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mv64x60_sram_check(edac_dev);
2704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return IRQ_HANDLED;
2724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
2734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2744f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int __devinit mv64x60_sram_err_probe(struct platform_device *pdev)
2754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
2764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_device_ctl_info *edac_dev;
2774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_sram_pdata *pdata;
2784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct resource *r;
2794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	int res = 0;
2804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devres_open_group(&pdev->dev, mv64x60_sram_err_probe, GFP_KERNEL))
2824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
2834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2844f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata),
2854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					      "sram", 1, NULL, 0, 0, NULL, 0,
2864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					      edac_dev_idx);
2874f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!edac_dev) {
2884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		devres_release_group(&pdev->dev, mv64x60_sram_err_probe);
2894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
2904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
2914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata = edac_dev->pvt_info;
2934f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->name = "mv64x60_sram_err";
2944f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->irq = NO_IRQ;
2954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev->dev = &pdev->dev;
2964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_set_drvdata(pdev, edac_dev);
297031d5518591006efd13a33a86909b9477b22917bKay Sievers	edac_dev->dev_name = dev_name(&pdev->dev);
2984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
2994f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!r) {
3014f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to get resource for "
3024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       "SRAM err regs\n", __func__);
3034f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOENT;
3044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
3054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
3064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devm_request_mem_region(&pdev->dev,
3084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     r->start,
30930a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall				     resource_size(r),
3104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     pdata->name)) {
3114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Error while request mem region\n",
3124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       __func__);
3134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -EBUSY;
3144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
3154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
3164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->sram_vbase = devm_ioremap(&pdev->dev,
3184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					 r->start,
31930a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall					 resource_size(r));
3204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!pdata->sram_vbase) {
3214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to setup SRAM err regs\n",
3224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       __func__);
3234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOMEM;
3244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
3254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
3264f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* setup SRAM err registers */
3284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0);
3294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev->mod_name = EDAC_MOD_STR;
3314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev->ctl_name = pdata->name;
3324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_POLL)
3344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_dev->edac_check = mv64x60_sram_check;
3354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->edac_idx = edac_dev_idx++;
3374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_device_add_device(edac_dev) > 0) {
3394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		debugf3("%s(): failed edac_device_add_device()\n", __func__);
3404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
3414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
3424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_INT) {
3444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		pdata->irq = platform_get_irq(pdev, 0);
3454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = devm_request_irq(&pdev->dev,
3464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       pdata->irq,
3474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       mv64x60_sram_isr,
3484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       IRQF_DISABLED,
3494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       "[EDAC] SRAM err",
3504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       edac_dev);
3514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		if (res < 0) {
3524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			printk(KERN_ERR
3534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			       "%s: Unable to request irq %d for "
3544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			       "MV64x60 SRAM ERR\n", __func__, pdata->irq);
3554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			res = -ENODEV;
3564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			goto err2;
3574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		}
3584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for SRAM Err\n",
3604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       pdata->irq);
3614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
3624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_remove_group(&pdev->dev, mv64x60_sram_err_probe);
3644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3654f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* get this far and it's successful */
3664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf3("%s(): success\n", __func__);
3674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
3694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3704f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr2:
3714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_del_device(&pdev->dev);
3724f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr:
3734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_release_group(&pdev->dev, mv64x60_sram_err_probe);
3744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_free_ctl_info(edac_dev);
3754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return res;
3764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
3774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3784f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int mv64x60_sram_err_remove(struct platform_device *pdev)
3794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
3804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
3814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf0("%s()\n", __func__);
3834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3844f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_del_device(&pdev->dev);
3854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_free_ctl_info(edac_dev);
3864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3874f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
3884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
3894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3904f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic struct platform_driver mv64x60_sram_err_driver = {
3914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.probe = mv64x60_sram_err_probe,
3924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.remove = mv64x60_sram_err_remove,
3934f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.driver = {
3944f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		   .name = "mv64x60_sram_err",
3954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
3964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang};
3974f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
3984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang/*********************** CPU err device **********************************/
3994f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void mv64x60_cpu_check(struct edac_device_ctl_info *edac_dev)
4004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
4014f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info;
4024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 cause;
4034f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) &
4054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	    MV64x60_CPU_CAUSE_MASK;
4064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!cause)
4074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return;
4084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4094f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Error on CPU interface\n");
4104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Cause register: 0x%08x\n", cause);
4114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Address Low: 0x%08x\n",
4124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_LO));
4134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Address High: 0x%08x\n",
4144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_HI));
4154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Data Low: 0x%08x\n",
4164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_LO));
4174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Data High: 0x%08x\n",
4184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_HI));
4194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_ERR "Parity: 0x%08x\n",
4204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	       in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_PARITY));
4214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0);
4224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
4244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
4254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4264f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic irqreturn_t mv64x60_cpu_isr(int irq, void *dev_id)
4274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
4284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_device_ctl_info *edac_dev = dev_id;
4294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info;
4304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 cause;
4314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) &
4334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	    MV64x60_CPU_CAUSE_MASK;
4344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!cause)
4354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return IRQ_NONE;
4364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mv64x60_cpu_check(edac_dev);
4384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return IRQ_HANDLED;
4404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
4414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4424f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev)
4434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
4444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_device_ctl_info *edac_dev;
4454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct resource *r;
4464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_cpu_pdata *pdata;
4474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	int res = 0;
4484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devres_open_group(&pdev->dev, mv64x60_cpu_err_probe, GFP_KERNEL))
4504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
4514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata),
4534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					      "cpu", 1, NULL, 0, 0, NULL, 0,
4544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					      edac_dev_idx);
4554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!edac_dev) {
4564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		devres_release_group(&pdev->dev, mv64x60_cpu_err_probe);
4574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
4584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
4594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata = edac_dev->pvt_info;
4614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->name = "mv64x60_cpu_err";
4624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->irq = NO_IRQ;
4634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev->dev = &pdev->dev;
4644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_set_drvdata(pdev, edac_dev);
465031d5518591006efd13a33a86909b9477b22917bKay Sievers	edac_dev->dev_name = dev_name(&pdev->dev);
4664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!r) {
4694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to get resource for "
4704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       "CPU err regs\n", __func__);
4714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOENT;
4724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
4734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
4744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devm_request_mem_region(&pdev->dev,
4764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     r->start,
47730a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall				     resource_size(r),
4784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     pdata->name)) {
4794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Error while requesting mem region\n",
4804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       __func__);
4814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -EBUSY;
4824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
4834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
4844f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev,
4864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					   r->start,
48730a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall					   resource_size(r));
4884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!pdata->cpu_vbase[0]) {
4894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__);
4904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOMEM;
4914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
4924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
4934f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
4944f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
4954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!r) {
4964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to get resource for "
4974f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       "CPU err regs\n", __func__);
4984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOENT;
4994f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
5004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
5014f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devm_request_mem_region(&pdev->dev,
5034f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     r->start,
50430a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall				     resource_size(r),
5054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     pdata->name)) {
5064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Error while requesting mem region\n",
5074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       __func__);
5084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -EBUSY;
5094f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
5104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
5114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev,
5134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang					   r->start,
51430a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall					   resource_size(r));
5154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!pdata->cpu_vbase[1]) {
5164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__);
5174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOMEM;
5184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
5194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
5204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* setup CPU err registers */
5224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0);
5234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0);
5244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0x000000ff);
5254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5264f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev->mod_name = EDAC_MOD_STR;
5274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_dev->ctl_name = pdata->name;
5284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_POLL)
5294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_dev->edac_check = mv64x60_cpu_check;
5304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->edac_idx = edac_dev_idx++;
5324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_device_add_device(edac_dev) > 0) {
5344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		debugf3("%s(): failed edac_device_add_device()\n", __func__);
5354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
5364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
5374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_INT) {
5394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		pdata->irq = platform_get_irq(pdev, 0);
5404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = devm_request_irq(&pdev->dev,
5414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       pdata->irq,
5424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       mv64x60_cpu_isr,
5434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       IRQF_DISABLED,
5444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       "[EDAC] CPU err",
5454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       edac_dev);
5464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		if (res < 0) {
5474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			printk(KERN_ERR
5484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			       "%s: Unable to request irq %d for MV64x60 "
5494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			       "CPU ERR\n", __func__, pdata->irq);
5504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			res = -ENODEV;
5514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			goto err2;
5524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		}
5534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_INFO EDAC_MOD_STR
5554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       " acquired irq %d for CPU Err\n", pdata->irq);
5564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
5574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe);
5594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* get this far and it's successful */
5614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf3("%s(): success\n", __func__);
5624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
5644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5654f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr2:
5664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_del_device(&pdev->dev);
5674f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr:
5684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_release_group(&pdev->dev, mv64x60_cpu_err_probe);
5694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_free_ctl_info(edac_dev);
5704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return res;
5714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
5724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5734f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int mv64x60_cpu_err_remove(struct platform_device *pdev)
5744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
5754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
5764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf0("%s()\n", __func__);
5784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_del_device(&pdev->dev);
5804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_device_free_ctl_info(edac_dev);
5814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
5824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
5834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5844f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic struct platform_driver mv64x60_cpu_err_driver = {
5854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.probe = mv64x60_cpu_err_probe,
5864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.remove = mv64x60_cpu_err_remove,
5874f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.driver = {
5884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		   .name = "mv64x60_cpu_err",
5894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
5904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang};
5914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang/*********************** DRAM err device **********************************/
5934f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
5944f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void mv64x60_mc_check(struct mem_ctl_info *mci)
5954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
5964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_mc_pdata *pdata = mci->pvt_info;
5974f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 reg;
5984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 err_addr;
5994f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 sdram_ecc;
6004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 comp_ecc;
6014f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 syndrome;
6024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6034f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR);
6044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!reg)
6054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return;
6064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	err_addr = reg & ~0x3;
6084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	sdram_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_RCVD);
6094f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	comp_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CALC);
6104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	syndrome = sdram_ecc ^ comp_ecc;
6114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
6134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!(reg & 0x1))
6144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_mc_handle_ce(mci, err_addr >> PAGE_SHIFT,
6154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				  err_addr & PAGE_MASK, syndrome, 0, 0,
6164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				  mci->ctl_name);
6174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	else	/* 2 bit error, UE */
6184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_mc_handle_ue(mci, err_addr >> PAGE_SHIFT,
6194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				  err_addr & PAGE_MASK, 0, mci->ctl_name);
6204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* clear the error */
6224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
6234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
6244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6254f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic irqreturn_t mv64x60_mc_isr(int irq, void *dev_id)
6264f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
6274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mem_ctl_info *mci = dev_id;
6284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_mc_pdata *pdata = mci->pvt_info;
6294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 reg;
6304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR);
6324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!reg)
6334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return IRQ_NONE;
6344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* writing 0's to the ECC err addr in check function clears irq */
6364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mv64x60_mc_check(mci);
6374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return IRQ_HANDLED;
6394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
6404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6414f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void get_total_mem(struct mv64x60_mc_pdata *pdata)
6424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
6434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct device_node *np = NULL;
6444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	const unsigned int *reg;
6454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	np = of_find_node_by_type(NULL, "memory");
6474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!np)
6484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return;
6494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
650596d3941035d4d4b484c820f10f57fd4816c6615Dave Jiang	reg = of_get_property(np, "reg", NULL);
6514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->total_mem = reg[1];
6534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
6544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6554f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void mv64x60_init_csrows(struct mem_ctl_info *mci,
6564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				struct mv64x60_mc_pdata *pdata)
6574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
6584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct csrow_info *csrow;
6594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 devtype;
6604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 ctl;
6614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	get_total_mem(pdata);
6634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
6654f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow = &mci->csrows[0];
6674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow->first_page = 0;
6684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT;
6694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
6704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow->grain = 8;
6714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
6734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devtype = (ctl >> 20) & 0x3;
6754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	switch (devtype) {
6764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	case 0x0:
6774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		csrow->dtype = DEV_X32;
6784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		break;
6794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	case 0x2:		/* could be X8 too, but no way to tell */
6804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		csrow->dtype = DEV_X16;
6814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		break;
6824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	case 0x3:
6834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		csrow->dtype = DEV_X4;
6844f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		break;
6854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	default:
6864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		csrow->dtype = DEV_UNKNOWN;
6874f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		break;
6884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
6894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	csrow->edac_mode = EDAC_SECDED;
6914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
6924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
6934f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
6944f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
6954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mem_ctl_info *mci;
6964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mv64x60_mc_pdata *pdata;
6974f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct resource *r;
6984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	u32 ctl;
6994f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	int res = 0;
7004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7014f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL))
7024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
7034f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci = edac_mc_alloc(sizeof(struct mv64x60_mc_pdata), 1, 1, edac_mc_idx);
7054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!mci) {
7064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: No memory for CPU err\n", __func__);
7074f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		devres_release_group(&pdev->dev, mv64x60_mc_err_probe);
7084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		return -ENOMEM;
7094f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
7104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata = mci->pvt_info;
7124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->dev = &pdev->dev;
7134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_set_drvdata(pdev, mci);
7144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->name = "mv64x60_mc_err";
7154f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->irq = NO_IRQ;
716031d5518591006efd13a33a86909b9477b22917bKay Sievers	mci->dev_name = dev_name(&pdev->dev);
7174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->edac_idx = edac_mc_idx++;
7184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!r) {
7214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to get resource for "
7224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       "MC err regs\n", __func__);
7234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOENT;
7244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
7254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
7264f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!devm_request_mem_region(&pdev->dev,
7284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     r->start,
72930a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall				     resource_size(r),
7304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				     pdata->name)) {
7314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Error while requesting mem region\n",
7324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       __func__);
7334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -EBUSY;
7344f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
7354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
7364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	pdata->mc_vbase = devm_ioremap(&pdev->dev,
7384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       r->start,
73930a61fff3a2b19506c66ea81fecb6a7747af3d47Julia Lawall				       resource_size(r));
7404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!pdata->mc_vbase) {
7414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__);
7424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENOMEM;
7434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
7444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
7454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
7474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (!(ctl & MV64X60_SDRAM_ECC)) {
7484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		/* Non-ECC RAM? */
7494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__);
7504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = -ENODEV;
7514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err2;
7524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
7534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf3("%s(): init mci\n", __func__);
7554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
7564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
7574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->edac_cap = EDAC_FLAG_SECDED;
7584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->mod_name = EDAC_MOD_STR;
7594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->mod_ver = MV64x60_REVISION;
7604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->ctl_name = mv64x60_ctl_name;
7614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_POLL)
7634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		mci->edac_check = mv64x60_mc_check;
7644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7654f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->ctl_page_to_phys = NULL;
7664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mci->scrub_mode = SCRUB_SW_SRC;
7684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	mv64x60_init_csrows(mci, pdata);
7704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* setup MC registers */
7724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
7734f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL);
7744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ctl = (ctl & 0xff00ffff) | 0x10000;
7754f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl);
7764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_mc_add_mc(mci)) {
7784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
7794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		goto err;
7804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
7814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (edac_op_state == EDAC_OPSTATE_INT) {
7834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		/* acquire interrupt that reports errors */
7844f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		pdata->irq = platform_get_irq(pdev, 0);
7854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		res = devm_request_irq(&pdev->dev,
7864f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       pdata->irq,
7874f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       mv64x60_mc_isr,
7884f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       IRQF_DISABLED,
7894f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       "[EDAC] MC err",
7904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang				       mci);
7914f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		if (res < 0) {
7924f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			printk(KERN_ERR "%s: Unable to request irq %d for "
7934f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			       "MV64x60 DRAM ERR\n", __func__, pdata->irq);
7944f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			res = -ENODEV;
7954f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			goto err2;
7964f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		}
7974f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
7984f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n",
7994f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		       pdata->irq);
8004f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
8014f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8024f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* get this far and it's successful */
8034f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf3("%s(): success\n", __func__);
8044f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8054f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
8064f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8074f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr2:
8084f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_mc_del_mc(&pdev->dev);
8094f4aeeabc061826376c9a72b4714d062664999eaDave Jiangerr:
8104f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	devres_release_group(&pdev->dev, mv64x60_mc_err_probe);
8114f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_mc_free(mci);
8124f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return res;
8134f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
8144f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8154f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int mv64x60_mc_err_remove(struct platform_device *pdev)
8164f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
8174f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
8184f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8194f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	debugf0("%s()\n", __func__);
8204f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8214f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_mc_del_mc(&pdev->dev);
8224f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	edac_mc_free(mci);
8234f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return 0;
8244f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
8254f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8264f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic struct platform_driver mv64x60_mc_err_driver = {
8274f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.probe = mv64x60_mc_err_probe,
8284f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.remove = mv64x60_mc_err_remove,
8294f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	.driver = {
8304f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		   .name = "mv64x60_mc_err",
8314f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
8324f4aeeabc061826376c9a72b4714d062664999eaDave Jiang};
8334f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8344f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic int __init mv64x60_edac_init(void)
8354f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
8364f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	int ret = 0;
8374f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8384f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n");
8394f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n");
8404f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	/* make sure error reporting method is sane */
8414f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	switch (edac_op_state) {
8424f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	case EDAC_OPSTATE_POLL:
8434f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	case EDAC_OPSTATE_INT:
8444f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		break;
8454f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	default:
8464f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		edac_op_state = EDAC_OPSTATE_INT;
8474f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		break;
8484f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	}
8494f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8504f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ret = platform_driver_register(&mv64x60_mc_err_driver);
8514f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (ret)
8524f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n");
8534f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8544f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ret = platform_driver_register(&mv64x60_cpu_err_driver);
8554f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (ret)
8564f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_WARNING EDAC_MOD_STR
8574f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			"CPU err failed to register\n");
8584f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8594f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ret = platform_driver_register(&mv64x60_sram_err_driver);
8604f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (ret)
8614f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_WARNING EDAC_MOD_STR
8624f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			"SRAM err failed to register\n");
8634f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8644f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#ifdef CONFIG_PCI
8654f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	ret = platform_driver_register(&mv64x60_pci_err_driver);
8664f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	if (ret)
8674f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		printk(KERN_WARNING EDAC_MOD_STR
8684f4aeeabc061826376c9a72b4714d062664999eaDave Jiang			"PCI err failed to register\n");
8694f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#endif
8704f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8714f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	return ret;
8724f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
8734f4aeeabc061826376c9a72b4714d062664999eaDave Jiangmodule_init(mv64x60_edac_init);
8744f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8754f4aeeabc061826376c9a72b4714d062664999eaDave Jiangstatic void __exit mv64x60_edac_exit(void)
8764f4aeeabc061826376c9a72b4714d062664999eaDave Jiang{
8774f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#ifdef CONFIG_PCI
8784f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_driver_unregister(&mv64x60_pci_err_driver);
8794f4aeeabc061826376c9a72b4714d062664999eaDave Jiang#endif
8804f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_driver_unregister(&mv64x60_sram_err_driver);
8814f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_driver_unregister(&mv64x60_cpu_err_driver);
8824f4aeeabc061826376c9a72b4714d062664999eaDave Jiang	platform_driver_unregister(&mv64x60_mc_err_driver);
8834f4aeeabc061826376c9a72b4714d062664999eaDave Jiang}
8844f4aeeabc061826376c9a72b4714d062664999eaDave Jiangmodule_exit(mv64x60_edac_exit);
8854f4aeeabc061826376c9a72b4714d062664999eaDave Jiang
8864f4aeeabc061826376c9a72b4714d062664999eaDave JiangMODULE_LICENSE("GPL");
8874f4aeeabc061826376c9a72b4714d062664999eaDave JiangMODULE_AUTHOR("Montavista Software, Inc.");
8884f4aeeabc061826376c9a72b4714d062664999eaDave Jiangmodule_param(edac_op_state, int, 0444);
8894f4aeeabc061826376c9a72b4714d062664999eaDave JiangMODULE_PARM_DESC(edac_op_state,
8904f4aeeabc061826376c9a72b4714d062664999eaDave Jiang		 "EDAC Error Reporting state: 0=Poll, 2=Interrupt");
891