Commit d870a9d5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'edac_for_4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp

Pull EDAC updates from Borislav Petkov:

 - hide EDAC workqueue from users (Borislav Petkov)

 - edac_subsys init/teardown cleanup (Borislav Petkov)

 - make mpc85xx-pci-edac a platform device (Scott Wood)

 - sb_edac KNL gen2 support (Jim Snow)

 - other small cleanups all over the place

* tag 'edac_for_4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp:
  EDAC, i5100: Use to_delayed_work()
  MAINTAINERS: Fix EDAC repo URLs format
  EDAC, sb_edac: Set fixed DIMM width on Xeon Knights Landing
  EDAC: Rework workqueue handling
  EDAC: Make edac_device workqueue setup/teardown functions static
  EDAC: Remove edac_get_sysfs_subsys() error handling
  EDAC: Unexport and make edac_subsys static
  EDAC: Rip out the edac_subsys reference counting
  EDAC: Robustify workqueues destruction
  EDAC, mc_sysfs: Fix freeing bus' name
  EDAC, mpc85xx: Make mpc85xx-pci-edac a platform device
  EDAC, sb_edac: Add Knights Landing (Xeon Phi gen 2) support
  EDAC, sb_edac: Add support for duplicate device IDs
  EDAC, sb_edac: Virtualize several hard-coded functions
  EDAC, mv64x60: Use platform_register/unregister_drivers()
  EDAC, mpc85xx: Use platform_register/unregister_drivers()
  EDAC: Add DDR4 flag
  EDAC: Remove references to bluesmoke.sourceforge.net
  EDAC, pci: Remove old disabled code
parents 2634bf25 1cac5503
EDAC - Error Detection And Correction EDAC - Error Detection And Correction
===================================== =====================================
"bluesmoke" was the name for this device driver when it was "out-of-tree" "bluesmoke" was the name for this device driver when it
and maintained at sourceforge.net. When it was pushed into 2.6.16 for the was "out-of-tree" and maintained at sourceforge.net -
first time, it was renamed to 'EDAC'. bluesmoke.sourceforge.net. That site is mostly archaic now and can be
used only for historical purposes.
When the subsystem was pushed into 2.6.16 for the first time, it was
renamed to 'EDAC'.
PURPOSE PURPOSE
------- -------
......
...@@ -3933,9 +3933,8 @@ M: Doug Thompson <dougthompson@xmission.com> ...@@ -3933,9 +3933,8 @@ M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de> M: Borislav Petkov <bp@alien8.de>
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net T: git git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git for-next
T: git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git#for-next T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git linux_next
T: git://git.kernel.org/pub/linux/kernel/git/mchehab/linux-edac.git#linux_next
S: Supported S: Supported
F: Documentation/edac.txt F: Documentation/edac.txt
F: drivers/edac/ F: drivers/edac/
...@@ -3945,7 +3944,6 @@ EDAC-AMD64 ...@@ -3945,7 +3944,6 @@ EDAC-AMD64
M: Doug Thompson <dougthompson@xmission.com> M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de> M: Borislav Petkov <bp@alien8.de>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/amd64_edac* F: drivers/edac/amd64_edac*
...@@ -3953,7 +3951,6 @@ EDAC-CALXEDA ...@@ -3953,7 +3951,6 @@ EDAC-CALXEDA
M: Doug Thompson <dougthompson@xmission.com> M: Doug Thompson <dougthompson@xmission.com>
M: Robert Richter <rric@kernel.org> M: Robert Richter <rric@kernel.org>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/highbank* F: drivers/edac/highbank*
...@@ -3962,7 +3959,6 @@ M: Ralf Baechle <ralf@linux-mips.org> ...@@ -3962,7 +3959,6 @@ M: Ralf Baechle <ralf@linux-mips.org>
M: David Daney <david.daney@cavium.com> M: David Daney <david.daney@cavium.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
L: linux-mips@linux-mips.org L: linux-mips@linux-mips.org
W: bluesmoke.sourceforge.net
S: Supported S: Supported
F: drivers/edac/octeon_edac* F: drivers/edac/octeon_edac*
...@@ -3970,63 +3966,54 @@ EDAC-E752X ...@@ -3970,63 +3966,54 @@ EDAC-E752X
M: Mark Gross <mark.gross@intel.com> M: Mark Gross <mark.gross@intel.com>
M: Doug Thompson <dougthompson@xmission.com> M: Doug Thompson <dougthompson@xmission.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/e752x_edac.c F: drivers/edac/e752x_edac.c
EDAC-E7XXX EDAC-E7XXX
M: Doug Thompson <dougthompson@xmission.com> M: Doug Thompson <dougthompson@xmission.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/e7xxx_edac.c F: drivers/edac/e7xxx_edac.c
EDAC-GHES EDAC-GHES
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/ghes_edac.c F: drivers/edac/ghes_edac.c
EDAC-I82443BXGX EDAC-I82443BXGX
M: Tim Small <tim@buttersideup.com> M: Tim Small <tim@buttersideup.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i82443bxgx_edac.c F: drivers/edac/i82443bxgx_edac.c
EDAC-I3000 EDAC-I3000
M: Jason Uhlenkott <juhlenko@akamai.com> M: Jason Uhlenkott <juhlenko@akamai.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i3000_edac.c F: drivers/edac/i3000_edac.c
EDAC-I5000 EDAC-I5000
M: Doug Thompson <dougthompson@xmission.com> M: Doug Thompson <dougthompson@xmission.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i5000_edac.c F: drivers/edac/i5000_edac.c
EDAC-I5400 EDAC-I5400
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i5400_edac.c F: drivers/edac/i5400_edac.c
EDAC-I7300 EDAC-I7300
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i7300_edac.c F: drivers/edac/i7300_edac.c
EDAC-I7CORE EDAC-I7CORE
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i7core_edac.c F: drivers/edac/i7core_edac.c
...@@ -4034,42 +4021,36 @@ EDAC-I82975X ...@@ -4034,42 +4021,36 @@ EDAC-I82975X
M: Ranganathan Desikan <ravi@jetztechnologies.com> M: Ranganathan Desikan <ravi@jetztechnologies.com>
M: "Arvind R." <arvino55@gmail.com> M: "Arvind R." <arvino55@gmail.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/i82975x_edac.c F: drivers/edac/i82975x_edac.c
EDAC-IE31200 EDAC-IE31200
M: Jason Baron <jbaron@akamai.com> M: Jason Baron <jbaron@akamai.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/ie31200_edac.c F: drivers/edac/ie31200_edac.c
EDAC-MPC85XX EDAC-MPC85XX
M: Johannes Thumshirn <morbidrsa@gmail.com> M: Johannes Thumshirn <morbidrsa@gmail.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/mpc85xx_edac.[ch] F: drivers/edac/mpc85xx_edac.[ch]
EDAC-PASEMI EDAC-PASEMI
M: Egor Martovetsky <egor@pasemi.com> M: Egor Martovetsky <egor@pasemi.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/pasemi_edac.c F: drivers/edac/pasemi_edac.c
EDAC-R82600 EDAC-R82600
M: Tim Small <tim@buttersideup.com> M: Tim Small <tim@buttersideup.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/r82600_edac.c F: drivers/edac/r82600_edac.c
EDAC-SBRIDGE EDAC-SBRIDGE
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/sb_edac.c F: drivers/edac/sb_edac.c
......
...@@ -21,10 +21,12 @@ ...@@ -21,10 +21,12 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/fsl/edac.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/memblock.h> #include <linux/memblock.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
...@@ -1255,6 +1257,25 @@ void fsl_pcibios_fixup_phb(struct pci_controller *phb) ...@@ -1255,6 +1257,25 @@ void fsl_pcibios_fixup_phb(struct pci_controller *phb)
#endif #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) static int fsl_pci_probe(struct platform_device *pdev)
{ {
struct device_node *node; struct device_node *node;
...@@ -1262,8 +1283,13 @@ static int fsl_pci_probe(struct platform_device *pdev) ...@@ -1262,8 +1283,13 @@ static int fsl_pci_probe(struct platform_device *pdev)
node = pdev->dev.of_node; node = pdev->dev.of_node;
ret = fsl_add_bridge(pdev, fsl_pci_primary == 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; return 0;
} }
......
...@@ -130,15 +130,6 @@ void fsl_pci_assign_primary(void); ...@@ -130,15 +130,6 @@ void fsl_pci_assign_primary(void);
static inline void fsl_pci_assign_primary(void) {} static inline void fsl_pci_assign_primary(void) {}
#endif #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 #ifdef CONFIG_FSL_PCI
extern int fsl_pci_mcheck_exception(struct pt_regs *); extern int fsl_pci_mcheck_exception(struct pt_regs *);
#else #else
......
...@@ -10,7 +10,7 @@ obj-$(CONFIG_EDAC) := edac_stub.o ...@@ -10,7 +10,7 @@ obj-$(CONFIG_EDAC) := edac_stub.o
obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o
edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o
edac_core-y += edac_module.o edac_device_sysfs.o edac_core-y += edac_module.o edac_device_sysfs.o wq.o
edac_core-$(CONFIG_EDAC_DEBUG) += debugfs.o edac_core-$(CONFIG_EDAC_DEBUG) += debugfs.o
......
...@@ -390,11 +390,9 @@ static void edac_device_workq_function(struct work_struct *work_req) ...@@ -390,11 +390,9 @@ static void edac_device_workq_function(struct work_struct *work_req)
* between integral seconds * between integral seconds
*/ */
if (edac_dev->poll_msec == 1000) if (edac_dev->poll_msec == 1000)
queue_delayed_work(edac_workqueue, &edac_dev->work, edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
round_jiffies_relative(edac_dev->delay));
else else
queue_delayed_work(edac_workqueue, &edac_dev->work, edac_queue_work(&edac_dev->work, edac_dev->delay);
edac_dev->delay);
} }
/* /*
...@@ -402,8 +400,8 @@ static void edac_device_workq_function(struct work_struct *work_req) ...@@ -402,8 +400,8 @@ static void edac_device_workq_function(struct work_struct *work_req)
* initialize a workq item for this edac_device instance * initialize a workq item for this edac_device instance
* passing in the new delay period in msec * passing in the new delay period in msec
*/ */
void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, static void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
unsigned msec) unsigned msec)
{ {
edac_dbg(0, "\n"); edac_dbg(0, "\n");
...@@ -422,29 +420,23 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, ...@@ -422,29 +420,23 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
* to fire together on the 1 second exactly * to fire together on the 1 second exactly
*/ */
if (edac_dev->poll_msec == 1000) if (edac_dev->poll_msec == 1000)
queue_delayed_work(edac_workqueue, &edac_dev->work, edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
round_jiffies_relative(edac_dev->delay));
else else
queue_delayed_work(edac_workqueue, &edac_dev->work, edac_queue_work(&edac_dev->work, edac_dev->delay);
edac_dev->delay);
} }
/* /*
* edac_device_workq_teardown * edac_device_workq_teardown
* stop the workq processing on this edac_dev * stop the workq processing on this edac_dev
*/ */
void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev) static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
{ {
int status;
if (!edac_dev->edac_check) if (!edac_dev->edac_check)
return; return;
status = cancel_delayed_work(&edac_dev->work); edac_dev->op_state = OP_OFFLINE;
if (status == 0) {
/* workq instance might be running, wait for it */ edac_stop_work(&edac_dev->work);
flush_workqueue(edac_workqueue);
}
} }
/* /*
...@@ -457,16 +449,15 @@ void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev) ...@@ -457,16 +449,15 @@ void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
unsigned long value) unsigned long value)
{ {
/* cancel the current workq request, without the mutex lock */ unsigned long jiffs = msecs_to_jiffies(value);
edac_device_workq_teardown(edac_dev);
/* acquire the mutex before doing the workq setup */ if (value == 1000)
mutex_lock(&device_ctls_mutex); jiffs = round_jiffies_relative(value);
/* restart the workq request, with new delay value */ edac_dev->poll_msec = value;
edac_device_workq_setup(edac_dev, value); edac_dev->delay = jiffs;
mutex_unlock(&device_ctls_mutex); edac_mod_work(&edac_dev->work, jiffs);
} }
/* /*
......
...@@ -237,11 +237,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) ...@@ -237,11 +237,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
/* get the /sys/devices/system/edac reference */ /* get the /sys/devices/system/edac reference */
edac_subsys = edac_get_sysfs_subsys(); edac_subsys = edac_get_sysfs_subsys();
if (edac_subsys == NULL) {
edac_dbg(1, "no edac_subsys error\n");
err = -ENODEV;
goto err_out;
}
/* Point to the 'edac_subsys' this instance 'reports' to */ /* Point to the 'edac_subsys' this instance 'reports' to */
edac_dev->edac_subsys = edac_subsys; edac_dev->edac_subsys = edac_subsys;
...@@ -256,7 +251,7 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) ...@@ -256,7 +251,7 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
if (!try_module_get(edac_dev->owner)) { if (!try_module_get(edac_dev->owner)) {
err = -ENODEV; err = -ENODEV;
goto err_mod_get; goto err_out;
} }
/* register */ /* register */
...@@ -282,9 +277,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) ...@@ -282,9 +277,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
err_kobj_reg: err_kobj_reg:
module_put(edac_dev->owner); module_put(edac_dev->owner);
err_mod_get:
edac_put_sysfs_subsys();
err_out: err_out:
return err; return err;
} }
...@@ -306,7 +298,6 @@ void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev) ...@@ -306,7 +298,6 @@ void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev)
* b) 'kfree' the memory * b) 'kfree' the memory
*/ */
kobject_put(&dev->kobj); kobject_put(&dev->kobj);
edac_put_sysfs_subsys();
} }
/* edac_dev -> instance information */ /* edac_dev -> instance information */
......
...@@ -548,8 +548,7 @@ static void edac_mc_workq_function(struct work_struct *work_req) ...@@ -548,8 +548,7 @@ static void edac_mc_workq_function(struct work_struct *work_req)
mutex_unlock(&mem_ctls_mutex); mutex_unlock(&mem_ctls_mutex);
/* Reschedule */ /* Reschedule */
queue_delayed_work(edac_workqueue, &mci->work, edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec()));
msecs_to_jiffies(edac_mc_get_poll_msec()));
} }
/* /*
...@@ -561,8 +560,7 @@ static void edac_mc_workq_function(struct work_struct *work_req) ...@@ -561,8 +560,7 @@ static void edac_mc_workq_function(struct work_struct *work_req)
* *
* called with the mem_ctls_mutex held * called with the mem_ctls_mutex held
*/ */
static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec, static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
bool init)
{ {
edac_dbg(0, "\n"); edac_dbg(0, "\n");
...@@ -570,10 +568,9 @@ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec, ...@@ -570,10 +568,9 @@ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec,
if (mci->op_state != OP_RUNNING_POLL) if (mci->op_state != OP_RUNNING_POLL)
return; return;
if (init) INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
mod_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec)); edac_queue_work(&mci->work, msecs_to_jiffies(msec));
} }
/* /*
...@@ -586,18 +583,9 @@ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec, ...@@ -586,18 +583,9 @@ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec,
*/ */
static void edac_mc_workq_teardown(struct mem_ctl_info *mci) static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
{ {
int status; mci->op_state = OP_OFFLINE;
if (mci->op_state != OP_RUNNING_POLL)
return;
status = cancel_delayed_work(&mci->work);
if (status == 0) {
edac_dbg(0, "not canceled, flush the queue\n");
/* workq instance might be running, wait for it */ edac_stop_work(&mci->work);
flush_workqueue(edac_workqueue);
}
} }
/* /*
...@@ -616,9 +604,8 @@ void edac_mc_reset_delay_period(unsigned long value) ...@@ -616,9 +604,8 @@ void edac_mc_reset_delay_period(unsigned long value)
list_for_each(item, &mc_devices) { list_for_each(item, &mc_devices) {
mci = list_entry(item, struct mem_ctl_info, link); mci = list_entry(item, struct mem_ctl_info, link);
edac_mc_workq_setup(mci, value, false); edac_mod_work(&mci->work, value);
} }
mutex_unlock(&mem_ctls_mutex); mutex_unlock(&mem_ctls_mutex);
} }
...@@ -789,7 +776,7 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, ...@@ -789,7 +776,7 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
/* This instance is NOW RUNNING */ /* This instance is NOW RUNNING */
mci->op_state = OP_RUNNING_POLL; mci->op_state = OP_RUNNING_POLL;
edac_mc_workq_setup(mci, edac_mc_get_poll_msec(), true); edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
} else { } else {
mci->op_state = OP_RUNNING_INTERRUPT; mci->op_state = OP_RUNNING_INTERRUPT;
} }
......
...@@ -880,21 +880,26 @@ static struct device_type mci_attr_type = { ...@@ -880,21 +880,26 @@ static struct device_type mci_attr_type = {
int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
const struct attribute_group **groups) const struct attribute_group **groups)
{ {
char *name;
int i, err; int i, err;
/* /*
* The memory controller needs its own bus, in order to avoid * The memory controller needs its own bus, in order to avoid
* namespace conflicts at /sys/bus/edac. * namespace conflicts at /sys/bus/edac.
*/ */
mci->bus->name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
if (!mci->bus->name) if (!name)
return -ENOMEM; return -ENOMEM;
mci->bus->name = name;
edac_dbg(0, "creating bus %s\n", mci->bus->name); edac_dbg(0, "creating bus %s\n", mci->bus->name);
err = bus_register(mci->bus); err = bus_register(mci->bus);
if (err < 0) if (err < 0) {
goto fail_free_name; kfree(name);
return err;
}
/* get the /sys/devices/system/edac subsys reference */ /* get the /sys/devices/system/edac subsys reference */
mci->dev.type = &mci_attr_type; mci->dev.type = &mci_attr_type;
...@@ -961,8 +966,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, ...@@ -961,8 +966,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
device_unregister(&mci->dev); device_unregister(&mci->dev);
fail_unregister_bus: fail_unregister_bus:
bus_unregister(mci->bus); bus_unregister(mci->bus);
fail_free_name: kfree(name);
kfree(mci->bus->name);
return err; return err;
} }
...@@ -993,10 +998,12 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -993,10 +998,12 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
void edac_unregister_sysfs(struct mem_ctl_info *mci) void edac_unregister_sysfs(struct mem_ctl_info *mci)
{ {
const char *name = mci->bus->name;
edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
device_unregister(&mci->dev); device_unregister(&mci->dev);
bus_unregister(mci->bus); bus_unregister(mci->bus);
kfree(mci->bus->name); kfree(name);
} }
static void mc_attr_release(struct device *dev) static void mc_attr_release(struct device *dev)
...@@ -1018,24 +1025,15 @@ static struct device_type mc_attr_type = { ...@@ -1018,24 +1025,15 @@ static struct device_type mc_attr_type = {
*/ */
int __init edac_mc_sysfs_init(void) int __init edac_mc_sysfs_init(void)
{ {
struct bus_type *edac_subsys;
int err; int err;
/* get the /sys/devices/system/edac subsys reference */
edac_subsys = edac_get_sysfs_subsys();
if (edac_subsys == NULL) {
edac_dbg(1, "no edac_subsys\n");
err = -EINVAL;
goto out;
}
mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
if (!mci_pdev) { if (!mci_pdev) {
err = -ENOMEM; err = -ENOMEM;
goto out_put_sysfs; goto out;
} }
mci_pdev->bus = edac_subsys; mci_pdev->bus = edac_get_sysfs_subsys();
mci_pdev->type = &mc_attr_type; mci_pdev->type = &mc_attr_type;
device_initialize(mci_pdev); device_initialize(mci_pdev);
dev_set_name(mci_pdev, "mc"); dev_set_name(mci_pdev, "mc");
...@@ -1050,8 +1048,6 @@ int __init edac_mc_sysfs_init(void) ...@@ -1050,8 +1048,6 @@ int __init edac_mc_sysfs_init(void)
out_dev_free: out_dev_free:
kfree(mci_pdev); kfree(mci_pdev);
out_put_sysfs:
edac_put_sysfs_subsys();
out: out:
return err; return err;
} }
...@@ -1059,5 +1055,4 @@ int __init edac_mc_sysfs_init(void) ...@@ -1059,5 +1055,4 @@ int __init edac_mc_sysfs_init(void)
void edac_mc_sysfs_exit(void) void edac_mc_sysfs_exit(void)
{ {
device_unregister(mci_pdev); device_unregister(mci_pdev);
edac_put_sysfs_subsys();
} }
...@@ -43,9 +43,6 @@ module_param_call(edac_debug_level, edac_set_debug_level, param_get_int, ...@@ -43,9 +43,6 @@ module_param_call(edac_debug_level, edac_set_debug_level, param_get_int,
MODULE_PARM_DESC(edac_debug_level, "EDAC debug level: [0-4], default: 2"); MODULE_PARM_DESC(edac_debug_level, "EDAC debug level: [0-4], default: 2");
#endif #endif
/* scope is to module level only */
struct workqueue_struct *edac_workqueue;
/* /*
* edac_op_state_to_string() * edac_op_state_to_string()
*/ */
...@@ -66,31 +63,37 @@ char *edac_op_state_to_string(int opstate) ...@@ -66,31 +63,37 @@ char *edac_op_state_to_string(int opstate)
} }
/* /*
* edac_workqueue_setup * sysfs object: /sys/devices/system/edac
* initialize the edac work queue for polling operations * need to export to other files
*/ */
static int edac_workqueue_setup(void) static struct bus_type edac_subsys = {
.name = "edac",
.dev_name = "edac",
};
static int edac_subsys_init(void)
{ {
edac_workqueue = create_singlethread_workqueue("edac-poller"); int err;
if (edac_workqueue == NULL)
return -ENODEV; /* create the /sys/devices/system/edac directory */
else err = subsys_system_register(&edac_subsys, NULL);
return 0; if (err)
printk(KERN_ERR "Error registering toplevel EDAC sysfs dir\n");
return err;
} }
/* static void edac_subsys_exit(void)
* edac_workqueue_teardown
* teardown the edac workqueue
*/
static void edac_workqueue_teardown(void)
{ {
if (edac_workqueue) { bus_unregister(&edac_subsys);
flush_workqueue(edac_workqueue);
destroy_workqueue(edac_workqueue);
edac_workqueue = NULL;
}
} }
/* return pointer to the 'edac' node in sysfs */
struct bus_type *edac_get_sysfs_subsys(void)
{
return &edac_subsys;
}
EXPORT_SYMBOL_GPL(edac_get_sysfs_subsys);
/* /*
* edac_init * edac_init
* module initialization entry point * module initialization entry point
...@@ -101,6 +104,10 @@ static int __init edac_init(void) ...@@ -101,6 +104,10 @@ static int __init edac_init(void)
edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n"); edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n");
err = edac_subsys_init();
if (err)
return err;
/* /*
* Harvest and clear any boot/initialization PCI parity errors * Harvest and clear any boot/initialization PCI parity errors
* *
...@@ -129,6 +136,8 @@ static int __init edac_init(void) ...@@ -129,6 +136,8 @@ static int __init edac_init(void)
edac_mc_sysfs_exit(); edac_mc_sysfs_exit();
err_sysfs: err_sysfs:
edac_subsys_exit();
return err; return err;
} }
...@@ -144,6 +153,7 @@ static void __exit edac_exit(void) ...@@ -144,6 +153,7 @@ static void __exit edac_exit(void)
edac_workqueue_teardown(); edac_workqueue_teardown();
edac_mc_sysfs_exit(); edac_mc_sysfs_exit();
edac_debugfs_exit(); edac_debugfs_exit();
edac_subsys_exit();
} }
/* /*
......
...@@ -47,10 +47,12 @@ extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev); ...@@ -47,10 +47,12 @@ extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev);
extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev); extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev);
/* edac core workqueue: single CPU mode */ /* edac core workqueue: single CPU mode */
extern struct workqueue_struct *edac_workqueue; int edac_workqueue_setup(void);
extern void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, void edac_workqueue_teardown(void);
unsigned msec); bool edac_queue_work(struct delayed_work *work, unsigned long delay);
extern void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev); bool edac_stop_work(struct delayed_work *work);
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
extern void edac_device_reset_delay_period(struct edac_device_ctl_info extern void edac_device_reset_delay_period(struct edac_device_ctl_info
*edac_dev, unsigned long value); *edac_dev, unsigned long value);
extern void edac_mc_reset_delay_period(unsigned long value); extern void edac_mc_reset_delay_period(unsigned long value);
......
...@@ -178,41 +178,6 @@ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) ...@@ -178,41 +178,6 @@ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
INIT_LIST_HEAD(&pci->link); INIT_LIST_HEAD(&pci->link);
} }
#if 0
/* Older code, but might use in the future */
/*
* edac_pci_find()
* Search for an edac_pci_ctl_info structure whose index is 'idx'
*
* If found, return a pointer to the structure
* Else return NULL.
*
* Caller must hold pci_ctls_mutex.
*/
struct edac_pci_ctl_info *edac_pci_find(int idx)
{
struct list_head *item;
struct edac_pci_ctl_info *pci;
/* Iterage over list, looking for exact match of ID */
list_for_each(item, &edac_pci_list) {
pci = list_entry(item, struct edac_pci_ctl_info, link);
if (pci->pci_idx >= idx) {
if (pci->pci_idx == idx)
return pci;
/* not on list, so terminate early */
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(edac_pci_find);
#endif
/* /*
* edac_pci_workq_function() * edac_pci_workq_function()
* *
...@@ -244,7 +209,7 @@ static void edac_pci_workq_function(struct work_struct *work_req) ...@@ -244,7 +209,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
delay = msecs_to_jiffies(msec); delay = msecs_to_jiffies(msec);
/* Reschedule only if we are in POLL mode */ /* Reschedule only if we are in POLL mode */
queue_delayed_work(edac_workqueue, &pci->work, delay); edac_queue_work(&pci->work, delay);
} }
mutex_unlock(&edac_pci_ctls_mutex); mutex_unlock(&edac_pci_ctls_mutex);
...@@ -264,8 +229,8 @@ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, ...@@ -264,8 +229,8 @@ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
edac_dbg(0, "\n"); edac_dbg(0, "\n");
INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
queue_delayed_work(edac_workqueue, &pci->work,
msecs_to_jiffies(edac_pci_get_poll_msec())); edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec()));
} }
/* /*
...@@ -273,38 +238,13 @@ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, ...@@ -273,38 +238,13 @@ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
* stop the workq processing on this edac_pci instance * stop the workq processing on this edac_pci instance
*/ */
static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
{
int status;
edac_dbg(0, "\n");
status = cancel_delayed_work(&pci->work);
if (status == 0)
flush_workqueue(edac_workqueue);
}
/*
* edac_pci_reset_delay_period
*
* called with a new period value for the workq period
* a) stop current workq timer
* b) restart workq timer with new value
*/
void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
unsigned long value)
{ {
edac_dbg(0, "\n"); edac_dbg(0, "\n");
edac_pci_workq_teardown(pci); pci->op_state = OP_OFFLINE;
/* need to lock for the setup */
mutex_lock(&edac_pci_ctls_mutex);
edac_pci_workq_setup(pci, value);
mutex_unlock(&edac_pci_ctls_mutex); edac_stop_work(&pci->work);
} }
EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period);
/* /*
* edac_pci_alloc_index: Allocate a unique PCI index number * edac_pci_alloc_index: Allocate a unique PCI index number
......
...@@ -331,10 +331,7 @@ static struct kobj_type ktype_edac_pci_main_kobj = { ...@@ -331,10 +331,7 @@ static struct kobj_type ktype_edac_pci_main_kobj = {
}; };
/** /**
* edac_pci_main_kobj_setup() * edac_pci_main_kobj_setup: Setup the sysfs for EDAC PCI attributes.
*
* setup the sysfs for EDAC PCI attributes
* assumes edac_subsys has already been initialized
*/ */
static int edac_pci_main_kobj_setup(void) static int edac_pci_main_kobj_setup(void)
{ {
...@@ -351,11 +348,6 @@ static int edac_pci_main_kobj_setup(void) ...@@ -351,11 +348,6 @@ static int edac_pci_main_kobj_setup(void)
* controls and attributes * controls and attributes
*/ */
edac_subsys = edac_get_sysfs_subsys(); edac_subsys = edac_get_sysfs_subsys();
if (edac_subsys == NULL) {
edac_dbg(1, "no edac_subsys\n");
err = -ENODEV;
goto decrement_count_fail;
}
/* Bump the reference count on this module to ensure the /* Bump the reference count on this module to ensure the
* modules isn't unloaded until we deconstruct the top * modules isn't unloaded until we deconstruct the top
...@@ -364,7 +356,7 @@ static int edac_pci_main_kobj_setup(void) ...@@ -364,7 +356,7 @@ static int edac_pci_main_kobj_setup(void)
if (!try_module_get(THIS_MODULE)) { if (!try_module_get(THIS_MODULE)) {
edac_dbg(1, "try_module_get() failed\n"); edac_dbg(1, "try_module_get() failed\n");
err = -ENODEV; err = -ENODEV;
goto mod_get_fail; goto decrement_count_fail;
} }
edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
...@@ -399,9 +391,6 @@ static int edac_pci_main_kobj_setup(void) ...@@ -399,9 +391,6 @@ static int edac_pci_main_kobj_setup(void)
kzalloc_fail: kzalloc_fail:
module_put(THIS_MODULE); module_put(THIS_MODULE);
mod_get_fail:
edac_put_sysfs_subsys();
decrement_count_fail: decrement_count_fail:
/* if are on this error exit, nothing to tear down */ /* if are on this error exit, nothing to tear down */
atomic_dec(&edac_pci_sysfs_refcount); atomic_dec(&edac_pci_sysfs_refcount);
...@@ -426,7 +415,6 @@ static void edac_pci_main_kobj_teardown(void) ...@@ -426,7 +415,6 @@ static void edac_pci_main_kobj_teardown(void)
if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) { if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) {
edac_dbg(0, "called kobject_put on main kobj\n"); edac_dbg(0, "called kobject_put on main kobj\n");
kobject_put(edac_pci_top_main_kobj); kobject_put(edac_pci_top_main_kobj);
edac_put_sysfs_subsys();
} }
} }
......
...@@ -26,8 +26,6 @@ EXPORT_SYMBOL_GPL(edac_handlers); ...@@ -26,8 +26,6 @@ EXPORT_SYMBOL_GPL(edac_handlers);
int edac_err_assert = 0; int edac_err_assert = 0;
EXPORT_SYMBOL_GPL(edac_err_assert); EXPORT_SYMBOL_GPL(edac_err_assert);
static atomic_t edac_subsys_valid = ATOMIC_INIT(0);
int edac_report_status = EDAC_REPORTING_ENABLED; int edac_report_status = EDAC_REPORTING_ENABLED;
EXPORT_SYMBOL_GPL(edac_report_status); EXPORT_SYMBOL_GPL(edac_report_status);
...@@ -68,42 +66,3 @@ void edac_atomic_assert_error(void) ...@@ -68,42 +66,3 @@ void edac_atomic_assert_error(void)
edac_err_assert++; edac_err_assert++;
} }
EXPORT_SYMBOL_GPL(edac_atomic_assert_error); EXPORT_SYMBOL_GPL(edac_atomic_assert_error);
/*
* sysfs object: /sys/devices/system/edac
* need to export to other files
*/
struct bus_type edac_subsys = {
.name = "edac",
.dev_name = "edac",
};
EXPORT_SYMBOL_GPL(edac_subsys);
/* return pointer to the 'edac' node in sysfs */
struct bus_type *edac_get_sysfs_subsys(void)
{
int err = 0;
if (atomic_read(&edac_subsys_valid))
goto out;
/* create the /sys/devices/system/edac directory */
err = subsys_system_register(&edac_subsys, NULL);
if (err) {
printk(KERN_ERR "Error registering toplevel EDAC sysfs dir\n");
return NULL;
}
out:
atomic_inc(&edac_subsys_valid);
return &edac_subsys;
}
EXPORT_SYMBOL_GPL(edac_get_sysfs_subsys);
void edac_put_sysfs_subsys(void)
{
/* last user unregisters it */
if (atomic_dec_and_test(&edac_subsys_valid))
bus_unregister(&edac_subsys);
}
EXPORT_SYMBOL_GPL(edac_put_sysfs_subsys);
...@@ -575,9 +575,7 @@ static void i5100_check_error(struct mem_ctl_info *mci) ...@@ -575,9 +575,7 @@ static void i5100_check_error(struct mem_ctl_info *mci)
static void i5100_refresh_scrubbing(struct work_struct *work) static void i5100_refresh_scrubbing(struct work_struct *work)
{ {
struct delayed_work *i5100_scrubbing = container_of(work, struct delayed_work *i5100_scrubbing = to_delayed_work(work);
struct delayed_work,
work);
struct i5100_priv *priv = container_of(i5100_scrubbing, struct i5100_priv *priv = container_of(i5100_scrubbing,
struct i5100_priv, struct i5100_priv,
i5100_scrubbing); i5100_scrubbing);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/edac.h> #include <linux/edac.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/fsl/edac.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -238,10 +239,12 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) ...@@ -238,10 +239,12 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED; 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 edac_pci_ctl_info *pci;
struct mpc85xx_pci_pdata *pdata; struct mpc85xx_pci_pdata *pdata;
struct mpc85xx_edac_pci_plat_data *plat_data;
struct device_node *of_node;
struct resource r; struct resource r;
int res = 0; int res = 0;
...@@ -266,7 +269,15 @@ int mpc85xx_pci_err_probe(struct platform_device *op) ...@@ -266,7 +269,15 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
pdata->name = "mpc85xx_pci_err"; pdata->name = "mpc85xx_pci_err";
pdata->irq = NO_IRQ; 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; pdata->is_pcie = true;
dev_set_drvdata(&op->dev, pci); dev_set_drvdata(&op->dev, pci);
...@@ -284,7 +295,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op) ...@@ -284,7 +295,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
pdata->edac_idx = edac_pci_idx++; 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) { if (res) {
printk(KERN_ERR "%s: Unable to get resource for " printk(KERN_ERR "%s: Unable to get resource for "
"PCI err regs\n", __func__); "PCI err regs\n", __func__);
...@@ -339,7 +350,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op) ...@@ -339,7 +350,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
} }
if (edac_op_state == EDAC_OPSTATE_INT) { 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, res = devm_request_irq(&op->dev, pdata->irq,
mpc85xx_pci_isr, mpc85xx_pci_isr,
IRQF_SHARED, IRQF_SHARED,
...@@ -386,8 +397,22 @@ int mpc85xx_pci_err_probe(struct platform_device *op) ...@@ -386,8 +397,22 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
devres_release_group(&op->dev, mpc85xx_pci_err_probe); devres_release_group(&op->dev, mpc85xx_pci_err_probe);
return res; 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 */ #endif /* CONFIG_PCI */
/**************************** L2 Err device ***************************/ /**************************** L2 Err device ***************************/
...@@ -1208,6 +1233,14 @@ static void __init mpc85xx_mc_clear_rfxe(void *data) ...@@ -1208,6 +1233,14 @@ static void __init mpc85xx_mc_clear_rfxe(void *data)
} }
#endif #endif
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) static int __init mpc85xx_mc_init(void)
{ {
int res = 0; int res = 0;
...@@ -1226,13 +1259,9 @@ static int __init mpc85xx_mc_init(void) ...@@ -1226,13 +1259,9 @@ static int __init mpc85xx_mc_init(void)
break; break;
} }
res = platform_driver_register(&mpc85xx_mc_err_driver); res = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
if (res)
printk(KERN_WARNING EDAC_MOD_STR "MC fails to register\n");
res = platform_driver_register(&mpc85xx_l2_err_driver);
if (res) if (res)
printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n"); printk(KERN_WARNING EDAC_MOD_STR "drivers fail to register\n");
#ifdef CONFIG_FSL_SOC_BOOKE #ifdef CONFIG_FSL_SOC_BOOKE
pvr = mfspr(SPRN_PVR); pvr = mfspr(SPRN_PVR);
...@@ -1270,8 +1299,7 @@ static void __exit mpc85xx_mc_exit(void) ...@@ -1270,8 +1299,7 @@ static void __exit mpc85xx_mc_exit(void)
on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0); on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0);
} }
#endif #endif
platform_driver_unregister(&mpc85xx_l2_err_driver); platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
platform_driver_unregister(&mpc85xx_mc_err_driver);
} }
module_exit(mpc85xx_mc_exit); module_exit(mpc85xx_mc_exit);
......
...@@ -847,6 +847,15 @@ static struct platform_driver mv64x60_mc_err_driver = { ...@@ -847,6 +847,15 @@ static struct platform_driver mv64x60_mc_err_driver = {
} }
}; };
static struct platform_driver * const drivers[] = {
&mv64x60_mc_err_driver,
&mv64x60_cpu_err_driver,
&mv64x60_sram_err_driver,
#ifdef CONFIG_PCI
&mv64x60_pci_err_driver,
#endif
};
static int __init mv64x60_edac_init(void) static int __init mv64x60_edac_init(void)
{ {
int ret = 0; int ret = 0;
...@@ -863,39 +872,13 @@ static int __init mv64x60_edac_init(void) ...@@ -863,39 +872,13 @@ static int __init mv64x60_edac_init(void)
break; break;
} }
ret = platform_driver_register(&mv64x60_mc_err_driver); return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
if (ret)
printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n");
ret = platform_driver_register(&mv64x60_cpu_err_driver);
if (ret)
printk(KERN_WARNING EDAC_MOD_STR
"CPU err failed to register\n");
ret = platform_driver_register(&mv64x60_sram_err_driver);
if (ret)
printk(KERN_WARNING EDAC_MOD_STR
"SRAM err failed to register\n");
#ifdef CONFIG_PCI
ret = platform_driver_register(&mv64x60_pci_err_driver);
if (ret)
printk(KERN_WARNING EDAC_MOD_STR
"PCI err failed to register\n");
#endif
return ret;
} }
module_init(mv64x60_edac_init); module_init(mv64x60_edac_init);
static void __exit mv64x60_edac_exit(void) static void __exit mv64x60_edac_exit(void)
{ {
#ifdef CONFIG_PCI platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
platform_driver_unregister(&mv64x60_pci_err_driver);
#endif
platform_driver_unregister(&mv64x60_sram_err_driver);
platform_driver_unregister(&mv64x60_cpu_err_driver);
platform_driver_unregister(&mv64x60_mc_err_driver);
} }
module_exit(mv64x60_edac_exit); module_exit(mv64x60_edac_exit);
......
...@@ -65,15 +65,20 @@ static const u32 ibridge_dram_rule[] = { ...@@ -65,15 +65,20 @@ static const u32 ibridge_dram_rule[] = {
0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8,
}; };
#define SAD_LIMIT(reg) ((GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff) static const u32 knl_dram_rule[] = {
#define DRAM_ATTR(reg) GET_BITFIELD(reg, 2, 3) 0x60, 0x68, 0x70, 0x78, 0x80, /* 0-4 */
#define INTERLEAVE_MODE(reg) GET_BITFIELD(reg, 1, 1) 0x88, 0x90, 0x98, 0xa0, 0xa8, /* 5-9 */
0xb0, 0xb8, 0xc0, 0xc8, 0xd0, /* 10-14 */
0xd8, 0xe0, 0xe8, 0xf0, 0xf8, /* 15-19 */
0x100, 0x108, 0x110, 0x118, /* 20-23 */
};
#define DRAM_RULE_ENABLE(reg) GET_BITFIELD(reg, 0, 0) #define DRAM_RULE_ENABLE(reg) GET_BITFIELD(reg, 0, 0)
#define A7MODE(reg) GET_BITFIELD(reg, 26, 26) #define A7MODE(reg) GET_BITFIELD(reg, 26, 26)
static char *get_dram_attr(u32 reg) static char *show_dram_attr(u32 attr)
{ {
switch(DRAM_ATTR(reg)) { switch (attr) {
case 0: case 0:
return "DRAM"; return "DRAM";
case 1: case 1:
...@@ -97,6 +102,14 @@ static const u32 ibridge_interleave_list[] = { ...@@ -97,6 +102,14 @@ static const u32 ibridge_interleave_list[] = {
0xdc, 0xe4, 0xec, 0xf4, 0xfc, 0xdc, 0xe4, 0xec, 0xf4, 0xfc,
}; };
static const u32 knl_interleave_list[] = {
0x64, 0x6c, 0x74, 0x7c, 0x84, /* 0-4 */
0x8c, 0x94, 0x9c, 0xa4, 0xac, /* 5-9 */
0xb4, 0xbc, 0xc4, 0xcc, 0xd4, /* 10-14 */
0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */
0x104, 0x10c, 0x114, 0x11c, /* 20-23 */
};
struct interleave_pkg { struct interleave_pkg {
unsigned char start; unsigned char start;
unsigned char end; unsigned char end;
...@@ -134,10 +147,13 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg, ...@@ -134,10 +147,13 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg,
/* Devices 12 Function 7 */ /* Devices 12 Function 7 */
#define TOLM 0x80 #define TOLM 0x80
#define TOHM 0x84 #define TOHM 0x84
#define HASWELL_TOLM 0xd0 #define HASWELL_TOLM 0xd0
#define HASWELL_TOHM_0 0xd4 #define HASWELL_TOHM_0 0xd4
#define HASWELL_TOHM_1 0xd8 #define HASWELL_TOHM_1 0xd8
#define KNL_TOLM 0xd0
#define KNL_TOHM_0 0xd4
#define KNL_TOHM_1 0xd8
#define GET_TOLM(reg) ((GET_BITFIELD(reg, 0, 3) << 28) | 0x3ffffff) #define GET_TOLM(reg) ((GET_BITFIELD(reg, 0, 3) << 28) | 0x3ffffff)
#define GET_TOHM(reg) ((GET_BITFIELD(reg, 0, 20) << 25) | 0x3ffffff) #define GET_TOHM(reg) ((GET_BITFIELD(reg, 0, 20) << 25) | 0x3ffffff)
...@@ -148,6 +164,8 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg, ...@@ -148,6 +164,8 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg,
#define SOURCE_ID(reg) GET_BITFIELD(reg, 9, 11) #define SOURCE_ID(reg) GET_BITFIELD(reg, 9, 11)
#define SOURCE_ID_KNL(reg) GET_BITFIELD(reg, 12, 14)
#define SAD_CONTROL 0xf4 #define SAD_CONTROL 0xf4
/* Device 14 function 0 */ /* Device 14 function 0 */
...@@ -170,6 +188,7 @@ static const u32 tad_dram_rule[] = { ...@@ -170,6 +188,7 @@ static const u32 tad_dram_rule[] = {
/* Device 15, function 0 */ /* Device 15, function 0 */
#define MCMTR 0x7c #define MCMTR 0x7c
#define KNL_MCMTR 0x624
#define IS_ECC_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 2, 2) #define IS_ECC_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 2, 2)
#define IS_LOCKSTEP_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 1, 1) #define IS_LOCKSTEP_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 1, 1)
...@@ -186,6 +205,8 @@ static const int mtr_regs[] = { ...@@ -186,6 +205,8 @@ static const int mtr_regs[] = {
0x80, 0x84, 0x88, 0x80, 0x84, 0x88,
}; };
static const int knl_mtr_reg = 0xb60;
#define RANK_DISABLE(mtr) GET_BITFIELD(mtr, 16, 19) #define RANK_DISABLE(mtr) GET_BITFIELD(mtr, 16, 19)
#define IS_DIMM_PRESENT(mtr) GET_BITFIELD(mtr, 14, 14) #define IS_DIMM_PRESENT(mtr) GET_BITFIELD(mtr, 14, 14)
#define RANK_CNT_BITS(mtr) GET_BITFIELD(mtr, 12, 13) #define RANK_CNT_BITS(mtr) GET_BITFIELD(mtr, 12, 13)
...@@ -256,6 +277,9 @@ static const u32 correrrthrsld[] = { ...@@ -256,6 +277,9 @@ static const u32 correrrthrsld[] = {
#define NUM_CHANNELS 8 /* 2MC per socket, four chan per MC */ #define NUM_CHANNELS 8 /* 2MC per socket, four chan per MC */
#define MAX_DIMMS 3 /* Max DIMMS per channel */ #define MAX_DIMMS 3 /* Max DIMMS per channel */
#define KNL_MAX_CHAS 38 /* KNL max num. of Cache Home Agents */
#define KNL_MAX_CHANNELS 6 /* KNL max num. of PCI channels */
#define KNL_MAX_EDCS 8 /* Embedded DRAM controllers */
#define CHANNEL_UNSPECIFIED 0xf /* Intel IA32 SDM 15-14 */ #define CHANNEL_UNSPECIFIED 0xf /* Intel IA32 SDM 15-14 */
enum type { enum type {
...@@ -263,6 +287,7 @@ enum type { ...@@ -263,6 +287,7 @@ enum type {
IVY_BRIDGE, IVY_BRIDGE,
HASWELL, HASWELL,
BROADWELL, BROADWELL,
KNIGHTS_LANDING,
}; };
struct sbridge_pvt; struct sbridge_pvt;
...@@ -273,6 +298,10 @@ struct sbridge_info { ...@@ -273,6 +298,10 @@ struct sbridge_info {
u64 (*get_tolm)(struct sbridge_pvt *pvt); u64 (*get_tolm)(struct sbridge_pvt *pvt);
u64 (*get_tohm)(struct sbridge_pvt *pvt); u64 (*get_tohm)(struct sbridge_pvt *pvt);
u64 (*rir_limit)(u32 reg); u64 (*rir_limit)(u32 reg);
u64 (*sad_limit)(u32 reg);
u32 (*interleave_mode)(u32 reg);
char* (*show_interleave_mode)(u32 reg);
u32 (*dram_attr)(u32 reg);
const u32 *dram_rule; const u32 *dram_rule;
const u32 *interleave_list; const u32 *interleave_list;
const struct interleave_pkg *interleave_pkg; const struct interleave_pkg *interleave_pkg;
...@@ -308,6 +337,16 @@ struct sbridge_dev { ...@@ -308,6 +337,16 @@ struct sbridge_dev {
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
}; };
struct knl_pvt {
struct pci_dev *pci_cha[KNL_MAX_CHAS];
struct pci_dev *pci_channel[KNL_MAX_CHANNELS];
struct pci_dev *pci_mc0;
struct pci_dev *pci_mc1;
struct pci_dev *pci_mc0_misc;
struct pci_dev *pci_mc1_misc;
struct pci_dev *pci_mc_info; /* tolm, tohm */
};
struct sbridge_pvt { struct sbridge_pvt {
struct pci_dev *pci_ta, *pci_ddrio, *pci_ras; struct pci_dev *pci_ta, *pci_ddrio, *pci_ras;
struct pci_dev *pci_sad0, *pci_sad1; struct pci_dev *pci_sad0, *pci_sad1;
...@@ -336,6 +375,7 @@ struct sbridge_pvt { ...@@ -336,6 +375,7 @@ struct sbridge_pvt {
/* Memory description */ /* Memory description */
u64 tolm, tohm; u64 tolm, tohm;
struct knl_pvt knl;
}; };
#define PCI_DESCR(device_id, opt) \ #define PCI_DESCR(device_id, opt) \
...@@ -509,6 +549,50 @@ static const struct pci_id_table pci_dev_descr_haswell_table[] = { ...@@ -509,6 +549,50 @@ static const struct pci_id_table pci_dev_descr_haswell_table[] = {
{0,} /* 0 terminated list. */ {0,} /* 0 terminated list. */
}; };
/* Knight's Landing Support */
/*
* KNL's memory channels are swizzled between memory controllers.
* MC0 is mapped to CH3,5,6 and MC1 is mapped to CH0,1,2
*/
#define knl_channel_remap(channel) ((channel + 3) % 6)
/* Memory controller, TAD tables, error injection - 2-8-0, 2-9-0 (2 of these) */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_MC 0x7840
/* DRAM channel stuff; bank addrs, dimmmtr, etc.. 2-8-2 - 2-9-4 (6 of these) */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_CHANNEL 0x7843
/* kdrwdbu TAD limits/offsets, MCMTR - 2-10-1, 2-11-1 (2 of these) */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_TA 0x7844
/* CHA broadcast registers, dram rules - 1-29-0 (1 of these) */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0 0x782a
/* SAD target - 1-29-1 (1 of these) */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_SAD1 0x782b
/* Caching / Home Agent */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_CHA 0x782c
/* Device with TOLM and TOHM, 0-5-0 (1 of these) */
#define PCI_DEVICE_ID_INTEL_KNL_IMC_TOLHM 0x7810
/*
* KNL differs from SB, IB, and Haswell in that it has multiple
* instances of the same device with the same device ID, so we handle that
* by creating as many copies in the table as we expect to find.
* (Like device ID must be grouped together.)
*/
static const struct pci_id_descr pci_dev_descr_knl[] = {
[0] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0, 0) },
[1] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_SAD1, 0) },
[2 ... 3] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_MC, 0)},
[4 ... 41] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_CHA, 0) },
[42 ... 47] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_CHANNEL, 0) },
[48] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_TA, 0) },
[49] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_TOLHM, 0) },
};
static const struct pci_id_table pci_dev_descr_knl_table[] = {
PCI_ID_TABLE_ENTRY(pci_dev_descr_knl),
{0,}
};
/* /*
* Broadwell support * Broadwell support
* *
...@@ -585,6 +669,7 @@ static const struct pci_device_id sbridge_pci_tbl[] = { ...@@ -585,6 +669,7 @@ static const struct pci_device_id sbridge_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0)},
{0,} /* 0 terminated list. */ {0,} /* 0 terminated list. */
}; };
...@@ -598,7 +683,7 @@ static inline int numrank(enum type type, u32 mtr) ...@@ -598,7 +683,7 @@ static inline int numrank(enum type type, u32 mtr)
int ranks = (1 << RANK_CNT_BITS(mtr)); int ranks = (1 << RANK_CNT_BITS(mtr));
int max = 4; int max = 4;
if (type == HASWELL || type == BROADWELL) if (type == HASWELL || type == BROADWELL || type == KNIGHTS_LANDING)
max = 8; max = 8;
if (ranks > max) { if (ranks > max) {
...@@ -636,10 +721,19 @@ static inline int numcol(u32 mtr) ...@@ -636,10 +721,19 @@ static inline int numcol(u32 mtr)
return 1 << cols; return 1 << cols;
} }
static struct sbridge_dev *get_sbridge_dev(u8 bus) static struct sbridge_dev *get_sbridge_dev(u8 bus, int multi_bus)
{ {
struct sbridge_dev *sbridge_dev; struct sbridge_dev *sbridge_dev;
/*
* If we have devices scattered across several busses that pertain
* to the same memory controller, we'll lump them all together.
*/
if (multi_bus) {
return list_first_entry_or_null(&sbridge_edac_list,
struct sbridge_dev, list);
}
list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) { list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
if (sbridge_dev->bus == bus) if (sbridge_dev->bus == bus)
return sbridge_dev; return sbridge_dev;
...@@ -718,6 +812,67 @@ static u64 rir_limit(u32 reg) ...@@ -718,6 +812,67 @@ static u64 rir_limit(u32 reg)
return ((u64)GET_BITFIELD(reg, 1, 10) << 29) | 0x1fffffff; return ((u64)GET_BITFIELD(reg, 1, 10) << 29) | 0x1fffffff;
} }
static u64 sad_limit(u32 reg)
{
return (GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff;
}
static u32 interleave_mode(u32 reg)
{
return GET_BITFIELD(reg, 1, 1);
}
char *show_interleave_mode(u32 reg)
{
return interleave_mode(reg) ? "8:6" : "[8:6]XOR[18:16]";
}
static u32 dram_attr(u32 reg)
{
return GET_BITFIELD(reg, 2, 3);
}
static u64 knl_sad_limit(u32 reg)
{
return (GET_BITFIELD(reg, 7, 26) << 26) | 0x3ffffff;
}
static u32 knl_interleave_mode(u32 reg)
{
return GET_BITFIELD(reg, 1, 2);
}
static char *knl_show_interleave_mode(u32 reg)
{
char *s;
switch (knl_interleave_mode(reg)) {
case 0:
s = "use address bits [8:6]";
break;
case 1:
s = "use address bits [10:8]";
break;
case 2:
s = "use address bits [14:12]";
break;
case 3:
s = "use address bits [32:30]";
break;
default:
WARN_ON(1);
break;
}
return s;
}
static u32 dram_attr_knl(u32 reg)
{
return GET_BITFIELD(reg, 3, 4);
}
static enum mem_type get_memory_type(struct sbridge_pvt *pvt) static enum mem_type get_memory_type(struct sbridge_pvt *pvt)
{ {
u32 reg; u32 reg;
...@@ -769,6 +924,12 @@ static enum mem_type haswell_get_memory_type(struct sbridge_pvt *pvt) ...@@ -769,6 +924,12 @@ static enum mem_type haswell_get_memory_type(struct sbridge_pvt *pvt)
return mtype; return mtype;
} }
static enum dev_type knl_get_width(struct sbridge_pvt *pvt, u32 mtr)
{
/* for KNL value is fixed */
return DEV_X16;
}
static enum dev_type sbridge_get_width(struct sbridge_pvt *pvt, u32 mtr) static enum dev_type sbridge_get_width(struct sbridge_pvt *pvt, u32 mtr)
{ {
/* there's no way to figure out */ /* there's no way to figure out */
...@@ -812,6 +973,12 @@ static enum dev_type broadwell_get_width(struct sbridge_pvt *pvt, u32 mtr) ...@@ -812,6 +973,12 @@ static enum dev_type broadwell_get_width(struct sbridge_pvt *pvt, u32 mtr)
return __ibridge_get_width(GET_BITFIELD(mtr, 8, 9)); return __ibridge_get_width(GET_BITFIELD(mtr, 8, 9));
} }
static enum mem_type knl_get_memory_type(struct sbridge_pvt *pvt)
{
/* DDR4 RDIMMS and LRDIMMS are supported */
return MEM_RDDR4;
}
static u8 get_node_id(struct sbridge_pvt *pvt) static u8 get_node_id(struct sbridge_pvt *pvt)
{ {
u32 reg; u32 reg;
...@@ -827,6 +994,15 @@ static u8 haswell_get_node_id(struct sbridge_pvt *pvt) ...@@ -827,6 +994,15 @@ static u8 haswell_get_node_id(struct sbridge_pvt *pvt)
return GET_BITFIELD(reg, 0, 3); return GET_BITFIELD(reg, 0, 3);
} }
static u8 knl_get_node_id(struct sbridge_pvt *pvt)
{
u32 reg;
pci_read_config_dword(pvt->pci_sad1, SAD_CONTROL, &reg);
return GET_BITFIELD(reg, 0, 2);
}
static u64 haswell_get_tolm(struct sbridge_pvt *pvt) static u64 haswell_get_tolm(struct sbridge_pvt *pvt)
{ {
u32 reg; u32 reg;
...@@ -848,6 +1024,26 @@ static u64 haswell_get_tohm(struct sbridge_pvt *pvt) ...@@ -848,6 +1024,26 @@ static u64 haswell_get_tohm(struct sbridge_pvt *pvt)
return rc | 0x1ffffff; return rc | 0x1ffffff;
} }
static u64 knl_get_tolm(struct sbridge_pvt *pvt)
{
u32 reg;
pci_read_config_dword(pvt->knl.pci_mc_info, KNL_TOLM, &reg);
return (GET_BITFIELD(reg, 26, 31) << 26) | 0x3ffffff;
}
static u64 knl_get_tohm(struct sbridge_pvt *pvt)
{
u64 rc;
u32 reg_lo, reg_hi;
pci_read_config_dword(pvt->knl.pci_mc_info, KNL_TOHM_0, &reg_lo);
pci_read_config_dword(pvt->knl.pci_mc_info, KNL_TOHM_1, &reg_hi);
rc = ((u64)reg_hi << 32) | reg_lo;
return rc | 0x3ffffff;
}
static u64 haswell_rir_limit(u32 reg) static u64 haswell_rir_limit(u32 reg)
{ {
return (((u64)GET_BITFIELD(reg, 1, 11) + 1) << 29) - 1; return (((u64)GET_BITFIELD(reg, 1, 11) + 1) << 29) - 1;
...@@ -905,11 +1101,22 @@ static int check_if_ecc_is_active(const u8 bus, enum type type) ...@@ -905,11 +1101,22 @@ static int check_if_ecc_is_active(const u8 bus, enum type type)
case BROADWELL: case BROADWELL:
id = PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA; id = PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA;
break; break;
case KNIGHTS_LANDING:
/*
* KNL doesn't group things by bus the same way
* SB/IB/Haswell does.
*/
id = PCI_DEVICE_ID_INTEL_KNL_IMC_TA;
break;
default: default:
return -ENODEV; return -ENODEV;
} }
pdev = get_pdev_same_bus(bus, id); if (type != KNIGHTS_LANDING)
pdev = get_pdev_same_bus(bus, id);
else
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, id, 0);
if (!pdev) { if (!pdev) {
sbridge_printk(KERN_ERR, "Couldn't find PCI device " sbridge_printk(KERN_ERR, "Couldn't find PCI device "
"%04x:%04x! on bus %02d\n", "%04x:%04x! on bus %02d\n",
...@@ -917,7 +1124,8 @@ static int check_if_ecc_is_active(const u8 bus, enum type type) ...@@ -917,7 +1124,8 @@ static int check_if_ecc_is_active(const u8 bus, enum type type)
return -ENODEV; return -ENODEV;
} }
pci_read_config_dword(pdev, MCMTR, &mcmtr); pci_read_config_dword(pdev,
type == KNIGHTS_LANDING ? KNL_MCMTR : MCMTR, &mcmtr);
if (!IS_ECC_ENABLED(mcmtr)) { if (!IS_ECC_ENABLED(mcmtr)) {
sbridge_printk(KERN_ERR, "ECC is disabled. Aborting\n"); sbridge_printk(KERN_ERR, "ECC is disabled. Aborting\n");
return -ENODEV; return -ENODEV;
...@@ -925,6 +1133,476 @@ static int check_if_ecc_is_active(const u8 bus, enum type type) ...@@ -925,6 +1133,476 @@ static int check_if_ecc_is_active(const u8 bus, enum type type)
return 0; return 0;
} }
/* Low bits of TAD limit, and some metadata. */
static const u32 knl_tad_dram_limit_lo[] = {
0x400, 0x500, 0x600, 0x700,
0x800, 0x900, 0xa00, 0xb00,
};
/* Low bits of TAD offset. */
static const u32 knl_tad_dram_offset_lo[] = {
0x404, 0x504, 0x604, 0x704,
0x804, 0x904, 0xa04, 0xb04,
};
/* High 16 bits of TAD limit and offset. */
static const u32 knl_tad_dram_hi[] = {
0x408, 0x508, 0x608, 0x708,
0x808, 0x908, 0xa08, 0xb08,
};
/* Number of ways a tad entry is interleaved. */
static const u32 knl_tad_ways[] = {
8, 6, 4, 3, 2, 1,
};
/*
* Retrieve the n'th Target Address Decode table entry
* from the memory controller's TAD table.
*
* @pvt: driver private data
* @entry: which entry you want to retrieve
* @mc: which memory controller (0 or 1)
* @offset: output tad range offset
* @limit: output address of first byte above tad range
* @ways: output number of interleave ways
*
* The offset value has curious semantics. It's a sort of running total
* of the sizes of all the memory regions that aren't mapped in this
* tad table.
*/
static int knl_get_tad(const struct sbridge_pvt *pvt,
const int entry,
const int mc,
u64 *offset,
u64 *limit,
int *ways)
{
u32 reg_limit_lo, reg_offset_lo, reg_hi;
struct pci_dev *pci_mc;
int way_id;
switch (mc) {
case 0:
pci_mc = pvt->knl.pci_mc0;
break;
case 1:
pci_mc = pvt->knl.pci_mc1;
break;
default:
WARN_ON(1);
return -EINVAL;
}
pci_read_config_dword(pci_mc,
knl_tad_dram_limit_lo[entry], &reg_limit_lo);
pci_read_config_dword(pci_mc,
knl_tad_dram_offset_lo[entry], &reg_offset_lo);
pci_read_config_dword(pci_mc,
knl_tad_dram_hi[entry], &reg_hi);
/* Is this TAD entry enabled? */
if (!GET_BITFIELD(reg_limit_lo, 0, 0))
return -ENODEV;
way_id = GET_BITFIELD(reg_limit_lo, 3, 5);
if (way_id < ARRAY_SIZE(knl_tad_ways)) {
*ways = knl_tad_ways[way_id];
} else {
*ways = 0;
sbridge_printk(KERN_ERR,
"Unexpected value %d in mc_tad_limit_lo wayness field\n",
way_id);
return -ENODEV;
}
/*
* The least significant 6 bits of base and limit are truncated.
* For limit, we fill the missing bits with 1s.
*/
*offset = ((u64) GET_BITFIELD(reg_offset_lo, 6, 31) << 6) |
((u64) GET_BITFIELD(reg_hi, 0, 15) << 32);
*limit = ((u64) GET_BITFIELD(reg_limit_lo, 6, 31) << 6) | 63 |
((u64) GET_BITFIELD(reg_hi, 16, 31) << 32);
return 0;
}
/* Determine which memory controller is responsible for a given channel. */
static int knl_channel_mc(int channel)
{
WARN_ON(channel < 0 || channel >= 6);
return channel < 3 ? 1 : 0;
}
/*
* Get the Nth entry from EDC_ROUTE_TABLE register.
* (This is the per-tile mapping of logical interleave targets to
* physical EDC modules.)
*
* entry 0: 0:2
* 1: 3:5
* 2: 6:8
* 3: 9:11
* 4: 12:14
* 5: 15:17
* 6: 18:20
* 7: 21:23
* reserved: 24:31
*/
static u32 knl_get_edc_route(int entry, u32 reg)
{
WARN_ON(entry >= KNL_MAX_EDCS);
return GET_BITFIELD(reg, entry*3, (entry*3)+2);
}
/*
* Get the Nth entry from MC_ROUTE_TABLE register.
* (This is the per-tile mapping of logical interleave targets to
* physical DRAM channels modules.)
*
* entry 0: mc 0:2 channel 18:19
* 1: mc 3:5 channel 20:21
* 2: mc 6:8 channel 22:23
* 3: mc 9:11 channel 24:25
* 4: mc 12:14 channel 26:27
* 5: mc 15:17 channel 28:29
* reserved: 30:31
*
* Though we have 3 bits to identify the MC, we should only see
* the values 0 or 1.
*/
static u32 knl_get_mc_route(int entry, u32 reg)
{
int mc, chan;
WARN_ON(entry >= KNL_MAX_CHANNELS);
mc = GET_BITFIELD(reg, entry*3, (entry*3)+2);
chan = GET_BITFIELD(reg, (entry*2) + 18, (entry*2) + 18 + 1);
return knl_channel_remap(mc*3 + chan);
}
/*
* Render the EDC_ROUTE register in human-readable form.
* Output string s should be at least KNL_MAX_EDCS*2 bytes.
*/
static void knl_show_edc_route(u32 reg, char *s)
{
int i;
for (i = 0; i < KNL_MAX_EDCS; i++) {
s[i*2] = knl_get_edc_route(i, reg) + '0';
s[i*2+1] = '-';
}
s[KNL_MAX_EDCS*2 - 1] = '\0';
}
/*
* Render the MC_ROUTE register in human-readable form.
* Output string s should be at least KNL_MAX_CHANNELS*2 bytes.
*/
static void knl_show_mc_route(u32 reg, char *s)
{
int i;
for (i = 0; i < KNL_MAX_CHANNELS; i++) {
s[i*2] = knl_get_mc_route(i, reg) + '0';
s[i*2+1] = '-';
}
s[KNL_MAX_CHANNELS*2 - 1] = '\0';
}
#define KNL_EDC_ROUTE 0xb8
#define KNL_MC_ROUTE 0xb4
/* Is this dram rule backed by regular DRAM in flat mode? */
#define KNL_EDRAM(reg) GET_BITFIELD(reg, 29, 29)
/* Is this dram rule cached? */
#define KNL_CACHEABLE(reg) GET_BITFIELD(reg, 28, 28)
/* Is this rule backed by edc ? */
#define KNL_EDRAM_ONLY(reg) GET_BITFIELD(reg, 29, 29)
/* Is this rule backed by DRAM, cacheable in EDRAM? */
#define KNL_CACHEABLE(reg) GET_BITFIELD(reg, 28, 28)
/* Is this rule mod3? */
#define KNL_MOD3(reg) GET_BITFIELD(reg, 27, 27)
/*
* Figure out how big our RAM modules are.
*
* The DIMMMTR register in KNL doesn't tell us the size of the DIMMs, so we
* have to figure this out from the SAD rules, interleave lists, route tables,
* and TAD rules.
*
* SAD rules can have holes in them (e.g. the 3G-4G hole), so we have to
* inspect the TAD rules to figure out how large the SAD regions really are.
*
* When we know the real size of a SAD region and how many ways it's
* interleaved, we know the individual contribution of each channel to
* TAD is size/ways.
*
* Finally, we have to check whether each channel participates in each SAD
* region.
*
* Fortunately, KNL only supports one DIMM per channel, so once we know how
* much memory the channel uses, we know the DIMM is at least that large.
* (The BIOS might possibly choose not to map all available memory, in which
* case we will underreport the size of the DIMM.)
*
* In theory, we could try to determine the EDC sizes as well, but that would
* only work in flat mode, not in cache mode.
*
* @mc_sizes: Output sizes of channels (must have space for KNL_MAX_CHANNELS
* elements)
*/
static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes)
{
u64 sad_base, sad_size, sad_limit = 0;
u64 tad_base, tad_size, tad_limit, tad_deadspace, tad_livespace;
int sad_rule = 0;
int tad_rule = 0;
int intrlv_ways, tad_ways;
u32 first_pkg, pkg;
int i;
u64 sad_actual_size[2]; /* sad size accounting for holes, per mc */
u32 dram_rule, interleave_reg;
u32 mc_route_reg[KNL_MAX_CHAS];
u32 edc_route_reg[KNL_MAX_CHAS];
int edram_only;
char edc_route_string[KNL_MAX_EDCS*2];
char mc_route_string[KNL_MAX_CHANNELS*2];
int cur_reg_start;
int mc;
int channel;
int way;
int participants[KNL_MAX_CHANNELS];
int participant_count = 0;
for (i = 0; i < KNL_MAX_CHANNELS; i++)
mc_sizes[i] = 0;
/* Read the EDC route table in each CHA. */
cur_reg_start = 0;
for (i = 0; i < KNL_MAX_CHAS; i++) {
pci_read_config_dword(pvt->knl.pci_cha[i],
KNL_EDC_ROUTE, &edc_route_reg[i]);
if (i > 0 && edc_route_reg[i] != edc_route_reg[i-1]) {
knl_show_edc_route(edc_route_reg[i-1],
edc_route_string);
if (cur_reg_start == i-1)
edac_dbg(0, "edc route table for CHA %d: %s\n",
cur_reg_start, edc_route_string);
else
edac_dbg(0, "edc route table for CHA %d-%d: %s\n",
cur_reg_start, i-1, edc_route_string);
cur_reg_start = i;
}
}
knl_show_edc_route(edc_route_reg[i-1], edc_route_string);
if (cur_reg_start == i-1)
edac_dbg(0, "edc route table for CHA %d: %s\n",
cur_reg_start, edc_route_string);
else
edac_dbg(0, "edc route table for CHA %d-%d: %s\n",
cur_reg_start, i-1, edc_route_string);
/* Read the MC route table in each CHA. */
cur_reg_start = 0;
for (i = 0; i < KNL_MAX_CHAS; i++) {
pci_read_config_dword(pvt->knl.pci_cha[i],
KNL_MC_ROUTE, &mc_route_reg[i]);
if (i > 0 && mc_route_reg[i] != mc_route_reg[i-1]) {
knl_show_mc_route(mc_route_reg[i-1], mc_route_string);
if (cur_reg_start == i-1)
edac_dbg(0, "mc route table for CHA %d: %s\n",
cur_reg_start, mc_route_string);
else
edac_dbg(0, "mc route table for CHA %d-%d: %s\n",
cur_reg_start, i-1, mc_route_string);
cur_reg_start = i;
}
}
knl_show_mc_route(mc_route_reg[i-1], mc_route_string);
if (cur_reg_start == i-1)
edac_dbg(0, "mc route table for CHA %d: %s\n",
cur_reg_start, mc_route_string);
else
edac_dbg(0, "mc route table for CHA %d-%d: %s\n",
cur_reg_start, i-1, mc_route_string);
/* Process DRAM rules */
for (sad_rule = 0; sad_rule < pvt->info.max_sad; sad_rule++) {
/* previous limit becomes the new base */
sad_base = sad_limit;
pci_read_config_dword(pvt->pci_sad0,
pvt->info.dram_rule[sad_rule], &dram_rule);
if (!DRAM_RULE_ENABLE(dram_rule))
break;
edram_only = KNL_EDRAM_ONLY(dram_rule);
sad_limit = pvt->info.sad_limit(dram_rule)+1;
sad_size = sad_limit - sad_base;
pci_read_config_dword(pvt->pci_sad0,
pvt->info.interleave_list[sad_rule], &interleave_reg);
/*
* Find out how many ways this dram rule is interleaved.
* We stop when we see the first channel again.
*/
first_pkg = sad_pkg(pvt->info.interleave_pkg,
interleave_reg, 0);
for (intrlv_ways = 1; intrlv_ways < 8; intrlv_ways++) {
pkg = sad_pkg(pvt->info.interleave_pkg,
interleave_reg, intrlv_ways);
if ((pkg & 0x8) == 0) {
/*
* 0 bit means memory is non-local,
* which KNL doesn't support
*/
edac_dbg(0, "Unexpected interleave target %d\n",
pkg);
return -1;
}
if (pkg == first_pkg)
break;
}
if (KNL_MOD3(dram_rule))
intrlv_ways *= 3;
edac_dbg(3, "dram rule %d (base 0x%llx, limit 0x%llx), %d way interleave%s\n",
sad_rule,
sad_base,
sad_limit,
intrlv_ways,
edram_only ? ", EDRAM" : "");
/*
* Find out how big the SAD region really is by iterating
* over TAD tables (SAD regions may contain holes).
* Each memory controller might have a different TAD table, so
* we have to look at both.
*
* Livespace is the memory that's mapped in this TAD table,
* deadspace is the holes (this could be the MMIO hole, or it
* could be memory that's mapped by the other TAD table but
* not this one).
*/
for (mc = 0; mc < 2; mc++) {
sad_actual_size[mc] = 0;
tad_livespace = 0;
for (tad_rule = 0;
tad_rule < ARRAY_SIZE(
knl_tad_dram_limit_lo);
tad_rule++) {
if (knl_get_tad(pvt,
tad_rule,
mc,
&tad_deadspace,
&tad_limit,
&tad_ways))
break;
tad_size = (tad_limit+1) -
(tad_livespace + tad_deadspace);
tad_livespace += tad_size;
tad_base = (tad_limit+1) - tad_size;
if (tad_base < sad_base) {
if (tad_limit > sad_base)
edac_dbg(0, "TAD region overlaps lower SAD boundary -- TAD tables may be configured incorrectly.\n");
} else if (tad_base < sad_limit) {
if (tad_limit+1 > sad_limit) {
edac_dbg(0, "TAD region overlaps upper SAD boundary -- TAD tables may be configured incorrectly.\n");
} else {
/* TAD region is completely inside SAD region */
edac_dbg(3, "TAD region %d 0x%llx - 0x%llx (%lld bytes) table%d\n",
tad_rule, tad_base,
tad_limit, tad_size,
mc);
sad_actual_size[mc] += tad_size;
}
}
tad_base = tad_limit+1;
}
}
for (mc = 0; mc < 2; mc++) {
edac_dbg(3, " total TAD DRAM footprint in table%d : 0x%llx (%lld bytes)\n",
mc, sad_actual_size[mc], sad_actual_size[mc]);
}
/* Ignore EDRAM rule */
if (edram_only)
continue;
/* Figure out which channels participate in interleave. */
for (channel = 0; channel < KNL_MAX_CHANNELS; channel++)
participants[channel] = 0;
/* For each channel, does at least one CHA have
* this channel mapped to the given target?
*/
for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
for (way = 0; way < intrlv_ways; way++) {
int target;
int cha;
if (KNL_MOD3(dram_rule))
target = way;
else
target = 0x7 & sad_pkg(
pvt->info.interleave_pkg, interleave_reg, way);
for (cha = 0; cha < KNL_MAX_CHAS; cha++) {
if (knl_get_mc_route(target,
mc_route_reg[cha]) == channel
&& participants[channel]) {
participant_count++;
participants[channel] = 1;
break;
}
}
}
}
if (participant_count != intrlv_ways)
edac_dbg(0, "participant_count (%d) != interleave_ways (%d): DIMM size may be incorrect\n",
participant_count, intrlv_ways);
for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
mc = knl_channel_mc(channel);
if (participants[channel]) {
edac_dbg(4, "mc channel %d contributes %lld bytes via sad entry %d\n",
channel,
sad_actual_size[mc]/intrlv_ways,
sad_rule);
mc_sizes[channel] +=
sad_actual_size[mc]/intrlv_ways;
}
}
}
return 0;
}
static int get_dimm_config(struct mem_ctl_info *mci) static int get_dimm_config(struct mem_ctl_info *mci)
{ {
struct sbridge_pvt *pvt = mci->pvt_info; struct sbridge_pvt *pvt = mci->pvt_info;
...@@ -934,13 +1612,20 @@ static int get_dimm_config(struct mem_ctl_info *mci) ...@@ -934,13 +1612,20 @@ static int get_dimm_config(struct mem_ctl_info *mci)
u32 reg; u32 reg;
enum edac_type mode; enum edac_type mode;
enum mem_type mtype; enum mem_type mtype;
int channels = pvt->info.type == KNIGHTS_LANDING ?
KNL_MAX_CHANNELS : NUM_CHANNELS;
u64 knl_mc_sizes[KNL_MAX_CHANNELS];
if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL) if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL ||
pvt->info.type == KNIGHTS_LANDING)
pci_read_config_dword(pvt->pci_sad1, SAD_TARGET, &reg); pci_read_config_dword(pvt->pci_sad1, SAD_TARGET, &reg);
else else
pci_read_config_dword(pvt->pci_br0, SAD_TARGET, &reg); pci_read_config_dword(pvt->pci_br0, SAD_TARGET, &reg);
pvt->sbridge_dev->source_id = SOURCE_ID(reg); if (pvt->info.type == KNIGHTS_LANDING)
pvt->sbridge_dev->source_id = SOURCE_ID_KNL(reg);
else
pvt->sbridge_dev->source_id = SOURCE_ID(reg);
pvt->sbridge_dev->node_id = pvt->info.get_node_id(pvt); pvt->sbridge_dev->node_id = pvt->info.get_node_id(pvt);
edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n", edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n",
...@@ -948,31 +1633,42 @@ static int get_dimm_config(struct mem_ctl_info *mci) ...@@ -948,31 +1633,42 @@ static int get_dimm_config(struct mem_ctl_info *mci)
pvt->sbridge_dev->node_id, pvt->sbridge_dev->node_id,
pvt->sbridge_dev->source_id); pvt->sbridge_dev->source_id);
pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg); /* KNL doesn't support mirroring or lockstep,
if (IS_MIRROR_ENABLED(reg)) { * and is always closed page
edac_dbg(0, "Memory mirror is enabled\n"); */
pvt->is_mirrored = true; if (pvt->info.type == KNIGHTS_LANDING) {
} else { mode = EDAC_S4ECD4ED;
edac_dbg(0, "Memory mirror is disabled\n");
pvt->is_mirrored = false; pvt->is_mirrored = false;
}
pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr); if (knl_get_dimm_capacity(pvt, knl_mc_sizes) != 0)
if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) { return -1;
edac_dbg(0, "Lockstep is enabled\n");
mode = EDAC_S8ECD8ED;
pvt->is_lockstep = true;
} else { } else {
edac_dbg(0, "Lockstep is disabled\n"); pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
mode = EDAC_S4ECD4ED; if (IS_MIRROR_ENABLED(reg)) {
pvt->is_lockstep = false; edac_dbg(0, "Memory mirror is enabled\n");
} pvt->is_mirrored = true;
if (IS_CLOSE_PG(pvt->info.mcmtr)) { } else {
edac_dbg(0, "address map is on closed page mode\n"); edac_dbg(0, "Memory mirror is disabled\n");
pvt->is_close_pg = true; pvt->is_mirrored = false;
} else { }
edac_dbg(0, "address map is on open page mode\n");
pvt->is_close_pg = false; pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr);
if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) {
edac_dbg(0, "Lockstep is enabled\n");
mode = EDAC_S8ECD8ED;
pvt->is_lockstep = true;
} else {
edac_dbg(0, "Lockstep is disabled\n");
mode = EDAC_S4ECD4ED;
pvt->is_lockstep = false;
}
if (IS_CLOSE_PG(pvt->info.mcmtr)) {
edac_dbg(0, "address map is on closed page mode\n");
pvt->is_close_pg = true;
} else {
edac_dbg(0, "address map is on open page mode\n");
pvt->is_close_pg = false;
}
} }
mtype = pvt->info.get_memory_type(pvt); mtype = pvt->info.get_memory_type(pvt);
...@@ -988,23 +1684,46 @@ static int get_dimm_config(struct mem_ctl_info *mci) ...@@ -988,23 +1684,46 @@ static int get_dimm_config(struct mem_ctl_info *mci)
else else
banks = 8; banks = 8;
for (i = 0; i < NUM_CHANNELS; i++) { for (i = 0; i < channels; i++) {
u32 mtr; u32 mtr;
if (!pvt->pci_tad[i]) int max_dimms_per_channel;
continue;
for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) { if (pvt->info.type == KNIGHTS_LANDING) {
max_dimms_per_channel = 1;
if (!pvt->knl.pci_channel[i])
continue;
} else {
max_dimms_per_channel = ARRAY_SIZE(mtr_regs);
if (!pvt->pci_tad[i])
continue;
}
for (j = 0; j < max_dimms_per_channel; j++) {
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers, dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
i, j, 0); i, j, 0);
pci_read_config_dword(pvt->pci_tad[i], if (pvt->info.type == KNIGHTS_LANDING) {
mtr_regs[j], &mtr); pci_read_config_dword(pvt->knl.pci_channel[i],
knl_mtr_reg, &mtr);
} else {
pci_read_config_dword(pvt->pci_tad[i],
mtr_regs[j], &mtr);
}
edac_dbg(4, "Channel #%d MTR%d = %x\n", i, j, mtr); edac_dbg(4, "Channel #%d MTR%d = %x\n", i, j, mtr);
if (IS_DIMM_PRESENT(mtr)) { if (IS_DIMM_PRESENT(mtr)) {
pvt->channel[i].dimms++; pvt->channel[i].dimms++;
ranks = numrank(pvt->info.type, mtr); ranks = numrank(pvt->info.type, mtr);
rows = numrow(mtr);
cols = numcol(mtr); if (pvt->info.type == KNIGHTS_LANDING) {
/* For DDR4, this is fixed. */
cols = 1 << 10;
rows = knl_mc_sizes[i] /
((u64) cols * ranks * banks * 8);
} else {
rows = numrow(mtr);
cols = numcol(mtr);
}
size = ((u64)rows * cols * banks * ranks) >> (20 - 3); size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size); npages = MiB_TO_PAGES(size);
...@@ -1069,7 +1788,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci) ...@@ -1069,7 +1788,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
/* SAD_LIMIT Address range is 45:26 */ /* SAD_LIMIT Address range is 45:26 */
pci_read_config_dword(pvt->pci_sad0, pvt->info.dram_rule[n_sads], pci_read_config_dword(pvt->pci_sad0, pvt->info.dram_rule[n_sads],
&reg); &reg);
limit = SAD_LIMIT(reg); limit = pvt->info.sad_limit(reg);
if (!DRAM_RULE_ENABLE(reg)) if (!DRAM_RULE_ENABLE(reg))
continue; continue;
...@@ -1081,10 +1800,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci) ...@@ -1081,10 +1800,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
gb = div_u64_rem(tmp_mb, 1024, &mb); gb = div_u64_rem(tmp_mb, 1024, &mb);
edac_dbg(0, "SAD#%d %s up to %u.%03u GB (0x%016Lx) Interleave: %s reg=0x%08x\n", edac_dbg(0, "SAD#%d %s up to %u.%03u GB (0x%016Lx) Interleave: %s reg=0x%08x\n",
n_sads, n_sads,
get_dram_attr(reg), show_dram_attr(pvt->info.dram_attr(reg)),
gb, (mb*1000)/1024, gb, (mb*1000)/1024,
((u64)tmp_mb) << 20L, ((u64)tmp_mb) << 20L,
INTERLEAVE_MODE(reg) ? "8:6" : "[8:6]XOR[18:16]", pvt->info.show_interleave_mode(reg),
reg); reg);
prv = limit; prv = limit;
...@@ -1101,6 +1820,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci) ...@@ -1101,6 +1820,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
} }
} }
if (pvt->info.type == KNIGHTS_LANDING)
return;
/* /*
* Step 3) Get TAD range * Step 3) Get TAD range
*/ */
...@@ -1248,7 +1970,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci, ...@@ -1248,7 +1970,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
if (!DRAM_RULE_ENABLE(reg)) if (!DRAM_RULE_ENABLE(reg))
continue; continue;
limit = SAD_LIMIT(reg); limit = pvt->info.sad_limit(reg);
if (limit <= prv) { if (limit <= prv) {
sprintf(msg, "Can't discover the memory socket"); sprintf(msg, "Can't discover the memory socket");
return -EINVAL; return -EINVAL;
...@@ -1262,8 +1984,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci, ...@@ -1262,8 +1984,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
return -EINVAL; return -EINVAL;
} }
dram_rule = reg; dram_rule = reg;
*area_type = get_dram_attr(dram_rule); *area_type = show_dram_attr(pvt->info.dram_attr(dram_rule));
interleave_mode = INTERLEAVE_MODE(dram_rule); interleave_mode = pvt->info.interleave_mode(dram_rule);
pci_read_config_dword(pvt->pci_sad0, pvt->info.interleave_list[n_sads], pci_read_config_dword(pvt->pci_sad0, pvt->info.interleave_list[n_sads],
&reg); &reg);
...@@ -1567,7 +2289,8 @@ static void sbridge_put_all_devices(void) ...@@ -1567,7 +2289,8 @@ static void sbridge_put_all_devices(void)
static int sbridge_get_onedevice(struct pci_dev **prev, static int sbridge_get_onedevice(struct pci_dev **prev,
u8 *num_mc, u8 *num_mc,
const struct pci_id_table *table, const struct pci_id_table *table,
const unsigned devno) const unsigned devno,
const int multi_bus)
{ {
struct sbridge_dev *sbridge_dev; struct sbridge_dev *sbridge_dev;
const struct pci_id_descr *dev_descr = &table->descr[devno]; const struct pci_id_descr *dev_descr = &table->descr[devno];
...@@ -1603,7 +2326,7 @@ static int sbridge_get_onedevice(struct pci_dev **prev, ...@@ -1603,7 +2326,7 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
} }
bus = pdev->bus->number; bus = pdev->bus->number;
sbridge_dev = get_sbridge_dev(bus); sbridge_dev = get_sbridge_dev(bus, multi_bus);
if (!sbridge_dev) { if (!sbridge_dev) {
sbridge_dev = alloc_sbridge_dev(bus, table); sbridge_dev = alloc_sbridge_dev(bus, table);
if (!sbridge_dev) { if (!sbridge_dev) {
...@@ -1652,21 +2375,32 @@ static int sbridge_get_onedevice(struct pci_dev **prev, ...@@ -1652,21 +2375,32 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
* @num_mc: pointer to the memory controllers count, to be incremented in case * @num_mc: pointer to the memory controllers count, to be incremented in case
* of success. * of success.
* @table: model specific table * @table: model specific table
* @allow_dups: allow for multiple devices to exist with the same device id
* (as implemented, this isn't expected to work correctly in the
* multi-socket case).
* @multi_bus: don't assume devices on different buses belong to different
* memory controllers.
* *
* returns 0 in case of success or error code * returns 0 in case of success or error code
*/ */
static int sbridge_get_all_devices(u8 *num_mc, static int sbridge_get_all_devices_full(u8 *num_mc,
const struct pci_id_table *table) const struct pci_id_table *table,
int allow_dups,
int multi_bus)
{ {
int i, rc; int i, rc;
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
while (table && table->descr) { while (table && table->descr) {
for (i = 0; i < table->n_devs; i++) { for (i = 0; i < table->n_devs; i++) {
pdev = NULL; if (!allow_dups || i == 0 ||
table->descr[i].dev_id !=
table->descr[i-1].dev_id) {
pdev = NULL;
}
do { do {
rc = sbridge_get_onedevice(&pdev, num_mc, rc = sbridge_get_onedevice(&pdev, num_mc,
table, i); table, i, multi_bus);
if (rc < 0) { if (rc < 0) {
if (i == 0) { if (i == 0) {
i = table->n_devs; i = table->n_devs;
...@@ -1675,7 +2409,7 @@ static int sbridge_get_all_devices(u8 *num_mc, ...@@ -1675,7 +2409,7 @@ static int sbridge_get_all_devices(u8 *num_mc,
sbridge_put_all_devices(); sbridge_put_all_devices();
return -ENODEV; return -ENODEV;
} }
} while (pdev); } while (pdev && !allow_dups);
} }
table++; table++;
} }
...@@ -1683,6 +2417,11 @@ static int sbridge_get_all_devices(u8 *num_mc, ...@@ -1683,6 +2417,11 @@ static int sbridge_get_all_devices(u8 *num_mc,
return 0; return 0;
} }
#define sbridge_get_all_devices(num_mc, table) \
sbridge_get_all_devices_full(num_mc, table, 0, 0)
#define sbridge_get_all_devices_knl(num_mc, table) \
sbridge_get_all_devices_full(num_mc, table, 1, 1)
static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
struct sbridge_dev *sbridge_dev) struct sbridge_dev *sbridge_dev)
{ {
...@@ -2038,6 +2777,131 @@ static int broadwell_mci_bind_devs(struct mem_ctl_info *mci, ...@@ -2038,6 +2777,131 @@ static int broadwell_mci_bind_devs(struct mem_ctl_info *mci,
return -ENODEV; return -ENODEV;
} }
static int knl_mci_bind_devs(struct mem_ctl_info *mci,
struct sbridge_dev *sbridge_dev)
{
struct sbridge_pvt *pvt = mci->pvt_info;
struct pci_dev *pdev;
int dev, func;
int i;
int devidx;
for (i = 0; i < sbridge_dev->n_devs; i++) {
pdev = sbridge_dev->pdev[i];
if (!pdev)
continue;
/* Extract PCI device and function. */
dev = (pdev->devfn >> 3) & 0x1f;
func = pdev->devfn & 0x7;
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_KNL_IMC_MC:
if (dev == 8)
pvt->knl.pci_mc0 = pdev;
else if (dev == 9)
pvt->knl.pci_mc1 = pdev;
else {
sbridge_printk(KERN_ERR,
"Memory controller in unexpected place! (dev %d, fn %d)\n",
dev, func);
continue;
}
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0:
pvt->pci_sad0 = pdev;
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD1:
pvt->pci_sad1 = pdev;
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_CHA:
/* There are one of these per tile, and range from
* 1.14.0 to 1.18.5.
*/
devidx = ((dev-14)*8)+func;
if (devidx < 0 || devidx >= KNL_MAX_CHAS) {
sbridge_printk(KERN_ERR,
"Caching and Home Agent in unexpected place! (dev %d, fn %d)\n",
dev, func);
continue;
}
WARN_ON(pvt->knl.pci_cha[devidx] != NULL);
pvt->knl.pci_cha[devidx] = pdev;
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_CHANNEL:
devidx = -1;
/*
* MC0 channels 0-2 are device 9 function 2-4,
* MC1 channels 3-5 are device 8 function 2-4.
*/
if (dev == 9)
devidx = func-2;
else if (dev == 8)
devidx = 3 + (func-2);
if (devidx < 0 || devidx >= KNL_MAX_CHANNELS) {
sbridge_printk(KERN_ERR,
"DRAM Channel Registers in unexpected place! (dev %d, fn %d)\n",
dev, func);
continue;
}
WARN_ON(pvt->knl.pci_channel[devidx] != NULL);
pvt->knl.pci_channel[devidx] = pdev;
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_TOLHM:
pvt->knl.pci_mc_info = pdev;
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_TA:
pvt->pci_ta = pdev;
break;
default:
sbridge_printk(KERN_ERR, "Unexpected device %d\n",
pdev->device);
break;
}
}
if (!pvt->knl.pci_mc0 || !pvt->knl.pci_mc1 ||
!pvt->pci_sad0 || !pvt->pci_sad1 ||
!pvt->pci_ta) {
goto enodev;
}
for (i = 0; i < KNL_MAX_CHANNELS; i++) {
if (!pvt->knl.pci_channel[i]) {
sbridge_printk(KERN_ERR, "Missing channel %d\n", i);
goto enodev;
}
}
for (i = 0; i < KNL_MAX_CHAS; i++) {
if (!pvt->knl.pci_cha[i]) {
sbridge_printk(KERN_ERR, "Missing CHA %d\n", i);
goto enodev;
}
}
return 0;
enodev:
sbridge_printk(KERN_ERR, "Some needed devices are missing\n");
return -ENODEV;
}
/**************************************************************************** /****************************************************************************
Error check routines Error check routines
****************************************************************************/ ****************************************************************************/
...@@ -2127,8 +2991,36 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci, ...@@ -2127,8 +2991,36 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
if (!GET_BITFIELD(m->status, 58, 58)) if (!GET_BITFIELD(m->status, 58, 58))
return; return;
rc = get_memory_error_data(mci, m->addr, &socket, &ha, if (pvt->info.type == KNIGHTS_LANDING) {
&channel_mask, &rank, &area_type, msg); if (channel == 14) {
edac_dbg(0, "%s%s err_code:%04x:%04x EDRAM bank %d\n",
overflow ? " OVERFLOW" : "",
(uncorrected_error && recoverable)
? " recoverable" : "",
mscod, errcode,
m->bank);
} else {
char A = *("A");
channel = knl_channel_remap(channel);
channel_mask = 1 << channel;
snprintf(msg, sizeof(msg),
"%s%s err_code:%04x:%04x channel:%d (DIMM_%c)",
overflow ? " OVERFLOW" : "",
(uncorrected_error && recoverable)
? " recoverable" : " ",
mscod, errcode, channel, A + channel);
edac_mc_handle_error(tp_event, mci, core_err_cnt,
m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
channel, 0, -1,
optype, msg);
}
return;
} else {
rc = get_memory_error_data(mci, m->addr, &socket, &ha,
&channel_mask, &rank, &area_type, msg);
}
if (rc < 0) if (rc < 0)
goto err_parsing; goto err_parsing;
new_mci = get_mci_for_node_id(socket); new_mci = get_mci_for_node_id(socket);
...@@ -2359,10 +3251,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2359,10 +3251,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
/* allocate a new MC control structure */ /* allocate a new MC control structure */
layers[0].type = EDAC_MC_LAYER_CHANNEL; layers[0].type = EDAC_MC_LAYER_CHANNEL;
layers[0].size = NUM_CHANNELS; layers[0].size = type == KNIGHTS_LANDING ?
KNL_MAX_CHANNELS : NUM_CHANNELS;
layers[0].is_virt_csrow = false; layers[0].is_virt_csrow = false;
layers[1].type = EDAC_MC_LAYER_SLOT; layers[1].type = EDAC_MC_LAYER_SLOT;
layers[1].size = MAX_DIMMS; layers[1].size = type == KNIGHTS_LANDING ? 1 : MAX_DIMMS;
layers[1].is_virt_csrow = true; layers[1].is_virt_csrow = true;
mci = edac_mc_alloc(sbridge_dev->mc, ARRAY_SIZE(layers), layers, mci = edac_mc_alloc(sbridge_dev->mc, ARRAY_SIZE(layers), layers,
sizeof(*pvt)); sizeof(*pvt));
...@@ -2380,7 +3273,8 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2380,7 +3273,8 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->sbridge_dev = sbridge_dev; pvt->sbridge_dev = sbridge_dev;
sbridge_dev->mci = mci; sbridge_dev->mci = mci;
mci->mtype_cap = MEM_FLAG_DDR3; mci->mtype_cap = type == KNIGHTS_LANDING ?
MEM_FLAG_DDR4 : MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = "sbridge_edac.c"; mci->mod_name = "sbridge_edac.c";
...@@ -2401,6 +3295,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2401,6 +3295,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = get_memory_type; pvt->info.get_memory_type = get_memory_type;
pvt->info.get_node_id = get_node_id; pvt->info.get_node_id = get_node_id;
pvt->info.rir_limit = rir_limit; pvt->info.rir_limit = rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list; pvt->info.interleave_list = ibridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
...@@ -2421,6 +3319,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2421,6 +3319,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = get_memory_type; pvt->info.get_memory_type = get_memory_type;
pvt->info.get_node_id = get_node_id; pvt->info.get_node_id = get_node_id;
pvt->info.rir_limit = rir_limit; pvt->info.rir_limit = rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
pvt->info.interleave_list = sbridge_interleave_list; pvt->info.interleave_list = sbridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list); pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list);
...@@ -2441,6 +3343,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2441,6 +3343,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = haswell_get_memory_type; pvt->info.get_memory_type = haswell_get_memory_type;
pvt->info.get_node_id = haswell_get_node_id; pvt->info.get_node_id = haswell_get_node_id;
pvt->info.rir_limit = haswell_rir_limit; pvt->info.rir_limit = haswell_rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list; pvt->info.interleave_list = ibridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
...@@ -2461,6 +3367,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2461,6 +3367,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = haswell_get_memory_type; pvt->info.get_memory_type = haswell_get_memory_type;
pvt->info.get_node_id = haswell_get_node_id; pvt->info.get_node_id = haswell_get_node_id;
pvt->info.rir_limit = haswell_rir_limit; pvt->info.rir_limit = haswell_rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list; pvt->info.interleave_list = ibridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
...@@ -2473,6 +3383,30 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -2473,6 +3383,30 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
if (unlikely(rc < 0)) if (unlikely(rc < 0))
goto fail0; goto fail0;
break; break;
case KNIGHTS_LANDING:
/* pvt->info.rankcfgr == ??? */
pvt->info.get_tolm = knl_get_tolm;
pvt->info.get_tohm = knl_get_tohm;
pvt->info.dram_rule = knl_dram_rule;
pvt->info.get_memory_type = knl_get_memory_type;
pvt->info.get_node_id = knl_get_node_id;
pvt->info.rir_limit = NULL;
pvt->info.sad_limit = knl_sad_limit;
pvt->info.interleave_mode = knl_interleave_mode;
pvt->info.show_interleave_mode = knl_show_interleave_mode;
pvt->info.dram_attr = dram_attr_knl;
pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule);
pvt->info.interleave_list = knl_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(knl_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = knl_get_width;
mci->ctl_name = kasprintf(GFP_KERNEL,
"Knights Landing Socket#%d", mci->mc_idx);
rc = knl_mci_bind_devs(mci, sbridge_dev);
if (unlikely(rc < 0))
goto fail0;
break;
} }
/* Get dimm basic config and the memory layout */ /* Get dimm basic config and the memory layout */
...@@ -2527,20 +3461,29 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -2527,20 +3461,29 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
switch (pdev->device) { switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA: case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA:
rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_ibridge_table); rc = sbridge_get_all_devices(&num_mc,
pci_dev_descr_ibridge_table);
type = IVY_BRIDGE; type = IVY_BRIDGE;
break; break;
case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0: case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0:
rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_sbridge_table); rc = sbridge_get_all_devices(&num_mc,
pci_dev_descr_sbridge_table);
type = SANDY_BRIDGE; type = SANDY_BRIDGE;
break; break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0: case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0:
rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_haswell_table); rc = sbridge_get_all_devices(&num_mc,
pci_dev_descr_haswell_table);
type = HASWELL; type = HASWELL;
break; break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0: case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0:
rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_broadwell_table); rc = sbridge_get_all_devices(&num_mc,
pci_dev_descr_broadwell_table);
type = BROADWELL; type = BROADWELL;
break;
case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0:
rc = sbridge_get_all_devices_knl(&num_mc,
pci_dev_descr_knl_table);
type = KNIGHTS_LANDING;
break; break;
} }
if (unlikely(rc < 0)) { if (unlikely(rc < 0)) {
......
#include "edac_module.h"
static struct workqueue_struct *wq;
bool edac_queue_work(struct delayed_work *work, unsigned long delay)
{
return queue_delayed_work(wq, work, delay);
}
EXPORT_SYMBOL_GPL(edac_queue_work);
bool edac_mod_work(struct delayed_work *work, unsigned long delay)
{
return mod_delayed_work(wq, work, delay);
}
EXPORT_SYMBOL_GPL(edac_mod_work);
bool edac_stop_work(struct delayed_work *work)
{
bool ret;
ret = cancel_delayed_work_sync(work);
flush_workqueue(wq);
return ret;
}
EXPORT_SYMBOL_GPL(edac_stop_work);
int edac_workqueue_setup(void)
{
wq = create_singlethread_workqueue("edac-poller");
if (!wq)
return -ENODEV;
else
return 0;
}
void edac_workqueue_teardown(void)
{
flush_workqueue(wq);
destroy_workqueue(wq);
wq = NULL;
}
...@@ -28,12 +28,10 @@ struct device; ...@@ -28,12 +28,10 @@ struct device;
extern int edac_op_state; extern int edac_op_state;
extern int edac_err_assert; extern int edac_err_assert;
extern atomic_t edac_handlers; extern atomic_t edac_handlers;
extern struct bus_type edac_subsys;
extern int edac_handler_set(void); extern int edac_handler_set(void);
extern void edac_atomic_assert_error(void); extern void edac_atomic_assert_error(void);
extern struct bus_type *edac_get_sysfs_subsys(void); extern struct bus_type *edac_get_sysfs_subsys(void);
extern void edac_put_sysfs_subsys(void);
enum { enum {
EDAC_REPORTING_ENABLED, EDAC_REPORTING_ENABLED,
...@@ -237,8 +235,10 @@ enum mem_type { ...@@ -237,8 +235,10 @@ enum mem_type {
#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2) #define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2) #define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
#define MEM_FLAG_XDR BIT(MEM_XDR) #define MEM_FLAG_XDR BIT(MEM_XDR)
#define MEM_FLAG_DDR3 BIT(MEM_DDR3) #define MEM_FLAG_DDR3 BIT(MEM_DDR3)
#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3) #define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
#define MEM_FLAG_DDR4 BIT(MEM_DDR4)
#define MEM_FLAG_RDDR4 BIT(MEM_RDDR4)
/** /**
* enum edac-type - Error Detection and Correction capabilities and mode * enum edac-type - Error Detection and Correction capabilities and mode
......
#ifndef FSL_EDAC_H
#define FSL_EDAC_H
struct mpc85xx_edac_pci_plat_data {
struct device_node *of_node;
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment