Lines Matching +full:versal +full:- +full:ddrmc
1 // SPDX-License-Identifier: GPL-2.0
3 * Xilinx Versal memory controller driver
15 #include <linux/firmware/xlnx-zynqmp.h>
16 #include <linux/firmware/xlnx-event-manager.h>
133 * https://docs.xilinx.com/r/en-US/am012-versal-register-reference/PCSR_LOCK-XRAM_SLCR-Register
147 * struct ecc_error_info - ECC error log information.
193 * struct ecc_status - ECC status information to report.
207 * struct edac_priv - DDR memory controller private instance data.
209 * @ddrmc_noc_baseaddr: Base address of the DDRMC NOC.
253 ddrmc_base = priv->ddrmc_baseaddr; in get_ce_error_info()
254 p = &priv->stat; in get_ce_error_info()
256 p->error_type = XDDR_ERR_TYPE_CE; in get_ce_error_info()
259 p->ceinfo[0].i = regval | reghi << 32; in get_ce_error_info()
269 p->ceinfo[1].i = regval | reghi << 32; in get_ce_error_info()
285 ddrmc_base = priv->ddrmc_baseaddr; in get_ue_error_info()
286 p = &priv->stat; in get_ue_error_info()
288 p->error_type = XDDR_ERR_TYPE_UE; in get_ue_error_info()
292 p->ueinfo[0].i = regval | reghi << 32; in get_ue_error_info()
302 p->ueinfo[1].i = regval | reghi << 32; in get_ue_error_info()
316 ddrmc_base = priv->ddrmc_baseaddr; in get_error_info()
317 p = &priv->stat; in get_error_info()
327 p->channel = 1; in get_error_info()
329 p->channel = 0; in get_error_info()
336 p->channel = 1; in get_error_info()
338 p->channel = 0; in get_error_info()
357 * convert_to_physical - Convert to physical address.
371 err_addr |= (row & BIT(0)) << priv->row_bit[index]; in convert_to_physical()
376 err_addr |= (pinf.col & BIT(0)) << priv->col_bit[index]; in convert_to_physical()
381 err_addr |= (pinf.bank & BIT(0)) << priv->bank_bit[index]; in convert_to_physical()
386 err_addr |= (pinf.group & BIT(0)) << priv->grp_bit[index]; in convert_to_physical()
391 err_addr |= (pinf.rank & BIT(0)) << priv->rank_bit[index]; in convert_to_physical()
396 err_addr |= (pinf.lrank & BIT(0)) << priv->lrank_bit[index]; in convert_to_physical()
400 err_addr |= (priv->stat.channel & BIT(0)) << priv->ch_bit; in convert_to_physical()
406 * handle_error - Handle Correctable and Uncorrectable errors.
414 struct edac_priv *priv = mci->pvt_info; in handle_error()
417 if (stat->error_type == XDDR_ERR_TYPE_CE) { in handle_error()
418 priv->ce_cnt++; in handle_error()
419 pinf = stat->ceinfo[stat->channel]; in handle_error()
420 snprintf(priv->message, XDDR_EDAC_MSG_SIZE, in handle_error()
422 "CE", priv->mc_id, in handle_error()
426 priv->ce_cnt, 0, 0, 0, 0, 0, -1, in handle_error()
427 priv->message, ""); in handle_error()
430 if (stat->error_type == XDDR_ERR_TYPE_UE) { in handle_error()
431 priv->ue_cnt++; in handle_error()
432 pinf = stat->ueinfo[stat->channel]; in handle_error()
433 snprintf(priv->message, XDDR_EDAC_MSG_SIZE, in handle_error()
435 "UE", priv->mc_id, in handle_error()
439 priv->ue_cnt, 0, 0, 0, 0, 0, -1, in handle_error()
440 priv->message, ""); in handle_error()
447 * err_callback - Handle Correctable and Uncorrectable errors.
460 priv = mci->pvt_info; in err_callback()
461 p = &priv->stat; in err_callback()
463 regval = readl(priv->ddrmc_baseaddr + XDDR_ISR_OFFSET); in err_callback()
466 p->error_type = XDDR_ERR_TYPE_CE; in err_callback()
468 p->error_type = XDDR_ERR_TYPE_UE; in err_callback()
473 handle_error(mci, &priv->stat); in err_callback()
476 writel(PCSR_UNLOCK_VAL, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in err_callback()
479 writel(regval, priv->ddrmc_baseaddr + XDDR_ISR_OFFSET); in err_callback()
482 writel(1, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in err_callback()
484 priv->ce_cnt, priv->ue_cnt); in err_callback()
488 * get_dwidth - Return the controller memory width.
523 * get_ecc_state - Return the controller ECC enable/disable status.
528 * Return: a ECC status boolean i.e true/false - enabled/disabled.
546 * get_memsize - Get the size of the attached memory device.
556 regval = readl(priv->ddrmc_baseaddr + XDDR_REG_CONFIG0_OFFSET); in get_memsize()
582 * init_csrows - Initialize the csrow data.
590 struct edac_priv *priv = mci->pvt_info; in init_csrows()
598 for (row = 0; row < mci->nr_csrows; row++) { in init_csrows()
599 csi = mci->csrows[row]; in init_csrows()
600 for (ch = 0; ch < csi->nr_channels; ch++) { in init_csrows()
601 dimm = csi->channels[ch]->dimm; in init_csrows()
602 dimm->edac_mode = EDAC_SECDED; in init_csrows()
603 dimm->mtype = MEM_DDR4; in init_csrows()
604 dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; in init_csrows()
605 dimm->grain = XDDR_EDAC_ERR_GRAIN; in init_csrows()
606 dimm->dtype = get_dwidth(priv->ddrmc_baseaddr); in init_csrows()
612 * mc_init - Initialize one driver instance.
617 * related driver-private data associated with the memory controller the
622 mci->pdev = &pdev->dev; in mc_init()
626 mci->mtype_cap = MEM_FLAG_DDR4; in mc_init()
627 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; in mc_init()
628 mci->scrub_cap = SCRUB_HW_SRC; in mc_init()
629 mci->scrub_mode = SCRUB_NONE; in mc_init()
631 mci->edac_cap = EDAC_FLAG_SECDED; in mc_init()
632 mci->ctl_name = "xlnx_ddr_controller"; in mc_init()
633 mci->dev_name = dev_name(&pdev->dev); in mc_init()
634 mci->mod_name = "xlnx_edac"; in mc_init()
644 writel(PCSR_UNLOCK_VAL, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in enable_intr()
648 priv->ddrmc_baseaddr + XDDR_IRQ_EN_OFFSET); in enable_intr()
651 priv->ddrmc_baseaddr + XDDR_IRQ1_EN_OFFSET); in enable_intr()
653 writel(1, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in enable_intr()
659 writel(PCSR_UNLOCK_VAL, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in disable_intr()
663 priv->ddrmc_baseaddr + XDDR_IRQ_DIS_OFFSET); in disable_intr()
666 writel(1, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in disable_intr()
673 * poison_setup - Update poison registers.
686 row |= (((priv->err_inject_addr >> priv->row_bit[index]) & in poison_setup()
691 col |= (((priv->err_inject_addr >> priv->col_bit[index]) & in poison_setup()
696 bank |= (((priv->err_inject_addr >> priv->bank_bit[index]) & in poison_setup()
701 grp |= (((priv->err_inject_addr >> priv->grp_bit[index]) & in poison_setup()
706 rank |= (((priv->err_inject_addr >> priv->rank_bit[index]) & in poison_setup()
711 lrank |= (((priv->err_inject_addr >> priv->lrank_bit[index]) & in poison_setup()
715 ch = (priv->err_inject_addr >> priv->ch_bit) & BIT(0); in poison_setup()
717 writel(0xFF, priv->ddrmc_baseaddr + ECCW1_FLIP_CTRL); in poison_setup()
719 writel(0xFF, priv->ddrmc_baseaddr + ECCW0_FLIP_CTRL); in poison_setup()
721 writel(0, priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC12_OFFSET); in poison_setup()
722 writel(0, priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC13_OFFSET); in poison_setup()
728 writel(regval, priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC14_OFFSET); in poison_setup()
734 writel(regval, priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC15_OFFSET); in poison_setup()
740 struct edac_priv *priv = mci->pvt_info; in xddr_inject_data_poison_store()
742 writel(0, priv->ddrmc_baseaddr + ECCW0_FLIP0_OFFSET); in xddr_inject_data_poison_store()
743 writel(0, priv->ddrmc_baseaddr + ECCW1_FLIP0_OFFSET); in xddr_inject_data_poison_store()
746 writel(ECC_CEPOISON_MASK, priv->ddrmc_baseaddr + in xddr_inject_data_poison_store()
748 writel(ECC_CEPOISON_MASK, priv->ddrmc_baseaddr + in xddr_inject_data_poison_store()
751 writel(ECC_UEPOISON_MASK, priv->ddrmc_baseaddr + in xddr_inject_data_poison_store()
753 writel(ECC_UEPOISON_MASK, priv->ddrmc_baseaddr + in xddr_inject_data_poison_store()
758 writel(1, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in xddr_inject_data_poison_store()
766 struct device *dev = file->private_data; in inject_data_poison_store()
768 struct edac_priv *priv = mci->pvt_info; in inject_data_poison_store()
771 writel(PCSR_UNLOCK_VAL, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET); in inject_data_poison_store()
772 writel(PCSR_UNLOCK_VAL, priv->ddrmc_noc_baseaddr + XDDR_PCSR_OFFSET); in inject_data_poison_store()
777 writel(1, priv->ddrmc_noc_baseaddr + XDDR_PCSR_OFFSET); in inject_data_poison_store()
792 struct edac_priv *priv = mci->pvt_info; in create_debugfs_attributes()
794 priv->debugfs = edac_debugfs_create_dir(mci->dev_name); in create_debugfs_attributes()
795 if (!priv->debugfs) in create_debugfs_attributes()
798 edac_debugfs_create_file("inject_error", 0200, priv->debugfs, in create_debugfs_attributes()
799 &mci->dev, &xddr_inject_enable_fops); in create_debugfs_attributes()
800 debugfs_create_x64("address", 0600, priv->debugfs, in create_debugfs_attributes()
801 &priv->err_inject_addr); in create_debugfs_attributes()
802 mci->debugfs = priv->debugfs; in create_debugfs_attributes()
810 priv->row_bit[start] = rows.row0; in process_bit()
811 priv->row_bit[start + 1] = rows.row1; in process_bit()
812 priv->row_bit[start + 2] = rows.row2; in process_bit()
813 priv->row_bit[start + 3] = rows.row3; in process_bit()
814 priv->row_bit[start + 4] = rows.row4; in process_bit()
822 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC5_OFFSET); in setup_row_address_map()
825 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC6_OFFSET); in setup_row_address_map()
828 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC7_OFFSET); in setup_row_address_map()
831 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC8_OFFSET); in setup_row_address_map()
834 priv->row_bit[15] = rows.row0; in setup_row_address_map()
835 priv->row_bit[16] = rows.row1; in setup_row_address_map()
836 priv->row_bit[17] = rows.row2; in setup_row_address_map()
844 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC8_OFFSET); in setup_column_address_map()
845 priv->col_bit[0] = FIELD_GET(MASK_24, regval); in setup_column_address_map()
847 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC9_OFFSET); in setup_column_address_map()
849 priv->col_bit[1] = cols.col1; in setup_column_address_map()
850 priv->col_bit[2] = cols.col2; in setup_column_address_map()
851 priv->col_bit[3] = cols.col3; in setup_column_address_map()
852 priv->col_bit[4] = cols.col4; in setup_column_address_map()
853 priv->col_bit[5] = cols.col5; in setup_column_address_map()
855 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC10_OFFSET); in setup_column_address_map()
857 priv->col_bit[6] = cols.col1; in setup_column_address_map()
858 priv->col_bit[7] = cols.col2; in setup_column_address_map()
859 priv->col_bit[8] = cols.col3; in setup_column_address_map()
860 priv->col_bit[9] = cols.col4; in setup_column_address_map()
867 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC10_OFFSET); in setup_bank_grp_ch_address_map()
868 priv->bank_bit[0] = FIELD_GET(MASK_24, regval); in setup_bank_grp_ch_address_map()
870 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC11_OFFSET); in setup_bank_grp_ch_address_map()
871 priv->bank_bit[1] = (regval & MASK_0); in setup_bank_grp_ch_address_map()
872 priv->grp_bit[0] = FIELD_GET(GRP_0_MASK, regval); in setup_bank_grp_ch_address_map()
873 priv->grp_bit[1] = FIELD_GET(GRP_1_MASK, regval); in setup_bank_grp_ch_address_map()
874 priv->ch_bit = FIELD_GET(CH_0_MASK, regval); in setup_bank_grp_ch_address_map()
881 regval = readl(priv->ddrmc_noc_baseaddr + XDDR_NOC_REG_ADEC4_OFFSET); in setup_rank_lrank_address_map()
882 priv->rank_bit[0] = (regval & MASK_0); in setup_rank_lrank_address_map()
883 priv->rank_bit[1] = FIELD_GET(RANK_1_MASK, regval); in setup_rank_lrank_address_map()
884 priv->lrank_bit[0] = FIELD_GET(LRANK_0_MASK, regval); in setup_rank_lrank_address_map()
885 priv->lrank_bit[1] = FIELD_GET(LRANK_1_MASK, regval); in setup_rank_lrank_address_map()
886 priv->lrank_bit[2] = FIELD_GET(MASK_24, regval); in setup_rank_lrank_address_map()
890 * setup_address_map - Set Address Map by querying ADDRMAP registers.
910 { .compatible = "xlnx,versal-ddrmc", },
963 return -ENXIO; in mc_probe()
966 edac_mc_id = emif_get_id(pdev->dev.of_node); in mc_probe()
989 return -ENOMEM; in mc_probe()
992 priv = mci->pvt_info; in mc_probe()
993 priv->ddrmc_baseaddr = ddrmc_baseaddr; in mc_probe()
994 priv->ddrmc_noc_baseaddr = ddrmc_noc_baseaddr; in mc_probe()
995 priv->ce_cnt = 0; in mc_probe()
996 priv->ue_cnt = 0; in mc_probe()
997 priv->mc_id = edac_mc_id; in mc_probe()
1013 if (rc == -EACCES) in mc_probe()
1014 rc = -EPROBE_DEFER; in mc_probe()
1027 edac_mc_del_mc(&pdev->dev); in mc_probe()
1037 struct edac_priv *priv = mci->pvt_info; in mc_remove()
1042 debugfs_remove_recursive(priv->debugfs); in mc_remove()
1050 edac_mc_del_mc(&pdev->dev); in mc_remove()
1058 .name = "xilinx-ddrmc-edac",
1068 MODULE_DESCRIPTION("Xilinx DDRMC ECC driver");