| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- // SPDX-License-Identifier: GPL-2.0-only
- // Copyright (c) 2022 Nuvoton Technology Corporation
- #include <linux/debugfs.h>
- #include <linux/iopoll.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- #include "edac_module.h"
- #define EDAC_MOD_NAME "npcm-edac"
- #define EDAC_MSG_SIZE 256
- /* chip serials */
- #define NPCM7XX_CHIP BIT(0)
- #define NPCM8XX_CHIP BIT(1)
- /* syndrome values */
- #define UE_SYNDROME 0x03
- /* error injection */
- #define ERROR_TYPE_CORRECTABLE 0
- #define ERROR_TYPE_UNCORRECTABLE 1
- #define ERROR_LOCATION_DATA 0
- #define ERROR_LOCATION_CHECKCODE 1
- #define ERROR_BIT_DATA_MAX 63
- #define ERROR_BIT_CHECKCODE_MAX 7
- static char data_synd[] = {
- 0xf4, 0xf1, 0xec, 0xea, 0xe9, 0xe6, 0xe5, 0xe3,
- 0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd3, 0xce, 0xcb,
- 0xb5, 0xb0, 0xad, 0xab, 0xa8, 0xa7, 0xa4, 0xa2,
- 0x9d, 0x9b, 0x98, 0x97, 0x94, 0x92, 0x8f, 0x8a,
- 0x75, 0x70, 0x6d, 0x6b, 0x68, 0x67, 0x64, 0x62,
- 0x5e, 0x5b, 0x58, 0x57, 0x54, 0x52, 0x4f, 0x4a,
- 0x34, 0x31, 0x2c, 0x2a, 0x29, 0x26, 0x25, 0x23,
- 0x1c, 0x1a, 0x19, 0x16, 0x15, 0x13, 0x0e, 0x0b
- };
- static struct regmap *npcm_regmap;
- struct npcm_platform_data {
- /* chip serials */
- int chip;
- /* memory controller registers */
- u32 ctl_ecc_en;
- u32 ctl_int_status;
- u32 ctl_int_ack;
- u32 ctl_int_mask_master;
- u32 ctl_int_mask_ecc;
- u32 ctl_ce_addr_l;
- u32 ctl_ce_addr_h;
- u32 ctl_ce_data_l;
- u32 ctl_ce_data_h;
- u32 ctl_ce_synd;
- u32 ctl_ue_addr_l;
- u32 ctl_ue_addr_h;
- u32 ctl_ue_data_l;
- u32 ctl_ue_data_h;
- u32 ctl_ue_synd;
- u32 ctl_source_id;
- u32 ctl_controller_busy;
- u32 ctl_xor_check_bits;
- /* masks and shifts */
- u32 ecc_en_mask;
- u32 int_status_ce_mask;
- u32 int_status_ue_mask;
- u32 int_ack_ce_mask;
- u32 int_ack_ue_mask;
- u32 int_mask_master_non_ecc_mask;
- u32 int_mask_master_global_mask;
- u32 int_mask_ecc_non_event_mask;
- u32 ce_addr_h_mask;
- u32 ce_synd_mask;
- u32 ce_synd_shift;
- u32 ue_addr_h_mask;
- u32 ue_synd_mask;
- u32 ue_synd_shift;
- u32 source_id_ce_mask;
- u32 source_id_ce_shift;
- u32 source_id_ue_mask;
- u32 source_id_ue_shift;
- u32 controller_busy_mask;
- u32 xor_check_bits_mask;
- u32 xor_check_bits_shift;
- u32 writeback_en_mask;
- u32 fwc_mask;
- };
- struct priv_data {
- void __iomem *reg;
- char message[EDAC_MSG_SIZE];
- const struct npcm_platform_data *pdata;
- /* error injection */
- struct dentry *debugfs;
- u8 error_type;
- u8 location;
- u8 bit;
- };
- static void handle_ce(struct mem_ctl_info *mci)
- {
- struct priv_data *priv = mci->pvt_info;
- const struct npcm_platform_data *pdata;
- u32 val_h = 0, val_l, id, synd;
- u64 addr = 0, data = 0;
- pdata = priv->pdata;
- regmap_read(npcm_regmap, pdata->ctl_ce_addr_l, &val_l);
- if (pdata->chip == NPCM8XX_CHIP) {
- regmap_read(npcm_regmap, pdata->ctl_ce_addr_h, &val_h);
- val_h &= pdata->ce_addr_h_mask;
- }
- addr = ((addr | val_h) << 32) | val_l;
- regmap_read(npcm_regmap, pdata->ctl_ce_data_l, &val_l);
- if (pdata->chip == NPCM8XX_CHIP)
- regmap_read(npcm_regmap, pdata->ctl_ce_data_h, &val_h);
- data = ((data | val_h) << 32) | val_l;
- regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
- id = (id & pdata->source_id_ce_mask) >> pdata->source_id_ce_shift;
- regmap_read(npcm_regmap, pdata->ctl_ce_synd, &synd);
- synd = (synd & pdata->ce_synd_mask) >> pdata->ce_synd_shift;
- snprintf(priv->message, EDAC_MSG_SIZE,
- "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, addr >> PAGE_SHIFT,
- addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
- }
- static void handle_ue(struct mem_ctl_info *mci)
- {
- struct priv_data *priv = mci->pvt_info;
- const struct npcm_platform_data *pdata;
- u32 val_h = 0, val_l, id, synd;
- u64 addr = 0, data = 0;
- pdata = priv->pdata;
- regmap_read(npcm_regmap, pdata->ctl_ue_addr_l, &val_l);
- if (pdata->chip == NPCM8XX_CHIP) {
- regmap_read(npcm_regmap, pdata->ctl_ue_addr_h, &val_h);
- val_h &= pdata->ue_addr_h_mask;
- }
- addr = ((addr | val_h) << 32) | val_l;
- regmap_read(npcm_regmap, pdata->ctl_ue_data_l, &val_l);
- if (pdata->chip == NPCM8XX_CHIP)
- regmap_read(npcm_regmap, pdata->ctl_ue_data_h, &val_h);
- data = ((data | val_h) << 32) | val_l;
- regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
- id = (id & pdata->source_id_ue_mask) >> pdata->source_id_ue_shift;
- regmap_read(npcm_regmap, pdata->ctl_ue_synd, &synd);
- synd = (synd & pdata->ue_synd_mask) >> pdata->ue_synd_shift;
- snprintf(priv->message, EDAC_MSG_SIZE,
- "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, addr >> PAGE_SHIFT,
- addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
- }
- static irqreturn_t edac_ecc_isr(int irq, void *dev_id)
- {
- const struct npcm_platform_data *pdata;
- struct mem_ctl_info *mci = dev_id;
- u32 status;
- pdata = ((struct priv_data *)mci->pvt_info)->pdata;
- regmap_read(npcm_regmap, pdata->ctl_int_status, &status);
- if (status & pdata->int_status_ce_mask) {
- handle_ce(mci);
- /* acknowledge the CE interrupt */
- regmap_write(npcm_regmap, pdata->ctl_int_ack,
- pdata->int_ack_ce_mask);
- return IRQ_HANDLED;
- } else if (status & pdata->int_status_ue_mask) {
- handle_ue(mci);
- /* acknowledge the UE interrupt */
- regmap_write(npcm_regmap, pdata->ctl_int_ack,
- pdata->int_ack_ue_mask);
- return IRQ_HANDLED;
- }
- WARN_ON_ONCE(1);
- return IRQ_NONE;
- }
- static ssize_t force_ecc_error(struct file *file, const char __user *data,
- size_t count, loff_t *ppos)
- {
- struct device *dev = file->private_data;
- struct mem_ctl_info *mci = to_mci(dev);
- struct priv_data *priv = mci->pvt_info;
- const struct npcm_platform_data *pdata;
- u32 val, syndrome;
- int ret;
- pdata = priv->pdata;
- edac_printk(KERN_INFO, EDAC_MOD_NAME,
- "force an ECC error, type = %d, location = %d, bit = %d\n",
- priv->error_type, priv->location, priv->bit);
- /* ensure no pending writes */
- ret = regmap_read_poll_timeout(npcm_regmap, pdata->ctl_controller_busy,
- val, !(val & pdata->controller_busy_mask),
- 1000, 10000);
- if (ret) {
- edac_printk(KERN_INFO, EDAC_MOD_NAME,
- "wait pending writes timeout\n");
- return count;
- }
- regmap_read(npcm_regmap, pdata->ctl_xor_check_bits, &val);
- val &= ~pdata->xor_check_bits_mask;
- /* write syndrome to XOR_CHECK_BITS */
- if (priv->error_type == ERROR_TYPE_CORRECTABLE) {
- if (priv->location == ERROR_LOCATION_DATA &&
- priv->bit > ERROR_BIT_DATA_MAX) {
- edac_printk(KERN_INFO, EDAC_MOD_NAME,
- "data bit should not exceed %d (%d)\n",
- ERROR_BIT_DATA_MAX, priv->bit);
- return count;
- }
- if (priv->location == ERROR_LOCATION_CHECKCODE &&
- priv->bit > ERROR_BIT_CHECKCODE_MAX) {
- edac_printk(KERN_INFO, EDAC_MOD_NAME,
- "checkcode bit should not exceed %d (%d)\n",
- ERROR_BIT_CHECKCODE_MAX, priv->bit);
- return count;
- }
- syndrome = priv->location ? 1 << priv->bit
- : data_synd[priv->bit];
- regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
- val | (syndrome << pdata->xor_check_bits_shift) |
- pdata->writeback_en_mask);
- } else if (priv->error_type == ERROR_TYPE_UNCORRECTABLE) {
- regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
- val | (UE_SYNDROME << pdata->xor_check_bits_shift));
- }
- /* force write check */
- regmap_update_bits(npcm_regmap, pdata->ctl_xor_check_bits,
- pdata->fwc_mask, pdata->fwc_mask);
- return count;
- }
- static const struct file_operations force_ecc_error_fops = {
- .open = simple_open,
- .write = force_ecc_error,
- .llseek = generic_file_llseek,
- };
- /*
- * Setup debugfs for error injection.
- *
- * Nodes:
- * error_type - 0: CE, 1: UE
- * location - 0: data, 1: checkcode
- * bit - 0 ~ 63 for data and 0 ~ 7 for checkcode
- * force_ecc_error - trigger
- *
- * Examples:
- * 1. Inject a correctable error (CE) at checkcode bit 7.
- * ~# echo 0 > /sys/kernel/debug/edac/npcm-edac/error_type
- * ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/location
- * ~# echo 7 > /sys/kernel/debug/edac/npcm-edac/bit
- * ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/force_ecc_error
- *
- * 2. Inject an uncorrectable error (UE).
- * ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/error_type
- * ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/force_ecc_error
- */
- static void setup_debugfs(struct mem_ctl_info *mci)
- {
- struct priv_data *priv = mci->pvt_info;
- priv->debugfs = edac_debugfs_create_dir(mci->mod_name);
- if (!priv->debugfs)
- return;
- edac_debugfs_create_x8("error_type", 0644, priv->debugfs, &priv->error_type);
- edac_debugfs_create_x8("location", 0644, priv->debugfs, &priv->location);
- edac_debugfs_create_x8("bit", 0644, priv->debugfs, &priv->bit);
- edac_debugfs_create_file("force_ecc_error", 0200, priv->debugfs,
- &mci->dev, &force_ecc_error_fops);
- }
- static int setup_irq(struct mem_ctl_info *mci, struct platform_device *pdev)
- {
- const struct npcm_platform_data *pdata;
- int ret, irq;
- pdata = ((struct priv_data *)mci->pvt_info)->pdata;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- edac_printk(KERN_ERR, EDAC_MOD_NAME, "IRQ not defined in DTS\n");
- return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, edac_ecc_isr, 0,
- dev_name(&pdev->dev), mci);
- if (ret < 0) {
- edac_printk(KERN_ERR, EDAC_MOD_NAME, "failed to request IRQ\n");
- return ret;
- }
- /* enable the functional group of ECC and mask the others */
- regmap_write(npcm_regmap, pdata->ctl_int_mask_master,
- pdata->int_mask_master_non_ecc_mask);
- if (pdata->chip == NPCM8XX_CHIP)
- regmap_write(npcm_regmap, pdata->ctl_int_mask_ecc,
- pdata->int_mask_ecc_non_event_mask);
- return 0;
- }
- static const struct regmap_config npcm_regmap_cfg = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- };
- static int edac_probe(struct platform_device *pdev)
- {
- const struct npcm_platform_data *pdata;
- struct device *dev = &pdev->dev;
- struct edac_mc_layer layers[1];
- struct mem_ctl_info *mci;
- struct priv_data *priv;
- void __iomem *reg;
- u32 val;
- int rc;
- reg = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(reg))
- return PTR_ERR(reg);
- npcm_regmap = devm_regmap_init_mmio(dev, reg, &npcm_regmap_cfg);
- if (IS_ERR(npcm_regmap))
- return PTR_ERR(npcm_regmap);
- pdata = of_device_get_match_data(dev);
- if (!pdata)
- return -EINVAL;
- /* bail out if ECC is not enabled */
- regmap_read(npcm_regmap, pdata->ctl_ecc_en, &val);
- if (!(val & pdata->ecc_en_mask)) {
- edac_printk(KERN_ERR, EDAC_MOD_NAME, "ECC is not enabled\n");
- return -EPERM;
- }
- edac_op_state = EDAC_OPSTATE_INT;
- layers[0].type = EDAC_MC_LAYER_ALL_MEM;
- layers[0].size = 1;
- mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
- sizeof(struct priv_data));
- if (!mci)
- return -ENOMEM;
- mci->pdev = &pdev->dev;
- priv = mci->pvt_info;
- priv->reg = reg;
- priv->pdata = pdata;
- platform_set_drvdata(pdev, mci);
- mci->mtype_cap = MEM_FLAG_DDR4;
- mci->edac_ctl_cap = EDAC_FLAG_SECDED;
- mci->scrub_cap = SCRUB_FLAG_HW_SRC;
- mci->scrub_mode = SCRUB_HW_SRC;
- mci->edac_cap = EDAC_FLAG_SECDED;
- mci->ctl_name = "npcm_ddr_controller";
- mci->dev_name = dev_name(&pdev->dev);
- mci->mod_name = EDAC_MOD_NAME;
- mci->ctl_page_to_phys = NULL;
- rc = setup_irq(mci, pdev);
- if (rc)
- goto free_edac_mc;
- rc = edac_mc_add_mc(mci);
- if (rc)
- goto free_edac_mc;
- if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
- setup_debugfs(mci);
- return rc;
- free_edac_mc:
- edac_mc_free(mci);
- return rc;
- }
- static void edac_remove(struct platform_device *pdev)
- {
- struct mem_ctl_info *mci = platform_get_drvdata(pdev);
- struct priv_data *priv = mci->pvt_info;
- const struct npcm_platform_data *pdata;
- pdata = priv->pdata;
- if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
- edac_debugfs_remove_recursive(priv->debugfs);
- edac_mc_del_mc(&pdev->dev);
- edac_mc_free(mci);
- regmap_write(npcm_regmap, pdata->ctl_int_mask_master,
- pdata->int_mask_master_global_mask);
- regmap_update_bits(npcm_regmap, pdata->ctl_ecc_en, pdata->ecc_en_mask, 0);
- }
- static const struct npcm_platform_data npcm750_edac = {
- .chip = NPCM7XX_CHIP,
- /* memory controller registers */
- .ctl_ecc_en = 0x174,
- .ctl_int_status = 0x1d0,
- .ctl_int_ack = 0x1d4,
- .ctl_int_mask_master = 0x1d8,
- .ctl_ce_addr_l = 0x188,
- .ctl_ce_data_l = 0x190,
- .ctl_ce_synd = 0x18c,
- .ctl_ue_addr_l = 0x17c,
- .ctl_ue_data_l = 0x184,
- .ctl_ue_synd = 0x180,
- .ctl_source_id = 0x194,
- /* masks and shifts */
- .ecc_en_mask = BIT(24),
- .int_status_ce_mask = GENMASK(4, 3),
- .int_status_ue_mask = GENMASK(6, 5),
- .int_ack_ce_mask = GENMASK(4, 3),
- .int_ack_ue_mask = GENMASK(6, 5),
- .int_mask_master_non_ecc_mask = GENMASK(30, 7) | GENMASK(2, 0),
- .int_mask_master_global_mask = BIT(31),
- .ce_synd_mask = GENMASK(6, 0),
- .ce_synd_shift = 0,
- .ue_synd_mask = GENMASK(6, 0),
- .ue_synd_shift = 0,
- .source_id_ce_mask = GENMASK(29, 16),
- .source_id_ce_shift = 16,
- .source_id_ue_mask = GENMASK(13, 0),
- .source_id_ue_shift = 0,
- };
- static const struct npcm_platform_data npcm845_edac = {
- .chip = NPCM8XX_CHIP,
- /* memory controller registers */
- .ctl_ecc_en = 0x16c,
- .ctl_int_status = 0x228,
- .ctl_int_ack = 0x244,
- .ctl_int_mask_master = 0x220,
- .ctl_int_mask_ecc = 0x260,
- .ctl_ce_addr_l = 0x18c,
- .ctl_ce_addr_h = 0x190,
- .ctl_ce_data_l = 0x194,
- .ctl_ce_data_h = 0x198,
- .ctl_ce_synd = 0x190,
- .ctl_ue_addr_l = 0x17c,
- .ctl_ue_addr_h = 0x180,
- .ctl_ue_data_l = 0x184,
- .ctl_ue_data_h = 0x188,
- .ctl_ue_synd = 0x180,
- .ctl_source_id = 0x19c,
- .ctl_controller_busy = 0x20c,
- .ctl_xor_check_bits = 0x174,
- /* masks and shifts */
- .ecc_en_mask = GENMASK(17, 16),
- .int_status_ce_mask = GENMASK(1, 0),
- .int_status_ue_mask = GENMASK(3, 2),
- .int_ack_ce_mask = GENMASK(1, 0),
- .int_ack_ue_mask = GENMASK(3, 2),
- .int_mask_master_non_ecc_mask = GENMASK(30, 3) | GENMASK(1, 0),
- .int_mask_master_global_mask = BIT(31),
- .int_mask_ecc_non_event_mask = GENMASK(8, 4),
- .ce_addr_h_mask = GENMASK(1, 0),
- .ce_synd_mask = GENMASK(15, 8),
- .ce_synd_shift = 8,
- .ue_addr_h_mask = GENMASK(1, 0),
- .ue_synd_mask = GENMASK(15, 8),
- .ue_synd_shift = 8,
- .source_id_ce_mask = GENMASK(29, 16),
- .source_id_ce_shift = 16,
- .source_id_ue_mask = GENMASK(13, 0),
- .source_id_ue_shift = 0,
- .controller_busy_mask = BIT(0),
- .xor_check_bits_mask = GENMASK(23, 16),
- .xor_check_bits_shift = 16,
- .writeback_en_mask = BIT(24),
- .fwc_mask = BIT(8),
- };
- static const struct of_device_id npcm_edac_of_match[] = {
- {
- .compatible = "nuvoton,npcm750-memory-controller",
- .data = &npcm750_edac
- },
- {
- .compatible = "nuvoton,npcm845-memory-controller",
- .data = &npcm845_edac
- },
- {},
- };
- MODULE_DEVICE_TABLE(of, npcm_edac_of_match);
- static struct platform_driver npcm_edac_driver = {
- .driver = {
- .name = "npcm-edac",
- .of_match_table = npcm_edac_of_match,
- },
- .probe = edac_probe,
- .remove_new = edac_remove,
- };
- module_platform_driver(npcm_edac_driver);
- MODULE_AUTHOR("Medad CChien <medadyoung@gmail.com>");
- MODULE_AUTHOR("Marvin Lin <kflin@nuvoton.com>");
- MODULE_DESCRIPTION("Nuvoton NPCM EDAC Driver");
- MODULE_LICENSE("GPL");
|