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/pm_runtime.h>
24 #include <linux/platform_data/elm.h>
26 #define ELM_IRQSTATUS 0x018
27 #define ELM_IRQENABLE 0x01c
28 #define ELM_LOCATION_CONFIG 0x020
29 #define ELM_PAGE_CTRL 0x080
30 #define ELM_SYNDROME_FRAGMENT_0 0x400
31 #define ELM_SYNDROME_FRAGMENT_6 0x418
32 #define ELM_LOCATION_STATUS 0x800
33 #define ELM_ERROR_LOCATION_0 0x880
35 /* ELM Interrupt Status Register */
36 #define INTR_STATUS_PAGE_VALID BIT(8)
38 /* ELM Interrupt Enable Register */
39 #define INTR_EN_PAGE_MASK BIT(8)
41 /* ELM Location Configuration Register */
42 #define ECC_BCH_LEVEL_MASK 0x3
45 #define ELM_SYNDROME_VALID BIT(16)
47 /* ELM_LOCATION_STATUS Register */
48 #define ECC_CORRECTABLE_MASK BIT(8)
49 #define ECC_NB_ERRORS_MASK 0x1f
51 /* ELM_ERROR_LOCATION_0-15 Registers */
52 #define ECC_ERROR_LOCATION_MASK 0x1fff
54 #define ELM_ECC_SIZE 0x7ff
56 #define SYNDROME_FRAGMENT_REG_SIZE 0x40
57 #define ERROR_LOCATION_SIZE 0x100
61 void __iomem
*elm_base
;
62 struct completion elm_completion
;
63 struct list_head list
;
64 enum bch_ecc bch_type
;
67 static LIST_HEAD(elm_devices
);
69 static void elm_write_reg(struct elm_info
*info
, int offset
, u32 val
)
71 writel(val
, info
->elm_base
+ offset
);
74 static u32
elm_read_reg(struct elm_info
*info
, int offset
)
76 return readl(info
->elm_base
+ offset
);
80 * elm_config - Configure ELM module
82 * @bch_type: Type of BCH ecc
84 int elm_config(struct device
*dev
, enum bch_ecc bch_type
)
87 struct elm_info
*info
= dev_get_drvdata(dev
);
90 dev_err(dev
, "Unable to configure elm - device not probed?\n");
94 reg_val
= (bch_type
& ECC_BCH_LEVEL_MASK
) | (ELM_ECC_SIZE
<< 16);
95 elm_write_reg(info
, ELM_LOCATION_CONFIG
, reg_val
);
96 info
->bch_type
= bch_type
;
100 EXPORT_SYMBOL(elm_config
);
103 * elm_configure_page_mode - Enable/Disable page mode
105 * @index: index number of syndrome fragment vector
106 * @enable: enable/disable flag for page mode
108 * Enable page mode for syndrome fragment index
110 static void elm_configure_page_mode(struct elm_info
*info
, int index
,
115 reg_val
= elm_read_reg(info
, ELM_PAGE_CTRL
);
117 reg_val
|= BIT(index
); /* enable page mode */
119 reg_val
&= ~BIT(index
); /* disable page mode */
121 elm_write_reg(info
, ELM_PAGE_CTRL
, reg_val
);
125 * elm_load_syndrome - Load ELM syndrome reg
127 * @err_vec: elm error vectors
128 * @ecc: buffer with calculated ecc
130 * Load syndrome fragment registers with calculated ecc in reverse order.
132 static void elm_load_syndrome(struct elm_info
*info
,
133 struct elm_errorvec
*err_vec
, u8
*ecc
)
138 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
140 /* Check error reported */
141 if (err_vec
[i
].error_reported
) {
142 elm_configure_page_mode(info
, i
, true);
143 offset
= ELM_SYNDROME_FRAGMENT_0
+
144 SYNDROME_FRAGMENT_REG_SIZE
* i
;
147 if (info
->bch_type
) {
149 /* syndrome fragment 0 = ecc[9-12B] */
150 val
= cpu_to_be32(*(u32
*) &ecc
[9]);
151 elm_write_reg(info
, offset
, val
);
153 /* syndrome fragment 1 = ecc[5-8B] */
155 val
= cpu_to_be32(*(u32
*) &ecc
[5]);
156 elm_write_reg(info
, offset
, val
);
158 /* syndrome fragment 2 = ecc[1-4B] */
160 val
= cpu_to_be32(*(u32
*) &ecc
[1]);
161 elm_write_reg(info
, offset
, val
);
163 /* syndrome fragment 3 = ecc[0B] */
166 elm_write_reg(info
, offset
, val
);
168 /* syndrome fragment 0 = ecc[20-52b] bits */
169 val
= (cpu_to_be32(*(u32
*) &ecc
[3]) >> 4) |
170 ((ecc
[2] & 0xf) << 28);
171 elm_write_reg(info
, offset
, val
);
173 /* syndrome fragment 1 = ecc[0-20b] bits */
175 val
= cpu_to_be32(*(u32
*) &ecc
[0]) >> 12;
176 elm_write_reg(info
, offset
, val
);
180 /* Update ecc pointer with ecc byte size */
181 ecc
+= info
->bch_type
? BCH8_SIZE
: BCH4_SIZE
;
186 * elm_start_processing - start elm syndrome processing
188 * @err_vec: elm error vectors
190 * Set syndrome valid bit for syndrome fragment registers for which
191 * elm syndrome fragment registers are loaded. This enables elm module
192 * to start processing syndrome vectors.
194 static void elm_start_processing(struct elm_info
*info
,
195 struct elm_errorvec
*err_vec
)
201 * Set syndrome vector valid, so that ELM module
202 * will process it for vectors error is reported
204 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
205 if (err_vec
[i
].error_reported
) {
206 offset
= ELM_SYNDROME_FRAGMENT_6
+
207 SYNDROME_FRAGMENT_REG_SIZE
* i
;
208 reg_val
= elm_read_reg(info
, offset
);
209 reg_val
|= ELM_SYNDROME_VALID
;
210 elm_write_reg(info
, offset
, reg_val
);
216 * elm_error_correction - locate correctable error position
218 * @err_vec: elm error vectors
220 * On completion of processing by elm module, error location status
221 * register updated with correctable/uncorrectable error information.
222 * In case of correctable errors, number of errors located from
223 * elm location status register & read the positions from
224 * elm error location register.
226 static void elm_error_correction(struct elm_info
*info
,
227 struct elm_errorvec
*err_vec
)
229 int i
, j
, errors
= 0;
233 for (i
= 0; i
< ERROR_VECTOR_MAX
; i
++) {
235 /* Check error reported */
236 if (err_vec
[i
].error_reported
) {
237 offset
= ELM_LOCATION_STATUS
+ ERROR_LOCATION_SIZE
* i
;
238 reg_val
= elm_read_reg(info
, offset
);
240 /* Check correctable error or not */
241 if (reg_val
& ECC_CORRECTABLE_MASK
) {
242 offset
= ELM_ERROR_LOCATION_0
+
243 ERROR_LOCATION_SIZE
* i
;
245 /* Read count of correctable errors */
246 err_vec
[i
].error_count
= reg_val
&
249 /* Update the error locations in error vector */
250 for (j
= 0; j
< err_vec
[i
].error_count
; j
++) {
252 reg_val
= elm_read_reg(info
, offset
);
253 err_vec
[i
].error_loc
[j
] = reg_val
&
254 ECC_ERROR_LOCATION_MASK
;
256 /* Update error location register */
260 errors
+= err_vec
[i
].error_count
;
262 err_vec
[i
].error_uncorrectable
= true;
265 /* Clearing interrupts for processed error vectors */
266 elm_write_reg(info
, ELM_IRQSTATUS
, BIT(i
));
268 /* Disable page mode */
269 elm_configure_page_mode(info
, i
, false);
275 * elm_decode_bch_error_page - Locate error position
276 * @dev: device pointer
277 * @ecc_calc: calculated ECC bytes from GPMC
278 * @err_vec: elm error vectors
280 * Called with one or more error reported vectors & vectors with
281 * error reported is updated in err_vec[].error_reported
283 void elm_decode_bch_error_page(struct device
*dev
, u8
*ecc_calc
,
284 struct elm_errorvec
*err_vec
)
286 struct elm_info
*info
= dev_get_drvdata(dev
);
289 /* Enable page mode interrupt */
290 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
291 elm_write_reg(info
, ELM_IRQSTATUS
, reg_val
& INTR_STATUS_PAGE_VALID
);
292 elm_write_reg(info
, ELM_IRQENABLE
, INTR_EN_PAGE_MASK
);
294 /* Load valid ecc byte to syndrome fragment register */
295 elm_load_syndrome(info
, err_vec
, ecc_calc
);
297 /* Enable syndrome processing for which syndrome fragment is updated */
298 elm_start_processing(info
, err_vec
);
300 /* Wait for ELM module to finish locating error correction */
301 wait_for_completion(&info
->elm_completion
);
303 /* Disable page mode interrupt */
304 reg_val
= elm_read_reg(info
, ELM_IRQENABLE
);
305 elm_write_reg(info
, ELM_IRQENABLE
, reg_val
& ~INTR_EN_PAGE_MASK
);
306 elm_error_correction(info
, err_vec
);
308 EXPORT_SYMBOL(elm_decode_bch_error_page
);
310 static irqreturn_t
elm_isr(int this_irq
, void *dev_id
)
313 struct elm_info
*info
= dev_id
;
315 reg_val
= elm_read_reg(info
, ELM_IRQSTATUS
);
317 /* All error vectors processed */
318 if (reg_val
& INTR_STATUS_PAGE_VALID
) {
319 elm_write_reg(info
, ELM_IRQSTATUS
,
320 reg_val
& INTR_STATUS_PAGE_VALID
);
321 complete(&info
->elm_completion
);
328 static int elm_probe(struct platform_device
*pdev
)
331 struct resource
*res
, *irq
;
332 struct elm_info
*info
;
334 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
336 dev_err(&pdev
->dev
, "failed to allocate memory\n");
340 info
->dev
= &pdev
->dev
;
342 irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
344 dev_err(&pdev
->dev
, "no irq resource defined\n");
348 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
350 dev_err(&pdev
->dev
, "no memory resource defined\n");
354 info
->elm_base
= devm_request_and_ioremap(&pdev
->dev
, res
);
356 return -EADDRNOTAVAIL
;
358 ret
= devm_request_irq(&pdev
->dev
, irq
->start
, elm_isr
, 0,
361 dev_err(&pdev
->dev
, "failure requesting irq %i\n", irq
->start
);
365 pm_runtime_enable(&pdev
->dev
);
366 if (pm_runtime_get_sync(&pdev
->dev
)) {
368 pm_runtime_disable(&pdev
->dev
);
369 dev_err(&pdev
->dev
, "can't enable clock\n");
373 init_completion(&info
->elm_completion
);
374 INIT_LIST_HEAD(&info
->list
);
375 list_add(&info
->list
, &elm_devices
);
376 platform_set_drvdata(pdev
, info
);
380 static int elm_remove(struct platform_device
*pdev
)
382 pm_runtime_put_sync(&pdev
->dev
);
383 pm_runtime_disable(&pdev
->dev
);
384 platform_set_drvdata(pdev
, NULL
);
389 static const struct of_device_id elm_of_match
[] = {
390 { .compatible
= "ti,am3352-elm" },
393 MODULE_DEVICE_TABLE(of
, elm_of_match
);
396 static struct platform_driver elm_driver
= {
399 .owner
= THIS_MODULE
,
400 .of_match_table
= of_match_ptr(elm_of_match
),
403 .remove
= elm_remove
,
406 module_platform_driver(elm_driver
);
408 MODULE_DESCRIPTION("ELM driver for BCH error correction");
409 MODULE_AUTHOR("Texas Instruments");
410 MODULE_ALIAS("platform: elm");
411 MODULE_LICENSE("GPL v2");