1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Error Location Module
5 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
8 #define DRIVER_NAME "omap-elm"
10 #include <linux/platform_device.h>
11 #include <linux/module.h>
12 #include <linux/interrupt.h>
15 #include <linux/sched.h>
16 #include <linux/pm_runtime.h>
17 #include <linux/platform_data/elm.h>
19 #define ELM_SYSCONFIG 0x010
20 #define ELM_IRQSTATUS 0x018
21 #define ELM_IRQENABLE 0x01c
22 #define ELM_LOCATION_CONFIG 0x020
23 #define ELM_PAGE_CTRL 0x080
24 #define ELM_SYNDROME_FRAGMENT_0 0x400
25 #define ELM_SYNDROME_FRAGMENT_1 0x404
26 #define ELM_SYNDROME_FRAGMENT_2 0x408
27 #define ELM_SYNDROME_FRAGMENT_3 0x40c
28 #define ELM_SYNDROME_FRAGMENT_4 0x410
29 #define ELM_SYNDROME_FRAGMENT_5 0x414
30 #define ELM_SYNDROME_FRAGMENT_6 0x418
31 #define ELM_LOCATION_STATUS 0x800
32 #define ELM_ERROR_LOCATION_0 0x880
34 /* ELM Interrupt Status Register */
35 #define INTR_STATUS_PAGE_VALID BIT(8)
37 /* ELM Interrupt Enable Register */
38 #define INTR_EN_PAGE_MASK BIT(8)
40 /* ELM Location Configuration Register */
41 #define ECC_BCH_LEVEL_MASK 0x3
44 #define ELM_SYNDROME_VALID BIT(16)
46 /* ELM_LOCATION_STATUS Register */
47 #define ECC_CORRECTABLE_MASK BIT(8)
48 #define ECC_NB_ERRORS_MASK 0x1f
50 /* ELM_ERROR_LOCATION_0-15 Registers */
51 #define ECC_ERROR_LOCATION_MASK 0x1fff
53 #define ELM_ECC_SIZE 0x7ff
55 #define SYNDROME_FRAGMENT_REG_SIZE 0x40
56 #define ERROR_LOCATION_SIZE 0x100
58 struct elm_registers
{
61 u32 elm_location_config
;
63 u32 elm_syndrome_fragment_6
[ERROR_VECTOR_MAX
];
64 u32 elm_syndrome_fragment_5
[ERROR_VECTOR_MAX
];
65 u32 elm_syndrome_fragment_4
[ERROR_VECTOR_MAX
];
66 u32 elm_syndrome_fragment_3
[ERROR_VECTOR_MAX
];
67 u32 elm_syndrome_fragment_2
[ERROR_VECTOR_MAX
];
68 u32 elm_syndrome_fragment_1
[ERROR_VECTOR_MAX
];
69 u32 elm_syndrome_fragment_0
[ERROR_VECTOR_MAX
];
74 void __iomem
*elm_base
;
75 struct completion elm_completion
;
76 struct list_head list
;
77 enum bch_ecc bch_type
;
78 struct elm_registers elm_regs
;
80 int ecc_syndrome_size
;
83 static LIST_HEAD(elm_devices
);
85 static void elm_write_reg(struct elm_info
*info
, int offset
, u32 val
)
87 writel(val
, info
->elm_base
+ offset
);
90 static u32
elm_read_reg(struct elm_info
*info
, int offset
)
92 return readl(info
->elm_base
+ offset
);
96 * elm_config - Configure ELM module
98 * @bch_type: Type of BCH ecc
100 int elm_config(struct device
*dev
, enum bch_ecc bch_type
,
101 int ecc_steps
, int ecc_step_size
, int ecc_syndrome_size
)
104 struct elm_info
*info
= dev_get_drvdata(dev
);
107 dev_err(dev
, "Unable to configure elm - device not probed?\n");
108 return -EPROBE_DEFER
;
110 /* ELM cannot detect ECC errors for chunks > 1KB */
111 if (ecc_step_size
> ((ELM_ECC_SIZE
+ 1) / 2)) {
112 dev_err(dev
, "unsupported config ecc-size=%d\n", ecc_step_size
);
115 /* ELM support 8 error syndrome process */
116 if (ecc_steps
> ERROR_VECTOR_MAX
) {
117 dev_err(dev
, "unsupported config ecc-step=%d\n", ecc_steps
);
121 reg_val
= (bch_type
& ECC_BCH_LEVEL_MASK
) | (ELM_ECC_SIZE
<< 16);
122 elm_write_reg(info
, ELM_LOCATION_CONFIG
, reg_val
);
123 info
->bch_type
= bch_type
;
124 info
->ecc_steps
= ecc_steps
;
125 info
->ecc_syndrome_size
= ecc_syndrome_size
;
129 EXPORT_SYMBOL(elm_config
);
132 * elm_configure_page_mode - Enable/Disable page mode
134 * @index: index number of syndrome fragment vector
135 * @enable: enable/disable flag for page mode
137 * Enable page mode for syndrome fragment index
139 static void elm_configure_page_mode(struct elm_info
*info
, int index
,
144 reg_val
= elm_read_reg(info
, ELM_PAGE_CTRL
);
146 reg_val
|= BIT(index
); /* enable page mode */
148 reg_val
&= ~BIT(index
); /* disable page mode */
150 elm_write_reg(info
, ELM_PAGE_CTRL
, reg_val
);
154 * elm_load_syndrome - Load ELM syndrome reg
156 * @err_vec: elm error vectors
157 * @ecc: buffer with calculated ecc
159 * Load syndrome fragment registers with calculated ecc in reverse order.
161 static void elm_load_syndrome(struct elm_info
*info
,
162 struct elm_errorvec
*err_vec
, u8
*ecc
)
167 for (i
= 0; i
< info
->ecc_steps
; i
++) {
169 /* Check error reported */
170 if (err_vec
[i
].error_reported
) {
171 elm_configure_page_mode(info
, i
, true);
172 offset
= ELM_SYNDROME_FRAGMENT_0
+
173 SYNDROME_FRAGMENT_REG_SIZE
* i
;
174 switch (info
->bch_type
) {
176 /* syndrome fragment 0 = ecc[9-12B] */
177 val
= cpu_to_be32(*(u32
*) &ecc
[9]);
178 elm_write_reg(info
, offset
, val
);
180 /* syndrome fragment 1 = ecc[5-8B] */
182 val
= cpu_to_be32(*(u32
*) &ecc
[5]);
183 elm_write_reg(info
, offset
, val
);
185 /* syndrome fragment 2 = ecc[1-4B] */
187 val
= cpu_to_be32(*(u32
*) &ecc
[1]);
188 elm_write_reg(info
, offset
, val
);
190 /* syndrome fragment 3 = ecc[0B] */
193 elm_write_reg(info
, offset
, val
);
196 /* syndrome fragment 0 = ecc[20-52b] bits */
197 val
= (cpu_to_be32(*(u32
*) &ecc
[3]) >> 4) |
198 ((ecc
[2] & 0xf) << 28);
199 elm_write_reg(info
, offset
, val
);
201 /* syndrome fragment 1 = ecc[0-20b] bits */
203 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 12;
204 elm_write_reg(info
, offset
, val
);
207 val
= cpu_to_be32(*(u32
*) &ecc
[22]);
208 elm_write_reg(info
, offset
, val
);
210 val
= cpu_to_be32(*(u32
*) &ecc
[18]);
211 elm_write_reg(info
, offset
, val
);
213 val
= cpu_to_be32(*(u32
*) &ecc
[14]);
214 elm_write_reg(info
, offset
, val
);
216 val
= cpu_to_be32(*(u32
*) &ecc
[10]);
217 elm_write_reg(info
, offset
, val
);
219 val
= cpu_to_be32(*(u32
*) &ecc
[6]);
220 elm_write_reg(info
, offset
, val
);
222 val
= cpu_to_be32(*(u32
*) &ecc
[2]);
223 elm_write_reg(info
, offset
, val
);
225 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 16;
226 elm_write_reg(info
, offset
, val
);
229 pr_err("invalid config bch_type\n");
233 /* Update ecc pointer with ecc byte size */
234 ecc
+= info
->ecc_syndrome_size
;
239 * elm_start_processing - start elm syndrome processing
241 * @err_vec: elm error vectors
243 * Set syndrome valid bit for syndrome fragment registers for which
244 * elm syndrome fragment registers are loaded. This enables elm module
245 * to start processing syndrome vectors.
247 static void elm_start_processing(struct elm_info
*info
,
248 struct elm_errorvec
*err_vec
)
254 * Set syndrome vector valid, so that ELM module
255 * will process it for vectors error is reported
257 for (i
= 0; i
< info
->ecc_steps
; i
++) {
258 if (err_vec
[i
].error_reported
) {
259 offset
= ELM_SYNDROME_FRAGMENT_6
+
260 SYNDROME_FRAGMENT_REG_SIZE
* i
;
261 reg_val
= elm_read_reg(info
, offset
);
262 reg_val
|= ELM_SYNDROME_VALID
;
263 elm_write_reg(info
, offset
, reg_val
);
269 * elm_error_correction - locate correctable error position
271 * @err_vec: elm error vectors
273 * On completion of processing by elm module, error location status
274 * register updated with correctable/uncorrectable error information.
275 * In case of correctable errors, number of errors located from
276 * elm location status register & read the positions from
277 * elm error location register.
279 static void elm_error_correction(struct elm_info
*info
,
280 struct elm_errorvec
*err_vec
)
282 int i
, j
, errors
= 0;
286 for (i
= 0; i
< info
->ecc_steps
; i
++) {
288 /* Check error reported */
289 if (err_vec
[i
].error_reported
) {
290 offset
= ELM_LOCATION_STATUS
+ ERROR_LOCATION_SIZE
* i
;
291 reg_val
= elm_read_reg(info
, offset
);
293 /* Check correctable error or not */
294 if (reg_val
& ECC_CORRECTABLE_MASK
) {
295 offset
= ELM_ERROR_LOCATION_0
+
296 ERROR_LOCATION_SIZE
* i
;
298 /* Read count of correctable errors */
299 err_vec
[i
].error_count
= reg_val
&
302 /* Update the error locations in error vector */
303 for (j
= 0; j
< err_vec
[i
].error_count
; j
++) {
305 reg_val
= elm_read_reg(info
, offset
);
306 err_vec
[i
].error_loc
[j
] = reg_val
&
307 ECC_ERROR_LOCATION_MASK
;
309 /* Update error location register */
313 errors
+= err_vec
[i
].error_count
;
315 err_vec
[i
].error_uncorrectable
= true;
318 /* Clearing interrupts for processed error vectors */
319 elm_write_reg(info
, ELM_IRQSTATUS
, BIT(i
));
321 /* Disable page mode */
322 elm_configure_page_mode(info
, i
, false);
328 * elm_decode_bch_error_page - Locate error position
329 * @dev: device pointer
330 * @ecc_calc: calculated ECC bytes from GPMC
331 * @err_vec: elm error vectors
333 * Called with one or more error reported vectors & vectors with
334 * error reported is updated in err_vec[].error_reported
336 void elm_decode_bch_error_page(struct device
*dev
, u8
*ecc_calc
,
337 struct elm_errorvec
*err_vec
)
339 struct elm_info
*info
= dev_get_drvdata(dev
);
342 /* Enable page mode interrupt */
343 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
344 elm_write_reg(info
, ELM_IRQSTATUS
, reg_val
& INTR_STATUS_PAGE_VALID
);
345 elm_write_reg(info
, ELM_IRQENABLE
, INTR_EN_PAGE_MASK
);
347 /* Load valid ecc byte to syndrome fragment register */
348 elm_load_syndrome(info
, err_vec
, ecc_calc
);
350 /* Enable syndrome processing for which syndrome fragment is updated */
351 elm_start_processing(info
, err_vec
);
353 /* Wait for ELM module to finish locating error correction */
354 wait_for_completion(&info
->elm_completion
);
356 /* Disable page mode interrupt */
357 reg_val
= elm_read_reg(info
, ELM_IRQENABLE
);
358 elm_write_reg(info
, ELM_IRQENABLE
, reg_val
& ~INTR_EN_PAGE_MASK
);
359 elm_error_correction(info
, err_vec
);
361 EXPORT_SYMBOL(elm_decode_bch_error_page
);
363 static irqreturn_t
elm_isr(int this_irq
, void *dev_id
)
366 struct elm_info
*info
= dev_id
;
368 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
370 /* All error vectors processed */
371 if (reg_val
& INTR_STATUS_PAGE_VALID
) {
372 elm_write_reg(info
, ELM_IRQSTATUS
,
373 reg_val
& INTR_STATUS_PAGE_VALID
);
374 complete(&info
->elm_completion
);
381 static int elm_probe(struct platform_device
*pdev
)
384 struct resource
*res
, *irq
;
385 struct elm_info
*info
;
387 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
391 info
->dev
= &pdev
->dev
;
393 irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
395 dev_err(&pdev
->dev
, "no irq resource defined\n");
399 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
400 info
->elm_base
= devm_ioremap_resource(&pdev
->dev
, res
);
401 if (IS_ERR(info
->elm_base
))
402 return PTR_ERR(info
->elm_base
);
404 ret
= devm_request_irq(&pdev
->dev
, irq
->start
, elm_isr
, 0,
407 dev_err(&pdev
->dev
, "failure requesting %pr\n", irq
);
411 pm_runtime_enable(&pdev
->dev
);
412 if (pm_runtime_get_sync(&pdev
->dev
) < 0) {
414 pm_runtime_disable(&pdev
->dev
);
415 dev_err(&pdev
->dev
, "can't enable clock\n");
419 init_completion(&info
->elm_completion
);
420 INIT_LIST_HEAD(&info
->list
);
421 list_add(&info
->list
, &elm_devices
);
422 platform_set_drvdata(pdev
, info
);
426 static int elm_remove(struct platform_device
*pdev
)
428 pm_runtime_put_sync(&pdev
->dev
);
429 pm_runtime_disable(&pdev
->dev
);
433 #ifdef CONFIG_PM_SLEEP
436 * saves ELM configurations to preserve them across Hardware powered-down
438 static int elm_context_save(struct elm_info
*info
)
440 struct elm_registers
*regs
= &info
->elm_regs
;
441 enum bch_ecc bch_type
= info
->bch_type
;
444 regs
->elm_irqenable
= elm_read_reg(info
, ELM_IRQENABLE
);
445 regs
->elm_sysconfig
= elm_read_reg(info
, ELM_SYSCONFIG
);
446 regs
->elm_location_config
= elm_read_reg(info
, ELM_LOCATION_CONFIG
);
447 regs
->elm_page_ctrl
= elm_read_reg(info
, ELM_PAGE_CTRL
);
448 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
449 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
452 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
453 ELM_SYNDROME_FRAGMENT_6
+ offset
);
454 regs
->elm_syndrome_fragment_5
[i
] = elm_read_reg(info
,
455 ELM_SYNDROME_FRAGMENT_5
+ offset
);
456 regs
->elm_syndrome_fragment_4
[i
] = elm_read_reg(info
,
457 ELM_SYNDROME_FRAGMENT_4
+ offset
);
460 regs
->elm_syndrome_fragment_3
[i
] = elm_read_reg(info
,
461 ELM_SYNDROME_FRAGMENT_3
+ offset
);
462 regs
->elm_syndrome_fragment_2
[i
] = elm_read_reg(info
,
463 ELM_SYNDROME_FRAGMENT_2
+ offset
);
466 regs
->elm_syndrome_fragment_1
[i
] = elm_read_reg(info
,
467 ELM_SYNDROME_FRAGMENT_1
+ offset
);
468 regs
->elm_syndrome_fragment_0
[i
] = elm_read_reg(info
,
469 ELM_SYNDROME_FRAGMENT_0
+ offset
);
474 /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
475 * to be saved for all BCH schemes*/
476 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
477 ELM_SYNDROME_FRAGMENT_6
+ offset
);
483 * elm_context_restore
484 * writes configurations saved duing power-down back into ELM registers
486 static int elm_context_restore(struct elm_info
*info
)
488 struct elm_registers
*regs
= &info
->elm_regs
;
489 enum bch_ecc bch_type
= info
->bch_type
;
492 elm_write_reg(info
, ELM_IRQENABLE
, regs
->elm_irqenable
);
493 elm_write_reg(info
, ELM_SYSCONFIG
, regs
->elm_sysconfig
);
494 elm_write_reg(info
, ELM_LOCATION_CONFIG
, regs
->elm_location_config
);
495 elm_write_reg(info
, ELM_PAGE_CTRL
, regs
->elm_page_ctrl
);
496 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
497 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
500 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
501 regs
->elm_syndrome_fragment_6
[i
]);
502 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_5
+ offset
,
503 regs
->elm_syndrome_fragment_5
[i
]);
504 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_4
+ offset
,
505 regs
->elm_syndrome_fragment_4
[i
]);
508 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_3
+ offset
,
509 regs
->elm_syndrome_fragment_3
[i
]);
510 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_2
+ offset
,
511 regs
->elm_syndrome_fragment_2
[i
]);
514 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_1
+ offset
,
515 regs
->elm_syndrome_fragment_1
[i
]);
516 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_0
+ offset
,
517 regs
->elm_syndrome_fragment_0
[i
]);
522 /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
523 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
524 regs
->elm_syndrome_fragment_6
[i
] &
530 static int elm_suspend(struct device
*dev
)
532 struct elm_info
*info
= dev_get_drvdata(dev
);
533 elm_context_save(info
);
534 pm_runtime_put_sync(dev
);
538 static int elm_resume(struct device
*dev
)
540 struct elm_info
*info
= dev_get_drvdata(dev
);
541 pm_runtime_get_sync(dev
);
542 elm_context_restore(info
);
547 static SIMPLE_DEV_PM_OPS(elm_pm_ops
, elm_suspend
, elm_resume
);
550 static const struct of_device_id elm_of_match
[] = {
551 { .compatible
= "ti,am3352-elm" },
554 MODULE_DEVICE_TABLE(of
, elm_of_match
);
557 static struct platform_driver elm_driver
= {
560 .of_match_table
= of_match_ptr(elm_of_match
),
564 .remove
= elm_remove
,
567 module_platform_driver(elm_driver
);
569 MODULE_DESCRIPTION("ELM driver for BCH error correction");
570 MODULE_AUTHOR("Texas Instruments");
571 MODULE_ALIAS("platform:" DRIVER_NAME
);
572 MODULE_LICENSE("GPL v2");