Commit 6d178ef2fd5e4a7f601874a6e641090e706da3c8

Authored by Ezequiel García
Committed by Brian Norris
1 parent d2f08c7521

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");