/* * edac_init * module initialization entry point */ static int __init edac_init(void) { int err = 0; edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n"); /* * Harvest and clear any boot/initialization PCI parity errors * * FIXME: This only clears errors logged by devices present at time of * module initialization. We should also do an initial clear * of each newly hotplugged device. */ edac_pci_clear_parity_errors(); err = edac_mc_sysfs_init(); if (err) goto error; edac_debugfs_init(); /* Setup/Initialize the workq for this core */ err = edac_workqueue_setup(); if (err) { edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); goto error; } return 0; error: return err; }
/** * zynqmp_ocm_edac_inject_uebitposition1_store - Set UE second bit postion * @dci: Pointer to the edac device struct * @data: Pointer to user data * @count: read the size bytes from buffer * * Set the second bit postion for UE Error generation,we need to configure * any two bitpositions to inject UE Error * Return: Number of bytes copied. */ static ssize_t zynqmp_ocm_edac_inject_uebitpos1_store( struct edac_device_ctl_info *dci, const char *data, size_t count) { struct zynqmp_ocm_edac_priv *priv = dci->pvt_info; u32 mask; if (!data) return -EFAULT; if (kstrtou8(data, 0, &priv->ue_bitpos1)) return -EINVAL; if (priv->ue_bitpos0 == priv->ue_bitpos1) { edac_printk(KERN_ERR, EDAC_DEVICE, "Bit positions should not be equal\n"); return -EINVAL; } /* If both bit postions are referring to 32 bit data, then configure * only FID0 register or if it is 64 bit data, then configure only * FID1 register. */ if ((priv->ue_bitpos0 >= 0 && priv->ue_bitpos0 <= 31) && (priv->ue_bitpos1 >= 0 && priv->ue_bitpos1 <= 31)) { mask = (1 << priv->ue_bitpos0); mask |= (1 << priv->ue_bitpos1); writel(mask, priv->baseaddr + OCM_FID0_OFST); writel(0, priv->baseaddr + OCM_FID1_OFST); } else if ((priv->ue_bitpos0 >= 32 && priv->ue_bitpos0 <= 63) && (priv->ue_bitpos1 >= 32 && priv->ue_bitpos1 <= 63)) { mask = (1 << (priv->ue_bitpos0 - 32)); mask |= (1 << (priv->ue_bitpos1 - 32)); writel(mask, priv->baseaddr + OCM_FID1_OFST); writel(0, priv->baseaddr + OCM_FID0_OFST); } /* If one bit position is referring a bit in 32 bit data and other in * 64 bit data, just configure FID0/FID1 based on uebitpos1. */ if ((priv->ue_bitpos0 >= 0 && priv->ue_bitpos0 <= 31) && (priv->ue_bitpos1 >= 32 && priv->ue_bitpos1 <= 63)) { writel(1 << (priv->ue_bitpos1 - 32), priv->baseaddr + OCM_FID1_OFST); } else if ((priv->ue_bitpos0 >= 32 && priv->ue_bitpos0 <= 63) && (priv->ue_bitpos1 >= 0 && priv->ue_bitpos1 <= 31)) { writel(1 << priv->ue_bitpos1, priv->baseaddr + OCM_FID0_OFST); } else { edac_printk(KERN_ERR, EDAC_DEVICE, "Bit position > 64 is not valid, Valid bits:[63:0]\n"); } edac_printk(KERN_INFO, EDAC_DEVICE, "UE at Bit Position0: %d Bit Position1: %d\n", priv->ue_bitpos0, priv->ue_bitpos1); return count; }
/* * edac_init * module initialization entry point */ static int __init edac_init(void) { int err = 0; edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n"); /* * Harvest and clear any boot/initialization PCI parity errors * * FIXME: This only clears errors logged by devices present at time of * module initialization. We should also do an initial clear * of each newly hotplugged device. */ edac_pci_clear_parity_errors(); /* * perform the registration of the /sys/devices/system/edac class object */ if (edac_register_sysfs_edac_name()) { edac_printk(KERN_ERR, EDAC_MC, "Error initializing 'edac' kobject\n"); err = -ENODEV; goto error; } /* * now set up the mc_kset under the edac class object */ err = edac_sysfs_setup_mc_kset(); if (err) goto sysfs_setup_fail; /* Setup/Initialize the workq for this core */ err = edac_workqueue_setup(); if (err) { edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); goto workq_fail; } return 0; /* Error teardown stack */ workq_fail: edac_sysfs_teardown_mc_kset(); sysfs_setup_fail: edac_unregister_sysfs_edac_name(); error: return err; }
static int dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank) { struct llcc_drv_data *drv = edev_ctl->pvt_info; int ret; ret = dump_syn_reg_values(drv, bank, err_type); if (ret) return ret; switch (err_type) { case LLCC_DRAM_CE: edac_device_handle_ce(edev_ctl, 0, bank, "LLCC Data RAM correctable Error"); break; case LLCC_DRAM_UE: edac_device_handle_ue(edev_ctl, 0, bank, "LLCC Data RAM uncorrectable Error"); break; case LLCC_TRAM_CE: edac_device_handle_ce(edev_ctl, 0, bank, "LLCC Tag RAM correctable Error"); break; case LLCC_TRAM_UE: edac_device_handle_ue(edev_ctl, 0, bank, "LLCC Tag RAM uncorrectable Error"); break; default: ret = -EINVAL; edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n", err_type); } return ret; }
/** * zynqmp_ocm_edac_inject_cebitpos_store - Set CE bit postion * @dci: Pointer to the edac device struct * @data: Pointer to user data * @count: read the size bytes from buffer * * Set any one bit to inject CE error * Return: Number of bytes copied. */ static ssize_t zynqmp_ocm_edac_inject_cebitpos_store( struct edac_device_ctl_info *dci, const char *data, size_t count) { struct zynqmp_ocm_edac_priv *priv = dci->pvt_info; if (!data) return -EFAULT; if (kstrtou8(data, 0, &priv->ce_bitpos)) return -EINVAL; if (priv->ce_bitpos >= 0 && priv->ce_bitpos <= 31) { writel(1 << priv->ce_bitpos, priv->baseaddr + OCM_FID0_OFST); writel(0, priv->baseaddr + OCM_FID1_OFST); } else if (priv->ce_bitpos >= 32 && priv->ce_bitpos <= 63) { writel(1 << (priv->ce_bitpos - 32), priv->baseaddr + OCM_FID1_OFST); writel(0, priv->baseaddr + OCM_FID0_OFST); } else { edac_printk(KERN_ERR, EDAC_DEVICE, "Bit number > 64 is not valid\n"); } return count; }
static irqreturn_t llcc_ecc_irq_handler(int irq, void *edev_ctl) { struct edac_device_ctl_info *edac_dev_ctl = edev_ctl; struct llcc_drv_data *drv = edac_dev_ctl->pvt_info; irqreturn_t irq_rc = IRQ_NONE; u32 drp_error, trp_error, i; int ret; /* Iterate over the banks and look for Tag RAM or Data RAM errors */ for (i = 0; i < drv->num_banks; i++) { ret = regmap_read(drv->regmap, drv->offsets[i] + DRP_INTERRUPT_STATUS, &drp_error); if (!ret && (drp_error & SB_ECC_ERROR)) { edac_printk(KERN_CRIT, EDAC_LLCC, "Single Bit Error detected in Data RAM\n"); ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i); } else if (!ret && (drp_error & DB_ECC_ERROR)) { edac_printk(KERN_CRIT, EDAC_LLCC, "Double Bit Error detected in Data RAM\n"); ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i); } if (!ret) irq_rc = IRQ_HANDLED; ret = regmap_read(drv->regmap, drv->offsets[i] + TRP_INTERRUPT_0_STATUS, &trp_error); if (!ret && (trp_error & SB_ECC_ERROR)) { edac_printk(KERN_CRIT, EDAC_LLCC, "Single Bit Error detected in Tag RAM\n"); ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i); } else if (!ret && (trp_error & DB_ECC_ERROR)) { edac_printk(KERN_CRIT, EDAC_LLCC, "Double Bit Error detected in Tag RAM\n"); ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i); } if (!ret) irq_rc = IRQ_HANDLED; } return irq_rc; }
/* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/ static int dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) { struct llcc_edac_reg_data reg_data = edac_reg_data[err_type]; int err_cnt, err_ways, ret, i; u32 synd_reg, synd_val; for (i = 0; i < reg_data.reg_cnt; i++) { synd_reg = reg_data.synd_reg + (i * 4); ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg, &synd_val); if (ret) goto clear; edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n", reg_data.name, i, synd_val); } ret = regmap_read(drv->regmap, drv->offsets[bank] + reg_data.count_status_reg, &err_cnt); if (ret) goto clear; err_cnt &= reg_data.count_mask; err_cnt >>= reg_data.count_shift; edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n", reg_data.name, err_cnt); ret = regmap_read(drv->regmap, drv->offsets[bank] + reg_data.ways_status_reg, &err_ways); if (ret) goto clear; err_ways &= reg_data.ways_mask; err_ways >>= reg_data.ways_shift; edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n", reg_data.name, err_ways); clear: return qcom_llcc_clear_error_status(err_type, drv); }
/* * ->get_sdram_scrub_rate() return value semantics same as above. */ static ssize_t mci_sdram_scrub_rate_show(struct device *dev, struct device_attribute *mattr, char *data) { struct mem_ctl_info *mci = to_mci(dev); int bandwidth = 0; bandwidth = mci->get_sdram_scrub_rate(mci); if (bandwidth < 0) { edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n"); return bandwidth; } return sprintf(data, "%d\n", bandwidth); }
/** * zynqmp_ocm_edac_inject_uebitpos0_store - set UE bit position0 * @dci: Pointer to the edac device struct * @data: Pointer to user data * @count: read the size bytes from buffer * * Set the first bit postion for UE Error generation,we need to configure * any two bitpositions to inject UE Error * Return: Number of bytes copied. */ static ssize_t zynqmp_ocm_edac_inject_uebitpos0_store( struct edac_device_ctl_info *dci, const char *data, size_t count) { struct zynqmp_ocm_edac_priv *priv = dci->pvt_info; if (!data) return -EFAULT; if (kstrtou8(data, 0, &priv->ue_bitpos0)) return -EINVAL; if (priv->ue_bitpos0 >= 0 && priv->ue_bitpos0 <= 31) writel(1 << priv->ue_bitpos0, priv->baseaddr + OCM_FID0_OFST); else if (priv->ue_bitpos0 >= 32 && priv->ue_bitpos0 <= 63) writel(1 << (priv->ue_bitpos0 - 32), priv->baseaddr + OCM_FID1_OFST); else edac_printk(KERN_ERR, EDAC_DEVICE, "Bit position > 64 is not valid\n"); edac_printk(KERN_INFO, EDAC_DEVICE, "Set another bit position for UE\n"); return count; }
/* Memory scrubbing interface: * * A MC driver can limit the scrubbing bandwidth based on the CPU type. * Therefore, ->set_sdram_scrub_rate should be made to return the actual * bandwidth that is accepted or 0 when scrubbing is to be disabled. * * Negative value still means that an error has occurred while setting * the scrub rate. */ static ssize_t mci_sdram_scrub_rate_store(struct device *dev, struct device_attribute *mattr, const char *data, size_t count) { struct mem_ctl_info *mci = to_mci(dev); unsigned long bandwidth = 0; int new_bw = 0; if (kstrtoul(data, 10, &bandwidth) < 0) return -EINVAL; new_bw = mci->set_sdram_scrub_rate(mci, bandwidth); if (new_bw < 0) { edac_printk(KERN_WARNING, EDAC_MC, "Error setting scrub rate to: %lu\n", bandwidth); return -EINVAL; } return count; }
/* Clear the error interrupt and counter registers */ static int qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv) { int ret = 0; switch (err_type) { case LLCC_DRAM_CE: case LLCC_DRAM_UE: ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR, DRP_TRP_INT_CLEAR); if (ret) return ret; ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR, DRP_TRP_CNT_CLEAR); if (ret) return ret; break; case LLCC_TRAM_CE: case LLCC_TRAM_UE: ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR, DRP_TRP_INT_CLEAR); if (ret) return ret; ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR, DRP_TRP_CNT_CLEAR); if (ret) return ret; break; default: ret = -EINVAL; edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n", err_type); } return ret; }
/* * PCI Parity polling * * Fucntion to retrieve the current parity status * and decode it * */ static void edac_pci_dev_parity_test(struct pci_dev *dev) { unsigned long flags; u16 status; u8 header_type; /* stop any interrupts until we can acquire the status */ local_irq_save(flags); /* read the STATUS register on this device */ status = get_pci_parity_status(dev, 0); /* read the device TYPE, looking for bridges */ pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); local_irq_restore(flags); debugf4("PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev)); /* check the status reg for errors on boards NOT marked as broken * if broken, we cannot trust any of the status bits */ if (status && !dev->broken_parity_status) { if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) { edac_printk(KERN_CRIT, EDAC_PCI, "Signaled System Error on %s\n", pci_name(dev)); atomic_inc(&pci_nonparity_count); } if (status & (PCI_STATUS_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Master Data Parity Error on %s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } if (status & (PCI_STATUS_DETECTED_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Detected Parity Error on %s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } } debugf4("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev_name(&dev->dev)); if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* On bridges, need to examine secondary status register */ status = get_pci_parity_status(dev, 1); debugf4("PCI SEC_STATUS= 0x%04x %s\n", status, dev_name(&dev->dev)); /* check the secondary status reg for errors, * on NOT broken boards */ if (status && !dev->broken_parity_status) { if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) { edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Signaled System Error on %s\n", pci_name(dev)); atomic_inc(&pci_nonparity_count); } if (status & (PCI_STATUS_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Master Data Parity Error on " "%s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } if (status & (PCI_STATUS_DETECTED_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Detected Parity Error on %s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } } } }
/* * PCI Parity polling * */ static void edac_pci_dev_parity_test(struct pci_dev *dev) { u16 status; u8 header_type; /* read the STATUS register on this device */ status = get_pci_parity_status(dev, 0); debugf2("PCI STATUS= 0x%04x %s\n", status, dev->dev.bus_id ); /* check the status reg for errors */ if (status) { if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) edac_printk(KERN_CRIT, EDAC_PCI, "Signaled System Error on %s\n", pci_name(dev)); if (status & (PCI_STATUS_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Master Data Parity Error on %s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } if (status & (PCI_STATUS_DETECTED_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Detected Parity Error on %s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } } /* read the device TYPE, looking for bridges */ pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); debugf2("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev->dev.bus_id ); if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* On bridges, need to examine secondary status register */ status = get_pci_parity_status(dev, 1); debugf2("PCI SEC_STATUS= 0x%04x %s\n", status, dev->dev.bus_id ); /* check the secondary status reg for errors */ if (status) { if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Signaled System Error on %s\n", pci_name(dev)); if (status & (PCI_STATUS_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Master Data Parity Error on " "%s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } if (status & (PCI_STATUS_DETECTED_PARITY)) { edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Detected Parity Error on %s\n", pci_name(dev)); atomic_inc(&pci_parity_count); } } } }
/** * zynqmp_ocm_edac_probe - Check controller and bind driver * @pdev: Pointer to the platform_device struct * * Probes a specific controller instance for binding with the driver. * * Return: 0 if the controller instance was successfully bound to the * driver; otherwise, < 0 on error. */ static int zynqmp_ocm_edac_probe(struct platform_device *pdev) { struct edac_device_ctl_info *dci; struct zynqmp_ocm_edac_priv *priv; int irq, status; struct resource *res; void __iomem *baseaddr; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); baseaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(baseaddr)) return PTR_ERR(baseaddr); if (!zynqmp_ocm_edac_get_eccstate(baseaddr)) { edac_printk(KERN_INFO, EDAC_DEVICE, "ECC not enabled - Disabling EDAC driver\n"); return -ENXIO; } dci = edac_device_alloc_ctl_info(sizeof(*priv), ZYNQMP_OCM_EDAC_STRING, 1, ZYNQMP_OCM_EDAC_STRING, 1, 0, NULL, 0, 0); if (!dci) { edac_printk(KERN_ERR, EDAC_DEVICE, "Unable to allocate EDAC device\n"); return -ENOMEM; } priv = dci->pvt_info; platform_set_drvdata(pdev, dci); dci->dev = &pdev->dev; priv->baseaddr = baseaddr; dci->mod_name = pdev->dev.driver->name; dci->ctl_name = ZYNQMP_OCM_EDAC_STRING; dci->dev_name = dev_name(&pdev->dev); zynqmp_set_ocm_edac_sysfs_attributes(dci); if (edac_device_add_device(dci)) goto free_dev_ctl; irq = platform_get_irq(pdev, 0); if (irq < 0) { edac_printk(KERN_ERR, EDAC_DEVICE, "No irq %d in DT\n", irq); return irq; } status = devm_request_irq(&pdev->dev, irq, zynqmp_ocm_edac_intr_handler, 0, dev_name(&pdev->dev), dci); if (status < 0) { edac_printk(KERN_ERR, EDAC_DEVICE, "Failed to request Irq\n"); goto free_edac_dev; } writel(OCM_CEUE_MASK, priv->baseaddr + OCM_IEN_OFST); return 0; free_edac_dev: edac_device_del_device(&pdev->dev); free_dev_ctl: edac_device_free_ctl_info(dci); return -1; }