| From 55a09d124336781839cfed610e18ea7945782d49 Mon Sep 17 00:00:00 2001 |
| From: Thor Thayer <tthayer@opensource.altera.com> |
| Date: Thu, 4 Jun 2015 09:28:47 -0500 |
| Subject: [PATCH 36/39] EDAC, altera: Add Arria10 EDAC support |
| |
| The Arria10 SDRAM and ECC system differs significantly from the |
| Cyclone5 and Arria5 SoCs. This patch adds support for the Arria10 |
| SoC. |
| 1) IRQ handler needs to support SHARED IRQ |
| 2) Support sberr and dberr address reporting. |
| |
| Signed-off-by: Thor Thayer <tthayer@opensource.altera.com> |
| Cc: Arnd Bergmann <arnd@arndb.de> |
| Cc: devicetree@vger.kernel.org |
| Cc: dinguyen@opensource.altera.com |
| Cc: galak@codeaurora.org |
| Cc: grant.likely@linaro.org |
| Cc: ijc+devicetree@hellion.org.uk |
| Cc: linux-arm-kernel@lists.infradead.org |
| Cc: linux-edac <linux-edac@vger.kernel.org> |
| Cc: m.chehab@samsung.com |
| Cc: mark.rutland@arm.com |
| Cc: pawel.moll@arm.com |
| Cc: robh+dt@kernel.org |
| Cc: tthayer.linux@gmail.com |
| Link: http://lkml.kernel.org/r/1433428128-7292-4-git-send-email-tthayer@opensource.altera.com |
| Signed-off-by: Borislav Petkov <bp@suse.de> |
| (cherry picked from commit 73bcc942f4271fab2ea41e6a3992d3c2164faaa8) |
| Signed-off-by: Dinh Nguyen <dinguyen@opensource.altera.com> |
| --- |
| drivers/edac/altera_edac.c | 135 +++++++++++++++++++++++++++++++++++++++------ |
| drivers/edac/altera_edac.h | 85 ++++++++++++++++++++++++++++ |
| 2 files changed, 204 insertions(+), 16 deletions(-) |
| |
| diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c |
| index 4ac4e6c11ece..182c741adf3e 100644 |
| --- a/drivers/edac/altera_edac.c |
| +++ b/drivers/edac/altera_edac.c |
| @@ -42,6 +42,7 @@ static const struct altr_sdram_prv_data c5_data = { |
| .ecc_stat_ce_mask = CV_DRAMSTS_SBEERR, |
| .ecc_stat_ue_mask = CV_DRAMSTS_DBEERR, |
| .ecc_saddr_offset = CV_ERRADDR_OFST, |
| + .ecc_daddr_offset = CV_ERRADDR_OFST, |
| .ecc_cecnt_offset = CV_SBECOUNT_OFST, |
| .ecc_uecnt_offset = CV_DBECOUNT_OFST, |
| .ecc_irq_en_offset = CV_DRAMINTR_OFST, |
| @@ -57,37 +58,62 @@ static const struct altr_sdram_prv_data c5_data = { |
| #endif |
| }; |
| |
| +static const struct altr_sdram_prv_data a10_data = { |
| + .ecc_ctrl_offset = A10_ECCCTRL1_OFST, |
| + .ecc_ctl_en_mask = A10_ECCCTRL1_ECC_EN, |
| + .ecc_stat_offset = A10_INTSTAT_OFST, |
| + .ecc_stat_ce_mask = A10_INTSTAT_SBEERR, |
| + .ecc_stat_ue_mask = A10_INTSTAT_DBEERR, |
| + .ecc_saddr_offset = A10_SERRADDR_OFST, |
| + .ecc_daddr_offset = A10_DERRADDR_OFST, |
| + .ecc_irq_en_offset = A10_ERRINTEN_OFST, |
| + .ecc_irq_en_mask = A10_ECC_IRQ_EN_MASK, |
| + .ecc_irq_clr_offset = A10_INTSTAT_OFST, |
| + .ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR), |
| + .ecc_cnt_rst_offset = A10_ECCCTRL1_OFST, |
| + .ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK, |
| +#ifdef CONFIG_EDAC_DEBUG |
| + .ce_ue_trgr_offset = A10_DIAGINTTEST_OFST, |
| + .ce_set_mask = A10_DIAGINT_TSERRA_MASK, |
| + .ue_set_mask = A10_DIAGINT_TDERRA_MASK, |
| +#endif |
| +}; |
| + |
| static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) |
| { |
| struct mem_ctl_info *mci = dev_id; |
| struct altr_sdram_mc_data *drvdata = mci->pvt_info; |
| const struct altr_sdram_prv_data *priv = drvdata->data; |
| - u32 status, err_count, err_addr; |
| - |
| - /* Error Address is shared by both SBE & DBE */ |
| - regmap_read(drvdata->mc_vbase, priv->ecc_saddr_offset, &err_addr); |
| + u32 status, err_count = 1, err_addr; |
| |
| regmap_read(drvdata->mc_vbase, priv->ecc_stat_offset, &status); |
| |
| if (status & priv->ecc_stat_ue_mask) { |
| - regmap_read(drvdata->mc_vbase, priv->ecc_uecnt_offset, |
| - &err_count); |
| + regmap_read(drvdata->mc_vbase, priv->ecc_daddr_offset, |
| + &err_addr); |
| + if (priv->ecc_uecnt_offset) |
| + regmap_read(drvdata->mc_vbase, priv->ecc_uecnt_offset, |
| + &err_count); |
| panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n", |
| err_count, err_addr); |
| } |
| if (status & priv->ecc_stat_ce_mask) { |
| - regmap_read(drvdata->mc_vbase, priv->ecc_cecnt_offset, |
| - &err_count); |
| + regmap_read(drvdata->mc_vbase, priv->ecc_saddr_offset, |
| + &err_addr); |
| + if (priv->ecc_uecnt_offset) |
| + regmap_read(drvdata->mc_vbase, priv->ecc_cecnt_offset, |
| + &err_count); |
| edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count, |
| err_addr >> PAGE_SHIFT, |
| err_addr & ~PAGE_MASK, 0, |
| 0, 0, -1, mci->ctl_name, ""); |
| - } |
| - |
| - regmap_write(drvdata->mc_vbase, priv->ecc_irq_clr_offset, |
| - priv->ecc_irq_clr_mask); |
| + /* Clear IRQ to resume */ |
| + regmap_write(drvdata->mc_vbase, priv->ecc_irq_clr_offset, |
| + priv->ecc_irq_clr_mask); |
| |
| - return IRQ_HANDLED; |
| + return IRQ_HANDLED; |
| + } |
| + return IRQ_NONE; |
| } |
| |
| #ifdef CONFIG_EDAC_DEBUG |
| @@ -203,10 +229,60 @@ static unsigned long get_total_mem(void) |
| |
| static const struct of_device_id altr_sdram_ctrl_of_match[] = { |
| { .compatible = "altr,sdram-edac", .data = (void *)&c5_data}, |
| + { .compatible = "altr,sdram-edac-a10", .data = (void *)&a10_data}, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match); |
| |
| +static int a10_init(struct regmap *mc_vbase) |
| +{ |
| + if (regmap_update_bits(mc_vbase, A10_INTMODE_OFST, |
| + A10_INTMODE_SB_INT, A10_INTMODE_SB_INT)) { |
| + edac_printk(KERN_ERR, EDAC_MC, |
| + "Error setting SB IRQ mode\n"); |
| + return -ENODEV; |
| + } |
| + |
| + if (regmap_write(mc_vbase, A10_SERRCNTREG_OFST, 1)) { |
| + edac_printk(KERN_ERR, EDAC_MC, |
| + "Error setting trigger count\n"); |
| + return -ENODEV; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int a10_unmask_irq(struct platform_device *pdev, u32 mask) |
| +{ |
| + void __iomem *sm_base; |
| + int ret = 0; |
| + |
| + if (!request_mem_region(A10_SYMAN_INTMASK_CLR, sizeof(u32), |
| + dev_name(&pdev->dev))) { |
| + edac_printk(KERN_ERR, EDAC_MC, |
| + "Unable to request mem region\n"); |
| + return -EBUSY; |
| + } |
| + |
| + sm_base = ioremap(A10_SYMAN_INTMASK_CLR, sizeof(u32)); |
| + if (!sm_base) { |
| + edac_printk(KERN_ERR, EDAC_MC, |
| + "Unable to ioremap device\n"); |
| + |
| + ret = -ENOMEM; |
| + goto release; |
| + } |
| + |
| + iowrite32(mask, sm_base); |
| + |
| + iounmap(sm_base); |
| + |
| +release: |
| + release_mem_region(A10_SYMAN_INTMASK_CLR, sizeof(u32)); |
| + |
| + return ret; |
| +} |
| + |
| static int altr_sdram_probe(struct platform_device *pdev) |
| { |
| const struct of_device_id *id; |
| @@ -217,8 +293,8 @@ static int altr_sdram_probe(struct platform_device *pdev) |
| struct regmap *mc_vbase; |
| struct dimm_info *dimm; |
| u32 read_reg; |
| - int irq, res = 0; |
| - unsigned long mem_size; |
| + int irq, irq2, res = 0; |
| + unsigned long mem_size, irqflags = 0; |
| |
| id = of_match_device(altr_sdram_ctrl_of_match, &pdev->dev); |
| if (!id) |
| @@ -283,6 +359,9 @@ static int altr_sdram_probe(struct platform_device *pdev) |
| return -ENODEV; |
| } |
| |
| + /* Arria10 has a 2nd IRQ */ |
| + irq2 = platform_get_irq(pdev, 1); |
| + |
| layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; |
| layers[0].size = 1; |
| layers[0].is_virt_csrow = true; |
| @@ -327,8 +406,32 @@ static int altr_sdram_probe(struct platform_device *pdev) |
| if (res < 0) |
| goto err; |
| |
| + /* Only the Arria10 has separate IRQs */ |
| + if (irq2 > 0) { |
| + /* Arria10 specific initialization */ |
| + res = a10_init(mc_vbase); |
| + if (res < 0) |
| + goto err2; |
| + |
| + res = devm_request_irq(&pdev->dev, irq2, |
| + altr_sdram_mc_err_handler, |
| + IRQF_SHARED, dev_name(&pdev->dev), mci); |
| + if (res < 0) { |
| + edac_mc_printk(mci, KERN_ERR, |
| + "Unable to request irq %d\n", irq2); |
| + res = -ENODEV; |
| + goto err2; |
| + } |
| + |
| + res = a10_unmask_irq(pdev, A10_DDR0_IRQ_MASK); |
| + if (res < 0) |
| + goto err2; |
| + |
| + irqflags = IRQF_SHARED; |
| + } |
| + |
| res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler, |
| - 0, dev_name(&pdev->dev), mci); |
| + irqflags, dev_name(&pdev->dev), mci); |
| if (res < 0) { |
| edac_mc_printk(mci, KERN_ERR, |
| "Unable to request irq %d\n", irq); |
| diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h |
| index b744d914d976..7b64dc7c4eb7 100644 |
| --- a/drivers/edac/altera_edac.h |
| +++ b/drivers/edac/altera_edac.h |
| @@ -80,6 +80,91 @@ |
| /* SDRAM Controller ECC Error Address Register */ |
| #define CV_ERRADDR_OFST 0x48 |
| |
| +/*-----------------------------------------*/ |
| + |
| +/* SDRAM Controller EccCtrl Register */ |
| +#define A10_ECCCTRL1_OFST 0x00 |
| + |
| +/* SDRAM Controller EccCtrl Register Bit Masks */ |
| +#define A10_ECCCTRL1_ECC_EN 0x001 |
| +#define A10_ECCCTRL1_CNT_RST 0x010 |
| +#define A10_ECCCTRL1_AWB_CNT_RST 0x100 |
| +#define A10_ECC_CNT_RESET_MASK (A10_ECCCTRL1_CNT_RST | \ |
| + A10_ECCCTRL1_AWB_CNT_RST) |
| + |
| +/* SDRAM Controller Address Width Register */ |
| +#define CV_DRAMADDRW 0xFFC2502C |
| +#define A10_DRAMADDRW 0xFFCFA0A8 |
| + |
| +/* SDRAM Controller Address Widths Field Register */ |
| +#define DRAMADDRW_COLBIT_MASK 0x001F |
| +#define DRAMADDRW_COLBIT_SHIFT 0 |
| +#define DRAMADDRW_ROWBIT_MASK 0x03E0 |
| +#define DRAMADDRW_ROWBIT_SHIFT 5 |
| +#define CV_DRAMADDRW_BANKBIT_MASK 0x1C00 |
| +#define CV_DRAMADDRW_BANKBIT_SHIFT 10 |
| +#define CV_DRAMADDRW_CSBIT_MASK 0xE000 |
| +#define CV_DRAMADDRW_CSBIT_SHIFT 13 |
| + |
| +#define A10_DRAMADDRW_BANKBIT_MASK 0x3C00 |
| +#define A10_DRAMADDRW_BANKBIT_SHIFT 10 |
| +#define A10_DRAMADDRW_GRPBIT_MASK 0xC000 |
| +#define A10_DRAMADDRW_GRPBIT_SHIFT 14 |
| +#define A10_DRAMADDRW_CSBIT_MASK 0x70000 |
| +#define A10_DRAMADDRW_CSBIT_SHIFT 16 |
| + |
| +/* SDRAM Controller Interface Data Width Register */ |
| +#define CV_DRAMIFWIDTH 0xFFC25030 |
| +#define A10_DRAMIFWIDTH 0xFFCFB008 |
| + |
| +/* SDRAM Controller Interface Data Width Defines */ |
| +#define CV_DRAMIFWIDTH_16B_ECC 24 |
| +#define CV_DRAMIFWIDTH_32B_ECC 40 |
| + |
| +#define A10_DRAMIFWIDTH_16B 0x0 |
| +#define A10_DRAMIFWIDTH_32B 0x1 |
| +#define A10_DRAMIFWIDTH_64B 0x2 |
| + |
| +/* SDRAM Controller DRAM IRQ Register */ |
| +#define A10_ERRINTEN_OFST 0x10 |
| + |
| +/* SDRAM Controller DRAM IRQ Register Bit Masks */ |
| +#define A10_ERRINTEN_SERRINTEN 0x01 |
| +#define A10_ERRINTEN_DERRINTEN 0x02 |
| +#define A10_ECC_IRQ_EN_MASK (A10_ERRINTEN_SERRINTEN | \ |
| + A10_ERRINTEN_DERRINTEN) |
| + |
| +/* SDRAM Interrupt Mode Register */ |
| +#define A10_INTMODE_OFST 0x1C |
| +#define A10_INTMODE_SB_INT 1 |
| + |
| +/* SDRAM Controller Error Status Register */ |
| +#define A10_INTSTAT_OFST 0x20 |
| + |
| +/* SDRAM Controller Error Status Register Bit Masks */ |
| +#define A10_INTSTAT_SBEERR 0x01 |
| +#define A10_INTSTAT_DBEERR 0x02 |
| + |
| +/* SDRAM Controller ECC Error Address Register */ |
| +#define A10_DERRADDR_OFST 0x2C |
| +#define A10_SERRADDR_OFST 0x30 |
| + |
| +/* SDRAM Controller ECC Diagnostic Register */ |
| +#define A10_DIAGINTTEST_OFST 0x24 |
| + |
| +#define A10_DIAGINT_TSERRA_MASK 0x0001 |
| +#define A10_DIAGINT_TDERRA_MASK 0x0100 |
| + |
| +#define A10_SBERR_IRQ 34 |
| +#define A10_DBERR_IRQ 32 |
| + |
| +/* SDRAM Single Bit Error Count Compare Set Register */ |
| +#define A10_SERRCNTREG_OFST 0x3C |
| + |
| +#define A10_SYMAN_INTMASK_CLR 0xFFD06098 |
| +#define A10_INTMASK_CLR_OFST 0x10 |
| +#define A10_DDR0_IRQ_MASK BIT(17) |
| + |
| struct altr_sdram_prv_data { |
| int ecc_ctrl_offset; |
| int ecc_ctl_en_mask; |
| -- |
| 2.6.2 |
| |