EDAC, mpc85xx: Make mpc85xx-pci-edac a platform device
authorScott Wood <scottwood@freescale.com>
Thu, 10 Dec 2015 19:07:12 +0000 (13:07 -0600)
committerBorislav Petkov <bp@suse.de>
Fri, 11 Dec 2015 15:56:16 +0000 (16:56 +0100)
Originally the mpc85xx-pci-edac driver bound directly to the PCI
controller node.

Commit

  905e75c46dba ("powerpc/fsl-pci: Unify pci/pcie initialization code")

turned the PCI controller code into a platform device. Since we can't
have two drivers binding to the same device, the EDAC code was changed
to be called into as a library-style submodule. However, this doesn't
work if the EDAC driver is built as a module.

Commit

  8d8fcba6d1ea ("EDAC: Rip out the edac_subsys reference counting")

exposed another problem with this approach -- mpc85xx_pci_err_probe()
was being called in the same early boot phase that the PCI controller
is initialized, rather than in the device_initcall phase that the EDAC
layer expects. This caused a crash on boot.

To fix this, the PCI controller code now creates a child platform device
specifically for EDAC, which the mpc85xx-pci-edac driver binds to.

Reported-by: Michael Ellerman <mpe@ellerman.id.au>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: Scott Wood <scottwood@freescale.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Daniel Axtens <dja@axtens.net>
Cc: Doug Thompson <dougthompson@xmission.com>
Cc: Jia Hongtao <B38951@freescale.com>
Cc: Jiri Kosina <jkosina@suse.com>
Cc: Kim Phillips <kim.phillips@freescale.com>
Cc: linux-edac <linux-edac@vger.kernel.org>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Masanari Iida <standby24x7@gmail.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Rob Herring <robh@kernel.org>
Link: http://lkml.kernel.org/r/1449774432-18593-1-git-send-email-scottwood@freescale.com
Signed-off-by: Borislav Petkov <bp@suse.de>
arch/powerpc/sysdev/fsl_pci.c
arch/powerpc/sysdev/fsl_pci.h
drivers/edac/mpc85xx_edac.c
include/linux/fsl/edac.h [new file with mode: 0644]

index 610f472f91d14c25cef49a118ecc7f1d9eaf73b1..a1ac80b3041a9519523480a470c121d6db4cd3ca 100644 (file)
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/string.h>
+#include <linux/fsl/edac.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/memblock.h>
 #include <linux/log2.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/suspend.h>
 #include <linux/syscore_ops.h>
@@ -1255,6 +1257,25 @@ void fsl_pcibios_fixup_phb(struct pci_controller *phb)
 #endif
 }
 
+static int add_err_dev(struct platform_device *pdev)
+{
+       struct platform_device *errdev;
+       struct mpc85xx_edac_pci_plat_data pd = {
+               .of_node = pdev->dev.of_node
+       };
+
+       errdev = platform_device_register_resndata(&pdev->dev,
+                                                  "mpc85xx-pci-edac",
+                                                  PLATFORM_DEVID_AUTO,
+                                                  pdev->resource,
+                                                  pdev->num_resources,
+                                                  &pd, sizeof(pd));
+       if (IS_ERR(errdev))
+               return PTR_ERR(errdev);
+
+       return 0;
+}
+
 static int fsl_pci_probe(struct platform_device *pdev)
 {
        struct device_node *node;
@@ -1262,8 +1283,13 @@ static int fsl_pci_probe(struct platform_device *pdev)
 
        node = pdev->dev.of_node;
        ret = fsl_add_bridge(pdev, fsl_pci_primary == node);
+       if (ret)
+               return ret;
 
-       mpc85xx_pci_err_probe(pdev);
+       ret = add_err_dev(pdev);
+       if (ret)
+               dev_err(&pdev->dev, "couldn't register error device: %d\n",
+                       ret);
 
        return 0;
 }
index c1cec771d5eae2f7f81381782dbff1193d21db46..151588530b0650f0454c43a4a8e90b079546f915 100644 (file)
@@ -130,15 +130,6 @@ void fsl_pci_assign_primary(void);
 static inline void fsl_pci_assign_primary(void) {}
 #endif
 
-#ifdef CONFIG_EDAC_MPC85XX
-int mpc85xx_pci_err_probe(struct platform_device *op);
-#else
-static inline int mpc85xx_pci_err_probe(struct platform_device *op)
-{
-       return -ENOTSUPP;
-}
-#endif
-
 #ifdef CONFIG_FSL_PCI
 extern int fsl_pci_mcheck_exception(struct pt_regs *);
 #else
index 3eab06351089e8d85cd91c87f662b28c25593192..b7139c160bafdaf71a4e3a2d45a00b5e67e6cc7f 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/edac.h>
 #include <linux/smp.h>
 #include <linux/gfp.h>
+#include <linux/fsl/edac.h>
 
 #include <linux/of_platform.h>
 #include <linux/of_device.h>
@@ -238,10 +239,12 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-int mpc85xx_pci_err_probe(struct platform_device *op)
+static int mpc85xx_pci_err_probe(struct platform_device *op)
 {
        struct edac_pci_ctl_info *pci;
        struct mpc85xx_pci_pdata *pdata;
+       struct mpc85xx_edac_pci_plat_data *plat_data;
+       struct device_node *of_node;
        struct resource r;
        int res = 0;
 
@@ -266,7 +269,15 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
        pdata->name = "mpc85xx_pci_err";
        pdata->irq = NO_IRQ;
 
-       if (mpc85xx_pcie_find_capability(op->dev.of_node) > 0)
+       plat_data = op->dev.platform_data;
+       if (!plat_data) {
+               dev_err(&op->dev, "no platform data");
+               res = -ENXIO;
+               goto err;
+       }
+       of_node = plat_data->of_node;
+
+       if (mpc85xx_pcie_find_capability(of_node) > 0)
                pdata->is_pcie = true;
 
        dev_set_drvdata(&op->dev, pci);
@@ -284,7 +295,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
 
        pdata->edac_idx = edac_pci_idx++;
 
-       res = of_address_to_resource(op->dev.of_node, 0, &r);
+       res = of_address_to_resource(of_node, 0, &r);
        if (res) {
                printk(KERN_ERR "%s: Unable to get resource for "
                       "PCI err regs\n", __func__);
@@ -339,7 +350,7 @@ 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);
+               pdata->irq = irq_of_parse_and_map(of_node, 0);
                res = devm_request_irq(&op->dev, pdata->irq,
                                       mpc85xx_pci_isr,
                                       IRQF_SHARED,
@@ -386,8 +397,22 @@ err:
        devres_release_group(&op->dev, mpc85xx_pci_err_probe);
        return res;
 }
-EXPORT_SYMBOL(mpc85xx_pci_err_probe);
 
+static const struct platform_device_id mpc85xx_pci_err_match[] = {
+       {
+               .name = "mpc85xx-pci-edac"
+       },
+       {}
+};
+
+static struct platform_driver mpc85xx_pci_err_driver = {
+       .probe = mpc85xx_pci_err_probe,
+       .id_table = mpc85xx_pci_err_match,
+       .driver = {
+               .name = "mpc85xx_pci_err",
+               .suppress_bind_attrs = true,
+       },
+};
 #endif                         /* CONFIG_PCI */
 
 /**************************** L2 Err device ***************************/
@@ -1211,6 +1236,9 @@ static void __init mpc85xx_mc_clear_rfxe(void *data)
 static struct platform_driver * const drivers[] = {
        &mpc85xx_mc_err_driver,
        &mpc85xx_l2_err_driver,
+#ifdef CONFIG_PCI
+       &mpc85xx_pci_err_driver,
+#endif
 };
 
 static int __init mpc85xx_mc_init(void)
diff --git a/include/linux/fsl/edac.h b/include/linux/fsl/edac.h
new file mode 100644 (file)
index 0000000..90d64d4
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef FSL_EDAC_H
+#define FSL_EDAC_H
+
+struct mpc85xx_edac_pci_plat_data {
+       struct device_node *of_node;
+};
+
+#endif