diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-09 21:36:19 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-09 21:36:19 -0500 |
| commit | ed824a625006e47d2584ee0bba9227a34cc06671 (patch) | |
| tree | 1f86ad02241da5b61f112833d37a6a814008cc0b | |
| parent | e07e0d4cb0c4bfe822ec8491cc06269096a38bea (diff) | |
| parent | 7a85951692eb133f20ae93b48494322837b9d1f5 (diff) | |
Merge tag 'edac_for_3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
Pull EDAC updates from Borislav Petkov:
- a new synopsys_edac.c driver for the Synopsys DDR controller, from
Punnaiah Choudary Kalluri.
- minor fixes/cleanups all around
- Mauro and I are adding the repo URLs to MAINTAINERS since people
asked for trees to base upcoming work on.
* tag 'edac_for_3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp:
EDAC: Add repo URLs to MAINTAINERS
EDAC, mv64x60_edac: Fix an error code in probe()
EDAC: edac_mc_sysfs: Make stuff static
EDAC: Fix the leak of mci->bus->name when bus_register fails
edac: i5100_edac: Remove unused i5100_recmema_dm_buf_id
EDAC, synps: Add EDAC support for zynq ddr ecc controller
mpc85xx_edac: Fix a typo in comments
EDAC, mce_amd_inj: Fix sparse non static symbol warning
| -rw-r--r-- | MAINTAINERS | 3 | ||||
| -rw-r--r-- | drivers/edac/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/edac/Makefile | 1 | ||||
| -rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 51 | ||||
| -rw-r--r-- | drivers/edac/i5100_edac.c | 5 | ||||
| -rw-r--r-- | drivers/edac/mce_amd_inj.c | 2 | ||||
| -rw-r--r-- | drivers/edac/mpc85xx_edac.c | 2 | ||||
| -rw-r--r-- | drivers/edac/mpc85xx_edac.h | 2 | ||||
| -rw-r--r-- | drivers/edac/mv64x60_edac.c | 3 | ||||
| -rw-r--r-- | drivers/edac/synopsys_edac.c | 535 |
10 files changed, 578 insertions, 33 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index d66a97dd3a12..26557b74a8ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -1586,6 +1586,7 @@ N: xilinx | |||
| 1586 | F: drivers/clocksource/cadence_ttc_timer.c | 1586 | F: drivers/clocksource/cadence_ttc_timer.c |
| 1587 | F: drivers/i2c/busses/i2c-cadence.c | 1587 | F: drivers/i2c/busses/i2c-cadence.c |
| 1588 | F: drivers/mmc/host/sdhci-of-arasan.c | 1588 | F: drivers/mmc/host/sdhci-of-arasan.c |
| 1589 | F: drivers/edac/synopsys_edac.c | ||
| 1589 | 1590 | ||
| 1590 | ARM SMMU DRIVER | 1591 | ARM SMMU DRIVER |
| 1591 | M: Will Deacon <will.deacon@arm.com> | 1592 | M: Will Deacon <will.deacon@arm.com> |
| @@ -3513,6 +3514,8 @@ M: Borislav Petkov <bp@alien8.de> | |||
| 3513 | M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 3514 | M: Mauro Carvalho Chehab <mchehab@osg.samsung.com> |
| 3514 | L: linux-edac@vger.kernel.org | 3515 | L: linux-edac@vger.kernel.org |
| 3515 | W: bluesmoke.sourceforge.net | 3516 | W: bluesmoke.sourceforge.net |
| 3517 | T: git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git#for-next | ||
| 3518 | T: git://git.kernel.org/pub/linux/kernel/git/mchehab/linux-edac.git#linux_next | ||
| 3516 | S: Supported | 3519 | S: Supported |
| 3517 | F: Documentation/edac.txt | 3520 | F: Documentation/edac.txt |
| 3518 | F: drivers/edac/ | 3521 | F: drivers/edac/ |
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 49c265255a07..cb59619df23f 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig | |||
| @@ -385,4 +385,11 @@ config EDAC_ALTERA_MC | |||
| 385 | preloader must initialize the SDRAM before loading | 385 | preloader must initialize the SDRAM before loading |
| 386 | the kernel. | 386 | the kernel. |
| 387 | 387 | ||
| 388 | config EDAC_SYNOPSYS | ||
| 389 | tristate "Synopsys DDR Memory Controller" | ||
| 390 | depends on EDAC_MM_EDAC && ARCH_ZYNQ | ||
| 391 | help | ||
| 392 | Support for error detection and correction on the Synopsys DDR | ||
| 393 | memory controller. | ||
| 394 | |||
| 388 | endif # EDAC | 395 | endif # EDAC |
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index d40c69a04df7..b255f362b1db 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
| @@ -67,3 +67,4 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o | |||
| 67 | obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o | 67 | obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o |
| 68 | 68 | ||
| 69 | obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o | 69 | obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o |
| 70 | obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o | ||
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 670d2829c547..c84eecb191ef 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c | |||
| @@ -157,7 +157,7 @@ struct dev_ch_attribute { | |||
| 157 | }; | 157 | }; |
| 158 | 158 | ||
| 159 | #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ | 159 | #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ |
| 160 | struct dev_ch_attribute dev_attr_legacy_##_name = \ | 160 | static struct dev_ch_attribute dev_attr_legacy_##_name = \ |
| 161 | { __ATTR(_name, _mode, _show, _store), (_var) } | 161 | { __ATTR(_name, _mode, _show, _store), (_var) } |
| 162 | 162 | ||
| 163 | #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) | 163 | #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) |
| @@ -850,20 +850,20 @@ static const struct file_operations debug_fake_inject_fops = { | |||
| 850 | #endif | 850 | #endif |
| 851 | 851 | ||
| 852 | /* default Control file */ | 852 | /* default Control file */ |
| 853 | DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); | 853 | static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); |
| 854 | 854 | ||
| 855 | /* default Attribute files */ | 855 | /* default Attribute files */ |
| 856 | DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); | 856 | static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); |
| 857 | DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); | 857 | static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); |
| 858 | DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); | 858 | static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); |
| 859 | DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); | 859 | static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); |
| 860 | DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); | 860 | static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); |
| 861 | DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); | 861 | static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); |
| 862 | DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); | 862 | static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); |
| 863 | DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); | 863 | static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); |
| 864 | 864 | ||
| 865 | /* memory scrubber attribute file */ | 865 | /* memory scrubber attribute file */ |
| 866 | DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL); | 866 | static DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL); |
| 867 | 867 | ||
| 868 | static struct attribute *mci_attrs[] = { | 868 | static struct attribute *mci_attrs[] = { |
| 869 | &dev_attr_reset_counters.attr, | 869 | &dev_attr_reset_counters.attr, |
| @@ -989,7 +989,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
| 989 | 989 | ||
| 990 | err = bus_register(mci->bus); | 990 | err = bus_register(mci->bus); |
| 991 | if (err < 0) | 991 | if (err < 0) |
| 992 | return err; | 992 | goto fail_free_name; |
| 993 | 993 | ||
| 994 | /* get the /sys/devices/system/edac subsys reference */ | 994 | /* get the /sys/devices/system/edac subsys reference */ |
| 995 | mci->dev.type = &mci_attr_type; | 995 | mci->dev.type = &mci_attr_type; |
| @@ -1005,9 +1005,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
| 1005 | err = device_add(&mci->dev); | 1005 | err = device_add(&mci->dev); |
| 1006 | if (err < 0) { | 1006 | if (err < 0) { |
| 1007 | edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); | 1007 | edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); |
| 1008 | bus_unregister(mci->bus); | 1008 | goto fail_unregister_bus; |
| 1009 | kfree(mci->bus->name); | ||
| 1010 | return err; | ||
| 1011 | } | 1009 | } |
| 1012 | 1010 | ||
| 1013 | if (mci->set_sdram_scrub_rate || mci->get_sdram_scrub_rate) { | 1011 | if (mci->set_sdram_scrub_rate || mci->get_sdram_scrub_rate) { |
| @@ -1015,15 +1013,16 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
| 1015 | dev_attr_sdram_scrub_rate.attr.mode |= S_IRUGO; | 1013 | dev_attr_sdram_scrub_rate.attr.mode |= S_IRUGO; |
| 1016 | dev_attr_sdram_scrub_rate.show = &mci_sdram_scrub_rate_show; | 1014 | dev_attr_sdram_scrub_rate.show = &mci_sdram_scrub_rate_show; |
| 1017 | } | 1015 | } |
| 1016 | |||
| 1018 | if (mci->set_sdram_scrub_rate) { | 1017 | if (mci->set_sdram_scrub_rate) { |
| 1019 | dev_attr_sdram_scrub_rate.attr.mode |= S_IWUSR; | 1018 | dev_attr_sdram_scrub_rate.attr.mode |= S_IWUSR; |
| 1020 | dev_attr_sdram_scrub_rate.store = &mci_sdram_scrub_rate_store; | 1019 | dev_attr_sdram_scrub_rate.store = &mci_sdram_scrub_rate_store; |
| 1021 | } | 1020 | } |
| 1022 | err = device_create_file(&mci->dev, | 1021 | |
| 1023 | &dev_attr_sdram_scrub_rate); | 1022 | err = device_create_file(&mci->dev, &dev_attr_sdram_scrub_rate); |
| 1024 | if (err) { | 1023 | if (err) { |
| 1025 | edac_dbg(1, "failure: create sdram_scrub_rate\n"); | 1024 | edac_dbg(1, "failure: create sdram_scrub_rate\n"); |
| 1026 | goto fail2; | 1025 | goto fail_unregister_dev; |
| 1027 | } | 1026 | } |
| 1028 | } | 1027 | } |
| 1029 | /* | 1028 | /* |
| @@ -1032,8 +1031,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
| 1032 | for (i = 0; i < mci->tot_dimms; i++) { | 1031 | for (i = 0; i < mci->tot_dimms; i++) { |
| 1033 | struct dimm_info *dimm = mci->dimms[i]; | 1032 | struct dimm_info *dimm = mci->dimms[i]; |
| 1034 | /* Only expose populated DIMMs */ | 1033 | /* Only expose populated DIMMs */ |
| 1035 | if (dimm->nr_pages == 0) | 1034 | if (!dimm->nr_pages) |
| 1036 | continue; | 1035 | continue; |
| 1036 | |||
| 1037 | #ifdef CONFIG_EDAC_DEBUG | 1037 | #ifdef CONFIG_EDAC_DEBUG |
| 1038 | edac_dbg(1, "creating dimm%d, located at ", i); | 1038 | edac_dbg(1, "creating dimm%d, located at ", i); |
| 1039 | if (edac_debug_level >= 1) { | 1039 | if (edac_debug_level >= 1) { |
| @@ -1048,14 +1048,14 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
| 1048 | err = edac_create_dimm_object(mci, dimm, i); | 1048 | err = edac_create_dimm_object(mci, dimm, i); |
| 1049 | if (err) { | 1049 | if (err) { |
| 1050 | edac_dbg(1, "failure: create dimm %d obj\n", i); | 1050 | edac_dbg(1, "failure: create dimm %d obj\n", i); |
| 1051 | goto fail; | 1051 | goto fail_unregister_dimm; |
| 1052 | } | 1052 | } |
| 1053 | } | 1053 | } |
| 1054 | 1054 | ||
| 1055 | #ifdef CONFIG_EDAC_LEGACY_SYSFS | 1055 | #ifdef CONFIG_EDAC_LEGACY_SYSFS |
| 1056 | err = edac_create_csrow_objects(mci); | 1056 | err = edac_create_csrow_objects(mci); |
| 1057 | if (err < 0) | 1057 | if (err < 0) |
| 1058 | goto fail; | 1058 | goto fail_unregister_dimm; |
| 1059 | #endif | 1059 | #endif |
| 1060 | 1060 | ||
| 1061 | #ifdef CONFIG_EDAC_DEBUG | 1061 | #ifdef CONFIG_EDAC_DEBUG |
| @@ -1063,16 +1063,19 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
| 1063 | #endif | 1063 | #endif |
| 1064 | return 0; | 1064 | return 0; |
| 1065 | 1065 | ||
| 1066 | fail: | 1066 | fail_unregister_dimm: |
| 1067 | for (i--; i >= 0; i--) { | 1067 | for (i--; i >= 0; i--) { |
| 1068 | struct dimm_info *dimm = mci->dimms[i]; | 1068 | struct dimm_info *dimm = mci->dimms[i]; |
| 1069 | if (dimm->nr_pages == 0) | 1069 | if (!dimm->nr_pages) |
| 1070 | continue; | 1070 | continue; |
| 1071 | |||
| 1071 | device_unregister(&dimm->dev); | 1072 | device_unregister(&dimm->dev); |
| 1072 | } | 1073 | } |
| 1073 | fail2: | 1074 | fail_unregister_dev: |
| 1074 | device_unregister(&mci->dev); | 1075 | device_unregister(&mci->dev); |
| 1076 | fail_unregister_bus: | ||
| 1075 | bus_unregister(mci->bus); | 1077 | bus_unregister(mci->bus); |
| 1078 | fail_free_name: | ||
| 1076 | kfree(mci->bus->name); | 1079 | kfree(mci->bus->name); |
| 1077 | return err; | 1080 | return err; |
| 1078 | } | 1081 | } |
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index 6247d186177e..e9f8a393915a 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c | |||
| @@ -279,11 +279,6 @@ static inline u32 i5100_recmema_rank(u32 a) | |||
| 279 | return i5100_nrecmema_rank(a); | 279 | return i5100_nrecmema_rank(a); |
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | static inline u32 i5100_recmema_dm_buf_id(u32 a) | ||
| 283 | { | ||
| 284 | return i5100_nrecmema_dm_buf_id(a); | ||
| 285 | } | ||
| 286 | |||
| 287 | static inline u32 i5100_recmemb_cas(u32 a) | 282 | static inline u32 i5100_recmemb_cas(u32 a) |
| 288 | { | 283 | { |
| 289 | return i5100_nrecmemb_cas(a); | 284 | return i5100_nrecmemb_cas(a); |
diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 0bd91a802c67..f7681b553fd5 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c | |||
| @@ -197,7 +197,7 @@ static int inj_bank_get(void *data, u64 *val) | |||
| 197 | 197 | ||
| 198 | DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); | 198 | DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); |
| 199 | 199 | ||
| 200 | struct dfs_node { | 200 | static struct dfs_node { |
| 201 | char *name; | 201 | char *name; |
| 202 | struct dentry *d; | 202 | struct dentry *d; |
| 203 | const struct file_operations *fops; | 203 | const struct file_operations *fops; |
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index ffb1a9a15ccd..1fa76a588af3 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Freescale MPC85xx Memory Controller kenel module | 2 | * Freescale MPC85xx Memory Controller kernel module |
| 3 | * | 3 | * |
| 4 | * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc. | 4 | * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc. |
| 5 | * | 5 | * |
diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h index 8c6256436227..4498baf9ce05 100644 --- a/drivers/edac/mpc85xx_edac.h +++ b/drivers/edac/mpc85xx_edac.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Freescale MPC85xx Memory Controller kenel module | 2 | * Freescale MPC85xx Memory Controller kernel module |
| 3 | * Author: Dave Jiang <djiang@mvista.com> | 3 | * Author: Dave Jiang <djiang@mvista.com> |
| 4 | * | 4 | * |
| 5 | * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under | 5 | * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under |
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index 6366e880f978..0574e1bbe45c 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c | |||
| @@ -789,7 +789,8 @@ static int mv64x60_mc_err_probe(struct platform_device *pdev) | |||
| 789 | ctl = (ctl & 0xff00ffff) | 0x10000; | 789 | ctl = (ctl & 0xff00ffff) | 0x10000; |
| 790 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); | 790 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); |
| 791 | 791 | ||
| 792 | if (edac_mc_add_mc(mci)) { | 792 | res = edac_mc_add_mc(mci); |
| 793 | if (res) { | ||
| 793 | edac_dbg(3, "failed edac_mc_add_mc()\n"); | 794 | edac_dbg(3, "failed edac_mc_add_mc()\n"); |
| 794 | goto err; | 795 | goto err; |
| 795 | } | 796 | } |
diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c new file mode 100644 index 000000000000..1c9691535e13 --- /dev/null +++ b/drivers/edac/synopsys_edac.c | |||
| @@ -0,0 +1,535 @@ | |||
| 1 | /* | ||
| 2 | * Synopsys DDR ECC Driver | ||
| 3 | * This driver is based on ppc4xx_edac.c drivers | ||
| 4 | * | ||
| 5 | * Copyright (C) 2012 - 2014 Xilinx, Inc. | ||
| 6 | * | ||
| 7 | * This program is free software: you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation, either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 18 | * License. See the file "COPYING" in the main directory of this archive | ||
| 19 | * for more details | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/edac.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | |||
| 26 | #include "edac_core.h" | ||
| 27 | |||
| 28 | /* Number of cs_rows needed per memory controller */ | ||
| 29 | #define SYNPS_EDAC_NR_CSROWS 1 | ||
| 30 | |||
| 31 | /* Number of channels per memory controller */ | ||
| 32 | #define SYNPS_EDAC_NR_CHANS 1 | ||
| 33 | |||
| 34 | /* Granularity of reported error in bytes */ | ||
| 35 | #define SYNPS_EDAC_ERR_GRAIN 1 | ||
| 36 | |||
| 37 | #define SYNPS_EDAC_MSG_SIZE 256 | ||
| 38 | |||
| 39 | #define SYNPS_EDAC_MOD_STRING "synps_edac" | ||
| 40 | #define SYNPS_EDAC_MOD_VER "1" | ||
| 41 | |||
| 42 | /* Synopsys DDR memory controller registers that are relevant to ECC */ | ||
| 43 | #define CTRL_OFST 0x0 | ||
| 44 | #define T_ZQ_OFST 0xA4 | ||
| 45 | |||
| 46 | /* ECC control register */ | ||
| 47 | #define ECC_CTRL_OFST 0xC4 | ||
| 48 | /* ECC log register */ | ||
| 49 | #define CE_LOG_OFST 0xC8 | ||
| 50 | /* ECC address register */ | ||
| 51 | #define CE_ADDR_OFST 0xCC | ||
| 52 | /* ECC data[31:0] register */ | ||
| 53 | #define CE_DATA_31_0_OFST 0xD0 | ||
| 54 | |||
| 55 | /* Uncorrectable error info registers */ | ||
| 56 | #define UE_LOG_OFST 0xDC | ||
| 57 | #define UE_ADDR_OFST 0xE0 | ||
| 58 | #define UE_DATA_31_0_OFST 0xE4 | ||
| 59 | |||
| 60 | #define STAT_OFST 0xF0 | ||
| 61 | #define SCRUB_OFST 0xF4 | ||
| 62 | |||
| 63 | /* Control register bit field definitions */ | ||
| 64 | #define CTRL_BW_MASK 0xC | ||
| 65 | #define CTRL_BW_SHIFT 2 | ||
| 66 | |||
| 67 | #define DDRCTL_WDTH_16 1 | ||
| 68 | #define DDRCTL_WDTH_32 0 | ||
| 69 | |||
| 70 | /* ZQ register bit field definitions */ | ||
| 71 | #define T_ZQ_DDRMODE_MASK 0x2 | ||
| 72 | |||
| 73 | /* ECC control register bit field definitions */ | ||
| 74 | #define ECC_CTRL_CLR_CE_ERR 0x2 | ||
| 75 | #define ECC_CTRL_CLR_UE_ERR 0x1 | ||
| 76 | |||
| 77 | /* ECC correctable/uncorrectable error log register definitions */ | ||
| 78 | #define LOG_VALID 0x1 | ||
| 79 | #define CE_LOG_BITPOS_MASK 0xFE | ||
| 80 | #define CE_LOG_BITPOS_SHIFT 1 | ||
| 81 | |||
| 82 | /* ECC correctable/uncorrectable error address register definitions */ | ||
| 83 | #define ADDR_COL_MASK 0xFFF | ||
| 84 | #define ADDR_ROW_MASK 0xFFFF000 | ||
| 85 | #define ADDR_ROW_SHIFT 12 | ||
| 86 | #define ADDR_BANK_MASK 0x70000000 | ||
| 87 | #define ADDR_BANK_SHIFT 28 | ||
| 88 | |||
| 89 | /* ECC statistic register definitions */ | ||
| 90 | #define STAT_UECNT_MASK 0xFF | ||
| 91 | #define STAT_CECNT_MASK 0xFF00 | ||
| 92 | #define STAT_CECNT_SHIFT 8 | ||
| 93 | |||
| 94 | /* ECC scrub register definitions */ | ||
| 95 | #define SCRUB_MODE_MASK 0x7 | ||
| 96 | #define SCRUB_MODE_SECDED 0x4 | ||
| 97 | |||
| 98 | /** | ||
| 99 | * struct ecc_error_info - ECC error log information | ||
| 100 | * @row: Row number | ||
| 101 | * @col: Column number | ||
| 102 | * @bank: Bank number | ||
| 103 | * @bitpos: Bit position | ||
| 104 | * @data: Data causing the error | ||
| 105 | */ | ||
| 106 | struct ecc_error_info { | ||
| 107 | u32 row; | ||
| 108 | u32 col; | ||
| 109 | u32 bank; | ||
| 110 | u32 bitpos; | ||
| 111 | u32 data; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * struct synps_ecc_status - ECC status information to report | ||
| 116 | * @ce_cnt: Correctable error count | ||
| 117 | * @ue_cnt: Uncorrectable error count | ||
| 118 | * @ceinfo: Correctable error log information | ||
| 119 | * @ueinfo: Uncorrectable error log information | ||
| 120 | */ | ||
| 121 | struct synps_ecc_status { | ||
| 122 | u32 ce_cnt; | ||
| 123 | u32 ue_cnt; | ||
| 124 | struct ecc_error_info ceinfo; | ||
| 125 | struct ecc_error_info ueinfo; | ||
| 126 | }; | ||
| 127 | |||
| 128 | /** | ||
| 129 | * struct synps_edac_priv - DDR memory controller private instance data | ||
| 130 | * @baseaddr: Base address of the DDR controller | ||
| 131 | * @message: Buffer for framing the event specific info | ||
| 132 | * @stat: ECC status information | ||
| 133 | * @ce_cnt: Correctable Error count | ||
| 134 | * @ue_cnt: Uncorrectable Error count | ||
| 135 | */ | ||
| 136 | struct synps_edac_priv { | ||
| 137 | void __iomem *baseaddr; | ||
| 138 | char message[SYNPS_EDAC_MSG_SIZE]; | ||
| 139 | struct synps_ecc_status stat; | ||
| 140 | u32 ce_cnt; | ||
| 141 | u32 ue_cnt; | ||
| 142 | }; | ||
| 143 | |||
| 144 | /** | ||
| 145 | * synps_edac_geterror_info - Get the current ecc error info | ||
| 146 | * @base: Pointer to the base address of the ddr memory controller | ||
| 147 | * @p: Pointer to the synopsys ecc status structure | ||
| 148 | * | ||
| 149 | * Determines there is any ecc error or not | ||
| 150 | * | ||
| 151 | * Return: one if there is no error otherwise returns zero | ||
| 152 | */ | ||
| 153 | static int synps_edac_geterror_info(void __iomem *base, | ||
| 154 | struct synps_ecc_status *p) | ||
| 155 | { | ||
| 156 | u32 regval, clearval = 0; | ||
| 157 | |||
| 158 | regval = readl(base + STAT_OFST); | ||
| 159 | if (!regval) | ||
| 160 | return 1; | ||
| 161 | |||
| 162 | p->ce_cnt = (regval & STAT_CECNT_MASK) >> STAT_CECNT_SHIFT; | ||
| 163 | p->ue_cnt = regval & STAT_UECNT_MASK; | ||
| 164 | |||
| 165 | regval = readl(base + CE_LOG_OFST); | ||
| 166 | if (!(p->ce_cnt && (regval & LOG_VALID))) | ||
| 167 | goto ue_err; | ||
| 168 | |||
| 169 | p->ceinfo.bitpos = (regval & CE_LOG_BITPOS_MASK) >> CE_LOG_BITPOS_SHIFT; | ||
| 170 | regval = readl(base + CE_ADDR_OFST); | ||
| 171 | p->ceinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; | ||
| 172 | p->ceinfo.col = regval & ADDR_COL_MASK; | ||
| 173 | p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; | ||
| 174 | p->ceinfo.data = readl(base + CE_DATA_31_0_OFST); | ||
| 175 | edac_dbg(3, "ce bit position: %d data: %d\n", p->ceinfo.bitpos, | ||
| 176 | p->ceinfo.data); | ||
| 177 | clearval = ECC_CTRL_CLR_CE_ERR; | ||
| 178 | |||
| 179 | ue_err: | ||
| 180 | regval = readl(base + UE_LOG_OFST); | ||
| 181 | if (!(p->ue_cnt && (regval & LOG_VALID))) | ||
| 182 | goto out; | ||
| 183 | |||
| 184 | regval = readl(base + UE_ADDR_OFST); | ||
| 185 | p->ueinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; | ||
| 186 | p->ueinfo.col = regval & ADDR_COL_MASK; | ||
| 187 | p->ueinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; | ||
| 188 | p->ueinfo.data = readl(base + UE_DATA_31_0_OFST); | ||
| 189 | clearval |= ECC_CTRL_CLR_UE_ERR; | ||
| 190 | |||
| 191 | out: | ||
| 192 | writel(clearval, base + ECC_CTRL_OFST); | ||
| 193 | writel(0x0, base + ECC_CTRL_OFST); | ||
| 194 | |||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | /** | ||
| 199 | * synps_edac_handle_error - Handle controller error types CE and UE | ||
| 200 | * @mci: Pointer to the edac memory controller instance | ||
| 201 | * @p: Pointer to the synopsys ecc status structure | ||
| 202 | * | ||
| 203 | * Handles the controller ECC correctable and un correctable error. | ||
| 204 | */ | ||
| 205 | static void synps_edac_handle_error(struct mem_ctl_info *mci, | ||
| 206 | struct synps_ecc_status *p) | ||
| 207 | { | ||
| 208 | struct synps_edac_priv *priv = mci->pvt_info; | ||
| 209 | struct ecc_error_info *pinf; | ||
| 210 | |||
| 211 | if (p->ce_cnt) { | ||
| 212 | pinf = &p->ceinfo; | ||
| 213 | snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, | ||
| 214 | "DDR ECC error type :%s Row %d Bank %d Col %d ", | ||
| 215 | "CE", pinf->row, pinf->bank, pinf->col); | ||
| 216 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, | ||
| 217 | p->ce_cnt, 0, 0, 0, 0, 0, -1, | ||
| 218 | priv->message, ""); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (p->ue_cnt) { | ||
| 222 | pinf = &p->ueinfo; | ||
| 223 | snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, | ||
| 224 | "DDR ECC error type :%s Row %d Bank %d Col %d ", | ||
| 225 | "UE", pinf->row, pinf->bank, pinf->col); | ||
| 226 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, | ||
| 227 | p->ue_cnt, 0, 0, 0, 0, 0, -1, | ||
| 228 | priv->message, ""); | ||
| 229 | } | ||
| 230 | |||
| 231 | memset(p, 0, sizeof(*p)); | ||
| 232 | } | ||
| 233 | |||
| 234 | /** | ||
| 235 | * synps_edac_check - Check controller for ECC errors | ||
| 236 | * @mci: Pointer to the edac memory controller instance | ||
| 237 | * | ||
| 238 | * Used to check and post ECC errors. Called by the polling thread | ||
| 239 | */ | ||
| 240 | static void synps_edac_check(struct mem_ctl_info *mci) | ||
| 241 | { | ||
| 242 | struct synps_edac_priv *priv = mci->pvt_info; | ||
| 243 | int status; | ||
| 244 | |||
| 245 | status = synps_edac_geterror_info(priv->baseaddr, &priv->stat); | ||
| 246 | if (status) | ||
| 247 | return; | ||
| 248 | |||
| 249 | priv->ce_cnt += priv->stat.ce_cnt; | ||
| 250 | priv->ue_cnt += priv->stat.ue_cnt; | ||
| 251 | synps_edac_handle_error(mci, &priv->stat); | ||
| 252 | |||
| 253 | edac_dbg(3, "Total error count ce %d ue %d\n", | ||
| 254 | priv->ce_cnt, priv->ue_cnt); | ||
| 255 | } | ||
| 256 | |||
| 257 | /** | ||
| 258 | * synps_edac_get_dtype - Return the controller memory width | ||
| 259 | * @base: Pointer to the ddr memory controller base address | ||
| 260 | * | ||
| 261 | * Get the EDAC device type width appropriate for the current controller | ||
| 262 | * configuration. | ||
| 263 | * | ||
| 264 | * Return: a device type width enumeration. | ||
| 265 | */ | ||
| 266 | static enum dev_type synps_edac_get_dtype(const void __iomem *base) | ||
| 267 | { | ||
| 268 | enum dev_type dt; | ||
| 269 | u32 width; | ||
| 270 | |||
| 271 | width = readl(base + CTRL_OFST); | ||
| 272 | width = (width & CTRL_BW_MASK) >> CTRL_BW_SHIFT; | ||
| 273 | |||
| 274 | switch (width) { | ||
| 275 | case DDRCTL_WDTH_16: | ||
| 276 | dt = DEV_X2; | ||
| 277 | break; | ||
| 278 | case DDRCTL_WDTH_32: | ||
| 279 | dt = DEV_X4; | ||
| 280 | break; | ||
| 281 | default: | ||
| 282 | dt = DEV_UNKNOWN; | ||
| 283 | } | ||
| 284 | |||
| 285 | return dt; | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * synps_edac_get_eccstate - Return the controller ecc enable/disable status | ||
| 290 | * @base: Pointer to the ddr memory controller base address | ||
| 291 | * | ||
| 292 | * Get the ECC enable/disable status for the controller | ||
| 293 | * | ||
| 294 | * Return: a ecc status boolean i.e true/false - enabled/disabled. | ||
| 295 | */ | ||
| 296 | static bool synps_edac_get_eccstate(void __iomem *base) | ||
| 297 | { | ||
| 298 | enum dev_type dt; | ||
| 299 | u32 ecctype; | ||
| 300 | bool state = false; | ||
| 301 | |||
| 302 | dt = synps_edac_get_dtype(base); | ||
| 303 | if (dt == DEV_UNKNOWN) | ||
| 304 | return state; | ||
| 305 | |||
| 306 | ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; | ||
| 307 | if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) | ||
| 308 | state = true; | ||
| 309 | |||
| 310 | return state; | ||
| 311 | } | ||
| 312 | |||
| 313 | /** | ||
| 314 | * synps_edac_get_memsize - reads the size of the attached memory device | ||
| 315 | * | ||
| 316 | * Return: the memory size in bytes | ||
| 317 | */ | ||
| 318 | static u32 synps_edac_get_memsize(void) | ||
| 319 | { | ||
| 320 | struct sysinfo inf; | ||
| 321 | |||
| 322 | si_meminfo(&inf); | ||
| 323 | |||
| 324 | return inf.totalram * inf.mem_unit; | ||
| 325 | } | ||
| 326 | |||
| 327 | /** | ||
| 328 | * synps_edac_get_mtype - Returns controller memory type | ||
| 329 | * @base: pointer to the synopsys ecc status structure | ||
| 330 | * | ||
| 331 | * Get the EDAC memory type appropriate for the current controller | ||
| 332 | * configuration. | ||
| 333 | * | ||
| 334 | * Return: a memory type enumeration. | ||
| 335 | */ | ||
| 336 | static enum mem_type synps_edac_get_mtype(const void __iomem *base) | ||
| 337 | { | ||
| 338 | enum mem_type mt; | ||
| 339 | u32 memtype; | ||
| 340 | |||
| 341 | memtype = readl(base + T_ZQ_OFST); | ||
| 342 | |||
| 343 | if (memtype & T_ZQ_DDRMODE_MASK) | ||
| 344 | mt = MEM_DDR3; | ||
| 345 | else | ||
| 346 | mt = MEM_DDR2; | ||
| 347 | |||
| 348 | return mt; | ||
| 349 | } | ||
| 350 | |||
| 351 | /** | ||
| 352 | * synps_edac_init_csrows - Initialize the cs row data | ||
| 353 | * @mci: Pointer to the edac memory controller instance | ||
| 354 | * | ||
| 355 | * Initializes the chip select rows associated with the EDAC memory | ||
| 356 | * controller instance | ||
| 357 | * | ||
| 358 | * Return: Unconditionally 0. | ||
| 359 | */ | ||
| 360 | static int synps_edac_init_csrows(struct mem_ctl_info *mci) | ||
| 361 | { | ||
| 362 | struct csrow_info *csi; | ||
| 363 | struct dimm_info *dimm; | ||
| 364 | struct synps_edac_priv *priv = mci->pvt_info; | ||
| 365 | u32 size; | ||
| 366 | int row, j; | ||
| 367 | |||
| 368 | for (row = 0; row < mci->nr_csrows; row++) { | ||
| 369 | csi = mci->csrows[row]; | ||
| 370 | size = synps_edac_get_memsize(); | ||
| 371 | |||
| 372 | for (j = 0; j < csi->nr_channels; j++) { | ||
| 373 | dimm = csi->channels[j]->dimm; | ||
| 374 | dimm->edac_mode = EDAC_FLAG_SECDED; | ||
| 375 | dimm->mtype = synps_edac_get_mtype(priv->baseaddr); | ||
| 376 | dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; | ||
| 377 | dimm->grain = SYNPS_EDAC_ERR_GRAIN; | ||
| 378 | dimm->dtype = synps_edac_get_dtype(priv->baseaddr); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | return 0; | ||
| 383 | } | ||
| 384 | |||
| 385 | /** | ||
| 386 | * synps_edac_mc_init - Initialize driver instance | ||
| 387 | * @mci: Pointer to the edac memory controller instance | ||
| 388 | * @pdev: Pointer to the platform_device struct | ||
| 389 | * | ||
| 390 | * Performs initialization of the EDAC memory controller instance and | ||
| 391 | * related driver-private data associated with the memory controller the | ||
| 392 | * instance is bound to. | ||
| 393 | * | ||
| 394 | * Return: Always zero. | ||
| 395 | */ | ||
| 396 | static int synps_edac_mc_init(struct mem_ctl_info *mci, | ||
| 397 | struct platform_device *pdev) | ||
| 398 | { | ||
| 399 | int status; | ||
| 400 | struct synps_edac_priv *priv; | ||
| 401 | |||
| 402 | mci->pdev = &pdev->dev; | ||
| 403 | priv = mci->pvt_info; | ||
| 404 | platform_set_drvdata(pdev, mci); | ||
| 405 | |||
| 406 | /* Initialize controller capabilities and configuration */ | ||
| 407 | mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2; | ||
| 408 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | ||
| 409 | mci->scrub_cap = SCRUB_HW_SRC; | ||
| 410 | mci->scrub_mode = SCRUB_NONE; | ||
| 411 | |||
| 412 | mci->edac_cap = EDAC_FLAG_SECDED; | ||
| 413 | mci->ctl_name = "synps_ddr_controller"; | ||
| 414 | mci->dev_name = SYNPS_EDAC_MOD_STRING; | ||
| 415 | mci->mod_name = SYNPS_EDAC_MOD_VER; | ||
| 416 | mci->mod_ver = "1"; | ||
| 417 | |||
| 418 | edac_op_state = EDAC_OPSTATE_POLL; | ||
| 419 | mci->edac_check = synps_edac_check; | ||
| 420 | mci->ctl_page_to_phys = NULL; | ||
| 421 | |||
| 422 | status = synps_edac_init_csrows(mci); | ||
| 423 | |||
| 424 | return status; | ||
| 425 | } | ||
| 426 | |||
| 427 | /** | ||
| 428 | * synps_edac_mc_probe - Check controller and bind driver | ||
| 429 | * @pdev: Pointer to the platform_device struct | ||
| 430 | * | ||
| 431 | * Probes a specific controller instance for binding with the driver. | ||
| 432 | * | ||
| 433 | * Return: 0 if the controller instance was successfully bound to the | ||
| 434 | * driver; otherwise, < 0 on error. | ||
| 435 | */ | ||
| 436 | static int synps_edac_mc_probe(struct platform_device *pdev) | ||
| 437 | { | ||
| 438 | struct mem_ctl_info *mci; | ||
| 439 | struct edac_mc_layer layers[2]; | ||
| 440 | struct synps_edac_priv *priv; | ||
| 441 | int rc; | ||
| 442 | struct resource *res; | ||
| 443 | void __iomem *baseaddr; | ||
| 444 | |||
| 445 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 446 | baseaddr = devm_ioremap_resource(&pdev->dev, res); | ||
| 447 | if (IS_ERR(baseaddr)) | ||
| 448 | return PTR_ERR(baseaddr); | ||
| 449 | |||
| 450 | if (!synps_edac_get_eccstate(baseaddr)) { | ||
| 451 | edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); | ||
| 452 | return -ENXIO; | ||
| 453 | } | ||
| 454 | |||
| 455 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; | ||
| 456 | layers[0].size = SYNPS_EDAC_NR_CSROWS; | ||
| 457 | layers[0].is_virt_csrow = true; | ||
| 458 | layers[1].type = EDAC_MC_LAYER_CHANNEL; | ||
| 459 | layers[1].size = SYNPS_EDAC_NR_CHANS; | ||
| 460 | layers[1].is_virt_csrow = false; | ||
| 461 | |||
| 462 | mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, | ||
| 463 | sizeof(struct synps_edac_priv)); | ||
| 464 | if (!mci) { | ||
| 465 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 466 | "Failed memory allocation for mc instance\n"); | ||
| 467 | return -ENOMEM; | ||
| 468 | } | ||
| 469 | |||
| 470 | priv = mci->pvt_info; | ||
| 471 | priv->baseaddr = baseaddr; | ||
| 472 | rc = synps_edac_mc_init(mci, pdev); | ||
| 473 | if (rc) { | ||
| 474 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 475 | "Failed to initialize instance\n"); | ||
| 476 | goto free_edac_mc; | ||
| 477 | } | ||
| 478 | |||
| 479 | rc = edac_mc_add_mc(mci); | ||
| 480 | if (rc) { | ||
| 481 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 482 | "Failed to register with EDAC core\n"); | ||
| 483 | goto free_edac_mc; | ||
| 484 | } | ||
| 485 | |||
| 486 | /* | ||
| 487 | * Start capturing the correctable and uncorrectable errors. A write of | ||
| 488 | * 0 starts the counters. | ||
| 489 | */ | ||
| 490 | writel(0x0, baseaddr + ECC_CTRL_OFST); | ||
| 491 | return rc; | ||
| 492 | |||
| 493 | free_edac_mc: | ||
| 494 | edac_mc_free(mci); | ||
| 495 | |||
| 496 | return rc; | ||
| 497 | } | ||
| 498 | |||
| 499 | /** | ||
| 500 | * synps_edac_mc_remove - Unbind driver from controller | ||
| 501 | * @pdev: Pointer to the platform_device struct | ||
| 502 | * | ||
| 503 | * Return: Unconditionally 0 | ||
| 504 | */ | ||
| 505 | static int synps_edac_mc_remove(struct platform_device *pdev) | ||
| 506 | { | ||
| 507 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); | ||
| 508 | |||
| 509 | edac_mc_del_mc(&pdev->dev); | ||
| 510 | edac_mc_free(mci); | ||
| 511 | |||
| 512 | return 0; | ||
| 513 | } | ||
| 514 | |||
| 515 | static struct of_device_id synps_edac_match[] = { | ||
| 516 | { .compatible = "xlnx,zynq-ddrc-a05", }, | ||
| 517 | { /* end of table */ } | ||
| 518 | }; | ||
| 519 | |||
| 520 | MODULE_DEVICE_TABLE(of, synps_edac_match); | ||
| 521 | |||
| 522 | static struct platform_driver synps_edac_mc_driver = { | ||
| 523 | .driver = { | ||
| 524 | .name = "synopsys-edac", | ||
| 525 | .of_match_table = synps_edac_match, | ||
| 526 | }, | ||
| 527 | .probe = synps_edac_mc_probe, | ||
| 528 | .remove = synps_edac_mc_remove, | ||
| 529 | }; | ||
| 530 | |||
| 531 | module_platform_driver(synps_edac_mc_driver); | ||
| 532 | |||
| 533 | MODULE_AUTHOR("Xilinx Inc"); | ||
| 534 | MODULE_DESCRIPTION("Synopsys DDR ECC driver"); | ||
| 535 | MODULE_LICENSE("GPL v2"); | ||
