Commit 6d178ef2fd5e4a7f601874a6e641090e706da3c8
Committed by
Brian Norris
1 parent
d2f08c7521
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
mtd: nand: Move ELM driver and rename as omap_elm
The ELM driver is only used by the OMAP NAND driver, so let's move it to the nand/ directory. Additionally, let's rename it to a less confusing name, so the module is built with a meaningful name, instead of the previous 'elm.ko'. Acked-by: Roger Quadros <rogerq@ti.com> Signed-off-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Showing 4 changed files with 580 additions and 580 deletions Side-by-side Diff
drivers/mtd/devices/Makefile
... | ... | @@ -12,7 +12,6 @@ |
12 | 12 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
13 | 13 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
14 | 14 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
15 | -obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o | |
16 | 15 | obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o |
17 | 16 | obj-$(CONFIG_MTD_SST25L) += sst25l.o |
18 | 17 | obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o |
drivers/mtd/devices/elm.c
1 | -/* | |
2 | - * Error Location Module | |
3 | - * | |
4 | - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | - * | |
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. | |
10 | - * | |
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. | |
15 | - * | |
16 | - */ | |
17 | - | |
18 | -#define DRIVER_NAME "omap-elm" | |
19 | - | |
20 | -#include <linux/platform_device.h> | |
21 | -#include <linux/module.h> | |
22 | -#include <linux/interrupt.h> | |
23 | -#include <linux/io.h> | |
24 | -#include <linux/of.h> | |
25 | -#include <linux/sched.h> | |
26 | -#include <linux/pm_runtime.h> | |
27 | -#include <linux/platform_data/elm.h> | |
28 | - | |
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 | |
43 | - | |
44 | -/* ELM Interrupt Status Register */ | |
45 | -#define INTR_STATUS_PAGE_VALID BIT(8) | |
46 | - | |
47 | -/* ELM Interrupt Enable Register */ | |
48 | -#define INTR_EN_PAGE_MASK BIT(8) | |
49 | - | |
50 | -/* ELM Location Configuration Register */ | |
51 | -#define ECC_BCH_LEVEL_MASK 0x3 | |
52 | - | |
53 | -/* ELM syndrome */ | |
54 | -#define ELM_SYNDROME_VALID BIT(16) | |
55 | - | |
56 | -/* ELM_LOCATION_STATUS Register */ | |
57 | -#define ECC_CORRECTABLE_MASK BIT(8) | |
58 | -#define ECC_NB_ERRORS_MASK 0x1f | |
59 | - | |
60 | -/* ELM_ERROR_LOCATION_0-15 Registers */ | |
61 | -#define ECC_ERROR_LOCATION_MASK 0x1fff | |
62 | - | |
63 | -#define ELM_ECC_SIZE 0x7ff | |
64 | - | |
65 | -#define SYNDROME_FRAGMENT_REG_SIZE 0x40 | |
66 | -#define ERROR_LOCATION_SIZE 0x100 | |
67 | - | |
68 | -struct elm_registers { | |
69 | - u32 elm_irqenable; | |
70 | - u32 elm_sysconfig; | |
71 | - u32 elm_location_config; | |
72 | - u32 elm_page_ctrl; | |
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]; | |
80 | -}; | |
81 | - | |
82 | -struct elm_info { | |
83 | - struct device *dev; | |
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; | |
89 | - int ecc_steps; | |
90 | - int ecc_syndrome_size; | |
91 | -}; | |
92 | - | |
93 | -static LIST_HEAD(elm_devices); | |
94 | - | |
95 | -static void elm_write_reg(struct elm_info *info, int offset, u32 val) | |
96 | -{ | |
97 | - writel(val, info->elm_base + offset); | |
98 | -} | |
99 | - | |
100 | -static u32 elm_read_reg(struct elm_info *info, int offset) | |
101 | -{ | |
102 | - return readl(info->elm_base + offset); | |
103 | -} | |
104 | - | |
105 | -/** | |
106 | - * elm_config - Configure ELM module | |
107 | - * @dev: ELM device | |
108 | - * @bch_type: Type of BCH ecc | |
109 | - */ | |
110 | -int elm_config(struct device *dev, enum bch_ecc bch_type, | |
111 | - int ecc_steps, int ecc_step_size, int ecc_syndrome_size) | |
112 | -{ | |
113 | - u32 reg_val; | |
114 | - struct elm_info *info = dev_get_drvdata(dev); | |
115 | - | |
116 | - if (!info) { | |
117 | - dev_err(dev, "Unable to configure elm - device not probed?\n"); | |
118 | - return -ENODEV; | |
119 | - } | |
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); | |
123 | - return -EINVAL; | |
124 | - } | |
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); | |
128 | - return -EINVAL; | |
129 | - } | |
130 | - | |
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; | |
136 | - | |
137 | - return 0; | |
138 | -} | |
139 | -EXPORT_SYMBOL(elm_config); | |
140 | - | |
141 | -/** | |
142 | - * elm_configure_page_mode - Enable/Disable page mode | |
143 | - * @info: elm info | |
144 | - * @index: index number of syndrome fragment vector | |
145 | - * @enable: enable/disable flag for page mode | |
146 | - * | |
147 | - * Enable page mode for syndrome fragment index | |
148 | - */ | |
149 | -static void elm_configure_page_mode(struct elm_info *info, int index, | |
150 | - bool enable) | |
151 | -{ | |
152 | - u32 reg_val; | |
153 | - | |
154 | - reg_val = elm_read_reg(info, ELM_PAGE_CTRL); | |
155 | - if (enable) | |
156 | - reg_val |= BIT(index); /* enable page mode */ | |
157 | - else | |
158 | - reg_val &= ~BIT(index); /* disable page mode */ | |
159 | - | |
160 | - elm_write_reg(info, ELM_PAGE_CTRL, reg_val); | |
161 | -} | |
162 | - | |
163 | -/** | |
164 | - * elm_load_syndrome - Load ELM syndrome reg | |
165 | - * @info: elm info | |
166 | - * @err_vec: elm error vectors | |
167 | - * @ecc: buffer with calculated ecc | |
168 | - * | |
169 | - * Load syndrome fragment registers with calculated ecc in reverse order. | |
170 | - */ | |
171 | -static void elm_load_syndrome(struct elm_info *info, | |
172 | - struct elm_errorvec *err_vec, u8 *ecc) | |
173 | -{ | |
174 | - int i, offset; | |
175 | - u32 val; | |
176 | - | |
177 | - for (i = 0; i < info->ecc_steps; i++) { | |
178 | - | |
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) { | |
185 | - case BCH8_ECC: | |
186 | - /* syndrome fragment 0 = ecc[9-12B] */ | |
187 | - val = cpu_to_be32(*(u32 *) &ecc[9]); | |
188 | - elm_write_reg(info, offset, val); | |
189 | - | |
190 | - /* syndrome fragment 1 = ecc[5-8B] */ | |
191 | - offset += 4; | |
192 | - val = cpu_to_be32(*(u32 *) &ecc[5]); | |
193 | - elm_write_reg(info, offset, val); | |
194 | - | |
195 | - /* syndrome fragment 2 = ecc[1-4B] */ | |
196 | - offset += 4; | |
197 | - val = cpu_to_be32(*(u32 *) &ecc[1]); | |
198 | - elm_write_reg(info, offset, val); | |
199 | - | |
200 | - /* syndrome fragment 3 = ecc[0B] */ | |
201 | - offset += 4; | |
202 | - val = ecc[0]; | |
203 | - elm_write_reg(info, offset, val); | |
204 | - break; | |
205 | - case BCH4_ECC: | |
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); | |
210 | - | |
211 | - /* syndrome fragment 1 = ecc[0-20b] bits */ | |
212 | - offset += 4; | |
213 | - val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; | |
214 | - elm_write_reg(info, offset, val); | |
215 | - break; | |
216 | - case BCH16_ECC: | |
217 | - val = cpu_to_be32(*(u32 *) &ecc[22]); | |
218 | - elm_write_reg(info, offset, val); | |
219 | - offset += 4; | |
220 | - val = cpu_to_be32(*(u32 *) &ecc[18]); | |
221 | - elm_write_reg(info, offset, val); | |
222 | - offset += 4; | |
223 | - val = cpu_to_be32(*(u32 *) &ecc[14]); | |
224 | - elm_write_reg(info, offset, val); | |
225 | - offset += 4; | |
226 | - val = cpu_to_be32(*(u32 *) &ecc[10]); | |
227 | - elm_write_reg(info, offset, val); | |
228 | - offset += 4; | |
229 | - val = cpu_to_be32(*(u32 *) &ecc[6]); | |
230 | - elm_write_reg(info, offset, val); | |
231 | - offset += 4; | |
232 | - val = cpu_to_be32(*(u32 *) &ecc[2]); | |
233 | - elm_write_reg(info, offset, val); | |
234 | - offset += 4; | |
235 | - val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; | |
236 | - elm_write_reg(info, offset, val); | |
237 | - break; | |
238 | - default: | |
239 | - pr_err("invalid config bch_type\n"); | |
240 | - } | |
241 | - } | |
242 | - | |
243 | - /* Update ecc pointer with ecc byte size */ | |
244 | - ecc += info->ecc_syndrome_size; | |
245 | - } | |
246 | -} | |
247 | - | |
248 | -/** | |
249 | - * elm_start_processing - start elm syndrome processing | |
250 | - * @info: elm info | |
251 | - * @err_vec: elm error vectors | |
252 | - * | |
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. | |
256 | - */ | |
257 | -static void elm_start_processing(struct elm_info *info, | |
258 | - struct elm_errorvec *err_vec) | |
259 | -{ | |
260 | - int i, offset; | |
261 | - u32 reg_val; | |
262 | - | |
263 | - /* | |
264 | - * Set syndrome vector valid, so that ELM module | |
265 | - * will process it for vectors error is reported | |
266 | - */ | |
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); | |
274 | - } | |
275 | - } | |
276 | -} | |
277 | - | |
278 | -/** | |
279 | - * elm_error_correction - locate correctable error position | |
280 | - * @info: elm info | |
281 | - * @err_vec: elm error vectors | |
282 | - * | |
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. | |
288 | - */ | |
289 | -static void elm_error_correction(struct elm_info *info, | |
290 | - struct elm_errorvec *err_vec) | |
291 | -{ | |
292 | - int i, j, errors = 0; | |
293 | - int offset; | |
294 | - u32 reg_val; | |
295 | - | |
296 | - for (i = 0; i < info->ecc_steps; i++) { | |
297 | - | |
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); | |
302 | - | |
303 | - /* Check correctable error or not */ | |
304 | - if (reg_val & ECC_CORRECTABLE_MASK) { | |
305 | - offset = ELM_ERROR_LOCATION_0 + | |
306 | - ERROR_LOCATION_SIZE * i; | |
307 | - | |
308 | - /* Read count of correctable errors */ | |
309 | - err_vec[i].error_count = reg_val & | |
310 | - ECC_NB_ERRORS_MASK; | |
311 | - | |
312 | - /* Update the error locations in error vector */ | |
313 | - for (j = 0; j < err_vec[i].error_count; j++) { | |
314 | - | |
315 | - reg_val = elm_read_reg(info, offset); | |
316 | - err_vec[i].error_loc[j] = reg_val & | |
317 | - ECC_ERROR_LOCATION_MASK; | |
318 | - | |
319 | - /* Update error location register */ | |
320 | - offset += 4; | |
321 | - } | |
322 | - | |
323 | - errors += err_vec[i].error_count; | |
324 | - } else { | |
325 | - err_vec[i].error_uncorrectable = true; | |
326 | - } | |
327 | - | |
328 | - /* Clearing interrupts for processed error vectors */ | |
329 | - elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); | |
330 | - | |
331 | - /* Disable page mode */ | |
332 | - elm_configure_page_mode(info, i, false); | |
333 | - } | |
334 | - } | |
335 | -} | |
336 | - | |
337 | -/** | |
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 | |
342 | - * | |
343 | - * Called with one or more error reported vectors & vectors with | |
344 | - * error reported is updated in err_vec[].error_reported | |
345 | - */ | |
346 | -void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, | |
347 | - struct elm_errorvec *err_vec) | |
348 | -{ | |
349 | - struct elm_info *info = dev_get_drvdata(dev); | |
350 | - u32 reg_val; | |
351 | - | |
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); | |
356 | - | |
357 | - /* Load valid ecc byte to syndrome fragment register */ | |
358 | - elm_load_syndrome(info, err_vec, ecc_calc); | |
359 | - | |
360 | - /* Enable syndrome processing for which syndrome fragment is updated */ | |
361 | - elm_start_processing(info, err_vec); | |
362 | - | |
363 | - /* Wait for ELM module to finish locating error correction */ | |
364 | - wait_for_completion(&info->elm_completion); | |
365 | - | |
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); | |
370 | -} | |
371 | -EXPORT_SYMBOL(elm_decode_bch_error_page); | |
372 | - | |
373 | -static irqreturn_t elm_isr(int this_irq, void *dev_id) | |
374 | -{ | |
375 | - u32 reg_val; | |
376 | - struct elm_info *info = dev_id; | |
377 | - | |
378 | - reg_val = elm_read_reg(info, ELM_IRQSTATUS); | |
379 | - | |
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); | |
385 | - return IRQ_HANDLED; | |
386 | - } | |
387 | - | |
388 | - return IRQ_NONE; | |
389 | -} | |
390 | - | |
391 | -static int elm_probe(struct platform_device *pdev) | |
392 | -{ | |
393 | - int ret = 0; | |
394 | - struct resource *res, *irq; | |
395 | - struct elm_info *info; | |
396 | - | |
397 | - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | |
398 | - if (!info) | |
399 | - return -ENOMEM; | |
400 | - | |
401 | - info->dev = &pdev->dev; | |
402 | - | |
403 | - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
404 | - if (!irq) { | |
405 | - dev_err(&pdev->dev, "no irq resource defined\n"); | |
406 | - return -ENODEV; | |
407 | - } | |
408 | - | |
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); | |
413 | - | |
414 | - ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, | |
415 | - pdev->name, info); | |
416 | - if (ret) { | |
417 | - dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start); | |
418 | - return ret; | |
419 | - } | |
420 | - | |
421 | - pm_runtime_enable(&pdev->dev); | |
422 | - if (pm_runtime_get_sync(&pdev->dev) < 0) { | |
423 | - ret = -EINVAL; | |
424 | - pm_runtime_disable(&pdev->dev); | |
425 | - dev_err(&pdev->dev, "can't enable clock\n"); | |
426 | - return ret; | |
427 | - } | |
428 | - | |
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); | |
433 | - return ret; | |
434 | -} | |
435 | - | |
436 | -static int elm_remove(struct platform_device *pdev) | |
437 | -{ | |
438 | - pm_runtime_put_sync(&pdev->dev); | |
439 | - pm_runtime_disable(&pdev->dev); | |
440 | - return 0; | |
441 | -} | |
442 | - | |
443 | -#ifdef CONFIG_PM_SLEEP | |
444 | -/** | |
445 | - * elm_context_save | |
446 | - * saves ELM configurations to preserve them across Hardware powered-down | |
447 | - */ | |
448 | -static int elm_context_save(struct elm_info *info) | |
449 | -{ | |
450 | - struct elm_registers *regs = &info->elm_regs; | |
451 | - enum bch_ecc bch_type = info->bch_type; | |
452 | - u32 offset = 0, i; | |
453 | - | |
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; | |
460 | - switch (bch_type) { | |
461 | - case BCH16_ECC: | |
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); | |
468 | - case BCH8_ECC: | |
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); | |
473 | - case BCH4_ECC: | |
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); | |
478 | - break; | |
479 | - default: | |
480 | - return -EINVAL; | |
481 | - } | |
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); | |
486 | - } | |
487 | - return 0; | |
488 | -} | |
489 | - | |
490 | -/** | |
491 | - * elm_context_restore | |
492 | - * writes configurations saved duing power-down back into ELM registers | |
493 | - */ | |
494 | -static int elm_context_restore(struct elm_info *info) | |
495 | -{ | |
496 | - struct elm_registers *regs = &info->elm_regs; | |
497 | - enum bch_ecc bch_type = info->bch_type; | |
498 | - u32 offset = 0, i; | |
499 | - | |
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; | |
506 | - switch (bch_type) { | |
507 | - case BCH16_ECC: | |
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]); | |
514 | - case BCH8_ECC: | |
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]); | |
519 | - case BCH4_ECC: | |
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]); | |
524 | - break; | |
525 | - default: | |
526 | - return -EINVAL; | |
527 | - } | |
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] & | |
531 | - ELM_SYNDROME_VALID); | |
532 | - } | |
533 | - return 0; | |
534 | -} | |
535 | - | |
536 | -static int elm_suspend(struct device *dev) | |
537 | -{ | |
538 | - struct elm_info *info = dev_get_drvdata(dev); | |
539 | - elm_context_save(info); | |
540 | - pm_runtime_put_sync(dev); | |
541 | - return 0; | |
542 | -} | |
543 | - | |
544 | -static int elm_resume(struct device *dev) | |
545 | -{ | |
546 | - struct elm_info *info = dev_get_drvdata(dev); | |
547 | - pm_runtime_get_sync(dev); | |
548 | - elm_context_restore(info); | |
549 | - return 0; | |
550 | -} | |
551 | -#endif | |
552 | - | |
553 | -static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); | |
554 | - | |
555 | -#ifdef CONFIG_OF | |
556 | -static const struct of_device_id elm_of_match[] = { | |
557 | - { .compatible = "ti,am3352-elm" }, | |
558 | - {}, | |
559 | -}; | |
560 | -MODULE_DEVICE_TABLE(of, elm_of_match); | |
561 | -#endif | |
562 | - | |
563 | -static struct platform_driver elm_driver = { | |
564 | - .driver = { | |
565 | - .name = DRIVER_NAME, | |
566 | - .owner = THIS_MODULE, | |
567 | - .of_match_table = of_match_ptr(elm_of_match), | |
568 | - .pm = &elm_pm_ops, | |
569 | - }, | |
570 | - .probe = elm_probe, | |
571 | - .remove = elm_remove, | |
572 | -}; | |
573 | - | |
574 | -module_platform_driver(elm_driver); | |
575 | - | |
576 | -MODULE_DESCRIPTION("ELM driver for BCH error correction"); | |
577 | -MODULE_AUTHOR("Texas Instruments"); | |
578 | -MODULE_ALIAS("platform: elm"); | |
579 | -MODULE_LICENSE("GPL v2"); |
drivers/mtd/nand/Makefile
... | ... | @@ -27,6 +27,7 @@ |
27 | 27 | obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o |
28 | 28 | obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o |
29 | 29 | obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o |
30 | +obj-$(CONFIG_MTD_NAND_OMAP_BCH) += omap_elm.o | |
30 | 31 | obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o |
31 | 32 | obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o |
32 | 33 | obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o |
drivers/mtd/nand/omap_elm.c
1 | +/* | |
2 | + * Error Location Module | |
3 | + * | |
4 | + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | + * | |
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. | |
10 | + * | |
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. | |
15 | + * | |
16 | + */ | |
17 | + | |
18 | +#define DRIVER_NAME "omap-elm" | |
19 | + | |
20 | +#include <linux/platform_device.h> | |
21 | +#include <linux/module.h> | |
22 | +#include <linux/interrupt.h> | |
23 | +#include <linux/io.h> | |
24 | +#include <linux/of.h> | |
25 | +#include <linux/sched.h> | |
26 | +#include <linux/pm_runtime.h> | |
27 | +#include <linux/platform_data/elm.h> | |
28 | + | |
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 | |
43 | + | |
44 | +/* ELM Interrupt Status Register */ | |
45 | +#define INTR_STATUS_PAGE_VALID BIT(8) | |
46 | + | |
47 | +/* ELM Interrupt Enable Register */ | |
48 | +#define INTR_EN_PAGE_MASK BIT(8) | |
49 | + | |
50 | +/* ELM Location Configuration Register */ | |
51 | +#define ECC_BCH_LEVEL_MASK 0x3 | |
52 | + | |
53 | +/* ELM syndrome */ | |
54 | +#define ELM_SYNDROME_VALID BIT(16) | |
55 | + | |
56 | +/* ELM_LOCATION_STATUS Register */ | |
57 | +#define ECC_CORRECTABLE_MASK BIT(8) | |
58 | +#define ECC_NB_ERRORS_MASK 0x1f | |
59 | + | |
60 | +/* ELM_ERROR_LOCATION_0-15 Registers */ | |
61 | +#define ECC_ERROR_LOCATION_MASK 0x1fff | |
62 | + | |
63 | +#define ELM_ECC_SIZE 0x7ff | |
64 | + | |
65 | +#define SYNDROME_FRAGMENT_REG_SIZE 0x40 | |
66 | +#define ERROR_LOCATION_SIZE 0x100 | |
67 | + | |
68 | +struct elm_registers { | |
69 | + u32 elm_irqenable; | |
70 | + u32 elm_sysconfig; | |
71 | + u32 elm_location_config; | |
72 | + u32 elm_page_ctrl; | |
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]; | |
80 | +}; | |
81 | + | |
82 | +struct elm_info { | |
83 | + struct device *dev; | |
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; | |
89 | + int ecc_steps; | |
90 | + int ecc_syndrome_size; | |
91 | +}; | |
92 | + | |
93 | +static LIST_HEAD(elm_devices); | |
94 | + | |
95 | +static void elm_write_reg(struct elm_info *info, int offset, u32 val) | |
96 | +{ | |
97 | + writel(val, info->elm_base + offset); | |
98 | +} | |
99 | + | |
100 | +static u32 elm_read_reg(struct elm_info *info, int offset) | |
101 | +{ | |
102 | + return readl(info->elm_base + offset); | |
103 | +} | |
104 | + | |
105 | +/** | |
106 | + * elm_config - Configure ELM module | |
107 | + * @dev: ELM device | |
108 | + * @bch_type: Type of BCH ecc | |
109 | + */ | |
110 | +int elm_config(struct device *dev, enum bch_ecc bch_type, | |
111 | + int ecc_steps, int ecc_step_size, int ecc_syndrome_size) | |
112 | +{ | |
113 | + u32 reg_val; | |
114 | + struct elm_info *info = dev_get_drvdata(dev); | |
115 | + | |
116 | + if (!info) { | |
117 | + dev_err(dev, "Unable to configure elm - device not probed?\n"); | |
118 | + return -ENODEV; | |
119 | + } | |
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); | |
123 | + return -EINVAL; | |
124 | + } | |
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); | |
128 | + return -EINVAL; | |
129 | + } | |
130 | + | |
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; | |
136 | + | |
137 | + return 0; | |
138 | +} | |
139 | +EXPORT_SYMBOL(elm_config); | |
140 | + | |
141 | +/** | |
142 | + * elm_configure_page_mode - Enable/Disable page mode | |
143 | + * @info: elm info | |
144 | + * @index: index number of syndrome fragment vector | |
145 | + * @enable: enable/disable flag for page mode | |
146 | + * | |
147 | + * Enable page mode for syndrome fragment index | |
148 | + */ | |
149 | +static void elm_configure_page_mode(struct elm_info *info, int index, | |
150 | + bool enable) | |
151 | +{ | |
152 | + u32 reg_val; | |
153 | + | |
154 | + reg_val = elm_read_reg(info, ELM_PAGE_CTRL); | |
155 | + if (enable) | |
156 | + reg_val |= BIT(index); /* enable page mode */ | |
157 | + else | |
158 | + reg_val &= ~BIT(index); /* disable page mode */ | |
159 | + | |
160 | + elm_write_reg(info, ELM_PAGE_CTRL, reg_val); | |
161 | +} | |
162 | + | |
163 | +/** | |
164 | + * elm_load_syndrome - Load ELM syndrome reg | |
165 | + * @info: elm info | |
166 | + * @err_vec: elm error vectors | |
167 | + * @ecc: buffer with calculated ecc | |
168 | + * | |
169 | + * Load syndrome fragment registers with calculated ecc in reverse order. | |
170 | + */ | |
171 | +static void elm_load_syndrome(struct elm_info *info, | |
172 | + struct elm_errorvec *err_vec, u8 *ecc) | |
173 | +{ | |
174 | + int i, offset; | |
175 | + u32 val; | |
176 | + | |
177 | + for (i = 0; i < info->ecc_steps; i++) { | |
178 | + | |
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) { | |
185 | + case BCH8_ECC: | |
186 | + /* syndrome fragment 0 = ecc[9-12B] */ | |
187 | + val = cpu_to_be32(*(u32 *) &ecc[9]); | |
188 | + elm_write_reg(info, offset, val); | |
189 | + | |
190 | + /* syndrome fragment 1 = ecc[5-8B] */ | |
191 | + offset += 4; | |
192 | + val = cpu_to_be32(*(u32 *) &ecc[5]); | |
193 | + elm_write_reg(info, offset, val); | |
194 | + | |
195 | + /* syndrome fragment 2 = ecc[1-4B] */ | |
196 | + offset += 4; | |
197 | + val = cpu_to_be32(*(u32 *) &ecc[1]); | |
198 | + elm_write_reg(info, offset, val); | |
199 | + | |
200 | + /* syndrome fragment 3 = ecc[0B] */ | |
201 | + offset += 4; | |
202 | + val = ecc[0]; | |
203 | + elm_write_reg(info, offset, val); | |
204 | + break; | |
205 | + case BCH4_ECC: | |
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); | |
210 | + | |
211 | + /* syndrome fragment 1 = ecc[0-20b] bits */ | |
212 | + offset += 4; | |
213 | + val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; | |
214 | + elm_write_reg(info, offset, val); | |
215 | + break; | |
216 | + case BCH16_ECC: | |
217 | + val = cpu_to_be32(*(u32 *) &ecc[22]); | |
218 | + elm_write_reg(info, offset, val); | |
219 | + offset += 4; | |
220 | + val = cpu_to_be32(*(u32 *) &ecc[18]); | |
221 | + elm_write_reg(info, offset, val); | |
222 | + offset += 4; | |
223 | + val = cpu_to_be32(*(u32 *) &ecc[14]); | |
224 | + elm_write_reg(info, offset, val); | |
225 | + offset += 4; | |
226 | + val = cpu_to_be32(*(u32 *) &ecc[10]); | |
227 | + elm_write_reg(info, offset, val); | |
228 | + offset += 4; | |
229 | + val = cpu_to_be32(*(u32 *) &ecc[6]); | |
230 | + elm_write_reg(info, offset, val); | |
231 | + offset += 4; | |
232 | + val = cpu_to_be32(*(u32 *) &ecc[2]); | |
233 | + elm_write_reg(info, offset, val); | |
234 | + offset += 4; | |
235 | + val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; | |
236 | + elm_write_reg(info, offset, val); | |
237 | + break; | |
238 | + default: | |
239 | + pr_err("invalid config bch_type\n"); | |
240 | + } | |
241 | + } | |
242 | + | |
243 | + /* Update ecc pointer with ecc byte size */ | |
244 | + ecc += info->ecc_syndrome_size; | |
245 | + } | |
246 | +} | |
247 | + | |
248 | +/** | |
249 | + * elm_start_processing - start elm syndrome processing | |
250 | + * @info: elm info | |
251 | + * @err_vec: elm error vectors | |
252 | + * | |
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. | |
256 | + */ | |
257 | +static void elm_start_processing(struct elm_info *info, | |
258 | + struct elm_errorvec *err_vec) | |
259 | +{ | |
260 | + int i, offset; | |
261 | + u32 reg_val; | |
262 | + | |
263 | + /* | |
264 | + * Set syndrome vector valid, so that ELM module | |
265 | + * will process it for vectors error is reported | |
266 | + */ | |
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); | |
274 | + } | |
275 | + } | |
276 | +} | |
277 | + | |
278 | +/** | |
279 | + * elm_error_correction - locate correctable error position | |
280 | + * @info: elm info | |
281 | + * @err_vec: elm error vectors | |
282 | + * | |
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. | |
288 | + */ | |
289 | +static void elm_error_correction(struct elm_info *info, | |
290 | + struct elm_errorvec *err_vec) | |
291 | +{ | |
292 | + int i, j, errors = 0; | |
293 | + int offset; | |
294 | + u32 reg_val; | |
295 | + | |
296 | + for (i = 0; i < info->ecc_steps; i++) { | |
297 | + | |
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); | |
302 | + | |
303 | + /* Check correctable error or not */ | |
304 | + if (reg_val & ECC_CORRECTABLE_MASK) { | |
305 | + offset = ELM_ERROR_LOCATION_0 + | |
306 | + ERROR_LOCATION_SIZE * i; | |
307 | + | |
308 | + /* Read count of correctable errors */ | |
309 | + err_vec[i].error_count = reg_val & | |
310 | + ECC_NB_ERRORS_MASK; | |
311 | + | |
312 | + /* Update the error locations in error vector */ | |
313 | + for (j = 0; j < err_vec[i].error_count; j++) { | |
314 | + | |
315 | + reg_val = elm_read_reg(info, offset); | |
316 | + err_vec[i].error_loc[j] = reg_val & | |
317 | + ECC_ERROR_LOCATION_MASK; | |
318 | + | |
319 | + /* Update error location register */ | |
320 | + offset += 4; | |
321 | + } | |
322 | + | |
323 | + errors += err_vec[i].error_count; | |
324 | + } else { | |
325 | + err_vec[i].error_uncorrectable = true; | |
326 | + } | |
327 | + | |
328 | + /* Clearing interrupts for processed error vectors */ | |
329 | + elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); | |
330 | + | |
331 | + /* Disable page mode */ | |
332 | + elm_configure_page_mode(info, i, false); | |
333 | + } | |
334 | + } | |
335 | +} | |
336 | + | |
337 | +/** | |
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 | |
342 | + * | |
343 | + * Called with one or more error reported vectors & vectors with | |
344 | + * error reported is updated in err_vec[].error_reported | |
345 | + */ | |
346 | +void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, | |
347 | + struct elm_errorvec *err_vec) | |
348 | +{ | |
349 | + struct elm_info *info = dev_get_drvdata(dev); | |
350 | + u32 reg_val; | |
351 | + | |
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); | |
356 | + | |
357 | + /* Load valid ecc byte to syndrome fragment register */ | |
358 | + elm_load_syndrome(info, err_vec, ecc_calc); | |
359 | + | |
360 | + /* Enable syndrome processing for which syndrome fragment is updated */ | |
361 | + elm_start_processing(info, err_vec); | |
362 | + | |
363 | + /* Wait for ELM module to finish locating error correction */ | |
364 | + wait_for_completion(&info->elm_completion); | |
365 | + | |
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); | |
370 | +} | |
371 | +EXPORT_SYMBOL(elm_decode_bch_error_page); | |
372 | + | |
373 | +static irqreturn_t elm_isr(int this_irq, void *dev_id) | |
374 | +{ | |
375 | + u32 reg_val; | |
376 | + struct elm_info *info = dev_id; | |
377 | + | |
378 | + reg_val = elm_read_reg(info, ELM_IRQSTATUS); | |
379 | + | |
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); | |
385 | + return IRQ_HANDLED; | |
386 | + } | |
387 | + | |
388 | + return IRQ_NONE; | |
389 | +} | |
390 | + | |
391 | +static int elm_probe(struct platform_device *pdev) | |
392 | +{ | |
393 | + int ret = 0; | |
394 | + struct resource *res, *irq; | |
395 | + struct elm_info *info; | |
396 | + | |
397 | + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | |
398 | + if (!info) | |
399 | + return -ENOMEM; | |
400 | + | |
401 | + info->dev = &pdev->dev; | |
402 | + | |
403 | + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
404 | + if (!irq) { | |
405 | + dev_err(&pdev->dev, "no irq resource defined\n"); | |
406 | + return -ENODEV; | |
407 | + } | |
408 | + | |
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); | |
413 | + | |
414 | + ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, | |
415 | + pdev->name, info); | |
416 | + if (ret) { | |
417 | + dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start); | |
418 | + return ret; | |
419 | + } | |
420 | + | |
421 | + pm_runtime_enable(&pdev->dev); | |
422 | + if (pm_runtime_get_sync(&pdev->dev) < 0) { | |
423 | + ret = -EINVAL; | |
424 | + pm_runtime_disable(&pdev->dev); | |
425 | + dev_err(&pdev->dev, "can't enable clock\n"); | |
426 | + return ret; | |
427 | + } | |
428 | + | |
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); | |
433 | + return ret; | |
434 | +} | |
435 | + | |
436 | +static int elm_remove(struct platform_device *pdev) | |
437 | +{ | |
438 | + pm_runtime_put_sync(&pdev->dev); | |
439 | + pm_runtime_disable(&pdev->dev); | |
440 | + return 0; | |
441 | +} | |
442 | + | |
443 | +#ifdef CONFIG_PM_SLEEP | |
444 | +/** | |
445 | + * elm_context_save | |
446 | + * saves ELM configurations to preserve them across Hardware powered-down | |
447 | + */ | |
448 | +static int elm_context_save(struct elm_info *info) | |
449 | +{ | |
450 | + struct elm_registers *regs = &info->elm_regs; | |
451 | + enum bch_ecc bch_type = info->bch_type; | |
452 | + u32 offset = 0, i; | |
453 | + | |
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; | |
460 | + switch (bch_type) { | |
461 | + case BCH16_ECC: | |
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); | |
468 | + case BCH8_ECC: | |
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); | |
473 | + case BCH4_ECC: | |
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); | |
478 | + break; | |
479 | + default: | |
480 | + return -EINVAL; | |
481 | + } | |
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); | |
486 | + } | |
487 | + return 0; | |
488 | +} | |
489 | + | |
490 | +/** | |
491 | + * elm_context_restore | |
492 | + * writes configurations saved duing power-down back into ELM registers | |
493 | + */ | |
494 | +static int elm_context_restore(struct elm_info *info) | |
495 | +{ | |
496 | + struct elm_registers *regs = &info->elm_regs; | |
497 | + enum bch_ecc bch_type = info->bch_type; | |
498 | + u32 offset = 0, i; | |
499 | + | |
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; | |
506 | + switch (bch_type) { | |
507 | + case BCH16_ECC: | |
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]); | |
514 | + case BCH8_ECC: | |
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]); | |
519 | + case BCH4_ECC: | |
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]); | |
524 | + break; | |
525 | + default: | |
526 | + return -EINVAL; | |
527 | + } | |
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] & | |
531 | + ELM_SYNDROME_VALID); | |
532 | + } | |
533 | + return 0; | |
534 | +} | |
535 | + | |
536 | +static int elm_suspend(struct device *dev) | |
537 | +{ | |
538 | + struct elm_info *info = dev_get_drvdata(dev); | |
539 | + elm_context_save(info); | |
540 | + pm_runtime_put_sync(dev); | |
541 | + return 0; | |
542 | +} | |
543 | + | |
544 | +static int elm_resume(struct device *dev) | |
545 | +{ | |
546 | + struct elm_info *info = dev_get_drvdata(dev); | |
547 | + pm_runtime_get_sync(dev); | |
548 | + elm_context_restore(info); | |
549 | + return 0; | |
550 | +} | |
551 | +#endif | |
552 | + | |
553 | +static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); | |
554 | + | |
555 | +#ifdef CONFIG_OF | |
556 | +static const struct of_device_id elm_of_match[] = { | |
557 | + { .compatible = "ti,am3352-elm" }, | |
558 | + {}, | |
559 | +}; | |
560 | +MODULE_DEVICE_TABLE(of, elm_of_match); | |
561 | +#endif | |
562 | + | |
563 | +static struct platform_driver elm_driver = { | |
564 | + .driver = { | |
565 | + .name = DRIVER_NAME, | |
566 | + .owner = THIS_MODULE, | |
567 | + .of_match_table = of_match_ptr(elm_of_match), | |
568 | + .pm = &elm_pm_ops, | |
569 | + }, | |
570 | + .probe = elm_probe, | |
571 | + .remove = elm_remove, | |
572 | +}; | |
573 | + | |
574 | +module_platform_driver(elm_driver); | |
575 | + | |
576 | +MODULE_DESCRIPTION("ELM driver for BCH error correction"); | |
577 | +MODULE_AUTHOR("Texas Instruments"); | |
578 | +MODULE_ALIAS("platform: elm"); | |
579 | +MODULE_LICENSE("GPL v2"); |
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4
-
mentioned in commit 0c53b4