1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Error Location Module
5 * Copyright (C) 2012 Texas Instruments Incorporated - https://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
99 * @ecc_steps: ECC steps to assign to config
100 * @ecc_step_size: ECC step size to assign to config
101 * @ecc_syndrome_size: ECC syndrome size to assign to config
103 int elm_config(struct device
*dev
, enum bch_ecc bch_type
,
104 int ecc_steps
, int ecc_step_size
, int ecc_syndrome_size
)
107 struct elm_info
*info
= dev_get_drvdata(dev
);
110 dev_err(dev
, "Unable to configure elm - device not probed?\n");
111 return -EPROBE_DEFER
;
113 /* ELM cannot detect ECC errors for chunks > 1KB */
114 if (ecc_step_size
> ((ELM_ECC_SIZE
+ 1) / 2)) {
115 dev_err(dev
, "unsupported config ecc-size=%d\n", ecc_step_size
);
118 /* ELM support 8 error syndrome process */
119 if (ecc_steps
> ERROR_VECTOR_MAX
) {
120 dev_err(dev
, "unsupported config ecc-step=%d\n", ecc_steps
);
124 reg_val
= (bch_type
& ECC_BCH_LEVEL_MASK
) | (ELM_ECC_SIZE
<< 16);
125 elm_write_reg(info
, ELM_LOCATION_CONFIG
, reg_val
);
126 info
->bch_type
= bch_type
;
127 info
->ecc_steps
= ecc_steps
;
128 info
->ecc_syndrome_size
= ecc_syndrome_size
;
132 EXPORT_SYMBOL(elm_config
);
135 * elm_configure_page_mode - Enable/Disable page mode
137 * @index: index number of syndrome fragment vector
138 * @enable: enable/disable flag for page mode
140 * Enable page mode for syndrome fragment index
142 static void elm_configure_page_mode(struct elm_info
*info
, int index
,
147 reg_val
= elm_read_reg(info
, ELM_PAGE_CTRL
);
149 reg_val
|= BIT(index
); /* enable page mode */
151 reg_val
&= ~BIT(index
); /* disable page mode */
153 elm_write_reg(info
, ELM_PAGE_CTRL
, reg_val
);
157 * elm_load_syndrome - Load ELM syndrome reg
159 * @err_vec: elm error vectors
160 * @ecc: buffer with calculated ecc
162 * Load syndrome fragment registers with calculated ecc in reverse order.
164 static void elm_load_syndrome(struct elm_info
*info
,
165 struct elm_errorvec
*err_vec
, u8
*ecc
)
170 for (i
= 0; i
< info
->ecc_steps
; i
++) {
172 /* Check error reported */
173 if (err_vec
[i
].error_reported
) {
174 elm_configure_page_mode(info
, i
, true);
175 offset
= ELM_SYNDROME_FRAGMENT_0
+
176 SYNDROME_FRAGMENT_REG_SIZE
* i
;
177 switch (info
->bch_type
) {
179 /* syndrome fragment 0 = ecc[9-12B] */
180 val
= cpu_to_be32(*(u32
*) &ecc
[9]);
181 elm_write_reg(info
, offset
, val
);
183 /* syndrome fragment 1 = ecc[5-8B] */
185 val
= cpu_to_be32(*(u32
*) &ecc
[5]);
186 elm_write_reg(info
, offset
, val
);
188 /* syndrome fragment 2 = ecc[1-4B] */
190 val
= cpu_to_be32(*(u32
*) &ecc
[1]);
191 elm_write_reg(info
, offset
, val
);
193 /* syndrome fragment 3 = ecc[0B] */
196 elm_write_reg(info
, offset
, val
);
199 /* syndrome fragment 0 = ecc[20-52b] bits */
200 val
= (cpu_to_be32(*(u32
*) &ecc
[3]) >> 4) |
201 ((ecc
[2] & 0xf) << 28);
202 elm_write_reg(info
, offset
, val
);
204 /* syndrome fragment 1 = ecc[0-20b] bits */
206 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 12;
207 elm_write_reg(info
, offset
, val
);
210 val
= cpu_to_be32(*(u32
*) &ecc
[22]);
211 elm_write_reg(info
, offset
, val
);
213 val
= cpu_to_be32(*(u32
*) &ecc
[18]);
214 elm_write_reg(info
, offset
, val
);
216 val
= cpu_to_be32(*(u32
*) &ecc
[14]);
217 elm_write_reg(info
, offset
, val
);
219 val
= cpu_to_be32(*(u32
*) &ecc
[10]);
220 elm_write_reg(info
, offset
, val
);
222 val
= cpu_to_be32(*(u32
*) &ecc
[6]);
223 elm_write_reg(info
, offset
, val
);
225 val
= cpu_to_be32(*(u32
*) &ecc
[2]);
226 elm_write_reg(info
, offset
, val
);
228 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 16;
229 elm_write_reg(info
, offset
, val
);
232 pr_err("invalid config bch_type\n");
236 /* Update ecc pointer with ecc byte size */
237 ecc
+= info
->ecc_syndrome_size
;
242 * elm_start_processing - start elm syndrome processing
244 * @err_vec: elm error vectors
246 * Set syndrome valid bit for syndrome fragment registers for which
247 * elm syndrome fragment registers are loaded. This enables elm module
248 * to start processing syndrome vectors.
250 static void elm_start_processing(struct elm_info
*info
,
251 struct elm_errorvec
*err_vec
)
257 * Set syndrome vector valid, so that ELM module
258 * will process it for vectors error is reported
260 for (i
= 0; i
< info
->ecc_steps
; i
++) {
261 if (err_vec
[i
].error_reported
) {
262 offset
= ELM_SYNDROME_FRAGMENT_6
+
263 SYNDROME_FRAGMENT_REG_SIZE
* i
;
264 reg_val
= elm_read_reg(info
, offset
);
265 reg_val
|= ELM_SYNDROME_VALID
;
266 elm_write_reg(info
, offset
, reg_val
);
272 * elm_error_correction - locate correctable error position
274 * @err_vec: elm error vectors
276 * On completion of processing by elm module, error location status
277 * register updated with correctable/uncorrectable error information.
278 * In case of correctable errors, number of errors located from
279 * elm location status register & read the positions from
280 * elm error location register.
282 static void elm_error_correction(struct elm_info
*info
,
283 struct elm_errorvec
*err_vec
)
285 int i
, j
, errors
= 0;
289 for (i
= 0; i
< info
->ecc_steps
; i
++) {
291 /* Check error reported */
292 if (err_vec
[i
].error_reported
) {
293 offset
= ELM_LOCATION_STATUS
+ ERROR_LOCATION_SIZE
* i
;
294 reg_val
= elm_read_reg(info
, offset
);
296 /* Check correctable error or not */
297 if (reg_val
& ECC_CORRECTABLE_MASK
) {
298 offset
= ELM_ERROR_LOCATION_0
+
299 ERROR_LOCATION_SIZE
* i
;
301 /* Read count of correctable errors */
302 err_vec
[i
].error_count
= reg_val
&
305 /* Update the error locations in error vector */
306 for (j
= 0; j
< err_vec
[i
].error_count
; j
++) {
308 reg_val
= elm_read_reg(info
, offset
);
309 err_vec
[i
].error_loc
[j
] = reg_val
&
310 ECC_ERROR_LOCATION_MASK
;
312 /* Update error location register */
316 errors
+= err_vec
[i
].error_count
;
318 err_vec
[i
].error_uncorrectable
= true;
321 /* Clearing interrupts for processed error vectors */
322 elm_write_reg(info
, ELM_IRQSTATUS
, BIT(i
));
324 /* Disable page mode */
325 elm_configure_page_mode(info
, i
, false);
331 * elm_decode_bch_error_page - Locate error position
332 * @dev: device pointer
333 * @ecc_calc: calculated ECC bytes from GPMC
334 * @err_vec: elm error vectors
336 * Called with one or more error reported vectors & vectors with
337 * error reported is updated in err_vec[].error_reported
339 void elm_decode_bch_error_page(struct device
*dev
, u8
*ecc_calc
,
340 struct elm_errorvec
*err_vec
)
342 struct elm_info
*info
= dev_get_drvdata(dev
);
345 /* Enable page mode interrupt */
346 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
347 elm_write_reg(info
, ELM_IRQSTATUS
, reg_val
& INTR_STATUS_PAGE_VALID
);
348 elm_write_reg(info
, ELM_IRQENABLE
, INTR_EN_PAGE_MASK
);
350 /* Load valid ecc byte to syndrome fragment register */
351 elm_load_syndrome(info
, err_vec
, ecc_calc
);
353 /* Enable syndrome processing for which syndrome fragment is updated */
354 elm_start_processing(info
, err_vec
);
356 /* Wait for ELM module to finish locating error correction */
357 wait_for_completion(&info
->elm_completion
);
359 /* Disable page mode interrupt */
360 reg_val
= elm_read_reg(info
, ELM_IRQENABLE
);
361 elm_write_reg(info
, ELM_IRQENABLE
, reg_val
& ~INTR_EN_PAGE_MASK
);
362 elm_error_correction(info
, err_vec
);
364 EXPORT_SYMBOL(elm_decode_bch_error_page
);
366 static irqreturn_t
elm_isr(int this_irq
, void *dev_id
)
369 struct elm_info
*info
= dev_id
;
371 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
373 /* All error vectors processed */
374 if (reg_val
& INTR_STATUS_PAGE_VALID
) {
375 elm_write_reg(info
, ELM_IRQSTATUS
,
376 reg_val
& INTR_STATUS_PAGE_VALID
);
377 complete(&info
->elm_completion
);
384 static int elm_probe(struct platform_device
*pdev
)
387 struct resource
*res
, *irq
;
388 struct elm_info
*info
;
390 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
394 info
->dev
= &pdev
->dev
;
396 irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
398 dev_err(&pdev
->dev
, "no irq resource defined\n");
402 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
403 info
->elm_base
= devm_ioremap_resource(&pdev
->dev
, res
);
404 if (IS_ERR(info
->elm_base
))
405 return PTR_ERR(info
->elm_base
);
407 ret
= devm_request_irq(&pdev
->dev
, irq
->start
, elm_isr
, 0,
410 dev_err(&pdev
->dev
, "failure requesting %pr\n", irq
);
414 pm_runtime_enable(&pdev
->dev
);
415 if (pm_runtime_get_sync(&pdev
->dev
) < 0) {
417 pm_runtime_put_sync(&pdev
->dev
);
418 pm_runtime_disable(&pdev
->dev
);
419 dev_err(&pdev
->dev
, "can't enable clock\n");
423 init_completion(&info
->elm_completion
);
424 INIT_LIST_HEAD(&info
->list
);
425 list_add(&info
->list
, &elm_devices
);
426 platform_set_drvdata(pdev
, info
);
430 static int elm_remove(struct platform_device
*pdev
)
432 pm_runtime_put_sync(&pdev
->dev
);
433 pm_runtime_disable(&pdev
->dev
);
437 #ifdef CONFIG_PM_SLEEP
440 * saves ELM configurations to preserve them across Hardware powered-down
442 static int elm_context_save(struct elm_info
*info
)
444 struct elm_registers
*regs
= &info
->elm_regs
;
445 enum bch_ecc bch_type
= info
->bch_type
;
448 regs
->elm_irqenable
= elm_read_reg(info
, ELM_IRQENABLE
);
449 regs
->elm_sysconfig
= elm_read_reg(info
, ELM_SYSCONFIG
);
450 regs
->elm_location_config
= elm_read_reg(info
, ELM_LOCATION_CONFIG
);
451 regs
->elm_page_ctrl
= elm_read_reg(info
, ELM_PAGE_CTRL
);
452 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
453 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
456 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
457 ELM_SYNDROME_FRAGMENT_6
+ offset
);
458 regs
->elm_syndrome_fragment_5
[i
] = elm_read_reg(info
,
459 ELM_SYNDROME_FRAGMENT_5
+ offset
);
460 regs
->elm_syndrome_fragment_4
[i
] = elm_read_reg(info
,
461 ELM_SYNDROME_FRAGMENT_4
+ offset
);
464 regs
->elm_syndrome_fragment_3
[i
] = elm_read_reg(info
,
465 ELM_SYNDROME_FRAGMENT_3
+ offset
);
466 regs
->elm_syndrome_fragment_2
[i
] = elm_read_reg(info
,
467 ELM_SYNDROME_FRAGMENT_2
+ offset
);
470 regs
->elm_syndrome_fragment_1
[i
] = elm_read_reg(info
,
471 ELM_SYNDROME_FRAGMENT_1
+ offset
);
472 regs
->elm_syndrome_fragment_0
[i
] = elm_read_reg(info
,
473 ELM_SYNDROME_FRAGMENT_0
+ offset
);
478 /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
479 * to be saved for all BCH schemes*/
480 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
481 ELM_SYNDROME_FRAGMENT_6
+ offset
);
487 * elm_context_restore
488 * writes configurations saved duing power-down back into ELM registers
490 static int elm_context_restore(struct elm_info
*info
)
492 struct elm_registers
*regs
= &info
->elm_regs
;
493 enum bch_ecc bch_type
= info
->bch_type
;
496 elm_write_reg(info
, ELM_IRQENABLE
, regs
->elm_irqenable
);
497 elm_write_reg(info
, ELM_SYSCONFIG
, regs
->elm_sysconfig
);
498 elm_write_reg(info
, ELM_LOCATION_CONFIG
, regs
->elm_location_config
);
499 elm_write_reg(info
, ELM_PAGE_CTRL
, regs
->elm_page_ctrl
);
500 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
501 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
504 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
505 regs
->elm_syndrome_fragment_6
[i
]);
506 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_5
+ offset
,
507 regs
->elm_syndrome_fragment_5
[i
]);
508 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_4
+ offset
,
509 regs
->elm_syndrome_fragment_4
[i
]);
512 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_3
+ offset
,
513 regs
->elm_syndrome_fragment_3
[i
]);
514 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_2
+ offset
,
515 regs
->elm_syndrome_fragment_2
[i
]);
518 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_1
+ offset
,
519 regs
->elm_syndrome_fragment_1
[i
]);
520 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_0
+ offset
,
521 regs
->elm_syndrome_fragment_0
[i
]);
526 /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
527 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
528 regs
->elm_syndrome_fragment_6
[i
] &
534 static int elm_suspend(struct device
*dev
)
536 struct elm_info
*info
= dev_get_drvdata(dev
);
537 elm_context_save(info
);
538 pm_runtime_put_sync(dev
);
542 static int elm_resume(struct device
*dev
)
544 struct elm_info
*info
= dev_get_drvdata(dev
);
545 pm_runtime_get_sync(dev
);
546 elm_context_restore(info
);
551 static SIMPLE_DEV_PM_OPS(elm_pm_ops
, elm_suspend
, elm_resume
);
554 static const struct of_device_id elm_of_match
[] = {
555 { .compatible
= "ti,am3352-elm" },
558 MODULE_DEVICE_TABLE(of
, elm_of_match
);
561 static struct platform_driver elm_driver
= {
564 .of_match_table
= of_match_ptr(elm_of_match
),
568 .remove
= elm_remove
,
571 module_platform_driver(elm_driver
);
573 MODULE_DESCRIPTION("ELM driver for BCH error correction");
574 MODULE_AUTHOR("Texas Instruments");
575 MODULE_ALIAS("platform:" DRIVER_NAME
);
576 MODULE_LICENSE("GPL v2");