Skip to content

Commit

Permalink
EDAC, altera: Combine Stratix10 and Arria10 probe functions
Browse files Browse the repository at this point in the history
On Stratix10, the ECC offsets are similar to the existing
Arria10 functions and this can be leveraged to simplify
the EDAC driver as follows:

1. Fold Stratix10 specifics into Arria10 structures and
functions.

2. Implement the Stratix10 System Manager register accesses
using a custom regmap to allow use with the Arria10 System
Manager regmaps.

3. Stratix10 double bit errors are implemented as SError
instead of interrupts so use a panic notifier.

Signed-off-by: Thor Thayer <thor.thayer@linux.intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: dinguyen@kernel.org
Cc: robh+dt@kernel.org
Cc: mark.rutland@arm.com
Cc: mchehab@kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-edac@vger.kernel.org
Link: https://lkml.kernel.org/r/1537883342-30180-3-git-send-email-thor.thayer@linux.intel.com
  • Loading branch information
Thor Thayer authored and Borislav Petkov committed Sep 25, 2018
1 parent 3ce078f commit d5fc912
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 183 deletions.
263 changes: 89 additions & 174 deletions drivers/edac/altera_edac.c
Original file line number Diff line number Diff line change
Expand Up @@ -2146,6 +2146,35 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
.xlate = irq_domain_xlate_twocell,
};

/************** Stratix 10 EDAC Double Bit Error Handler ************/
#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m)

/*
* The double bit error is handled through SError which is fatal. This is
* called as a panic notifier to printout ECC error info as part of the panic.
*/
static int s10_edac_dberr_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct altr_arria10_edac *edac = to_a10edac(this, panic_notifier);
int err_addr, dberror;

regmap_read(edac->ecc_mgr_map, S10_SYSMGR_ECC_INTSTAT_DERR_OFST,
&dberror);
regmap_write(edac->ecc_mgr_map, S10_SYSMGR_UE_VAL_OFST, dberror);
if (dberror & S10_DDR0_IRQ_MASK) {
regmap_read(edac->ecc_mgr_map, S10_DERRADDR_OFST, &err_addr);
regmap_write(edac->ecc_mgr_map, S10_SYSMGR_UE_ADDR_OFST,
err_addr);
edac_printk(KERN_ERR, EDAC_MC,
"EDAC: [Uncorrectable errors @ 0x%08X]\n\n",
err_addr);
}

return NOTIFY_DONE;
}

/****************** Arria 10 EDAC Probe Function *********************/
static int altr_edac_a10_probe(struct platform_device *pdev)
{
struct altr_arria10_edac *edac;
Expand All @@ -2159,8 +2188,33 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->a10_ecc_devices);

edac->ecc_mgr_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
if (socfpga_is_a10()) {
edac->ecc_mgr_map =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"altr,sysmgr-syscon");
} else {
struct device_node *sysmgr_np;
struct resource res;
void __iomem *base;

sysmgr_np = of_parse_phandle(pdev->dev.of_node,
"altr,sysmgr-syscon", 0);
if (!sysmgr_np) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Unable to find altr,sysmgr-syscon\n");
return -ENODEV;
}

if (of_address_to_resource(sysmgr_np, 0, &res))
return -ENOMEM;

/* Need physical address for SMCC call */
base = (void __iomem *)res.start;

edac->ecc_mgr_map = devm_regmap_init(&pdev->dev, NULL, base,
&s10_sdram_regmap_cfg);
}

if (IS_ERR(edac->ecc_mgr_map)) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Unable to get syscon altr,sysmgr-syscon\n");
Expand All @@ -2187,14 +2241,38 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
altr_edac_a10_irq_handler,
edac);

edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0) {
dev_err(&pdev->dev, "No DBERR IRQ resource\n");
return edac->db_irq;
if (socfpga_is_a10()) {
edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0) {
dev_err(&pdev->dev, "No DBERR IRQ resource\n");
return edac->db_irq;
}
irq_set_chained_handler_and_data(edac->db_irq,
altr_edac_a10_irq_handler,
edac);
} else {
int dberror, err_addr;

edac->panic_notifier.notifier_call = s10_edac_dberr_handler;
atomic_notifier_chain_register(&panic_notifier_list,
&edac->panic_notifier);

/* Printout a message if uncorrectable error previously. */
regmap_read(edac->ecc_mgr_map, S10_SYSMGR_UE_VAL_OFST,
&dberror);
if (dberror) {
regmap_read(edac->ecc_mgr_map, S10_SYSMGR_UE_ADDR_OFST,
&err_addr);
edac_printk(KERN_ERR, EDAC_DEVICE,
"Previous Boot UE detected[0x%X] @ 0x%X\n",
dberror, err_addr);
/* Reset the sticky registers */
regmap_write(edac->ecc_mgr_map,
S10_SYSMGR_UE_VAL_OFST, 0);
regmap_write(edac->ecc_mgr_map,
S10_SYSMGR_UE_ADDR_OFST, 0);
}
}
irq_set_chained_handler_and_data(edac->db_irq,
altr_edac_a10_irq_handler,
edac);

for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child))
Expand All @@ -2211,7 +2289,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)

altr_edac_a10_device_add(edac, child);

else if (of_device_is_compatible(child, "altr,sdram-edac-a10"))
else if ((of_device_is_compatible(child, "altr,sdram-edac-a10")) ||
(of_device_is_compatible(child, "altr,sdram-edac-s10")))
of_platform_populate(pdev->dev.of_node,
altr_sdram_ctrl_of_match,
NULL, &pdev->dev);
Expand All @@ -2222,6 +2301,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)

static const struct of_device_id altr_edac_a10_of_match[] = {
{ .compatible = "altr,socfpga-a10-ecc-manager" },
{ .compatible = "altr,socfpga-s10-ecc-manager" },
{},
};
MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);
Expand All @@ -2235,171 +2315,6 @@ static struct platform_driver altr_edac_a10_driver = {
};
module_platform_driver(altr_edac_a10_driver);

/************** Stratix 10 EDAC Device Controller Functions> ************/

#define to_s10edac(p, m) container_of(p, struct altr_stratix10_edac, m)

/*
* The double bit error is handled through SError which is fatal. This is
* called as a panic notifier to printout ECC error info as part of the panic.
*/
static int s10_edac_dberr_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct altr_stratix10_edac *edac = to_s10edac(this, panic_notifier);
int err_addr, dberror;

s10_protected_reg_read(edac, S10_SYSMGR_ECC_INTSTAT_DERR_OFST,
&dberror);
/* Remember the UE Errors for a reboot */
s10_protected_reg_write(edac, S10_SYSMGR_UE_VAL_OFST, dberror);
if (dberror & S10_DDR0_IRQ_MASK) {
s10_protected_reg_read(edac, S10_DERRADDR_OFST, &err_addr);
/* Remember the UE Error address */
s10_protected_reg_write(edac, S10_SYSMGR_UE_ADDR_OFST,
err_addr);
edac_printk(KERN_ERR, EDAC_MC,
"EDAC: [Uncorrectable errors @ 0x%08X]\n\n",
err_addr);
}

return NOTIFY_DONE;
}

static void altr_edac_s10_irq_handler(struct irq_desc *desc)
{
struct altr_stratix10_edac *edac = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
int irq = irq_desc_get_irq(desc);
int bit, sm_offset, irq_status;

sm_offset = S10_SYSMGR_ECC_INTSTAT_SERR_OFST;

chained_irq_enter(chip, desc);

s10_protected_reg_read(NULL, sm_offset, &irq_status);

for_each_set_bit(bit, (unsigned long *)&irq_status, 32) {
irq = irq_linear_revmap(edac->domain, bit);
if (irq)
generic_handle_irq(irq);
}

chained_irq_exit(chip, desc);
}

static void s10_eccmgr_irq_mask(struct irq_data *d)
{
struct altr_stratix10_edac *edac = irq_data_get_irq_chip_data(d);

s10_protected_reg_write(edac, S10_SYSMGR_ECC_INTMASK_SET_OFST,
BIT(d->hwirq));
}

static void s10_eccmgr_irq_unmask(struct irq_data *d)
{
struct altr_stratix10_edac *edac = irq_data_get_irq_chip_data(d);

s10_protected_reg_write(edac, S10_SYSMGR_ECC_INTMASK_CLR_OFST,
BIT(d->hwirq));
}

static int s10_eccmgr_irqdomain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
struct altr_stratix10_edac *edac = d->host_data;

irq_set_chip_and_handler(irq, &edac->irq_chip, handle_simple_irq);
irq_set_chip_data(irq, edac);
irq_set_noprobe(irq);

return 0;
}

static const struct irq_domain_ops s10_eccmgr_ic_ops = {
.map = s10_eccmgr_irqdomain_map,
.xlate = irq_domain_xlate_twocell,
};

static int altr_edac_s10_probe(struct platform_device *pdev)
{
struct altr_stratix10_edac *edac;
struct device_node *child;
int dberror, err_addr;

edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL);
if (!edac)
return -ENOMEM;

edac->dev = &pdev->dev;
platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->s10_ecc_devices);

edac->irq_chip.name = pdev->dev.of_node->name;
edac->irq_chip.irq_mask = s10_eccmgr_irq_mask;
edac->irq_chip.irq_unmask = s10_eccmgr_irq_unmask;
edac->domain = irq_domain_add_linear(pdev->dev.of_node, 64,
&s10_eccmgr_ic_ops, edac);
if (!edac->domain) {
dev_err(&pdev->dev, "Error adding IRQ domain\n");
return -ENOMEM;
}

edac->sb_irq = platform_get_irq(pdev, 0);
if (edac->sb_irq < 0) {
dev_err(&pdev->dev, "No SBERR IRQ resource\n");
return edac->sb_irq;
}

irq_set_chained_handler_and_data(edac->sb_irq,
altr_edac_s10_irq_handler,
edac);

edac->panic_notifier.notifier_call = s10_edac_dberr_handler;
atomic_notifier_chain_register(&panic_notifier_list,
&edac->panic_notifier);

/* Printout a message if uncorrectable error previously. */
s10_protected_reg_read(edac, S10_SYSMGR_UE_VAL_OFST, &dberror);
if (dberror) {
s10_protected_reg_read(edac, S10_SYSMGR_UE_ADDR_OFST,
&err_addr);
edac_printk(KERN_ERR, EDAC_DEVICE,
"Previous Boot UE detected[0x%X] @ 0x%X\n",
dberror, err_addr);
/* Reset the sticky registers */
s10_protected_reg_write(edac, S10_SYSMGR_UE_VAL_OFST, 0);
s10_protected_reg_write(edac, S10_SYSMGR_UE_ADDR_OFST, 0);
}

for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child))
continue;

if (of_device_is_compatible(child, "altr,sdram-edac-s10"))
of_platform_populate(pdev->dev.of_node,
altr_sdram_ctrl_of_match,
NULL, &pdev->dev);
}

return 0;
}

static const struct of_device_id altr_edac_s10_of_match[] = {
{ .compatible = "altr,socfpga-s10-ecc-manager" },
{},
};
MODULE_DEVICE_TABLE(of, altr_edac_s10_of_match);

static struct platform_driver altr_edac_s10_driver = {
.probe = altr_edac_s10_probe,
.driver = {
.name = "socfpga_s10_ecc_manager",
.of_match_table = altr_edac_s10_of_match,
},
};
module_platform_driver(altr_edac_s10_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Thor Thayer");
MODULE_DESCRIPTION("EDAC Driver for Altera Memories");
10 changes: 1 addition & 9 deletions drivers/edac/altera_edac.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ struct altr_arria10_edac {
struct irq_domain *domain;
struct irq_chip irq_chip;
struct list_head a10_ecc_devices;
struct notifier_block panic_notifier;
};

/*
Expand Down Expand Up @@ -437,13 +438,4 @@ struct altr_arria10_edac {
#define INTEL_SIP_SMC_REG_WRITE \
INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_WRITE)

struct altr_stratix10_edac {
struct device *dev;
int sb_irq;
struct irq_domain *domain;
struct irq_chip irq_chip;
struct list_head s10_ecc_devices;
struct notifier_block panic_notifier;
};

#endif /* #ifndef _ALTERA_EDAC_H */

0 comments on commit d5fc912

Please sign in to comment.