diff options
-rw-r--r-- | drivers/edac/altera_edac.c | 167 | ||||
-rw-r--r-- | drivers/edac/altera_edac.h | 5 |
2 files changed, 130 insertions, 42 deletions
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 5b4d223d6d68..c254f90b2e70 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c | |||
@@ -22,9 +22,11 @@ | |||
22 | #include <linux/edac.h> | 22 | #include <linux/edac.h> |
23 | #include <linux/genalloc.h> | 23 | #include <linux/genalloc.h> |
24 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
25 | #include <linux/irqchip/chained_irq.h> | ||
25 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
26 | #include <linux/mfd/syscon.h> | 27 | #include <linux/mfd/syscon.h> |
27 | #include <linux/of_address.h> | 28 | #include <linux/of_address.h> |
29 | #include <linux/of_irq.h> | ||
28 | #include <linux/of_platform.h> | 30 | #include <linux/of_platform.h> |
29 | #include <linux/platform_device.h> | 31 | #include <linux/platform_device.h> |
30 | #include <linux/regmap.h> | 32 | #include <linux/regmap.h> |
@@ -882,22 +884,29 @@ static void ocram_free_mem(void *p, size_t size, void *other) | |||
882 | gen_pool_free((struct gen_pool *)other, (u32)p, size); | 884 | gen_pool_free((struct gen_pool *)other, (u32)p, size); |
883 | } | 885 | } |
884 | 886 | ||
885 | static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci, | 887 | static irqreturn_t altr_edac_a10_ecc_irq(int irq, void *dev_id) |
886 | bool sberr) | ||
887 | { | 888 | { |
889 | struct altr_edac_device_dev *dci = dev_id; | ||
888 | void __iomem *base = dci->base; | 890 | void __iomem *base = dci->base; |
889 | 891 | ||
890 | if (sberr) { | 892 | if (irq == dci->sb_irq) { |
891 | writel(ALTR_A10_ECC_SERRPENA, | 893 | writel(ALTR_A10_ECC_SERRPENA, |
892 | base + ALTR_A10_ECC_INTSTAT_OFST); | 894 | base + ALTR_A10_ECC_INTSTAT_OFST); |
893 | edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); | 895 | edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); |
894 | } else { | 896 | |
897 | return IRQ_HANDLED; | ||
898 | } else if (irq == dci->db_irq) { | ||
895 | writel(ALTR_A10_ECC_DERRPENA, | 899 | writel(ALTR_A10_ECC_DERRPENA, |
896 | base + ALTR_A10_ECC_INTSTAT_OFST); | 900 | base + ALTR_A10_ECC_INTSTAT_OFST); |
897 | edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); | 901 | edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); |
898 | panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); | 902 | panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); |
903 | |||
904 | return IRQ_HANDLED; | ||
899 | } | 905 | } |
900 | return IRQ_HANDLED; | 906 | |
907 | WARN_ON(1); | ||
908 | |||
909 | return IRQ_NONE; | ||
901 | } | 910 | } |
902 | 911 | ||
903 | const struct edac_device_prv_data ocramecc_data = { | 912 | const struct edac_device_prv_data ocramecc_data = { |
@@ -988,22 +997,30 @@ static int altr_l2_check_deps(struct altr_edac_device_dev *device) | |||
988 | return -ENODEV; | 997 | return -ENODEV; |
989 | } | 998 | } |
990 | 999 | ||
991 | static irqreturn_t altr_edac_a10_l2_irq(struct altr_edac_device_dev *dci, | 1000 | static irqreturn_t altr_edac_a10_l2_irq(int irq, void *dev_id) |
992 | bool sberr) | ||
993 | { | 1001 | { |
994 | if (sberr) { | 1002 | struct altr_edac_device_dev *dci = dev_id; |
1003 | |||
1004 | if (irq == dci->sb_irq) { | ||
995 | regmap_write(dci->edac->ecc_mgr_map, | 1005 | regmap_write(dci->edac->ecc_mgr_map, |
996 | A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST, | 1006 | A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST, |
997 | A10_SYSGMR_MPU_CLEAR_L2_ECC_SB); | 1007 | A10_SYSGMR_MPU_CLEAR_L2_ECC_SB); |
998 | edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); | 1008 | edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); |
999 | } else { | 1009 | |
1010 | return IRQ_HANDLED; | ||
1011 | } else if (irq == dci->db_irq) { | ||
1000 | regmap_write(dci->edac->ecc_mgr_map, | 1012 | regmap_write(dci->edac->ecc_mgr_map, |
1001 | A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST, | 1013 | A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST, |
1002 | A10_SYSGMR_MPU_CLEAR_L2_ECC_MB); | 1014 | A10_SYSGMR_MPU_CLEAR_L2_ECC_MB); |
1003 | edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); | 1015 | edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); |
1004 | panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); | 1016 | panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); |
1017 | |||
1018 | return IRQ_HANDLED; | ||
1005 | } | 1019 | } |
1006 | return IRQ_HANDLED; | 1020 | |
1021 | WARN_ON(1); | ||
1022 | |||
1023 | return IRQ_NONE; | ||
1007 | } | 1024 | } |
1008 | 1025 | ||
1009 | const struct edac_device_prv_data l2ecc_data = { | 1026 | const struct edac_device_prv_data l2ecc_data = { |
@@ -1075,28 +1092,28 @@ static ssize_t altr_edac_a10_device_trig(struct file *file, | |||
1075 | return count; | 1092 | return count; |
1076 | } | 1093 | } |
1077 | 1094 | ||
1078 | static irqreturn_t altr_edac_a10_irq_handler(int irq, void *dev_id) | 1095 | static void altr_edac_a10_irq_handler(struct irq_desc *desc) |
1079 | { | 1096 | { |
1080 | irqreturn_t rc = IRQ_NONE; | 1097 | int dberr, bit, sm_offset, irq_status; |
1081 | struct altr_arria10_edac *edac = dev_id; | 1098 | struct altr_arria10_edac *edac = irq_desc_get_handler_data(desc); |
1082 | struct altr_edac_device_dev *dci; | 1099 | struct irq_chip *chip = irq_desc_get_chip(desc); |
1083 | int irq_status; | 1100 | int irq = irq_desc_get_irq(desc); |
1084 | bool sberr = (irq == edac->sb_irq) ? 1 : 0; | 1101 | |
1085 | int sm_offset = sberr ? A10_SYSMGR_ECC_INTSTAT_SERR_OFST : | 1102 | dberr = (irq == edac->db_irq) ? 1 : 0; |
1086 | A10_SYSMGR_ECC_INTSTAT_DERR_OFST; | 1103 | sm_offset = dberr ? A10_SYSMGR_ECC_INTSTAT_DERR_OFST : |
1104 | A10_SYSMGR_ECC_INTSTAT_SERR_OFST; | ||
1105 | |||
1106 | chained_irq_enter(chip, desc); | ||
1087 | 1107 | ||
1088 | regmap_read(edac->ecc_mgr_map, sm_offset, &irq_status); | 1108 | regmap_read(edac->ecc_mgr_map, sm_offset, &irq_status); |
1089 | 1109 | ||
1090 | if ((irq != edac->sb_irq) && (irq != edac->db_irq)) { | 1110 | for_each_set_bit(bit, (unsigned long *)&irq_status, 32) { |
1091 | WARN_ON(1); | 1111 | irq = irq_linear_revmap(edac->domain, dberr * 32 + bit); |
1092 | } else { | 1112 | if (irq) |
1093 | list_for_each_entry(dci, &edac->a10_ecc_devices, next) { | 1113 | generic_handle_irq(irq); |
1094 | if (irq_status & dci->data->irq_status_mask) | ||
1095 | rc = dci->data->ecc_irq_handler(dci, sberr); | ||
1096 | } | ||
1097 | } | 1114 | } |
1098 | 1115 | ||
1099 | return rc; | 1116 | chained_irq_exit(chip, desc); |
1100 | } | 1117 | } |
1101 | 1118 | ||
1102 | static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, | 1119 | static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, |
@@ -1168,6 +1185,34 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, | |||
1168 | goto err_release_group1; | 1185 | goto err_release_group1; |
1169 | } | 1186 | } |
1170 | 1187 | ||
1188 | altdev->sb_irq = irq_of_parse_and_map(np, 0); | ||
1189 | if (!altdev->sb_irq) { | ||
1190 | edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating SBIRQ\n"); | ||
1191 | rc = -ENODEV; | ||
1192 | goto err_release_group1; | ||
1193 | } | ||
1194 | rc = devm_request_irq(edac->dev, altdev->sb_irq, | ||
1195 | prv->ecc_irq_handler, | ||
1196 | IRQF_SHARED, ecc_name, altdev); | ||
1197 | if (rc) { | ||
1198 | edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n"); | ||
1199 | goto err_release_group1; | ||
1200 | } | ||
1201 | |||
1202 | altdev->db_irq = irq_of_parse_and_map(np, 1); | ||
1203 | if (!altdev->db_irq) { | ||
1204 | edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n"); | ||
1205 | rc = -ENODEV; | ||
1206 | goto err_release_group1; | ||
1207 | } | ||
1208 | rc = devm_request_irq(edac->dev, altdev->db_irq, | ||
1209 | prv->ecc_irq_handler, | ||
1210 | IRQF_SHARED, ecc_name, altdev); | ||
1211 | if (rc) { | ||
1212 | edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n"); | ||
1213 | goto err_release_group1; | ||
1214 | } | ||
1215 | |||
1171 | rc = edac_device_add_device(dci); | 1216 | rc = edac_device_add_device(dci); |
1172 | if (rc) { | 1217 | if (rc) { |
1173 | dev_err(edac->dev, "edac_device_add_device failed\n"); | 1218 | dev_err(edac->dev, "edac_device_add_device failed\n"); |
@@ -1186,7 +1231,6 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, | |||
1186 | err_release_group1: | 1231 | err_release_group1: |
1187 | edac_device_free_ctl_info(dci); | 1232 | edac_device_free_ctl_info(dci); |
1188 | err_release_group: | 1233 | err_release_group: |
1189 | edac_printk(KERN_ALERT, EDAC_DEVICE, "%s: %d\n", __func__, __LINE__); | ||
1190 | devres_release_group(edac->dev, NULL); | 1234 | devres_release_group(edac->dev, NULL); |
1191 | edac_printk(KERN_ERR, EDAC_DEVICE, | 1235 | edac_printk(KERN_ERR, EDAC_DEVICE, |
1192 | "%s:Error setting up EDAC device: %d\n", ecc_name, rc); | 1236 | "%s:Error setting up EDAC device: %d\n", ecc_name, rc); |
@@ -1194,11 +1238,43 @@ err_release_group: | |||
1194 | return rc; | 1238 | return rc; |
1195 | } | 1239 | } |
1196 | 1240 | ||
1241 | static void a10_eccmgr_irq_mask(struct irq_data *d) | ||
1242 | { | ||
1243 | struct altr_arria10_edac *edac = irq_data_get_irq_chip_data(d); | ||
1244 | |||
1245 | regmap_write(edac->ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_SET_OFST, | ||
1246 | BIT(d->hwirq)); | ||
1247 | } | ||
1248 | |||
1249 | static void a10_eccmgr_irq_unmask(struct irq_data *d) | ||
1250 | { | ||
1251 | struct altr_arria10_edac *edac = irq_data_get_irq_chip_data(d); | ||
1252 | |||
1253 | regmap_write(edac->ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_CLR_OFST, | ||
1254 | BIT(d->hwirq)); | ||
1255 | } | ||
1256 | |||
1257 | static int a10_eccmgr_irqdomain_map(struct irq_domain *d, unsigned int irq, | ||
1258 | irq_hw_number_t hwirq) | ||
1259 | { | ||
1260 | struct altr_arria10_edac *edac = d->host_data; | ||
1261 | |||
1262 | irq_set_chip_and_handler(irq, &edac->irq_chip, handle_simple_irq); | ||
1263 | irq_set_chip_data(irq, edac); | ||
1264 | irq_set_noprobe(irq); | ||
1265 | |||
1266 | return 0; | ||
1267 | } | ||
1268 | |||
1269 | struct irq_domain_ops a10_eccmgr_ic_ops = { | ||
1270 | .map = a10_eccmgr_irqdomain_map, | ||
1271 | .xlate = irq_domain_xlate_twocell, | ||
1272 | }; | ||
1273 | |||
1197 | static int altr_edac_a10_probe(struct platform_device *pdev) | 1274 | static int altr_edac_a10_probe(struct platform_device *pdev) |
1198 | { | 1275 | { |
1199 | struct altr_arria10_edac *edac; | 1276 | struct altr_arria10_edac *edac; |
1200 | struct device_node *child; | 1277 | struct device_node *child; |
1201 | int rc; | ||
1202 | 1278 | ||
1203 | edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL); | 1279 | edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL); |
1204 | if (!edac) | 1280 | if (!edac) |
@@ -1216,23 +1292,34 @@ static int altr_edac_a10_probe(struct platform_device *pdev) | |||
1216 | return PTR_ERR(edac->ecc_mgr_map); | 1292 | return PTR_ERR(edac->ecc_mgr_map); |
1217 | } | 1293 | } |
1218 | 1294 | ||
1295 | edac->irq_chip.name = pdev->dev.of_node->name; | ||
1296 | edac->irq_chip.irq_mask = a10_eccmgr_irq_mask; | ||
1297 | edac->irq_chip.irq_unmask = a10_eccmgr_irq_unmask; | ||
1298 | edac->domain = irq_domain_add_linear(pdev->dev.of_node, 64, | ||
1299 | &a10_eccmgr_ic_ops, edac); | ||
1300 | if (!edac->domain) { | ||
1301 | dev_err(&pdev->dev, "Error adding IRQ domain\n"); | ||
1302 | return -ENOMEM; | ||
1303 | } | ||
1304 | |||
1219 | edac->sb_irq = platform_get_irq(pdev, 0); | 1305 | edac->sb_irq = platform_get_irq(pdev, 0); |
1220 | rc = devm_request_irq(&pdev->dev, edac->sb_irq, | 1306 | if (edac->sb_irq < 0) { |
1221 | altr_edac_a10_irq_handler, | 1307 | dev_err(&pdev->dev, "No SBERR IRQ resource\n"); |
1222 | IRQF_SHARED, dev_name(&pdev->dev), edac); | 1308 | return edac->sb_irq; |
1223 | if (rc) { | ||
1224 | edac_printk(KERN_ERR, EDAC_DEVICE, "No SBERR IRQ resource\n"); | ||
1225 | return rc; | ||
1226 | } | 1309 | } |
1227 | 1310 | ||
1311 | irq_set_chained_handler_and_data(edac->sb_irq, | ||
1312 | altr_edac_a10_irq_handler, | ||
1313 | edac); | ||
1314 | |||
1228 | edac->db_irq = platform_get_irq(pdev, 1); | 1315 | edac->db_irq = platform_get_irq(pdev, 1); |
1229 | rc = devm_request_irq(&pdev->dev, edac->db_irq, | 1316 | if (edac->db_irq < 0) { |
1230 | altr_edac_a10_irq_handler, | 1317 | dev_err(&pdev->dev, "No DBERR IRQ resource\n"); |
1231 | IRQF_SHARED, dev_name(&pdev->dev), edac); | 1318 | return edac->db_irq; |
1232 | if (rc) { | ||
1233 | edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n"); | ||
1234 | return rc; | ||
1235 | } | 1319 | } |
1320 | irq_set_chained_handler_and_data(edac->db_irq, | ||
1321 | altr_edac_a10_irq_handler, | ||
1322 | edac); | ||
1236 | 1323 | ||
1237 | for_each_child_of_node(pdev->dev.of_node, child) { | 1324 | for_each_child_of_node(pdev->dev.of_node, child) { |
1238 | if (!of_device_is_available(child)) | 1325 | if (!of_device_is_available(child)) |
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h index 42090f36ba6e..62b0fa010f95 100644 --- a/drivers/edac/altera_edac.h +++ b/drivers/edac/altera_edac.h | |||
@@ -295,8 +295,7 @@ struct edac_device_prv_data { | |||
295 | int ce_set_mask; | 295 | int ce_set_mask; |
296 | int ue_set_mask; | 296 | int ue_set_mask; |
297 | int set_err_ofst; | 297 | int set_err_ofst; |
298 | irqreturn_t (*ecc_irq_handler)(struct altr_edac_device_dev *dci, | 298 | irqreturn_t (*ecc_irq_handler)(int irq, void *dev_id); |
299 | bool sb); | ||
300 | int trig_alloc_sz; | 299 | int trig_alloc_sz; |
301 | const struct file_operations *inject_fops; | 300 | const struct file_operations *inject_fops; |
302 | }; | 301 | }; |
@@ -320,6 +319,8 @@ struct altr_arria10_edac { | |||
320 | struct regmap *ecc_mgr_map; | 319 | struct regmap *ecc_mgr_map; |
321 | int sb_irq; | 320 | int sb_irq; |
322 | int db_irq; | 321 | int db_irq; |
322 | struct irq_domain *domain; | ||
323 | struct irq_chip irq_chip; | ||
323 | struct list_head a10_ecc_devices; | 324 | struct list_head a10_ecc_devices; |
324 | }; | 325 | }; |
325 | 326 | ||