Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Error Location Module
0004  *
0005  * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/
0006  */
0007 
0008 #define DRIVER_NAME "omap-elm"
0009 
0010 #include <linux/platform_device.h>
0011 #include <linux/module.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/io.h>
0014 #include <linux/of.h>
0015 #include <linux/sched.h>
0016 #include <linux/pm_runtime.h>
0017 #include <linux/platform_data/elm.h>
0018 
0019 #define ELM_SYSCONFIG           0x010
0020 #define ELM_IRQSTATUS           0x018
0021 #define ELM_IRQENABLE           0x01c
0022 #define ELM_LOCATION_CONFIG     0x020
0023 #define ELM_PAGE_CTRL           0x080
0024 #define ELM_SYNDROME_FRAGMENT_0     0x400
0025 #define ELM_SYNDROME_FRAGMENT_1     0x404
0026 #define ELM_SYNDROME_FRAGMENT_2     0x408
0027 #define ELM_SYNDROME_FRAGMENT_3     0x40c
0028 #define ELM_SYNDROME_FRAGMENT_4     0x410
0029 #define ELM_SYNDROME_FRAGMENT_5     0x414
0030 #define ELM_SYNDROME_FRAGMENT_6     0x418
0031 #define ELM_LOCATION_STATUS     0x800
0032 #define ELM_ERROR_LOCATION_0        0x880
0033 
0034 /* ELM Interrupt Status Register */
0035 #define INTR_STATUS_PAGE_VALID      BIT(8)
0036 
0037 /* ELM Interrupt Enable Register */
0038 #define INTR_EN_PAGE_MASK       BIT(8)
0039 
0040 /* ELM Location Configuration Register */
0041 #define ECC_BCH_LEVEL_MASK      0x3
0042 
0043 /* ELM syndrome */
0044 #define ELM_SYNDROME_VALID      BIT(16)
0045 
0046 /* ELM_LOCATION_STATUS Register */
0047 #define ECC_CORRECTABLE_MASK        BIT(8)
0048 #define ECC_NB_ERRORS_MASK      0x1f
0049 
0050 /* ELM_ERROR_LOCATION_0-15 Registers */
0051 #define ECC_ERROR_LOCATION_MASK     0x1fff
0052 
0053 #define ELM_ECC_SIZE            0x7ff
0054 
0055 #define SYNDROME_FRAGMENT_REG_SIZE  0x40
0056 #define ERROR_LOCATION_SIZE     0x100
0057 
0058 struct elm_registers {
0059     u32 elm_irqenable;
0060     u32 elm_sysconfig;
0061     u32 elm_location_config;
0062     u32 elm_page_ctrl;
0063     u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX];
0064     u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX];
0065     u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX];
0066     u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX];
0067     u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX];
0068     u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX];
0069     u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX];
0070 };
0071 
0072 struct elm_info {
0073     struct device *dev;
0074     void __iomem *elm_base;
0075     struct completion elm_completion;
0076     struct list_head list;
0077     enum bch_ecc bch_type;
0078     struct elm_registers elm_regs;
0079     int ecc_steps;
0080     int ecc_syndrome_size;
0081 };
0082 
0083 static LIST_HEAD(elm_devices);
0084 
0085 static void elm_write_reg(struct elm_info *info, int offset, u32 val)
0086 {
0087     writel(val, info->elm_base + offset);
0088 }
0089 
0090 static u32 elm_read_reg(struct elm_info *info, int offset)
0091 {
0092     return readl(info->elm_base + offset);
0093 }
0094 
0095 /**
0096  * elm_config - Configure ELM module
0097  * @dev:    ELM device
0098  * @bch_type:   Type of BCH ecc
0099  * @ecc_steps:  ECC steps to assign to config
0100  * @ecc_step_size:  ECC step size to assign to config
0101  * @ecc_syndrome_size:  ECC syndrome size to assign to config
0102  */
0103 int elm_config(struct device *dev, enum bch_ecc bch_type,
0104     int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
0105 {
0106     u32 reg_val;
0107     struct elm_info *info = dev_get_drvdata(dev);
0108 
0109     if (!info) {
0110         dev_err(dev, "Unable to configure elm - device not probed?\n");
0111         return -EPROBE_DEFER;
0112     }
0113     /* ELM cannot detect ECC errors for chunks > 1KB */
0114     if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
0115         dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);
0116         return -EINVAL;
0117     }
0118     /* ELM support 8 error syndrome process */
0119     if (ecc_steps > ERROR_VECTOR_MAX && ecc_steps % ERROR_VECTOR_MAX) {
0120         dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
0121         return -EINVAL;
0122     }
0123 
0124     reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
0125     elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
0126     info->bch_type      = bch_type;
0127     info->ecc_steps     = ecc_steps;
0128     info->ecc_syndrome_size = ecc_syndrome_size;
0129 
0130     return 0;
0131 }
0132 EXPORT_SYMBOL(elm_config);
0133 
0134 /**
0135  * elm_configure_page_mode - Enable/Disable page mode
0136  * @info:   elm info
0137  * @index:  index number of syndrome fragment vector
0138  * @enable: enable/disable flag for page mode
0139  *
0140  * Enable page mode for syndrome fragment index
0141  */
0142 static void elm_configure_page_mode(struct elm_info *info, int index,
0143         bool enable)
0144 {
0145     u32 reg_val;
0146 
0147     reg_val = elm_read_reg(info, ELM_PAGE_CTRL);
0148     if (enable)
0149         reg_val |= BIT(index);  /* enable page mode */
0150     else
0151         reg_val &= ~BIT(index); /* disable page mode */
0152 
0153     elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
0154 }
0155 
0156 /**
0157  * elm_load_syndrome - Load ELM syndrome reg
0158  * @info:   elm info
0159  * @err_vec:    elm error vectors
0160  * @ecc:    buffer with calculated ecc
0161  *
0162  * Load syndrome fragment registers with calculated ecc in reverse order.
0163  */
0164 static void elm_load_syndrome(struct elm_info *info,
0165         struct elm_errorvec *err_vec, u8 *ecc)
0166 {
0167     int i, offset;
0168     u32 val;
0169 
0170     for (i = 0; i < info->ecc_steps; i++) {
0171 
0172         /* Check error reported */
0173         if (err_vec[i].error_reported) {
0174             elm_configure_page_mode(info, i, true);
0175             offset = ELM_SYNDROME_FRAGMENT_0 +
0176                 SYNDROME_FRAGMENT_REG_SIZE * i;
0177             switch (info->bch_type) {
0178             case BCH8_ECC:
0179                 /* syndrome fragment 0 = ecc[9-12B] */
0180                 val = cpu_to_be32(*(u32 *) &ecc[9]);
0181                 elm_write_reg(info, offset, val);
0182 
0183                 /* syndrome fragment 1 = ecc[5-8B] */
0184                 offset += 4;
0185                 val = cpu_to_be32(*(u32 *) &ecc[5]);
0186                 elm_write_reg(info, offset, val);
0187 
0188                 /* syndrome fragment 2 = ecc[1-4B] */
0189                 offset += 4;
0190                 val = cpu_to_be32(*(u32 *) &ecc[1]);
0191                 elm_write_reg(info, offset, val);
0192 
0193                 /* syndrome fragment 3 = ecc[0B] */
0194                 offset += 4;
0195                 val = ecc[0];
0196                 elm_write_reg(info, offset, val);
0197                 break;
0198             case BCH4_ECC:
0199                 /* syndrome fragment 0 = ecc[20-52b] bits */
0200                 val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
0201                     ((ecc[2] & 0xf) << 28);
0202                 elm_write_reg(info, offset, val);
0203 
0204                 /* syndrome fragment 1 = ecc[0-20b] bits */
0205                 offset += 4;
0206                 val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
0207                 elm_write_reg(info, offset, val);
0208                 break;
0209             case BCH16_ECC:
0210                 val = cpu_to_be32(*(u32 *) &ecc[22]);
0211                 elm_write_reg(info, offset, val);
0212                 offset += 4;
0213                 val = cpu_to_be32(*(u32 *) &ecc[18]);
0214                 elm_write_reg(info, offset, val);
0215                 offset += 4;
0216                 val = cpu_to_be32(*(u32 *) &ecc[14]);
0217                 elm_write_reg(info, offset, val);
0218                 offset += 4;
0219                 val = cpu_to_be32(*(u32 *) &ecc[10]);
0220                 elm_write_reg(info, offset, val);
0221                 offset += 4;
0222                 val = cpu_to_be32(*(u32 *) &ecc[6]);
0223                 elm_write_reg(info, offset, val);
0224                 offset += 4;
0225                 val = cpu_to_be32(*(u32 *) &ecc[2]);
0226                 elm_write_reg(info, offset, val);
0227                 offset += 4;
0228                 val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16;
0229                 elm_write_reg(info, offset, val);
0230                 break;
0231             default:
0232                 pr_err("invalid config bch_type\n");
0233             }
0234         }
0235 
0236         /* Update ecc pointer with ecc byte size */
0237         ecc += info->ecc_syndrome_size;
0238     }
0239 }
0240 
0241 /**
0242  * elm_start_processing - start elm syndrome processing
0243  * @info:   elm info
0244  * @err_vec:    elm error vectors
0245  *
0246  * Set syndrome valid bit for syndrome fragment registers for which
0247  * elm syndrome fragment registers are loaded. This enables elm module
0248  * to start processing syndrome vectors.
0249  */
0250 static void elm_start_processing(struct elm_info *info,
0251         struct elm_errorvec *err_vec)
0252 {
0253     int i, offset;
0254     u32 reg_val;
0255 
0256     /*
0257      * Set syndrome vector valid, so that ELM module
0258      * will process it for vectors error is reported
0259      */
0260     for (i = 0; i < info->ecc_steps; i++) {
0261         if (err_vec[i].error_reported) {
0262             offset = ELM_SYNDROME_FRAGMENT_6 +
0263                 SYNDROME_FRAGMENT_REG_SIZE * i;
0264             reg_val = elm_read_reg(info, offset);
0265             reg_val |= ELM_SYNDROME_VALID;
0266             elm_write_reg(info, offset, reg_val);
0267         }
0268     }
0269 }
0270 
0271 /**
0272  * elm_error_correction - locate correctable error position
0273  * @info:   elm info
0274  * @err_vec:    elm error vectors
0275  *
0276  * On completion of processing by elm module, error location status
0277  * register updated with correctable/uncorrectable error information.
0278  * In case of correctable errors, number of errors located from
0279  * elm location status register & read the positions from
0280  * elm error location register.
0281  */
0282 static void elm_error_correction(struct elm_info *info,
0283         struct elm_errorvec *err_vec)
0284 {
0285     int i, j;
0286     int offset;
0287     u32 reg_val;
0288 
0289     for (i = 0; i < info->ecc_steps; i++) {
0290 
0291         /* Check error reported */
0292         if (err_vec[i].error_reported) {
0293             offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;
0294             reg_val = elm_read_reg(info, offset);
0295 
0296             /* Check correctable error or not */
0297             if (reg_val & ECC_CORRECTABLE_MASK) {
0298                 offset = ELM_ERROR_LOCATION_0 +
0299                     ERROR_LOCATION_SIZE * i;
0300 
0301                 /* Read count of correctable errors */
0302                 err_vec[i].error_count = reg_val &
0303                     ECC_NB_ERRORS_MASK;
0304 
0305                 /* Update the error locations in error vector */
0306                 for (j = 0; j < err_vec[i].error_count; j++) {
0307 
0308                     reg_val = elm_read_reg(info, offset);
0309                     err_vec[i].error_loc[j] = reg_val &
0310                         ECC_ERROR_LOCATION_MASK;
0311 
0312                     /* Update error location register */
0313                     offset += 4;
0314                 }
0315             } else {
0316                 err_vec[i].error_uncorrectable = true;
0317             }
0318 
0319             /* Clearing interrupts for processed error vectors */
0320             elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
0321 
0322             /* Disable page mode */
0323             elm_configure_page_mode(info, i, false);
0324         }
0325     }
0326 }
0327 
0328 /**
0329  * elm_decode_bch_error_page - Locate error position
0330  * @dev:    device pointer
0331  * @ecc_calc:   calculated ECC bytes from GPMC
0332  * @err_vec:    elm error vectors
0333  *
0334  * Called with one or more error reported vectors & vectors with
0335  * error reported is updated in err_vec[].error_reported
0336  */
0337 void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
0338         struct elm_errorvec *err_vec)
0339 {
0340     struct elm_info *info = dev_get_drvdata(dev);
0341     u32 reg_val;
0342 
0343     /* Enable page mode interrupt */
0344     reg_val = elm_read_reg(info, ELM_IRQSTATUS);
0345     elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);
0346     elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);
0347 
0348     /* Load valid ecc byte to syndrome fragment register */
0349     elm_load_syndrome(info, err_vec, ecc_calc);
0350 
0351     /* Enable syndrome processing for which syndrome fragment is updated */
0352     elm_start_processing(info, err_vec);
0353 
0354     /* Wait for ELM module to finish locating error correction */
0355     wait_for_completion(&info->elm_completion);
0356 
0357     /* Disable page mode interrupt */
0358     reg_val = elm_read_reg(info, ELM_IRQENABLE);
0359     elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);
0360     elm_error_correction(info, err_vec);
0361 }
0362 EXPORT_SYMBOL(elm_decode_bch_error_page);
0363 
0364 static irqreturn_t elm_isr(int this_irq, void *dev_id)
0365 {
0366     u32 reg_val;
0367     struct elm_info *info = dev_id;
0368 
0369     reg_val = elm_read_reg(info, ELM_IRQSTATUS);
0370 
0371     /* All error vectors processed */
0372     if (reg_val & INTR_STATUS_PAGE_VALID) {
0373         elm_write_reg(info, ELM_IRQSTATUS,
0374                 reg_val & INTR_STATUS_PAGE_VALID);
0375         complete(&info->elm_completion);
0376         return IRQ_HANDLED;
0377     }
0378 
0379     return IRQ_NONE;
0380 }
0381 
0382 static int elm_probe(struct platform_device *pdev)
0383 {
0384     int ret = 0;
0385     struct elm_info *info;
0386     int irq;
0387 
0388     info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
0389     if (!info)
0390         return -ENOMEM;
0391 
0392     info->dev = &pdev->dev;
0393 
0394     irq = platform_get_irq(pdev, 0);
0395     if (irq < 0)
0396         return irq;
0397 
0398     info->elm_base = devm_platform_ioremap_resource(pdev, 0);
0399     if (IS_ERR(info->elm_base))
0400         return PTR_ERR(info->elm_base);
0401 
0402     ret = devm_request_irq(&pdev->dev, irq, elm_isr, 0,
0403                    pdev->name, info);
0404     if (ret) {
0405         dev_err(&pdev->dev, "failure requesting %d\n", irq);
0406         return ret;
0407     }
0408 
0409     pm_runtime_enable(&pdev->dev);
0410     if (pm_runtime_get_sync(&pdev->dev) < 0) {
0411         ret = -EINVAL;
0412         pm_runtime_put_sync(&pdev->dev);
0413         pm_runtime_disable(&pdev->dev);
0414         dev_err(&pdev->dev, "can't enable clock\n");
0415         return ret;
0416     }
0417 
0418     init_completion(&info->elm_completion);
0419     INIT_LIST_HEAD(&info->list);
0420     list_add(&info->list, &elm_devices);
0421     platform_set_drvdata(pdev, info);
0422     return ret;
0423 }
0424 
0425 static int elm_remove(struct platform_device *pdev)
0426 {
0427     pm_runtime_put_sync(&pdev->dev);
0428     pm_runtime_disable(&pdev->dev);
0429     return 0;
0430 }
0431 
0432 #ifdef CONFIG_PM_SLEEP
0433 /*
0434  * elm_context_save
0435  * saves ELM configurations to preserve them across Hardware powered-down
0436  */
0437 static int elm_context_save(struct elm_info *info)
0438 {
0439     struct elm_registers *regs = &info->elm_regs;
0440     enum bch_ecc bch_type = info->bch_type;
0441     u32 offset = 0, i;
0442 
0443     regs->elm_irqenable       = elm_read_reg(info, ELM_IRQENABLE);
0444     regs->elm_sysconfig       = elm_read_reg(info, ELM_SYSCONFIG);
0445     regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG);
0446     regs->elm_page_ctrl       = elm_read_reg(info, ELM_PAGE_CTRL);
0447     for (i = 0; i < ERROR_VECTOR_MAX; i++) {
0448         offset = i * SYNDROME_FRAGMENT_REG_SIZE;
0449         switch (bch_type) {
0450         case BCH16_ECC:
0451             regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
0452                     ELM_SYNDROME_FRAGMENT_6 + offset);
0453             regs->elm_syndrome_fragment_5[i] = elm_read_reg(info,
0454                     ELM_SYNDROME_FRAGMENT_5 + offset);
0455             regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,
0456                     ELM_SYNDROME_FRAGMENT_4 + offset);
0457             fallthrough;
0458         case BCH8_ECC:
0459             regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
0460                     ELM_SYNDROME_FRAGMENT_3 + offset);
0461             regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,
0462                     ELM_SYNDROME_FRAGMENT_2 + offset);
0463             fallthrough;
0464         case BCH4_ECC:
0465             regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,
0466                     ELM_SYNDROME_FRAGMENT_1 + offset);
0467             regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,
0468                     ELM_SYNDROME_FRAGMENT_0 + offset);
0469             break;
0470         default:
0471             return -EINVAL;
0472         }
0473         /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
0474          * to be saved for all BCH schemes*/
0475         regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
0476                     ELM_SYNDROME_FRAGMENT_6 + offset);
0477     }
0478     return 0;
0479 }
0480 
0481 /*
0482  * elm_context_restore
0483  * writes configurations saved duing power-down back into ELM registers
0484  */
0485 static int elm_context_restore(struct elm_info *info)
0486 {
0487     struct elm_registers *regs = &info->elm_regs;
0488     enum bch_ecc bch_type = info->bch_type;
0489     u32 offset = 0, i;
0490 
0491     elm_write_reg(info, ELM_IRQENABLE,   regs->elm_irqenable);
0492     elm_write_reg(info, ELM_SYSCONFIG,   regs->elm_sysconfig);
0493     elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config);
0494     elm_write_reg(info, ELM_PAGE_CTRL,   regs->elm_page_ctrl);
0495     for (i = 0; i < ERROR_VECTOR_MAX; i++) {
0496         offset = i * SYNDROME_FRAGMENT_REG_SIZE;
0497         switch (bch_type) {
0498         case BCH16_ECC:
0499             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
0500                     regs->elm_syndrome_fragment_6[i]);
0501             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset,
0502                     regs->elm_syndrome_fragment_5[i]);
0503             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,
0504                     regs->elm_syndrome_fragment_4[i]);
0505             fallthrough;
0506         case BCH8_ECC:
0507             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
0508                     regs->elm_syndrome_fragment_3[i]);
0509             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,
0510                     regs->elm_syndrome_fragment_2[i]);
0511             fallthrough;
0512         case BCH4_ECC:
0513             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,
0514                     regs->elm_syndrome_fragment_1[i]);
0515             elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,
0516                     regs->elm_syndrome_fragment_0[i]);
0517             break;
0518         default:
0519             return -EINVAL;
0520         }
0521         /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
0522         elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
0523                     regs->elm_syndrome_fragment_6[i] &
0524                              ELM_SYNDROME_VALID);
0525     }
0526     return 0;
0527 }
0528 
0529 static int elm_suspend(struct device *dev)
0530 {
0531     struct elm_info *info = dev_get_drvdata(dev);
0532     elm_context_save(info);
0533     pm_runtime_put_sync(dev);
0534     return 0;
0535 }
0536 
0537 static int elm_resume(struct device *dev)
0538 {
0539     struct elm_info *info = dev_get_drvdata(dev);
0540     pm_runtime_get_sync(dev);
0541     elm_context_restore(info);
0542     return 0;
0543 }
0544 #endif
0545 
0546 static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
0547 
0548 #ifdef CONFIG_OF
0549 static const struct of_device_id elm_of_match[] = {
0550     { .compatible = "ti,am3352-elm" },
0551     { .compatible = "ti,am64-elm" },
0552     {},
0553 };
0554 MODULE_DEVICE_TABLE(of, elm_of_match);
0555 #endif
0556 
0557 static struct platform_driver elm_driver = {
0558     .driver = {
0559         .name   = DRIVER_NAME,
0560         .of_match_table = of_match_ptr(elm_of_match),
0561         .pm = &elm_pm_ops,
0562     },
0563     .probe  = elm_probe,
0564     .remove = elm_remove,
0565 };
0566 
0567 module_platform_driver(elm_driver);
0568 
0569 MODULE_DESCRIPTION("ELM driver for BCH error correction");
0570 MODULE_AUTHOR("Texas Instruments");
0571 MODULE_ALIAS("platform:" DRIVER_NAME);
0572 MODULE_LICENSE("GPL v2");