2 * Error Location Module
4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 #define DRIVER_NAME "omap-elm"
20 #include <linux/platform_device.h>
21 #include <linux/module.h>
22 #include <linux/interrupt.h>
25 #include <linux/sched.h>
26 #include <linux/pm_runtime.h>
27 #include <linux/platform_data/elm.h>
29 #define ELM_SYSCONFIG 0x010
30 #define ELM_IRQSTATUS 0x018
31 #define ELM_IRQENABLE 0x01c
32 #define ELM_LOCATION_CONFIG 0x020
33 #define ELM_PAGE_CTRL 0x080
34 #define ELM_SYNDROME_FRAGMENT_0 0x400
35 #define ELM_SYNDROME_FRAGMENT_1 0x404
36 #define ELM_SYNDROME_FRAGMENT_2 0x408
37 #define ELM_SYNDROME_FRAGMENT_3 0x40c
38 #define ELM_SYNDROME_FRAGMENT_4 0x410
39 #define ELM_SYNDROME_FRAGMENT_5 0x414
40 #define ELM_SYNDROME_FRAGMENT_6 0x418
41 #define ELM_LOCATION_STATUS 0x800
42 #define ELM_ERROR_LOCATION_0 0x880
44 /* ELM Interrupt Status Register */
45 #define INTR_STATUS_PAGE_VALID BIT(8)
47 /* ELM Interrupt Enable Register */
48 #define INTR_EN_PAGE_MASK BIT(8)
50 /* ELM Location Configuration Register */
51 #define ECC_BCH_LEVEL_MASK 0x3
54 #define ELM_SYNDROME_VALID BIT(16)
56 /* ELM_LOCATION_STATUS Register */
57 #define ECC_CORRECTABLE_MASK BIT(8)
58 #define ECC_NB_ERRORS_MASK 0x1f
60 /* ELM_ERROR_LOCATION_0-15 Registers */
61 #define ECC_ERROR_LOCATION_MASK 0x1fff
63 #define ELM_ECC_SIZE 0x7ff
65 #define SYNDROME_FRAGMENT_REG_SIZE 0x40
66 #define ERROR_LOCATION_SIZE 0x100
68 struct elm_registers
{
71 u32 elm_location_config
;
73 u32 elm_syndrome_fragment_6
[ERROR_VECTOR_MAX
];
74 u32 elm_syndrome_fragment_5
[ERROR_VECTOR_MAX
];
75 u32 elm_syndrome_fragment_4
[ERROR_VECTOR_MAX
];
76 u32 elm_syndrome_fragment_3
[ERROR_VECTOR_MAX
];
77 u32 elm_syndrome_fragment_2
[ERROR_VECTOR_MAX
];
78 u32 elm_syndrome_fragment_1
[ERROR_VECTOR_MAX
];
79 u32 elm_syndrome_fragment_0
[ERROR_VECTOR_MAX
];
84 void __iomem
*elm_base
;
85 struct completion elm_completion
;
86 struct list_head list
;
87 enum bch_ecc bch_type
;
88 struct elm_registers elm_regs
;
90 int ecc_syndrome_size
;
93 static LIST_HEAD(elm_devices
);
95 static void elm_write_reg(struct elm_info
*info
, int offset
, u32 val
)
97 writel(val
, info
->elm_base
+ offset
);
100 static u32
elm_read_reg(struct elm_info
*info
, int offset
)
102 return readl(info
->elm_base
+ offset
);
106 * elm_config - Configure ELM module
108 * @bch_type: Type of BCH ecc
110 int elm_config(struct device
*dev
, enum bch_ecc bch_type
,
111 int ecc_steps
, int ecc_step_size
, int ecc_syndrome_size
)
114 struct elm_info
*info
= dev_get_drvdata(dev
);
117 dev_err(dev
, "Unable to configure elm - device not probed?\n");
118 return -EPROBE_DEFER
;
120 /* ELM cannot detect ECC errors for chunks > 1KB */
121 if (ecc_step_size
> ((ELM_ECC_SIZE
+ 1) / 2)) {
122 dev_err(dev
, "unsupported config ecc-size=%d\n", ecc_step_size
);
125 /* ELM support 8 error syndrome process */
126 if (ecc_steps
> ERROR_VECTOR_MAX
) {
127 dev_err(dev
, "unsupported config ecc-step=%d\n", ecc_steps
);
131 reg_val
= (bch_type
& ECC_BCH_LEVEL_MASK
) | (ELM_ECC_SIZE
<< 16);
132 elm_write_reg(info
, ELM_LOCATION_CONFIG
, reg_val
);
133 info
->bch_type
= bch_type
;
134 info
->ecc_steps
= ecc_steps
;
135 info
->ecc_syndrome_size
= ecc_syndrome_size
;
139 EXPORT_SYMBOL(elm_config
);
142 * elm_configure_page_mode - Enable/Disable page mode
144 * @index: index number of syndrome fragment vector
145 * @enable: enable/disable flag for page mode
147 * Enable page mode for syndrome fragment index
149 static void elm_configure_page_mode(struct elm_info
*info
, int index
,
154 reg_val
= elm_read_reg(info
, ELM_PAGE_CTRL
);
156 reg_val
|= BIT(index
); /* enable page mode */
158 reg_val
&= ~BIT(index
); /* disable page mode */
160 elm_write_reg(info
, ELM_PAGE_CTRL
, reg_val
);
164 * elm_load_syndrome - Load ELM syndrome reg
166 * @err_vec: elm error vectors
167 * @ecc: buffer with calculated ecc
169 * Load syndrome fragment registers with calculated ecc in reverse order.
171 static void elm_load_syndrome(struct elm_info
*info
,
172 struct elm_errorvec
*err_vec
, u8
*ecc
)
177 for (i
= 0; i
< info
->ecc_steps
; i
++) {
179 /* Check error reported */
180 if (err_vec
[i
].error_reported
) {
181 elm_configure_page_mode(info
, i
, true);
182 offset
= ELM_SYNDROME_FRAGMENT_0
+
183 SYNDROME_FRAGMENT_REG_SIZE
* i
;
184 switch (info
->bch_type
) {
186 /* syndrome fragment 0 = ecc[9-12B] */
187 val
= cpu_to_be32(*(u32
*) &ecc
[9]);
188 elm_write_reg(info
, offset
, val
);
190 /* syndrome fragment 1 = ecc[5-8B] */
192 val
= cpu_to_be32(*(u32
*) &ecc
[5]);
193 elm_write_reg(info
, offset
, val
);
195 /* syndrome fragment 2 = ecc[1-4B] */
197 val
= cpu_to_be32(*(u32
*) &ecc
[1]);
198 elm_write_reg(info
, offset
, val
);
200 /* syndrome fragment 3 = ecc[0B] */
203 elm_write_reg(info
, offset
, val
);
206 /* syndrome fragment 0 = ecc[20-52b] bits */
207 val
= (cpu_to_be32(*(u32
*) &ecc
[3]) >> 4) |
208 ((ecc
[2] & 0xf) << 28);
209 elm_write_reg(info
, offset
, val
);
211 /* syndrome fragment 1 = ecc[0-20b] bits */
213 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 12;
214 elm_write_reg(info
, offset
, val
);
217 val
= cpu_to_be32(*(u32
*) &ecc
[22]);
218 elm_write_reg(info
, offset
, val
);
220 val
= cpu_to_be32(*(u32
*) &ecc
[18]);
221 elm_write_reg(info
, offset
, val
);
223 val
= cpu_to_be32(*(u32
*) &ecc
[14]);
224 elm_write_reg(info
, offset
, val
);
226 val
= cpu_to_be32(*(u32
*) &ecc
[10]);
227 elm_write_reg(info
, offset
, val
);
229 val
= cpu_to_be32(*(u32
*) &ecc
[6]);
230 elm_write_reg(info
, offset
, val
);
232 val
= cpu_to_be32(*(u32
*) &ecc
[2]);
233 elm_write_reg(info
, offset
, val
);
235 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 16;
236 elm_write_reg(info
, offset
, val
);
239 pr_err("invalid config bch_type\n");
243 /* Update ecc pointer with ecc byte size */
244 ecc
+= info
->ecc_syndrome_size
;
249 * elm_start_processing - start elm syndrome processing
251 * @err_vec: elm error vectors
253 * Set syndrome valid bit for syndrome fragment registers for which
254 * elm syndrome fragment registers are loaded. This enables elm module
255 * to start processing syndrome vectors.
257 static void elm_start_processing(struct elm_info
*info
,
258 struct elm_errorvec
*err_vec
)
264 * Set syndrome vector valid, so that ELM module
265 * will process it for vectors error is reported
267 for (i
= 0; i
< info
->ecc_steps
; i
++) {
268 if (err_vec
[i
].error_reported
) {
269 offset
= ELM_SYNDROME_FRAGMENT_6
+
270 SYNDROME_FRAGMENT_REG_SIZE
* i
;
271 reg_val
= elm_read_reg(info
, offset
);
272 reg_val
|= ELM_SYNDROME_VALID
;
273 elm_write_reg(info
, offset
, reg_val
);
279 * elm_error_correction - locate correctable error position
281 * @err_vec: elm error vectors
283 * On completion of processing by elm module, error location status
284 * register updated with correctable/uncorrectable error information.
285 * In case of correctable errors, number of errors located from
286 * elm location status register & read the positions from
287 * elm error location register.
289 static void elm_error_correction(struct elm_info
*info
,
290 struct elm_errorvec
*err_vec
)
292 int i
, j
, errors
= 0;
296 for (i
= 0; i
< info
->ecc_steps
; i
++) {
298 /* Check error reported */
299 if (err_vec
[i
].error_reported
) {
300 offset
= ELM_LOCATION_STATUS
+ ERROR_LOCATION_SIZE
* i
;
301 reg_val
= elm_read_reg(info
, offset
);
303 /* Check correctable error or not */
304 if (reg_val
& ECC_CORRECTABLE_MASK
) {
305 offset
= ELM_ERROR_LOCATION_0
+
306 ERROR_LOCATION_SIZE
* i
;
308 /* Read count of correctable errors */
309 err_vec
[i
].error_count
= reg_val
&
312 /* Update the error locations in error vector */
313 for (j
= 0; j
< err_vec
[i
].error_count
; j
++) {
315 reg_val
= elm_read_reg(info
, offset
);
316 err_vec
[i
].error_loc
[j
] = reg_val
&
317 ECC_ERROR_LOCATION_MASK
;
319 /* Update error location register */
323 errors
+= err_vec
[i
].error_count
;
325 err_vec
[i
].error_uncorrectable
= true;
328 /* Clearing interrupts for processed error vectors */
329 elm_write_reg(info
, ELM_IRQSTATUS
, BIT(i
));
331 /* Disable page mode */
332 elm_configure_page_mode(info
, i
, false);
338 * elm_decode_bch_error_page - Locate error position
339 * @dev: device pointer
340 * @ecc_calc: calculated ECC bytes from GPMC
341 * @err_vec: elm error vectors
343 * Called with one or more error reported vectors & vectors with
344 * error reported is updated in err_vec[].error_reported
346 void elm_decode_bch_error_page(struct device
*dev
, u8
*ecc_calc
,
347 struct elm_errorvec
*err_vec
)
349 struct elm_info
*info
= dev_get_drvdata(dev
);
352 /* Enable page mode interrupt */
353 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
354 elm_write_reg(info
, ELM_IRQSTATUS
, reg_val
& INTR_STATUS_PAGE_VALID
);
355 elm_write_reg(info
, ELM_IRQENABLE
, INTR_EN_PAGE_MASK
);
357 /* Load valid ecc byte to syndrome fragment register */
358 elm_load_syndrome(info
, err_vec
, ecc_calc
);
360 /* Enable syndrome processing for which syndrome fragment is updated */
361 elm_start_processing(info
, err_vec
);
363 /* Wait for ELM module to finish locating error correction */
364 wait_for_completion(&info
->elm_completion
);
366 /* Disable page mode interrupt */
367 reg_val
= elm_read_reg(info
, ELM_IRQENABLE
);
368 elm_write_reg(info
, ELM_IRQENABLE
, reg_val
& ~INTR_EN_PAGE_MASK
);
369 elm_error_correction(info
, err_vec
);
371 EXPORT_SYMBOL(elm_decode_bch_error_page
);
373 static irqreturn_t
elm_isr(int this_irq
, void *dev_id
)
376 struct elm_info
*info
= dev_id
;
378 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
380 /* All error vectors processed */
381 if (reg_val
& INTR_STATUS_PAGE_VALID
) {
382 elm_write_reg(info
, ELM_IRQSTATUS
,
383 reg_val
& INTR_STATUS_PAGE_VALID
);
384 complete(&info
->elm_completion
);
391 static int elm_probe(struct platform_device
*pdev
)
394 struct resource
*res
, *irq
;
395 struct elm_info
*info
;
397 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
401 info
->dev
= &pdev
->dev
;
403 irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
405 dev_err(&pdev
->dev
, "no irq resource defined\n");
409 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
410 info
->elm_base
= devm_ioremap_resource(&pdev
->dev
, res
);
411 if (IS_ERR(info
->elm_base
))
412 return PTR_ERR(info
->elm_base
);
414 ret
= devm_request_irq(&pdev
->dev
, irq
->start
, elm_isr
, 0,
417 dev_err(&pdev
->dev
, "failure requesting irq %i\n", irq
->start
);
421 pm_runtime_enable(&pdev
->dev
);
422 if (pm_runtime_get_sync(&pdev
->dev
) < 0) {
424 pm_runtime_disable(&pdev
->dev
);
425 dev_err(&pdev
->dev
, "can't enable clock\n");
429 init_completion(&info
->elm_completion
);
430 INIT_LIST_HEAD(&info
->list
);
431 list_add(&info
->list
, &elm_devices
);
432 platform_set_drvdata(pdev
, info
);
436 static int elm_remove(struct platform_device
*pdev
)
438 pm_runtime_put_sync(&pdev
->dev
);
439 pm_runtime_disable(&pdev
->dev
);
443 #ifdef CONFIG_PM_SLEEP
446 * saves ELM configurations to preserve them across Hardware powered-down
448 static int elm_context_save(struct elm_info
*info
)
450 struct elm_registers
*regs
= &info
->elm_regs
;
451 enum bch_ecc bch_type
= info
->bch_type
;
454 regs
->elm_irqenable
= elm_read_reg(info
, ELM_IRQENABLE
);
455 regs
->elm_sysconfig
= elm_read_reg(info
, ELM_SYSCONFIG
);
456 regs
->elm_location_config
= elm_read_reg(info
, ELM_LOCATION_CONFIG
);
457 regs
->elm_page_ctrl
= elm_read_reg(info
, ELM_PAGE_CTRL
);
458 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
459 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
462 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
463 ELM_SYNDROME_FRAGMENT_6
+ offset
);
464 regs
->elm_syndrome_fragment_5
[i
] = elm_read_reg(info
,
465 ELM_SYNDROME_FRAGMENT_5
+ offset
);
466 regs
->elm_syndrome_fragment_4
[i
] = elm_read_reg(info
,
467 ELM_SYNDROME_FRAGMENT_4
+ offset
);
469 regs
->elm_syndrome_fragment_3
[i
] = elm_read_reg(info
,
470 ELM_SYNDROME_FRAGMENT_3
+ offset
);
471 regs
->elm_syndrome_fragment_2
[i
] = elm_read_reg(info
,
472 ELM_SYNDROME_FRAGMENT_2
+ offset
);
474 regs
->elm_syndrome_fragment_1
[i
] = elm_read_reg(info
,
475 ELM_SYNDROME_FRAGMENT_1
+ offset
);
476 regs
->elm_syndrome_fragment_0
[i
] = elm_read_reg(info
,
477 ELM_SYNDROME_FRAGMENT_0
+ offset
);
482 /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
483 * to be saved for all BCH schemes*/
484 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
485 ELM_SYNDROME_FRAGMENT_6
+ offset
);
491 * elm_context_restore
492 * writes configurations saved duing power-down back into ELM registers
494 static int elm_context_restore(struct elm_info
*info
)
496 struct elm_registers
*regs
= &info
->elm_regs
;
497 enum bch_ecc bch_type
= info
->bch_type
;
500 elm_write_reg(info
, ELM_IRQENABLE
, regs
->elm_irqenable
);
501 elm_write_reg(info
, ELM_SYSCONFIG
, regs
->elm_sysconfig
);
502 elm_write_reg(info
, ELM_LOCATION_CONFIG
, regs
->elm_location_config
);
503 elm_write_reg(info
, ELM_PAGE_CTRL
, regs
->elm_page_ctrl
);
504 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
505 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
508 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
509 regs
->elm_syndrome_fragment_6
[i
]);
510 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_5
+ offset
,
511 regs
->elm_syndrome_fragment_5
[i
]);
512 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_4
+ offset
,
513 regs
->elm_syndrome_fragment_4
[i
]);
515 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_3
+ offset
,
516 regs
->elm_syndrome_fragment_3
[i
]);
517 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_2
+ offset
,
518 regs
->elm_syndrome_fragment_2
[i
]);
520 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_1
+ offset
,
521 regs
->elm_syndrome_fragment_1
[i
]);
522 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_0
+ offset
,
523 regs
->elm_syndrome_fragment_0
[i
]);
528 /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
529 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
530 regs
->elm_syndrome_fragment_6
[i
] &
536 static int elm_suspend(struct device
*dev
)
538 struct elm_info
*info
= dev_get_drvdata(dev
);
539 elm_context_save(info
);
540 pm_runtime_put_sync(dev
);
544 static int elm_resume(struct device
*dev
)
546 struct elm_info
*info
= dev_get_drvdata(dev
);
547 pm_runtime_get_sync(dev
);
548 elm_context_restore(info
);
553 static SIMPLE_DEV_PM_OPS(elm_pm_ops
, elm_suspend
, elm_resume
);
556 static const struct of_device_id elm_of_match
[] = {
557 { .compatible
= "ti,am3352-elm" },
560 MODULE_DEVICE_TABLE(of
, elm_of_match
);
563 static struct platform_driver elm_driver
= {
566 .of_match_table
= of_match_ptr(elm_of_match
),
570 .remove
= elm_remove
,
573 module_platform_driver(elm_driver
);
575 MODULE_DESCRIPTION("ELM driver for BCH error correction");
576 MODULE_AUTHOR("Texas Instruments");
577 MODULE_ALIAS("platform:" DRIVER_NAME
);
578 MODULE_LICENSE("GPL v2");