diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-16 11:36:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-16 11:36:55 -0400 |
commit | 047486d8e7c2a7e8d75b068b69cb67b47364f5d4 (patch) | |
tree | 8c9b5f7a68128f9b9a695717e662918c1683996c /drivers/edac | |
parent | 9256d5a308c95a50c6e85d682492ae1f86a70f9b (diff) | |
parent | 7cc5a5d3cd4cca0a3852d1500e8c50fe191bcc9d (diff) |
Merge tag 'edac_for_4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
Pull EDAC updates from Borislav Petkov:
- Altera: L2 cache and On-Chip RAM support (Thor Thayer).
- EDAC: Workqueue handling cleanups (Borislav Petkov).
- Xgene: Register bus error handling (Loc Ho).
- Misc small fixes.
* tag 'edac_for_4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp:
ARM: socfpga: Enable OCRAM ECC on startup
ARM: socfpga: Enable L2 cache ECC on startup
ARM: dts: Add Altera L2 Cache and OCRAM EDAC entries
EDAC, altera: Add Altera L2 cache and OCRAM support
EDAC: Use edac_debugfs_remove_recursive() in edac_debugfs_exit()
EDAC, mpc85xx: Silence unused variable warning
EDAC: Cleanup/sync workqueue functions
EDAC: Kill workqueue setup/teardown functions
EDAC: Balance workqueue setup and teardown
arm64: Update the APM X-Gene EDAC node with the RB register resource
EDAC, xgene: Add missing SoC register bus error handling
Documentation, EDAC: Update xgene binding for missing register bus
EDAC, amd64_edac: Shift wrapping issue in f1x_get_norm_dct_addr()
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/Kconfig | 26 | ||||
-rw-r--r-- | drivers/edac/Makefile | 2 | ||||
-rw-r--r-- | drivers/edac/altera_edac.c | 492 | ||||
-rw-r--r-- | drivers/edac/amd64_edac.c | 2 | ||||
-rw-r--r-- | drivers/edac/debugfs.c | 2 | ||||
-rw-r--r-- | drivers/edac/edac_mc.c | 64 | ||||
-rw-r--r-- | drivers/edac/edac_pci.c | 67 | ||||
-rw-r--r-- | drivers/edac/mpc85xx_edac.c | 2 | ||||
-rw-r--r-- | drivers/edac/xgene_edac.c | 70 |
9 files changed, 616 insertions, 111 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index ef25000a5bc6..37755e63cc28 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig | |||
@@ -367,14 +367,30 @@ config EDAC_OCTEON_PCI | |||
367 | Support for error detection and correction on the | 367 | Support for error detection and correction on the |
368 | Cavium Octeon family of SOCs. | 368 | Cavium Octeon family of SOCs. |
369 | 369 | ||
370 | config EDAC_ALTERA_MC | 370 | config EDAC_ALTERA |
371 | bool "Altera SDRAM Memory Controller EDAC" | 371 | bool "Altera SOCFPGA ECC" |
372 | depends on EDAC_MM_EDAC=y && ARCH_SOCFPGA | 372 | depends on EDAC_MM_EDAC=y && ARCH_SOCFPGA |
373 | help | 373 | help |
374 | Support for error detection and correction on the | 374 | Support for error detection and correction on the |
375 | Altera SDRAM memory controller. Note that the | 375 | Altera SOCs. This must be selected for SDRAM ECC. |
376 | preloader must initialize the SDRAM before loading | 376 | Note that the preloader must initialize the SDRAM |
377 | the kernel. | 377 | before loading the kernel. |
378 | |||
379 | config EDAC_ALTERA_L2C | ||
380 | bool "Altera L2 Cache ECC" | ||
381 | depends on EDAC_ALTERA=y | ||
382 | select CACHE_L2X0 | ||
383 | help | ||
384 | Support for error detection and correction on the | ||
385 | Altera L2 cache Memory for Altera SoCs. This option | ||
386 | requires L2 cache so it will force that selection. | ||
387 | |||
388 | config EDAC_ALTERA_OCRAM | ||
389 | bool "Altera On-Chip RAM ECC" | ||
390 | depends on EDAC_ALTERA=y && SRAM && GENERIC_ALLOCATOR | ||
391 | help | ||
392 | Support for error detection and correction on the | ||
393 | Altera On-Chip RAM Memory for Altera SoCs. | ||
378 | 394 | ||
379 | config EDAC_SYNOPSYS | 395 | config EDAC_SYNOPSYS |
380 | tristate "Synopsys DDR Memory Controller" | 396 | tristate "Synopsys DDR Memory Controller" |
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index be163e20fe56..f9e4a3e0e6e9 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
@@ -67,6 +67,6 @@ obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o | |||
67 | obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o | 67 | obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o |
68 | obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o | 68 | obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o |
69 | 69 | ||
70 | obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o | 70 | obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o |
71 | obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o | 71 | obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o |
72 | obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o | 72 | obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o |
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 929640981d8a..63e42098726d 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright Altera Corporation (C) 2014-2015. All rights reserved. | 2 | * Copyright Altera Corporation (C) 2014-2016. All rights reserved. |
3 | * Copyright 2011-2012 Calxeda, Inc. | 3 | * Copyright 2011-2012 Calxeda, Inc. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | 5 | * This program is free software; you can redistribute it and/or modify it |
@@ -17,8 +17,10 @@ | |||
17 | * Adapted from the highbank_mc_edac driver. | 17 | * Adapted from the highbank_mc_edac driver. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <asm/cacheflush.h> | ||
20 | #include <linux/ctype.h> | 21 | #include <linux/ctype.h> |
21 | #include <linux/edac.h> | 22 | #include <linux/edac.h> |
23 | #include <linux/genalloc.h> | ||
22 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
23 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
24 | #include <linux/mfd/syscon.h> | 26 | #include <linux/mfd/syscon.h> |
@@ -34,6 +36,7 @@ | |||
34 | 36 | ||
35 | #define EDAC_MOD_STR "altera_edac" | 37 | #define EDAC_MOD_STR "altera_edac" |
36 | #define EDAC_VERSION "1" | 38 | #define EDAC_VERSION "1" |
39 | #define EDAC_DEVICE "Altera" | ||
37 | 40 | ||
38 | static const struct altr_sdram_prv_data c5_data = { | 41 | static const struct altr_sdram_prv_data c5_data = { |
39 | .ecc_ctrl_offset = CV_CTLCFG_OFST, | 42 | .ecc_ctrl_offset = CV_CTLCFG_OFST, |
@@ -75,6 +78,31 @@ static const struct altr_sdram_prv_data a10_data = { | |||
75 | .ue_set_mask = A10_DIAGINT_TDERRA_MASK, | 78 | .ue_set_mask = A10_DIAGINT_TDERRA_MASK, |
76 | }; | 79 | }; |
77 | 80 | ||
81 | /************************** EDAC Device Defines **************************/ | ||
82 | |||
83 | /* OCRAM ECC Management Group Defines */ | ||
84 | #define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04 | ||
85 | #define ALTR_OCR_ECC_EN BIT(0) | ||
86 | #define ALTR_OCR_ECC_INJS BIT(1) | ||
87 | #define ALTR_OCR_ECC_INJD BIT(2) | ||
88 | #define ALTR_OCR_ECC_SERR BIT(3) | ||
89 | #define ALTR_OCR_ECC_DERR BIT(4) | ||
90 | |||
91 | /* L2 ECC Management Group Defines */ | ||
92 | #define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00 | ||
93 | #define ALTR_L2_ECC_EN BIT(0) | ||
94 | #define ALTR_L2_ECC_INJS BIT(1) | ||
95 | #define ALTR_L2_ECC_INJD BIT(2) | ||
96 | |||
97 | #define ALTR_UE_TRIGGER_CHAR 'U' /* Trigger for UE */ | ||
98 | #define ALTR_TRIGGER_READ_WRD_CNT 32 /* Line size x 4 */ | ||
99 | #define ALTR_TRIG_OCRAM_BYTE_SIZE 128 /* Line size x 4 */ | ||
100 | #define ALTR_TRIG_L2C_BYTE_SIZE 4096 /* Full Page */ | ||
101 | |||
102 | /*********************** EDAC Memory Controller Functions ****************/ | ||
103 | |||
104 | /* The SDRAM controller uses the EDAC Memory Controller framework. */ | ||
105 | |||
78 | static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) | 106 | static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) |
79 | { | 107 | { |
80 | struct mem_ctl_info *mci = dev_id; | 108 | struct mem_ctl_info *mci = dev_id; |
@@ -504,6 +532,466 @@ static struct platform_driver altr_sdram_edac_driver = { | |||
504 | 532 | ||
505 | module_platform_driver(altr_sdram_edac_driver); | 533 | module_platform_driver(altr_sdram_edac_driver); |
506 | 534 | ||
535 | /************************* EDAC Parent Probe *************************/ | ||
536 | |||
537 | static const struct of_device_id altr_edac_device_of_match[]; | ||
538 | |||
539 | static const struct of_device_id altr_edac_of_match[] = { | ||
540 | { .compatible = "altr,socfpga-ecc-manager" }, | ||
541 | {}, | ||
542 | }; | ||
543 | MODULE_DEVICE_TABLE(of, altr_edac_of_match); | ||
544 | |||
545 | static int altr_edac_probe(struct platform_device *pdev) | ||
546 | { | ||
547 | of_platform_populate(pdev->dev.of_node, altr_edac_device_of_match, | ||
548 | NULL, &pdev->dev); | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | static struct platform_driver altr_edac_driver = { | ||
553 | .probe = altr_edac_probe, | ||
554 | .driver = { | ||
555 | .name = "socfpga_ecc_manager", | ||
556 | .of_match_table = altr_edac_of_match, | ||
557 | }, | ||
558 | }; | ||
559 | module_platform_driver(altr_edac_driver); | ||
560 | |||
561 | /************************* EDAC Device Functions *************************/ | ||
562 | |||
563 | /* | ||
564 | * EDAC Device Functions (shared between various IPs). | ||
565 | * The discrete memories use the EDAC Device framework. The probe | ||
566 | * and error handling functions are very similar between memories | ||
567 | * so they are shared. The memory allocation and freeing for EDAC | ||
568 | * trigger testing are different for each memory. | ||
569 | */ | ||
570 | |||
571 | const struct edac_device_prv_data ocramecc_data; | ||
572 | const struct edac_device_prv_data l2ecc_data; | ||
573 | |||
574 | struct edac_device_prv_data { | ||
575 | int (*setup)(struct platform_device *pdev, void __iomem *base); | ||
576 | int ce_clear_mask; | ||
577 | int ue_clear_mask; | ||
578 | char dbgfs_name[20]; | ||
579 | void * (*alloc_mem)(size_t size, void **other); | ||
580 | void (*free_mem)(void *p, size_t size, void *other); | ||
581 | int ecc_enable_mask; | ||
582 | int ce_set_mask; | ||
583 | int ue_set_mask; | ||
584 | int trig_alloc_sz; | ||
585 | }; | ||
586 | |||
587 | struct altr_edac_device_dev { | ||
588 | void __iomem *base; | ||
589 | int sb_irq; | ||
590 | int db_irq; | ||
591 | const struct edac_device_prv_data *data; | ||
592 | struct dentry *debugfs_dir; | ||
593 | char *edac_dev_name; | ||
594 | }; | ||
595 | |||
596 | static irqreturn_t altr_edac_device_handler(int irq, void *dev_id) | ||
597 | { | ||
598 | irqreturn_t ret_value = IRQ_NONE; | ||
599 | struct edac_device_ctl_info *dci = dev_id; | ||
600 | struct altr_edac_device_dev *drvdata = dci->pvt_info; | ||
601 | const struct edac_device_prv_data *priv = drvdata->data; | ||
602 | |||
603 | if (irq == drvdata->sb_irq) { | ||
604 | if (priv->ce_clear_mask) | ||
605 | writel(priv->ce_clear_mask, drvdata->base); | ||
606 | edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name); | ||
607 | ret_value = IRQ_HANDLED; | ||
608 | } else if (irq == drvdata->db_irq) { | ||
609 | if (priv->ue_clear_mask) | ||
610 | writel(priv->ue_clear_mask, drvdata->base); | ||
611 | edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name); | ||
612 | panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); | ||
613 | ret_value = IRQ_HANDLED; | ||
614 | } else { | ||
615 | WARN_ON(1); | ||
616 | } | ||
617 | |||
618 | return ret_value; | ||
619 | } | ||
620 | |||
621 | static ssize_t altr_edac_device_trig(struct file *file, | ||
622 | const char __user *user_buf, | ||
623 | size_t count, loff_t *ppos) | ||
624 | |||
625 | { | ||
626 | u32 *ptemp, i, error_mask; | ||
627 | int result = 0; | ||
628 | u8 trig_type; | ||
629 | unsigned long flags; | ||
630 | struct edac_device_ctl_info *edac_dci = file->private_data; | ||
631 | struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; | ||
632 | const struct edac_device_prv_data *priv = drvdata->data; | ||
633 | void *generic_ptr = edac_dci->dev; | ||
634 | |||
635 | if (!user_buf || get_user(trig_type, user_buf)) | ||
636 | return -EFAULT; | ||
637 | |||
638 | if (!priv->alloc_mem) | ||
639 | return -ENOMEM; | ||
640 | |||
641 | /* | ||
642 | * Note that generic_ptr is initialized to the device * but in | ||
643 | * some alloc_functions, this is overridden and returns data. | ||
644 | */ | ||
645 | ptemp = priv->alloc_mem(priv->trig_alloc_sz, &generic_ptr); | ||
646 | if (!ptemp) { | ||
647 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
648 | "Inject: Buffer Allocation error\n"); | ||
649 | return -ENOMEM; | ||
650 | } | ||
651 | |||
652 | if (trig_type == ALTR_UE_TRIGGER_CHAR) | ||
653 | error_mask = priv->ue_set_mask; | ||
654 | else | ||
655 | error_mask = priv->ce_set_mask; | ||
656 | |||
657 | edac_printk(KERN_ALERT, EDAC_DEVICE, | ||
658 | "Trigger Error Mask (0x%X)\n", error_mask); | ||
659 | |||
660 | local_irq_save(flags); | ||
661 | /* write ECC corrupted data out. */ | ||
662 | for (i = 0; i < (priv->trig_alloc_sz / sizeof(*ptemp)); i++) { | ||
663 | /* Read data so we're in the correct state */ | ||
664 | rmb(); | ||
665 | if (ACCESS_ONCE(ptemp[i])) | ||
666 | result = -1; | ||
667 | /* Toggle Error bit (it is latched), leave ECC enabled */ | ||
668 | writel(error_mask, drvdata->base); | ||
669 | writel(priv->ecc_enable_mask, drvdata->base); | ||
670 | ptemp[i] = i; | ||
671 | } | ||
672 | /* Ensure it has been written out */ | ||
673 | wmb(); | ||
674 | local_irq_restore(flags); | ||
675 | |||
676 | if (result) | ||
677 | edac_printk(KERN_ERR, EDAC_DEVICE, "Mem Not Cleared\n"); | ||
678 | |||
679 | /* Read out written data. ECC error caused here */ | ||
680 | for (i = 0; i < ALTR_TRIGGER_READ_WRD_CNT; i++) | ||
681 | if (ACCESS_ONCE(ptemp[i]) != i) | ||
682 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
683 | "Read doesn't match written data\n"); | ||
684 | |||
685 | if (priv->free_mem) | ||
686 | priv->free_mem(ptemp, priv->trig_alloc_sz, generic_ptr); | ||
687 | |||
688 | return count; | ||
689 | } | ||
690 | |||
691 | static const struct file_operations altr_edac_device_inject_fops = { | ||
692 | .open = simple_open, | ||
693 | .write = altr_edac_device_trig, | ||
694 | .llseek = generic_file_llseek, | ||
695 | }; | ||
696 | |||
697 | static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci, | ||
698 | const struct edac_device_prv_data *priv) | ||
699 | { | ||
700 | struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; | ||
701 | |||
702 | if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) | ||
703 | return; | ||
704 | |||
705 | drvdata->debugfs_dir = edac_debugfs_create_dir(drvdata->edac_dev_name); | ||
706 | if (!drvdata->debugfs_dir) | ||
707 | return; | ||
708 | |||
709 | if (!edac_debugfs_create_file(priv->dbgfs_name, S_IWUSR, | ||
710 | drvdata->debugfs_dir, edac_dci, | ||
711 | &altr_edac_device_inject_fops)) | ||
712 | debugfs_remove_recursive(drvdata->debugfs_dir); | ||
713 | } | ||
714 | |||
715 | static const struct of_device_id altr_edac_device_of_match[] = { | ||
716 | #ifdef CONFIG_EDAC_ALTERA_L2C | ||
717 | { .compatible = "altr,socfpga-l2-ecc", .data = (void *)&l2ecc_data }, | ||
718 | #endif | ||
719 | #ifdef CONFIG_EDAC_ALTERA_OCRAM | ||
720 | { .compatible = "altr,socfpga-ocram-ecc", | ||
721 | .data = (void *)&ocramecc_data }, | ||
722 | #endif | ||
723 | {}, | ||
724 | }; | ||
725 | MODULE_DEVICE_TABLE(of, altr_edac_device_of_match); | ||
726 | |||
727 | /* | ||
728 | * altr_edac_device_probe() | ||
729 | * This is a generic EDAC device driver that will support | ||
730 | * various Altera memory devices such as the L2 cache ECC and | ||
731 | * OCRAM ECC as well as the memories for other peripherals. | ||
732 | * Module specific initialization is done by passing the | ||
733 | * function index in the device tree. | ||
734 | */ | ||
735 | static int altr_edac_device_probe(struct platform_device *pdev) | ||
736 | { | ||
737 | struct edac_device_ctl_info *dci; | ||
738 | struct altr_edac_device_dev *drvdata; | ||
739 | struct resource *r; | ||
740 | int res = 0; | ||
741 | struct device_node *np = pdev->dev.of_node; | ||
742 | char *ecc_name = (char *)np->name; | ||
743 | static int dev_instance; | ||
744 | |||
745 | if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) { | ||
746 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
747 | "Unable to open devm\n"); | ||
748 | return -ENOMEM; | ||
749 | } | ||
750 | |||
751 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
752 | if (!r) { | ||
753 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
754 | "Unable to get mem resource\n"); | ||
755 | res = -ENODEV; | ||
756 | goto fail; | ||
757 | } | ||
758 | |||
759 | if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r), | ||
760 | dev_name(&pdev->dev))) { | ||
761 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
762 | "%s:Error requesting mem region\n", ecc_name); | ||
763 | res = -EBUSY; | ||
764 | goto fail; | ||
765 | } | ||
766 | |||
767 | dci = edac_device_alloc_ctl_info(sizeof(*drvdata), ecc_name, | ||
768 | 1, ecc_name, 1, 0, NULL, 0, | ||
769 | dev_instance++); | ||
770 | |||
771 | if (!dci) { | ||
772 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
773 | "%s: Unable to allocate EDAC device\n", ecc_name); | ||
774 | res = -ENOMEM; | ||
775 | goto fail; | ||
776 | } | ||
777 | |||
778 | drvdata = dci->pvt_info; | ||
779 | dci->dev = &pdev->dev; | ||
780 | platform_set_drvdata(pdev, dci); | ||
781 | drvdata->edac_dev_name = ecc_name; | ||
782 | |||
783 | drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); | ||
784 | if (!drvdata->base) | ||
785 | goto fail1; | ||
786 | |||
787 | /* Get driver specific data for this EDAC device */ | ||
788 | drvdata->data = of_match_node(altr_edac_device_of_match, np)->data; | ||
789 | |||
790 | /* Check specific dependencies for the module */ | ||
791 | if (drvdata->data->setup) { | ||
792 | res = drvdata->data->setup(pdev, drvdata->base); | ||
793 | if (res) | ||
794 | goto fail1; | ||
795 | } | ||
796 | |||
797 | drvdata->sb_irq = platform_get_irq(pdev, 0); | ||
798 | res = devm_request_irq(&pdev->dev, drvdata->sb_irq, | ||
799 | altr_edac_device_handler, | ||
800 | 0, dev_name(&pdev->dev), dci); | ||
801 | if (res) | ||
802 | goto fail1; | ||
803 | |||
804 | drvdata->db_irq = platform_get_irq(pdev, 1); | ||
805 | res = devm_request_irq(&pdev->dev, drvdata->db_irq, | ||
806 | altr_edac_device_handler, | ||
807 | 0, dev_name(&pdev->dev), dci); | ||
808 | if (res) | ||
809 | goto fail1; | ||
810 | |||
811 | dci->mod_name = "Altera ECC Manager"; | ||
812 | dci->dev_name = drvdata->edac_dev_name; | ||
813 | |||
814 | res = edac_device_add_device(dci); | ||
815 | if (res) | ||
816 | goto fail1; | ||
817 | |||
818 | altr_create_edacdev_dbgfs(dci, drvdata->data); | ||
819 | |||
820 | devres_close_group(&pdev->dev, NULL); | ||
821 | |||
822 | return 0; | ||
823 | |||
824 | fail1: | ||
825 | edac_device_free_ctl_info(dci); | ||
826 | fail: | ||
827 | devres_release_group(&pdev->dev, NULL); | ||
828 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
829 | "%s:Error setting up EDAC device: %d\n", ecc_name, res); | ||
830 | |||
831 | return res; | ||
832 | } | ||
833 | |||
834 | static int altr_edac_device_remove(struct platform_device *pdev) | ||
835 | { | ||
836 | struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); | ||
837 | struct altr_edac_device_dev *drvdata = dci->pvt_info; | ||
838 | |||
839 | debugfs_remove_recursive(drvdata->debugfs_dir); | ||
840 | edac_device_del_device(&pdev->dev); | ||
841 | edac_device_free_ctl_info(dci); | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | static struct platform_driver altr_edac_device_driver = { | ||
847 | .probe = altr_edac_device_probe, | ||
848 | .remove = altr_edac_device_remove, | ||
849 | .driver = { | ||
850 | .name = "altr_edac_device", | ||
851 | .of_match_table = altr_edac_device_of_match, | ||
852 | }, | ||
853 | }; | ||
854 | module_platform_driver(altr_edac_device_driver); | ||
855 | |||
856 | /*********************** OCRAM EDAC Device Functions *********************/ | ||
857 | |||
858 | #ifdef CONFIG_EDAC_ALTERA_OCRAM | ||
859 | |||
860 | static void *ocram_alloc_mem(size_t size, void **other) | ||
861 | { | ||
862 | struct device_node *np; | ||
863 | struct gen_pool *gp; | ||
864 | void *sram_addr; | ||
865 | |||
866 | np = of_find_compatible_node(NULL, NULL, "altr,socfpga-ocram-ecc"); | ||
867 | if (!np) | ||
868 | return NULL; | ||
869 | |||
870 | gp = of_gen_pool_get(np, "iram", 0); | ||
871 | of_node_put(np); | ||
872 | if (!gp) | ||
873 | return NULL; | ||
874 | |||
875 | sram_addr = (void *)gen_pool_alloc(gp, size); | ||
876 | if (!sram_addr) | ||
877 | return NULL; | ||
878 | |||
879 | memset(sram_addr, 0, size); | ||
880 | /* Ensure data is written out */ | ||
881 | wmb(); | ||
882 | |||
883 | /* Remember this handle for freeing later */ | ||
884 | *other = gp; | ||
885 | |||
886 | return sram_addr; | ||
887 | } | ||
888 | |||
889 | static void ocram_free_mem(void *p, size_t size, void *other) | ||
890 | { | ||
891 | gen_pool_free((struct gen_pool *)other, (u32)p, size); | ||
892 | } | ||
893 | |||
894 | /* | ||
895 | * altr_ocram_check_deps() | ||
896 | * Test for OCRAM cache ECC dependencies upon entry because | ||
897 | * platform specific startup should have initialized the | ||
898 | * On-Chip RAM memory and enabled the ECC. | ||
899 | * Can't turn on ECC here because accessing un-initialized | ||
900 | * memory will cause CE/UE errors possibly causing an ABORT. | ||
901 | */ | ||
902 | static int altr_ocram_check_deps(struct platform_device *pdev, | ||
903 | void __iomem *base) | ||
904 | { | ||
905 | if (readl(base) & ALTR_OCR_ECC_EN) | ||
906 | return 0; | ||
907 | |||
908 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
909 | "OCRAM: No ECC present or ECC disabled.\n"); | ||
910 | return -ENODEV; | ||
911 | } | ||
912 | |||
913 | const struct edac_device_prv_data ocramecc_data = { | ||
914 | .setup = altr_ocram_check_deps, | ||
915 | .ce_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_SERR), | ||
916 | .ue_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_DERR), | ||
917 | .dbgfs_name = "altr_ocram_trigger", | ||
918 | .alloc_mem = ocram_alloc_mem, | ||
919 | .free_mem = ocram_free_mem, | ||
920 | .ecc_enable_mask = ALTR_OCR_ECC_EN, | ||
921 | .ce_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJS), | ||
922 | .ue_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJD), | ||
923 | .trig_alloc_sz = ALTR_TRIG_OCRAM_BYTE_SIZE, | ||
924 | }; | ||
925 | |||
926 | #endif /* CONFIG_EDAC_ALTERA_OCRAM */ | ||
927 | |||
928 | /********************* L2 Cache EDAC Device Functions ********************/ | ||
929 | |||
930 | #ifdef CONFIG_EDAC_ALTERA_L2C | ||
931 | |||
932 | static void *l2_alloc_mem(size_t size, void **other) | ||
933 | { | ||
934 | struct device *dev = *other; | ||
935 | void *ptemp = devm_kzalloc(dev, size, GFP_KERNEL); | ||
936 | |||
937 | if (!ptemp) | ||
938 | return NULL; | ||
939 | |||
940 | /* Make sure everything is written out */ | ||
941 | wmb(); | ||
942 | |||
943 | /* | ||
944 | * Clean all cache levels up to LoC (includes L2) | ||
945 | * This ensures the corrupted data is written into | ||
946 | * L2 cache for readback test (which causes ECC error). | ||
947 | */ | ||
948 | flush_cache_all(); | ||
949 | |||
950 | return ptemp; | ||
951 | } | ||
952 | |||
953 | static void l2_free_mem(void *p, size_t size, void *other) | ||
954 | { | ||
955 | struct device *dev = other; | ||
956 | |||
957 | if (dev && p) | ||
958 | devm_kfree(dev, p); | ||
959 | } | ||
960 | |||
961 | /* | ||
962 | * altr_l2_check_deps() | ||
963 | * Test for L2 cache ECC dependencies upon entry because | ||
964 | * platform specific startup should have initialized the L2 | ||
965 | * memory and enabled the ECC. | ||
966 | * Bail if ECC is not enabled. | ||
967 | * Note that L2 Cache Enable is forced at build time. | ||
968 | */ | ||
969 | static int altr_l2_check_deps(struct platform_device *pdev, | ||
970 | void __iomem *base) | ||
971 | { | ||
972 | if (readl(base) & ALTR_L2_ECC_EN) | ||
973 | return 0; | ||
974 | |||
975 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
976 | "L2: No ECC present, or ECC disabled\n"); | ||
977 | return -ENODEV; | ||
978 | } | ||
979 | |||
980 | const struct edac_device_prv_data l2ecc_data = { | ||
981 | .setup = altr_l2_check_deps, | ||
982 | .ce_clear_mask = 0, | ||
983 | .ue_clear_mask = 0, | ||
984 | .dbgfs_name = "altr_l2_trigger", | ||
985 | .alloc_mem = l2_alloc_mem, | ||
986 | .free_mem = l2_free_mem, | ||
987 | .ecc_enable_mask = ALTR_L2_ECC_EN, | ||
988 | .ce_set_mask = (ALTR_L2_ECC_EN | ALTR_L2_ECC_INJS), | ||
989 | .ue_set_mask = (ALTR_L2_ECC_EN | ALTR_L2_ECC_INJD), | ||
990 | .trig_alloc_sz = ALTR_TRIG_L2C_BYTE_SIZE, | ||
991 | }; | ||
992 | |||
993 | #endif /* CONFIG_EDAC_ALTERA_L2C */ | ||
994 | |||
507 | MODULE_LICENSE("GPL v2"); | 995 | MODULE_LICENSE("GPL v2"); |
508 | MODULE_AUTHOR("Thor Thayer"); | 996 | MODULE_AUTHOR("Thor Thayer"); |
509 | MODULE_DESCRIPTION("EDAC Driver for Altera SDRAM Controller"); | 997 | MODULE_DESCRIPTION("EDAC Driver for Altera Memories"); |
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 9eee13ef83a5..d87a47547ba5 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -1452,7 +1452,7 @@ static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, | |||
1452 | u64 chan_off; | 1452 | u64 chan_off; |
1453 | u64 dram_base = get_dram_base(pvt, range); | 1453 | u64 dram_base = get_dram_base(pvt, range); |
1454 | u64 hole_off = f10_dhar_offset(pvt); | 1454 | u64 hole_off = f10_dhar_offset(pvt); |
1455 | u64 dct_sel_base_off = (pvt->dct_sel_hi & 0xFFFFFC00) << 16; | 1455 | u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; |
1456 | 1456 | ||
1457 | if (hi_rng) { | 1457 | if (hi_rng) { |
1458 | /* | 1458 | /* |
diff --git a/drivers/edac/debugfs.c b/drivers/edac/debugfs.c index 54d2f668cb0a..92dbb7e2320c 100644 --- a/drivers/edac/debugfs.c +++ b/drivers/edac/debugfs.c | |||
@@ -53,7 +53,7 @@ int __init edac_debugfs_init(void) | |||
53 | 53 | ||
54 | void edac_debugfs_exit(void) | 54 | void edac_debugfs_exit(void) |
55 | { | 55 | { |
56 | debugfs_remove(edac_debugfs); | 56 | debugfs_remove_recursive(edac_debugfs); |
57 | } | 57 | } |
58 | 58 | ||
59 | int edac_create_debugfs_nodes(struct mem_ctl_info *mci) | 59 | int edac_create_debugfs_nodes(struct mem_ctl_info *mci) |
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 8adfc167c2e3..1472f48c8ac6 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c | |||
@@ -535,60 +535,21 @@ static void edac_mc_workq_function(struct work_struct *work_req) | |||
535 | 535 | ||
536 | mutex_lock(&mem_ctls_mutex); | 536 | mutex_lock(&mem_ctls_mutex); |
537 | 537 | ||
538 | /* if this control struct has movd to offline state, we are done */ | 538 | if (mci->op_state != OP_RUNNING_POLL) { |
539 | if (mci->op_state == OP_OFFLINE) { | ||
540 | mutex_unlock(&mem_ctls_mutex); | 539 | mutex_unlock(&mem_ctls_mutex); |
541 | return; | 540 | return; |
542 | } | 541 | } |
543 | 542 | ||
544 | /* Only poll controllers that are running polled and have a check */ | 543 | if (edac_mc_assert_error_check_and_clear()) |
545 | if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL)) | ||
546 | mci->edac_check(mci); | 544 | mci->edac_check(mci); |
547 | 545 | ||
548 | mutex_unlock(&mem_ctls_mutex); | 546 | mutex_unlock(&mem_ctls_mutex); |
549 | 547 | ||
550 | /* Reschedule */ | 548 | /* Queue ourselves again. */ |
551 | edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec())); | 549 | edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec())); |
552 | } | 550 | } |
553 | 551 | ||
554 | /* | 552 | /* |
555 | * edac_mc_workq_setup | ||
556 | * initialize a workq item for this mci | ||
557 | * passing in the new delay period in msec | ||
558 | * | ||
559 | * locking model: | ||
560 | * | ||
561 | * called with the mem_ctls_mutex held | ||
562 | */ | ||
563 | static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec) | ||
564 | { | ||
565 | edac_dbg(0, "\n"); | ||
566 | |||
567 | /* if this instance is not in the POLL state, then simply return */ | ||
568 | if (mci->op_state != OP_RUNNING_POLL) | ||
569 | return; | ||
570 | |||
571 | INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function); | ||
572 | |||
573 | edac_queue_work(&mci->work, msecs_to_jiffies(msec)); | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * edac_mc_workq_teardown | ||
578 | * stop the workq processing on this mci | ||
579 | * | ||
580 | * locking model: | ||
581 | * | ||
582 | * called WITHOUT lock held | ||
583 | */ | ||
584 | static void edac_mc_workq_teardown(struct mem_ctl_info *mci) | ||
585 | { | ||
586 | mci->op_state = OP_OFFLINE; | ||
587 | |||
588 | edac_stop_work(&mci->work); | ||
589 | } | ||
590 | |||
591 | /* | ||
592 | * edac_mc_reset_delay_period(unsigned long value) | 553 | * edac_mc_reset_delay_period(unsigned long value) |
593 | * | 554 | * |
594 | * user space has updated our poll period value, need to | 555 | * user space has updated our poll period value, need to |
@@ -771,12 +732,12 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, | |||
771 | goto fail1; | 732 | goto fail1; |
772 | } | 733 | } |
773 | 734 | ||
774 | /* If there IS a check routine, then we are running POLLED */ | 735 | if (mci->edac_check) { |
775 | if (mci->edac_check != NULL) { | ||
776 | /* This instance is NOW RUNNING */ | ||
777 | mci->op_state = OP_RUNNING_POLL; | 736 | mci->op_state = OP_RUNNING_POLL; |
778 | 737 | ||
779 | edac_mc_workq_setup(mci, edac_mc_get_poll_msec()); | 738 | INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function); |
739 | edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec())); | ||
740 | |||
780 | } else { | 741 | } else { |
781 | mci->op_state = OP_RUNNING_INTERRUPT; | 742 | mci->op_state = OP_RUNNING_INTERRUPT; |
782 | } | 743 | } |
@@ -823,15 +784,16 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) | |||
823 | return NULL; | 784 | return NULL; |
824 | } | 785 | } |
825 | 786 | ||
787 | /* mark MCI offline: */ | ||
788 | mci->op_state = OP_OFFLINE; | ||
789 | |||
826 | if (!del_mc_from_global_list(mci)) | 790 | if (!del_mc_from_global_list(mci)) |
827 | edac_mc_owner = NULL; | 791 | edac_mc_owner = NULL; |
828 | mutex_unlock(&mem_ctls_mutex); | ||
829 | 792 | ||
830 | /* flush workq processes */ | 793 | mutex_unlock(&mem_ctls_mutex); |
831 | edac_mc_workq_teardown(mci); | ||
832 | 794 | ||
833 | /* marking MCI offline */ | 795 | if (mci->edac_check) |
834 | mci->op_state = OP_OFFLINE; | 796 | edac_stop_work(&mci->work); |
835 | 797 | ||
836 | /* remove from sysfs */ | 798 | /* remove from sysfs */ |
837 | edac_remove_sysfs_mci_device(mci); | 799 | edac_remove_sysfs_mci_device(mci); |
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index 99685388d3fb..8f2f2899a7a2 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c | |||
@@ -195,55 +195,24 @@ static void edac_pci_workq_function(struct work_struct *work_req) | |||
195 | 195 | ||
196 | mutex_lock(&edac_pci_ctls_mutex); | 196 | mutex_lock(&edac_pci_ctls_mutex); |
197 | 197 | ||
198 | if (pci->op_state == OP_RUNNING_POLL) { | 198 | if (pci->op_state != OP_RUNNING_POLL) { |
199 | /* we might be in POLL mode, but there may NOT be a poll func | 199 | mutex_unlock(&edac_pci_ctls_mutex); |
200 | */ | 200 | return; |
201 | if ((pci->edac_check != NULL) && edac_pci_get_check_errors()) | ||
202 | pci->edac_check(pci); | ||
203 | |||
204 | /* if we are on a one second period, then use round */ | ||
205 | msec = edac_pci_get_poll_msec(); | ||
206 | if (msec == 1000) | ||
207 | delay = round_jiffies_relative(msecs_to_jiffies(msec)); | ||
208 | else | ||
209 | delay = msecs_to_jiffies(msec); | ||
210 | |||
211 | /* Reschedule only if we are in POLL mode */ | ||
212 | edac_queue_work(&pci->work, delay); | ||
213 | } | 201 | } |
214 | 202 | ||
215 | mutex_unlock(&edac_pci_ctls_mutex); | 203 | if (edac_pci_get_check_errors()) |
216 | } | 204 | pci->edac_check(pci); |
217 | |||
218 | /* | ||
219 | * edac_pci_workq_setup() | ||
220 | * initialize a workq item for this edac_pci instance | ||
221 | * passing in the new delay period in msec | ||
222 | * | ||
223 | * locking model: | ||
224 | * called when 'edac_pci_ctls_mutex' is locked | ||
225 | */ | ||
226 | static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, | ||
227 | unsigned int msec) | ||
228 | { | ||
229 | edac_dbg(0, "\n"); | ||
230 | 205 | ||
231 | INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); | 206 | /* if we are on a one second period, then use round */ |
207 | msec = edac_pci_get_poll_msec(); | ||
208 | if (msec == 1000) | ||
209 | delay = round_jiffies_relative(msecs_to_jiffies(msec)); | ||
210 | else | ||
211 | delay = msecs_to_jiffies(msec); | ||
232 | 212 | ||
233 | edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec())); | 213 | edac_queue_work(&pci->work, delay); |
234 | } | ||
235 | 214 | ||
236 | /* | 215 | mutex_unlock(&edac_pci_ctls_mutex); |
237 | * edac_pci_workq_teardown() | ||
238 | * stop the workq processing on this edac_pci instance | ||
239 | */ | ||
240 | static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) | ||
241 | { | ||
242 | edac_dbg(0, "\n"); | ||
243 | |||
244 | pci->op_state = OP_OFFLINE; | ||
245 | |||
246 | edac_stop_work(&pci->work); | ||
247 | } | 216 | } |
248 | 217 | ||
249 | /* | 218 | /* |
@@ -289,10 +258,12 @@ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) | |||
289 | goto fail1; | 258 | goto fail1; |
290 | } | 259 | } |
291 | 260 | ||
292 | if (pci->edac_check != NULL) { | 261 | if (pci->edac_check) { |
293 | pci->op_state = OP_RUNNING_POLL; | 262 | pci->op_state = OP_RUNNING_POLL; |
294 | 263 | ||
295 | edac_pci_workq_setup(pci, 1000); | 264 | INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); |
265 | edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec())); | ||
266 | |||
296 | } else { | 267 | } else { |
297 | pci->op_state = OP_RUNNING_INTERRUPT; | 268 | pci->op_state = OP_RUNNING_INTERRUPT; |
298 | } | 269 | } |
@@ -350,8 +321,8 @@ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) | |||
350 | 321 | ||
351 | mutex_unlock(&edac_pci_ctls_mutex); | 322 | mutex_unlock(&edac_pci_ctls_mutex); |
352 | 323 | ||
353 | /* stop the workq timer */ | 324 | if (pci->edac_check) |
354 | edac_pci_workq_teardown(pci); | 325 | edac_stop_work(&pci->work); |
355 | 326 | ||
356 | edac_printk(KERN_INFO, EDAC_PCI, | 327 | edac_printk(KERN_INFO, EDAC_PCI, |
357 | "Removed device %d for %s %s: DEV %s\n", | 328 | "Removed device %d for %s %s: DEV %s\n", |
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index b7139c160baf..ca63d0da8889 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c | |||
@@ -1244,7 +1244,7 @@ static struct platform_driver * const drivers[] = { | |||
1244 | static int __init mpc85xx_mc_init(void) | 1244 | static int __init mpc85xx_mc_init(void) |
1245 | { | 1245 | { |
1246 | int res = 0; | 1246 | int res = 0; |
1247 | u32 pvr = 0; | 1247 | u32 __maybe_unused pvr = 0; |
1248 | 1248 | ||
1249 | printk(KERN_INFO "Freescale(R) MPC85xx EDAC driver, " | 1249 | printk(KERN_INFO "Freescale(R) MPC85xx EDAC driver, " |
1250 | "(C) 2006 Montavista Software\n"); | 1250 | "(C) 2006 Montavista Software\n"); |
diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c index 41f876414a18..bf19b6e3bd12 100644 --- a/drivers/edac/xgene_edac.c +++ b/drivers/edac/xgene_edac.c | |||
@@ -61,6 +61,7 @@ struct xgene_edac { | |||
61 | struct regmap *mcba_map; | 61 | struct regmap *mcba_map; |
62 | struct regmap *mcbb_map; | 62 | struct regmap *mcbb_map; |
63 | struct regmap *efuse_map; | 63 | struct regmap *efuse_map; |
64 | struct regmap *rb_map; | ||
64 | void __iomem *pcp_csr; | 65 | void __iomem *pcp_csr; |
65 | spinlock_t lock; | 66 | spinlock_t lock; |
66 | struct dentry *dfs; | 67 | struct dentry *dfs; |
@@ -1057,7 +1058,7 @@ static bool xgene_edac_l3_promote_to_uc_err(u32 l3cesr, u32 l3celr) | |||
1057 | case 0x041: | 1058 | case 0x041: |
1058 | return true; | 1059 | return true; |
1059 | } | 1060 | } |
1060 | } else if (L3C_ELR_ERRSYN(l3celr) == 9) | 1061 | } else if (L3C_ELR_ERRWAY(l3celr) == 9) |
1061 | return true; | 1062 | return true; |
1062 | 1063 | ||
1063 | return false; | 1064 | return false; |
@@ -1353,6 +1354,17 @@ static int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3) | |||
1353 | #define GLBL_MDED_ERRH 0x0848 | 1354 | #define GLBL_MDED_ERRH 0x0848 |
1354 | #define GLBL_MDED_ERRHMASK 0x084c | 1355 | #define GLBL_MDED_ERRHMASK 0x084c |
1355 | 1356 | ||
1357 | /* IO Bus Registers */ | ||
1358 | #define RBCSR 0x0000 | ||
1359 | #define STICKYERR_MASK BIT(0) | ||
1360 | #define RBEIR 0x0008 | ||
1361 | #define AGENT_OFFLINE_ERR_MASK BIT(30) | ||
1362 | #define UNIMPL_RBPAGE_ERR_MASK BIT(29) | ||
1363 | #define WORD_ALIGNED_ERR_MASK BIT(28) | ||
1364 | #define PAGE_ACCESS_ERR_MASK BIT(27) | ||
1365 | #define WRITE_ACCESS_MASK BIT(26) | ||
1366 | #define RBERRADDR_RD(src) ((src) & 0x03FFFFFF) | ||
1367 | |||
1356 | static const char * const soc_mem_err_v1[] = { | 1368 | static const char * const soc_mem_err_v1[] = { |
1357 | "10GbE0", | 1369 | "10GbE0", |
1358 | "10GbE1", | 1370 | "10GbE1", |
@@ -1470,6 +1482,51 @@ static void xgene_edac_rb_report(struct edac_device_ctl_info *edac_dev) | |||
1470 | u32 err_addr_hi; | 1482 | u32 err_addr_hi; |
1471 | u32 reg; | 1483 | u32 reg; |
1472 | 1484 | ||
1485 | /* If the register bus resource isn't available, just skip it */ | ||
1486 | if (!ctx->edac->rb_map) | ||
1487 | goto rb_skip; | ||
1488 | |||
1489 | /* | ||
1490 | * Check RB access errors | ||
1491 | * 1. Out of range | ||
1492 | * 2. Un-implemented page | ||
1493 | * 3. Un-aligned access | ||
1494 | * 4. Offline slave IP | ||
1495 | */ | ||
1496 | if (regmap_read(ctx->edac->rb_map, RBCSR, ®)) | ||
1497 | return; | ||
1498 | if (reg & STICKYERR_MASK) { | ||
1499 | bool write; | ||
1500 | u32 address; | ||
1501 | |||
1502 | dev_err(edac_dev->dev, "IOB bus access error(s)\n"); | ||
1503 | if (regmap_read(ctx->edac->rb_map, RBEIR, ®)) | ||
1504 | return; | ||
1505 | write = reg & WRITE_ACCESS_MASK ? 1 : 0; | ||
1506 | address = RBERRADDR_RD(reg); | ||
1507 | if (reg & AGENT_OFFLINE_ERR_MASK) | ||
1508 | dev_err(edac_dev->dev, | ||
1509 | "IOB bus %s access to offline agent error\n", | ||
1510 | write ? "write" : "read"); | ||
1511 | if (reg & UNIMPL_RBPAGE_ERR_MASK) | ||
1512 | dev_err(edac_dev->dev, | ||
1513 | "IOB bus %s access to unimplemented page error\n", | ||
1514 | write ? "write" : "read"); | ||
1515 | if (reg & WORD_ALIGNED_ERR_MASK) | ||
1516 | dev_err(edac_dev->dev, | ||
1517 | "IOB bus %s word aligned access error\n", | ||
1518 | write ? "write" : "read"); | ||
1519 | if (reg & PAGE_ACCESS_ERR_MASK) | ||
1520 | dev_err(edac_dev->dev, | ||
1521 | "IOB bus %s to page out of range access error\n", | ||
1522 | write ? "write" : "read"); | ||
1523 | if (regmap_write(ctx->edac->rb_map, RBEIR, 0)) | ||
1524 | return; | ||
1525 | if (regmap_write(ctx->edac->rb_map, RBCSR, 0)) | ||
1526 | return; | ||
1527 | } | ||
1528 | rb_skip: | ||
1529 | |||
1473 | /* IOB Bridge agent transaction error interrupt */ | 1530 | /* IOB Bridge agent transaction error interrupt */ |
1474 | reg = readl(ctx->dev_csr + IOBBATRANSERRINTSTS); | 1531 | reg = readl(ctx->dev_csr + IOBBATRANSERRINTSTS); |
1475 | if (!reg) | 1532 | if (!reg) |
@@ -1852,6 +1909,17 @@ static int xgene_edac_probe(struct platform_device *pdev) | |||
1852 | goto out_err; | 1909 | goto out_err; |
1853 | } | 1910 | } |
1854 | 1911 | ||
1912 | /* | ||
1913 | * NOTE: The register bus resource is optional for compatibility | ||
1914 | * reason. | ||
1915 | */ | ||
1916 | edac->rb_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, | ||
1917 | "regmap-rb"); | ||
1918 | if (IS_ERR(edac->rb_map)) { | ||
1919 | dev_warn(edac->dev, "missing syscon regmap rb\n"); | ||
1920 | edac->rb_map = NULL; | ||
1921 | } | ||
1922 | |||
1855 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1923 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1856 | edac->pcp_csr = devm_ioremap_resource(&pdev->dev, res); | 1924 | edac->pcp_csr = devm_ioremap_resource(&pdev->dev, res); |
1857 | if (IS_ERR(edac->pcp_csr)) { | 1925 | if (IS_ERR(edac->pcp_csr)) { |