0001
0002
0003
0004
0005
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
0035 #define INTR_STATUS_PAGE_VALID BIT(8)
0036
0037
0038 #define INTR_EN_PAGE_MASK BIT(8)
0039
0040
0041 #define ECC_BCH_LEVEL_MASK 0x3
0042
0043
0044 #define ELM_SYNDROME_VALID BIT(16)
0045
0046
0047 #define ECC_CORRECTABLE_MASK BIT(8)
0048 #define ECC_NB_ERRORS_MASK 0x1f
0049
0050
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
0097
0098
0099
0100
0101
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
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
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
0136
0137
0138
0139
0140
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);
0150 else
0151 reg_val &= ~BIT(index);
0152
0153 elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
0154 }
0155
0156
0157
0158
0159
0160
0161
0162
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
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
0180 val = cpu_to_be32(*(u32 *) &ecc[9]);
0181 elm_write_reg(info, offset, val);
0182
0183
0184 offset += 4;
0185 val = cpu_to_be32(*(u32 *) &ecc[5]);
0186 elm_write_reg(info, offset, val);
0187
0188
0189 offset += 4;
0190 val = cpu_to_be32(*(u32 *) &ecc[1]);
0191 elm_write_reg(info, offset, val);
0192
0193
0194 offset += 4;
0195 val = ecc[0];
0196 elm_write_reg(info, offset, val);
0197 break;
0198 case BCH4_ECC:
0199
0200 val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
0201 ((ecc[2] & 0xf) << 28);
0202 elm_write_reg(info, offset, val);
0203
0204
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
0237 ecc += info->ecc_syndrome_size;
0238 }
0239 }
0240
0241
0242
0243
0244
0245
0246
0247
0248
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
0258
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
0273
0274
0275
0276
0277
0278
0279
0280
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
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
0297 if (reg_val & ECC_CORRECTABLE_MASK) {
0298 offset = ELM_ERROR_LOCATION_0 +
0299 ERROR_LOCATION_SIZE * i;
0300
0301
0302 err_vec[i].error_count = reg_val &
0303 ECC_NB_ERRORS_MASK;
0304
0305
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
0313 offset += 4;
0314 }
0315 } else {
0316 err_vec[i].error_uncorrectable = true;
0317 }
0318
0319
0320 elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
0321
0322
0323 elm_configure_page_mode(info, i, false);
0324 }
0325 }
0326 }
0327
0328
0329
0330
0331
0332
0333
0334
0335
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
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
0349 elm_load_syndrome(info, err_vec, ecc_calc);
0350
0351
0352 elm_start_processing(info, err_vec);
0353
0354
0355 wait_for_completion(&info->elm_completion);
0356
0357
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
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
0435
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
0474
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
0483
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
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");