diff options
Diffstat (limited to 'drivers/edac/altera_edac.c')
| -rw-r--r-- | drivers/edac/altera_edac.c | 670 | 
1 files changed, 218 insertions, 452 deletions
| diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 5762c3c383f2..c89d82aa2776 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -69,25 +69,6 @@ static const struct altr_sdram_prv_data a10_data = {  	.ue_set_mask        = A10_DIAGINT_TDERRA_MASK,  }; -static const struct altr_sdram_prv_data s10_data = { -	.ecc_ctrl_offset    = S10_ECCCTRL1_OFST, -	.ecc_ctl_en_mask    = A10_ECCCTRL1_ECC_EN, -	.ecc_stat_offset    = S10_INTSTAT_OFST, -	.ecc_stat_ce_mask   = A10_INTSTAT_SBEERR, -	.ecc_stat_ue_mask   = A10_INTSTAT_DBEERR, -	.ecc_saddr_offset   = S10_SERRADDR_OFST, -	.ecc_daddr_offset   = S10_DERRADDR_OFST, -	.ecc_irq_en_offset  = S10_ERRINTEN_OFST, -	.ecc_irq_en_mask    = A10_ECC_IRQ_EN_MASK, -	.ecc_irq_clr_offset = S10_INTSTAT_OFST, -	.ecc_irq_clr_mask   = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR), -	.ecc_cnt_rst_offset = S10_ECCCTRL1_OFST, -	.ecc_cnt_rst_mask   = A10_ECC_CNT_RESET_MASK, -	.ce_ue_trgr_offset  = S10_DIAGINTTEST_OFST, -	.ce_set_mask        = A10_DIAGINT_TSERRA_MASK, -	.ue_set_mask        = A10_DIAGINT_TDERRA_MASK, -}; -  /*********************** EDAC Memory Controller Functions ****************/  /* The SDRAM controller uses the EDAC Memory Controller framework.       */ @@ -239,7 +220,7 @@ static unsigned long get_total_mem(void)  static const struct of_device_id altr_sdram_ctrl_of_match[] = {  	{ .compatible = "altr,sdram-edac", .data = &c5_data},  	{ .compatible = "altr,sdram-edac-a10", .data = &a10_data}, -	{ .compatible = "altr,sdram-edac-s10", .data = &s10_data}, +	{ .compatible = "altr,sdram-edac-s10", .data = &a10_data},  	{},  };  MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match); @@ -293,6 +274,7 @@ release:  	return ret;  } +static int socfpga_is_a10(void);  static int altr_sdram_probe(struct platform_device *pdev)  {  	const struct of_device_id *id; @@ -416,7 +398,7 @@ static int altr_sdram_probe(struct platform_device *pdev)  		goto err;  	/* Only the Arria10 has separate IRQs */ -	if (irq2 > 0) { +	if (socfpga_is_a10()) {  		/* Arria10 specific initialization */  		res = a10_init(mc_vbase);  		if (res < 0) @@ -502,8 +484,9 @@ static int s10_protected_reg_write(void *context, unsigned int reg,  				   unsigned int val)  {  	struct arm_smccc_res result; +	unsigned long offset = (unsigned long)context; -	arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, reg, val, 0, 0, +	arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, offset + reg, val, 0, 0,  		      0, 0, 0, &result);  	return (int)result.a0; @@ -523,8 +506,9 @@ static int s10_protected_reg_read(void *context, unsigned int reg,  				  unsigned int *val)  {  	struct arm_smccc_res result; +	unsigned long offset = (unsigned long)context; -	arm_smccc_smc(INTEL_SIP_SMC_REG_READ, reg, 0, 0, 0, +	arm_smccc_smc(INTEL_SIP_SMC_REG_READ, offset + reg, 0, 0, 0,  		      0, 0, 0, &result);  	*val = (unsigned int)result.a1; @@ -532,245 +516,18 @@ static int s10_protected_reg_read(void *context, unsigned int reg,  	return (int)result.a0;  } -static bool s10_sdram_writeable_reg(struct device *dev, unsigned int reg) -{ -	switch (reg) { -	case S10_ECCCTRL1_OFST: -	case S10_ERRINTEN_OFST: -	case S10_INTMODE_OFST: -	case S10_INTSTAT_OFST: -	case S10_DIAGINTTEST_OFST: -	case S10_SYSMGR_ECC_INTMASK_VAL_OFST: -	case S10_SYSMGR_ECC_INTMASK_SET_OFST: -	case S10_SYSMGR_ECC_INTMASK_CLR_OFST: -		return true; -	} -	return false; -} - -static bool s10_sdram_readable_reg(struct device *dev, unsigned int reg) -{ -	switch (reg) { -	case S10_ECCCTRL1_OFST: -	case S10_ERRINTEN_OFST: -	case S10_INTMODE_OFST: -	case S10_INTSTAT_OFST: -	case S10_DERRADDR_OFST: -	case S10_SERRADDR_OFST: -	case S10_DIAGINTTEST_OFST: -	case S10_SYSMGR_ECC_INTMASK_VAL_OFST: -	case S10_SYSMGR_ECC_INTMASK_SET_OFST: -	case S10_SYSMGR_ECC_INTMASK_CLR_OFST: -	case S10_SYSMGR_ECC_INTSTAT_SERR_OFST: -	case S10_SYSMGR_ECC_INTSTAT_DERR_OFST: -		return true; -	} -	return false; -} - -static bool s10_sdram_volatile_reg(struct device *dev, unsigned int reg) -{ -	switch (reg) { -	case S10_ECCCTRL1_OFST: -	case S10_ERRINTEN_OFST: -	case S10_INTMODE_OFST: -	case S10_INTSTAT_OFST: -	case S10_DERRADDR_OFST: -	case S10_SERRADDR_OFST: -	case S10_DIAGINTTEST_OFST: -	case S10_SYSMGR_ECC_INTMASK_VAL_OFST: -	case S10_SYSMGR_ECC_INTMASK_SET_OFST: -	case S10_SYSMGR_ECC_INTMASK_CLR_OFST: -	case S10_SYSMGR_ECC_INTSTAT_SERR_OFST: -	case S10_SYSMGR_ECC_INTSTAT_DERR_OFST: -		return true; -	} -	return false; -} -  static const struct regmap_config s10_sdram_regmap_cfg = {  	.name = "s10_ddr",  	.reg_bits = 32,  	.reg_stride = 4,  	.val_bits = 32, -	.max_register = 0xffffffff, -	.writeable_reg = s10_sdram_writeable_reg, -	.readable_reg = s10_sdram_readable_reg, -	.volatile_reg = s10_sdram_volatile_reg, +	.max_register = 0xffd12228,  	.reg_read = s10_protected_reg_read,  	.reg_write = s10_protected_reg_write, -	.use_single_rw = true, +	.use_single_read = true, +	.use_single_write = true,  }; -static int altr_s10_sdram_probe(struct platform_device *pdev) -{ -	const struct of_device_id *id; -	struct edac_mc_layer layers[2]; -	struct mem_ctl_info *mci; -	struct altr_sdram_mc_data *drvdata; -	const struct altr_sdram_prv_data *priv; -	struct regmap *regmap; -	struct dimm_info *dimm; -	u32 read_reg; -	int irq, ret = 0; -	unsigned long mem_size; - -	id = of_match_device(altr_sdram_ctrl_of_match, &pdev->dev); -	if (!id) -		return -ENODEV; - -	/* Grab specific offsets and masks for Stratix10 */ -	priv = of_match_node(altr_sdram_ctrl_of_match, -			     pdev->dev.of_node)->data; - -	regmap = devm_regmap_init(&pdev->dev, NULL, (void *)priv, -				  &s10_sdram_regmap_cfg); -	if (IS_ERR(regmap)) -		return PTR_ERR(regmap); - -	/* Validate the SDRAM controller has ECC enabled */ -	if (regmap_read(regmap, priv->ecc_ctrl_offset, &read_reg) || -	    ((read_reg & priv->ecc_ctl_en_mask) != priv->ecc_ctl_en_mask)) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "No ECC/ECC disabled [0x%08X]\n", read_reg); -		return -ENODEV; -	} - -	/* Grab memory size from device tree. */ -	mem_size = get_total_mem(); -	if (!mem_size) { -		edac_printk(KERN_ERR, EDAC_MC, "Unable to calculate memory size\n"); -		return -ENODEV; -	} - -	/* Ensure the SDRAM Interrupt is disabled */ -	if (regmap_update_bits(regmap, priv->ecc_irq_en_offset, -			       priv->ecc_irq_en_mask, 0)) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "Error disabling SDRAM ECC IRQ\n"); -		return -ENODEV; -	} - -	/* Toggle to clear the SDRAM Error count */ -	if (regmap_update_bits(regmap, priv->ecc_cnt_rst_offset, -			       priv->ecc_cnt_rst_mask, -			       priv->ecc_cnt_rst_mask)) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "Error clearing SDRAM ECC count\n"); -		return -ENODEV; -	} - -	if (regmap_update_bits(regmap, priv->ecc_cnt_rst_offset, -			       priv->ecc_cnt_rst_mask, 0)) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "Error clearing SDRAM ECC count\n"); -		return -ENODEV; -	} - -	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "No irq %d in DT\n", irq); -		return -ENODEV; -	} - -	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; -	layers[0].size = 1; -	layers[0].is_virt_csrow = true; -	layers[1].type = EDAC_MC_LAYER_CHANNEL; -	layers[1].size = 1; -	layers[1].is_virt_csrow = false; -	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, -			    sizeof(struct altr_sdram_mc_data)); -	if (!mci) -		return -ENOMEM; - -	mci->pdev = &pdev->dev; -	drvdata = mci->pvt_info; -	drvdata->mc_vbase = regmap; -	drvdata->data = priv; -	platform_set_drvdata(pdev, mci); - -	if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "Unable to get managed device resource\n"); -		ret = -ENOMEM; -		goto free; -	} - -	mci->mtype_cap = MEM_FLAG_DDR3; -	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; -	mci->edac_cap = EDAC_FLAG_SECDED; -	mci->mod_name = EDAC_MOD_STR; -	mci->ctl_name = dev_name(&pdev->dev); -	mci->scrub_mode = SCRUB_SW_SRC; -	mci->dev_name = dev_name(&pdev->dev); - -	dimm = *mci->dimms; -	dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1; -	dimm->grain = 8; -	dimm->dtype = DEV_X8; -	dimm->mtype = MEM_DDR3; -	dimm->edac_mode = EDAC_SECDED; - -	ret = edac_mc_add_mc(mci); -	if (ret < 0) -		goto err; - -	ret = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler, -			       IRQF_SHARED, dev_name(&pdev->dev), mci); -	if (ret < 0) { -		edac_mc_printk(mci, KERN_ERR, -			       "Unable to request irq %d\n", irq); -		ret = -ENODEV; -		goto err2; -	} - -	if (regmap_write(regmap, S10_SYSMGR_ECC_INTMASK_CLR_OFST, -			 S10_DDR0_IRQ_MASK)) { -		edac_printk(KERN_ERR, EDAC_MC, -			    "Error clearing SDRAM ECC count\n"); -		ret = -ENODEV; -		goto err2; -	} - -	if (regmap_update_bits(drvdata->mc_vbase, priv->ecc_irq_en_offset, -			       priv->ecc_irq_en_mask, priv->ecc_irq_en_mask)) { -		edac_mc_printk(mci, KERN_ERR, -			       "Error enabling SDRAM ECC IRQ\n"); -		ret = -ENODEV; -		goto err2; -	} - -	altr_sdr_mc_create_debugfs_nodes(mci); - -	devres_close_group(&pdev->dev, NULL); - -	return 0; - -err2: -	edac_mc_del_mc(&pdev->dev); -err: -	devres_release_group(&pdev->dev, NULL); -free: -	edac_mc_free(mci); -	edac_printk(KERN_ERR, EDAC_MC, -		    "EDAC Probe Failed; Error %d\n", ret); - -	return ret; -} - -static int altr_s10_sdram_remove(struct platform_device *pdev) -{ -	struct mem_ctl_info *mci = platform_get_drvdata(pdev); - -	edac_mc_del_mc(&pdev->dev); -	edac_mc_free(mci); -	platform_set_drvdata(pdev, NULL); - -	return 0; -} -  /************** </Stratix10 EDAC Memory Controller Functions> ***********/  /* @@ -804,20 +561,6 @@ static struct platform_driver altr_sdram_edac_driver = {  module_platform_driver(altr_sdram_edac_driver); -static struct platform_driver altr_s10_sdram_edac_driver = { -	.probe = altr_s10_sdram_probe, -	.remove = altr_s10_sdram_remove, -	.driver = { -		.name = "altr_s10_sdram_edac", -#ifdef CONFIG_PM -		.pm = &altr_sdram_pm_ops, -#endif -		.of_match_table = altr_sdram_ctrl_of_match, -	}, -}; - -module_platform_driver(altr_s10_sdram_edac_driver); -  /************************* EDAC Parent Probe *************************/  static const struct of_device_id altr_edac_device_of_match[]; @@ -971,6 +714,16 @@ static const struct file_operations altr_edac_a10_device_inject_fops = {  	.llseek = generic_file_llseek,  }; +static ssize_t altr_edac_a10_device_trig2(struct file *file, +					  const char __user *user_buf, +					  size_t count, loff_t *ppos); + +static const struct file_operations altr_edac_a10_device_inject2_fops = { +	.open = simple_open, +	.write = altr_edac_a10_device_trig2, +	.llseek = generic_file_llseek, +}; +  static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,  				      const struct edac_device_prv_data *priv)  { @@ -1252,6 +1005,16 @@ static int __maybe_unused altr_init_memory_port(void __iomem *ioaddr, int port)  	return ret;  } +static int socfpga_is_a10(void) +{ +	return of_machine_is_compatible("altr,socfpga-arria10"); +} + +static int socfpga_is_s10(void) +{ +	return of_machine_is_compatible("altr,socfpga-stratix10"); +} +  static __init int __maybe_unused  altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,  			u32 ecc_ctrl_en_mask, bool dual_port) @@ -1266,8 +1029,32 @@ altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,  	/* Get the ECC Manager - parent of the device EDACs */  	np_eccmgr = of_get_parent(np); -	ecc_mgr_map = syscon_regmap_lookup_by_phandle(np_eccmgr, -						      "altr,sysmgr-syscon"); + +	if (socfpga_is_a10()) { +		ecc_mgr_map = syscon_regmap_lookup_by_phandle(np_eccmgr, +							      "altr,sysmgr-syscon"); +	} else { +		struct device_node *sysmgr_np; +		struct resource res; +		uintptr_t base; + +		sysmgr_np = of_parse_phandle(np_eccmgr, +					     "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 = res.start; + +		ecc_mgr_map = regmap_init(NULL, NULL, (void *)base, +					  &s10_sdram_regmap_cfg); +	}  	of_node_put(np_eccmgr);  	if (IS_ERR(ecc_mgr_map)) {  		edac_printk(KERN_ERR, EDAC_DEVICE, @@ -1325,11 +1112,6 @@ out:  	return ret;  } -static int socfpga_is_a10(void) -{ -	return of_machine_is_compatible("altr,socfpga-arria10"); -} -  static int validate_parent_available(struct device_node *np);  static const struct of_device_id altr_edac_a10_device_of_match[];  static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat) @@ -1337,7 +1119,7 @@ static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)  	int irq;  	struct device_node *child, *np; -	if (!socfpga_is_a10()) +	if (!socfpga_is_a10() && !socfpga_is_s10())  		return -ENODEV;  	np = of_find_compatible_node(NULL, NULL, @@ -1583,7 +1365,7 @@ static const struct edac_device_prv_data a10_enetecc_data = {  	.ue_set_mask = ALTR_A10_ECC_TDERRA,  	.set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,  	.ecc_irq_handler = altr_edac_a10_ecc_irq, -	.inject_fops = &altr_edac_a10_device_inject_fops, +	.inject_fops = &altr_edac_a10_device_inject2_fops,  };  static int __init socfpga_init_ethernet_ecc(void) @@ -1661,7 +1443,7 @@ static const struct edac_device_prv_data a10_usbecc_data = {  	.ue_set_mask = ALTR_A10_ECC_TDERRA,  	.set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,  	.ecc_irq_handler = altr_edac_a10_ecc_irq, -	.inject_fops = &altr_edac_a10_device_inject_fops, +	.inject_fops = &altr_edac_a10_device_inject2_fops,  };  static int __init socfpga_init_usb_ecc(void) @@ -1859,7 +1641,7 @@ static int __init socfpga_init_sdmmc_ecc(void)  	int rc = -ENODEV;  	struct device_node *child; -	if (!socfpga_is_a10()) +	if (!socfpga_is_a10() && !socfpga_is_s10())  		return -ENODEV;  	child = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc"); @@ -1943,6 +1725,74 @@ static ssize_t altr_edac_a10_device_trig(struct file *file,  		writel(priv->ue_set_mask, set_addr);  	else  		writel(priv->ce_set_mask, set_addr); + +	/* Ensure the interrupt test bits are set */ +	wmb(); +	local_irq_restore(flags); + +	return count; +} + +/* + * The Stratix10 EDAC Error Injection Functions differ from Arria10 + * slightly. A few Arria10 peripherals can use this injection function. + * Inject the error into the memory and then readback to trigger the IRQ. + */ +static ssize_t altr_edac_a10_device_trig2(struct file *file, +					  const char __user *user_buf, +					  size_t count, loff_t *ppos) +{ +	struct edac_device_ctl_info *edac_dci = file->private_data; +	struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; +	const struct edac_device_prv_data *priv = drvdata->data; +	void __iomem *set_addr = (drvdata->base + priv->set_err_ofst); +	unsigned long flags; +	u8 trig_type; + +	if (!user_buf || get_user(trig_type, user_buf)) +		return -EFAULT; + +	local_irq_save(flags); +	if (trig_type == ALTR_UE_TRIGGER_CHAR) { +		writel(priv->ue_set_mask, set_addr); +	} else { +		/* Setup write of 0 to first 4 bytes */ +		writel(0x0, drvdata->base + ECC_BLK_WDATA0_OFST); +		writel(0x0, drvdata->base + ECC_BLK_WDATA1_OFST); +		writel(0x0, drvdata->base + ECC_BLK_WDATA2_OFST); +		writel(0x0, drvdata->base + ECC_BLK_WDATA3_OFST); +		/* Setup write of 4 bytes */ +		writel(ECC_WORD_WRITE, drvdata->base + ECC_BLK_DBYTECTRL_OFST); +		/* Setup Address to 0 */ +		writel(0x0, drvdata->base + ECC_BLK_ADDRESS_OFST); +		/* Setup accctrl to write & data override */ +		writel(ECC_WRITE_DOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); +		/* Kick it. */ +		writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); +		/* Setup accctrl to read & ecc override */ +		writel(ECC_READ_EOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); +		/* Kick it. */ +		writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); +		/* Setup write for single bit change */ +		writel(0x1, drvdata->base + ECC_BLK_WDATA0_OFST); +		writel(0x0, drvdata->base + ECC_BLK_WDATA1_OFST); +		writel(0x0, drvdata->base + ECC_BLK_WDATA2_OFST); +		writel(0x0, drvdata->base + ECC_BLK_WDATA3_OFST); +		/* Copy Read ECC to Write ECC */ +		writel(readl(drvdata->base + ECC_BLK_RECC0_OFST), +		       drvdata->base + ECC_BLK_WECC0_OFST); +		writel(readl(drvdata->base + ECC_BLK_RECC1_OFST), +		       drvdata->base + ECC_BLK_WECC1_OFST); +		/* Setup accctrl to write & ecc override & data override */ +		writel(ECC_WRITE_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); +		/* Kick it. */ +		writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); +		/* Setup accctrl to read & ecc overwrite & data overwrite */ +		writel(ECC_READ_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); +		/* Kick it. */ +		writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); +	} +  	/* Ensure the interrupt test bits are set */  	wmb();  	local_irq_restore(flags); @@ -2146,6 +1996,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, A10_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; @@ -2159,8 +2038,34 @@ 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; +		uintptr_t 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 = res.start; + +		edac->ecc_mgr_map = devm_regmap_init(&pdev->dev, NULL, +						     (void *)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"); @@ -2187,14 +2092,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)) @@ -2211,7 +2140,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); @@ -2222,6 +2152,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); @@ -2235,171 +2166,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"); |