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 #include <linux/platform_device.h>
19 #include <linux/module.h>
20 #include <linux/interrupt.h>
23 #include <linux/sched.h>
24 #include <linux/pm_runtime.h>
25 #include <linux/platform_data/elm.h>
27 #define ELM_SYSCONFIG 0x010
28 #define ELM_IRQSTATUS 0x018
29 #define ELM_IRQENABLE 0x01c
30 #define ELM_LOCATION_CONFIG 0x020
31 #define ELM_PAGE_CTRL 0x080
32 #define ELM_SYNDROME_FRAGMENT_0 0x400
33 #define ELM_SYNDROME_FRAGMENT_1 0x404
34 #define ELM_SYNDROME_FRAGMENT_2 0x408
35 #define ELM_SYNDROME_FRAGMENT_3 0x40c
36 #define ELM_SYNDROME_FRAGMENT_4 0x410
37 #define ELM_SYNDROME_FRAGMENT_5 0x414
38 #define ELM_SYNDROME_FRAGMENT_6 0x418
39 #define ELM_LOCATION_STATUS 0x800
40 #define ELM_ERROR_LOCATION_0 0x880
42 /* ELM Interrupt Status Register */
43 #define INTR_STATUS_PAGE_VALID BIT(8)
45 /* ELM Interrupt Enable Register */
46 #define INTR_EN_PAGE_MASK BIT(8)
48 /* ELM Location Configuration Register */
49 #define ECC_BCH_LEVEL_MASK 0x3
52 #define ELM_SYNDROME_VALID BIT(16)
54 /* ELM_LOCATION_STATUS Register */
55 #define ECC_CORRECTABLE_MASK BIT(8)
56 #define ECC_NB_ERRORS_MASK 0x1f
58 /* ELM_ERROR_LOCATION_0-15 Registers */
59 #define ECC_ERROR_LOCATION_MASK 0x1fff
61 #define ELM_ECC_SIZE 0x7ff
63 #define SYNDROME_FRAGMENT_REG_SIZE 0x40
64 #define ERROR_LOCATION_SIZE 0x100
66 struct elm_registers
{
69 u32 elm_location_config
;
71 u32 elm_syndrome_fragment_6
[ERROR_VECTOR_MAX
];
72 u32 elm_syndrome_fragment_5
[ERROR_VECTOR_MAX
];
73 u32 elm_syndrome_fragment_4
[ERROR_VECTOR_MAX
];
74 u32 elm_syndrome_fragment_3
[ERROR_VECTOR_MAX
];
75 u32 elm_syndrome_fragment_2
[ERROR_VECTOR_MAX
];
76 u32 elm_syndrome_fragment_1
[ERROR_VECTOR_MAX
];
77 u32 elm_syndrome_fragment_0
[ERROR_VECTOR_MAX
];
82 void __iomem
*elm_base
;
83 struct completion elm_completion
;
84 struct list_head list
;
85 enum bch_ecc bch_type
;
86 struct elm_registers elm_regs
;
89 static LIST_HEAD(elm_devices
);
91 static void elm_write_reg(struct elm_info
*info
, int offset
, u32 val
)
93 writel(val
, info
->elm_base
+ offset
);
96 static u32
elm_read_reg(struct elm_info
*info
, int offset
)
98 return readl(info
->elm_base
+ offset
);
102 * elm_config - Configure ELM module
104 * @bch_type: Type of BCH ecc
106 int elm_config(struct device
*dev
, enum bch_ecc bch_type
)
109 struct elm_info
*info
= dev_get_drvdata(dev
);
112 dev_err(dev
, "Unable to configure elm - device not probed?\n");
116 reg_val
= (bch_type
& ECC_BCH_LEVEL_MASK
) | (ELM_ECC_SIZE
<< 16);
117 elm_write_reg(info
, ELM_LOCATION_CONFIG
, reg_val
);
118 info
->bch_type
= bch_type
;
122 EXPORT_SYMBOL(elm_config
);
125 * elm_configure_page_mode - Enable/Disable page mode
127 * @index: index number of syndrome fragment vector
128 * @enable: enable/disable flag for page mode
130 * Enable page mode for syndrome fragment index
132 static void elm_configure_page_mode(struct elm_info
*info
, int index
,
137 reg_val
= elm_read_reg(info
, ELM_PAGE_CTRL
);
139 reg_val
|= BIT(index
); /* enable page mode */
141 reg_val
&= ~BIT(index
); /* disable page mode */
143 elm_write_reg(info
, ELM_PAGE_CTRL
, reg_val
);
147 * elm_load_syndrome - Load ELM syndrome reg
149 * @err_vec: elm error vectors
150 * @ecc: buffer with calculated ecc
152 * Load syndrome fragment registers with calculated ecc in reverse order.
154 static void elm_load_syndrome(struct elm_info
*info
,
155 struct elm_errorvec
*err_vec
, u8
*ecc
)
160 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
162 /* Check error reported */
163 if (err_vec
[i
].error_reported
) {
164 elm_configure_page_mode(info
, i
, true);
165 offset
= ELM_SYNDROME_FRAGMENT_0
+
166 SYNDROME_FRAGMENT_REG_SIZE
* i
;
169 if (info
->bch_type
) {
171 /* syndrome fragment 0 = ecc[9-12B] */
172 val
= cpu_to_be32(*(u32
*) &ecc
[9]);
173 elm_write_reg(info
, offset
, val
);
175 /* syndrome fragment 1 = ecc[5-8B] */
177 val
= cpu_to_be32(*(u32
*) &ecc
[5]);
178 elm_write_reg(info
, offset
, val
);
180 /* syndrome fragment 2 = ecc[1-4B] */
182 val
= cpu_to_be32(*(u32
*) &ecc
[1]);
183 elm_write_reg(info
, offset
, val
);
185 /* syndrome fragment 3 = ecc[0B] */
188 elm_write_reg(info
, offset
, val
);
190 /* syndrome fragment 0 = ecc[20-52b] bits */
191 val
= (cpu_to_be32(*(u32
*) &ecc
[3]) >> 4) |
192 ((ecc
[2] & 0xf) << 28);
193 elm_write_reg(info
, offset
, val
);
195 /* syndrome fragment 1 = ecc[0-20b] bits */
197 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 12;
198 elm_write_reg(info
, offset
, val
);
202 /* Update ecc pointer with ecc byte size */
203 ecc
+= info
->bch_type
? BCH8_SIZE
: BCH4_SIZE
;
208 * elm_start_processing - start elm syndrome processing
210 * @err_vec: elm error vectors
212 * Set syndrome valid bit for syndrome fragment registers for which
213 * elm syndrome fragment registers are loaded. This enables elm module
214 * to start processing syndrome vectors.
216 static void elm_start_processing(struct elm_info
*info
,
217 struct elm_errorvec
*err_vec
)
223 * Set syndrome vector valid, so that ELM module
224 * will process it for vectors error is reported
226 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
227 if (err_vec
[i
].error_reported
) {
228 offset
= ELM_SYNDROME_FRAGMENT_6
+
229 SYNDROME_FRAGMENT_REG_SIZE
* i
;
230 reg_val
= elm_read_reg(info
, offset
);
231 reg_val
|= ELM_SYNDROME_VALID
;
232 elm_write_reg(info
, offset
, reg_val
);
238 * elm_error_correction - locate correctable error position
240 * @err_vec: elm error vectors
242 * On completion of processing by elm module, error location status
243 * register updated with correctable/uncorrectable error information.
244 * In case of correctable errors, number of errors located from
245 * elm location status register & read the positions from
246 * elm error location register.
248 static void elm_error_correction(struct elm_info
*info
,
249 struct elm_errorvec
*err_vec
)
251 int i
, j
, errors
= 0;
255 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
257 /* Check error reported */
258 if (err_vec
[i
].error_reported
) {
259 offset
= ELM_LOCATION_STATUS
+ ERROR_LOCATION_SIZE
* i
;
260 reg_val
= elm_read_reg(info
, offset
);
262 /* Check correctable error or not */
263 if (reg_val
& ECC_CORRECTABLE_MASK
) {
264 offset
= ELM_ERROR_LOCATION_0
+
265 ERROR_LOCATION_SIZE
* i
;
267 /* Read count of correctable errors */
268 err_vec
[i
].error_count
= reg_val
&
271 /* Update the error locations in error vector */
272 for (j
= 0; j
< err_vec
[i
].error_count
; j
++) {
274 reg_val
= elm_read_reg(info
, offset
);
275 err_vec
[i
].error_loc
[j
] = reg_val
&
276 ECC_ERROR_LOCATION_MASK
;
278 /* Update error location register */
282 errors
+= err_vec
[i
].error_count
;
284 err_vec
[i
].error_uncorrectable
= true;
287 /* Clearing interrupts for processed error vectors */
288 elm_write_reg(info
, ELM_IRQSTATUS
, BIT(i
));
290 /* Disable page mode */
291 elm_configure_page_mode(info
, i
, false);
297 * elm_decode_bch_error_page - Locate error position
298 * @dev: device pointer
299 * @ecc_calc: calculated ECC bytes from GPMC
300 * @err_vec: elm error vectors
302 * Called with one or more error reported vectors & vectors with
303 * error reported is updated in err_vec[].error_reported
305 void elm_decode_bch_error_page(struct device
*dev
, u8
*ecc_calc
,
306 struct elm_errorvec
*err_vec
)
308 struct elm_info
*info
= dev_get_drvdata(dev
);
311 /* Enable page mode interrupt */
312 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
313 elm_write_reg(info
, ELM_IRQSTATUS
, reg_val
& INTR_STATUS_PAGE_VALID
);
314 elm_write_reg(info
, ELM_IRQENABLE
, INTR_EN_PAGE_MASK
);
316 /* Load valid ecc byte to syndrome fragment register */
317 elm_load_syndrome(info
, err_vec
, ecc_calc
);
319 /* Enable syndrome processing for which syndrome fragment is updated */
320 elm_start_processing(info
, err_vec
);
322 /* Wait for ELM module to finish locating error correction */
323 wait_for_completion(&info
->elm_completion
);
325 /* Disable page mode interrupt */
326 reg_val
= elm_read_reg(info
, ELM_IRQENABLE
);
327 elm_write_reg(info
, ELM_IRQENABLE
, reg_val
& ~INTR_EN_PAGE_MASK
);
328 elm_error_correction(info
, err_vec
);
330 EXPORT_SYMBOL(elm_decode_bch_error_page
);
332 static irqreturn_t
elm_isr(int this_irq
, void *dev_id
)
335 struct elm_info
*info
= dev_id
;
337 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
339 /* All error vectors processed */
340 if (reg_val
& INTR_STATUS_PAGE_VALID
) {
341 elm_write_reg(info
, ELM_IRQSTATUS
,
342 reg_val
& INTR_STATUS_PAGE_VALID
);
343 complete(&info
->elm_completion
);
350 static int elm_probe(struct platform_device
*pdev
)
353 struct resource
*res
, *irq
;
354 struct elm_info
*info
;
356 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
358 dev_err(&pdev
->dev
, "failed to allocate memory\n");
362 info
->dev
= &pdev
->dev
;
364 irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
366 dev_err(&pdev
->dev
, "no irq resource defined\n");
370 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
371 info
->elm_base
= devm_ioremap_resource(&pdev
->dev
, res
);
372 if (IS_ERR(info
->elm_base
))
373 return PTR_ERR(info
->elm_base
);
375 ret
= devm_request_irq(&pdev
->dev
, irq
->start
, elm_isr
, 0,
378 dev_err(&pdev
->dev
, "failure requesting irq %i\n", irq
->start
);
382 pm_runtime_enable(&pdev
->dev
);
383 if (pm_runtime_get_sync(&pdev
->dev
)) {
385 pm_runtime_disable(&pdev
->dev
);
386 dev_err(&pdev
->dev
, "can't enable clock\n");
390 init_completion(&info
->elm_completion
);
391 INIT_LIST_HEAD(&info
->list
);
392 list_add(&info
->list
, &elm_devices
);
393 platform_set_drvdata(pdev
, info
);
397 static int elm_remove(struct platform_device
*pdev
)
399 pm_runtime_put_sync(&pdev
->dev
);
400 pm_runtime_disable(&pdev
->dev
);
406 * saves ELM configurations to preserve them across Hardware powered-down
408 static int elm_context_save(struct elm_info
*info
)
410 struct elm_registers
*regs
= &info
->elm_regs
;
411 enum bch_ecc bch_type
= info
->bch_type
;
414 regs
->elm_irqenable
= elm_read_reg(info
, ELM_IRQENABLE
);
415 regs
->elm_sysconfig
= elm_read_reg(info
, ELM_SYSCONFIG
);
416 regs
->elm_location_config
= elm_read_reg(info
, ELM_LOCATION_CONFIG
);
417 regs
->elm_page_ctrl
= elm_read_reg(info
, ELM_PAGE_CTRL
);
418 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
419 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
422 regs
->elm_syndrome_fragment_3
[i
] = elm_read_reg(info
,
423 ELM_SYNDROME_FRAGMENT_3
+ offset
);
424 regs
->elm_syndrome_fragment_2
[i
] = elm_read_reg(info
,
425 ELM_SYNDROME_FRAGMENT_2
+ offset
);
427 regs
->elm_syndrome_fragment_1
[i
] = elm_read_reg(info
,
428 ELM_SYNDROME_FRAGMENT_1
+ offset
);
429 regs
->elm_syndrome_fragment_0
[i
] = elm_read_reg(info
,
430 ELM_SYNDROME_FRAGMENT_0
+ offset
);
435 /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
436 * to be saved for all BCH schemes*/
437 regs
->elm_syndrome_fragment_6
[i
] = elm_read_reg(info
,
438 ELM_SYNDROME_FRAGMENT_6
+ offset
);
444 * elm_context_restore
445 * writes configurations saved duing power-down back into ELM registers
447 static int elm_context_restore(struct elm_info
*info
)
449 struct elm_registers
*regs
= &info
->elm_regs
;
450 enum bch_ecc bch_type
= info
->bch_type
;
453 elm_write_reg(info
, ELM_IRQENABLE
, regs
->elm_irqenable
);
454 elm_write_reg(info
, ELM_SYSCONFIG
, regs
->elm_sysconfig
);
455 elm_write_reg(info
, ELM_LOCATION_CONFIG
, regs
->elm_location_config
);
456 elm_write_reg(info
, ELM_PAGE_CTRL
, regs
->elm_page_ctrl
);
457 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
458 offset
= i
* SYNDROME_FRAGMENT_REG_SIZE
;
461 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_3
+ offset
,
462 regs
->elm_syndrome_fragment_3
[i
]);
463 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_2
+ offset
,
464 regs
->elm_syndrome_fragment_2
[i
]);
466 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_1
+ offset
,
467 regs
->elm_syndrome_fragment_1
[i
]);
468 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_0
+ offset
,
469 regs
->elm_syndrome_fragment_0
[i
]);
474 /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
475 elm_write_reg(info
, ELM_SYNDROME_FRAGMENT_6
+ offset
,
476 regs
->elm_syndrome_fragment_6
[i
] &
482 static int elm_suspend(struct device
*dev
)
484 struct elm_info
*info
= dev_get_drvdata(dev
);
485 elm_context_save(info
);
486 pm_runtime_put_sync(dev
);
490 static int elm_resume(struct device
*dev
)
492 struct elm_info
*info
= dev_get_drvdata(dev
);
493 pm_runtime_get_sync(dev
);
494 elm_context_restore(info
);
498 static SIMPLE_DEV_PM_OPS(elm_pm_ops
, elm_suspend
, elm_resume
);
501 static const struct of_device_id elm_of_match
[] = {
502 { .compatible
= "ti,am3352-elm" },
505 MODULE_DEVICE_TABLE(of
, elm_of_match
);
508 static struct platform_driver elm_driver
= {
511 .owner
= THIS_MODULE
,
512 .of_match_table
= of_match_ptr(elm_of_match
),
516 .remove
= elm_remove
,
519 module_platform_driver(elm_driver
);
521 MODULE_DESCRIPTION("ELM driver for BCH error correction");
522 MODULE_AUTHOR("Texas Instruments");
523 MODULE_ALIAS("platform: elm");
524 MODULE_LICENSE("GPL v2");