diff mbox

[v3,2/3] mtd: devices: elm: Add support for ELM error correction

Message ID 1354189595-12784-3-git-send-email-avinashphilip@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

avinash philip Nov. 29, 2012, 11:46 a.m. UTC
The ELM hardware module can be used to speedup BCH 4/8/16 ECC scheme
error correction.
For now only 4 & 8 bit support is added

Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
---
Changes since v2:
	- Remove __devinit & __devexit annotations

Changes since v1:
	- Change build attribute to CONFIG_MTD_NAND_OMAP_BCH
	- Reduced indentation using by passing elm_info , offset
	  to elm_read & elm_write
	- Removed syndrome manipulation functions.

:000000 100644 0000000... b88ee83... A	Documentation/devicetree/bindings/mtd/elm.txt
:100644 100644 395733a... 369a194... M	drivers/mtd/devices/Makefile
:000000 100644 0000000... d2667f3... A	drivers/mtd/devices/elm.c
:000000 100644 0000000... d4fce31... A	include/linux/platform_data/elm.h
 Documentation/devicetree/bindings/mtd/elm.txt |   17 +
 drivers/mtd/devices/Makefile                  |    4 +-
 drivers/mtd/devices/elm.c                     |  418 +++++++++++++++++++++++++
 include/linux/platform_data/elm.h             |   54 ++++
 4 files changed, 493 insertions(+), 1 deletions(-)

Comments

Sekhar Nori Dec. 7, 2012, 10:37 a.m. UTC | #1
On 11/29/2012 5:16 PM, Philip, Avinash wrote:
> The ELM hardware module can be used to speedup BCH 4/8/16 ECC scheme
> error correction.
> For now only 4 & 8 bit support is added
> 
> Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> ---
> Changes since v2:
> 	- Remove __devinit & __devexit annotations
> 
> Changes since v1:
> 	- Change build attribute to CONFIG_MTD_NAND_OMAP_BCH
> 	- Reduced indentation using by passing elm_info , offset
> 	  to elm_read & elm_write
> 	- Removed syndrome manipulation functions.
> 
> :000000 100644 0000000... b88ee83... A	Documentation/devicetree/bindings/mtd/elm.txt
> :100644 100644 395733a... 369a194... M	drivers/mtd/devices/Makefile
> :000000 100644 0000000... d2667f3... A	drivers/mtd/devices/elm.c
> :000000 100644 0000000... d4fce31... A	include/linux/platform_data/elm.h
>  Documentation/devicetree/bindings/mtd/elm.txt |   17 +
>  drivers/mtd/devices/Makefile                  |    4 +-
>  drivers/mtd/devices/elm.c                     |  418 +++++++++++++++++++++++++
>  include/linux/platform_data/elm.h             |   54 ++++
>  4 files changed, 493 insertions(+), 1 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt
> new file mode 100644
> index 0000000..b88ee83
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/elm.txt
> @@ -0,0 +1,17 @@
> +Error location module
> +
> +Required properties:
> +- compatible: Must be "ti,elm"
> +- reg: physical base address and size of the registers map.
> +- interrupts: Interrupt number for the elm.
> +- interrupt-parent: The parent interrupt controller
> +
> +Optional properties:
> +- ti,hwmods: Name of the hwmod associated to the elm
> +
> +Example:
> +elm: elm@0 {
> +	compatible	= "ti,elm";
> +	reg = <0x48080000 0x2000>;
> +	interrupts = <4>;
> +};
> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
> index 395733a..369a194 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -17,8 +17,10 @@ obj-$(CONFIG_MTD_LART)		+= lart.o
>  obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
>  obj-$(CONFIG_MTD_DATAFLASH)	+= mtd_dataflash.o
>  obj-$(CONFIG_MTD_M25P80)	+= m25p80.o
> +obj-$(CONFIG_MTD_NAND_OMAP_BCH)	+= elm.o
>  obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
>  obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
>  obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
>  
> -CFLAGS_docg3.o			+= -I$(src)
> \ No newline at end of file
> +
> +CFLAGS_docg3.o			+= -I$(src)
> diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c
> new file mode 100644
> index 0000000..d2667f3
> --- /dev/null
> +++ b/drivers/mtd/devices/elm.c
> @@ -0,0 +1,418 @@
> +/*
> + * Error Location Module
> + *
> + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/platform_data/elm.h>
> +
> +#define ELM_IRQSTATUS			0x018
> +#define ELM_IRQENABLE			0x01c
> +#define ELM_LOCATION_CONFIG		0x020
> +#define ELM_PAGE_CTRL			0x080
> +#define ELM_SYNDROME_FRAGMENT_0		0x400
> +#define ELM_SYNDROME_FRAGMENT_6		0x418
> +#define ELM_LOCATION_STATUS		0x800
> +#define ELM_ERROR_LOCATION_0		0x880
> +
> +/* ELM Interrupt Status Register */
> +#define INTR_STATUS_PAGE_VALID		BIT(8)
> +
> +/* ELM Interrupt Enable Register */
> +#define INTR_EN_PAGE_MASK		BIT(8)
> +
> +/* ELM Location Configuration Register */
> +#define ECC_BCH_LEVEL_MASK		0x3
> +
> +/* ELM syndrome */
> +#define ELM_SYNDROME_VALID		BIT(16)
> +
> +/* ELM_LOCATION_STATUS Register */
> +#define ECC_CORRECTABLE_MASK		BIT(8)
> +#define ECC_NB_ERRORS_MASK		0x1f
> +
> +/* ELM_ERROR_LOCATION_0-15 Registers */
> +#define ECC_ERROR_LOCATION_MASK		0x1fff
> +
> +#define ELM_ECC_SIZE			0x7ff
> +
> +#define SYNDROME_FRAGMENT_REG_SIZE	0x40
> +#define ERROR_LOCATION_SIZE		0x100
> +
> +struct elm_info {
> +	struct device *dev;
> +	void __iomem *elm_base;
> +	struct completion elm_completion;
> +	struct list_head list;
> +	enum bch_ecc bch_type;
> +};
> +
> +static LIST_HEAD(elm_devices);
> +
> +static void elm_write_reg(struct elm_info *info, int offset, u32 val)
> +{
> +	writel(val, info->elm_base + offset);
> +}
> +
> +static u32 elm_read_reg(struct elm_info *info, int offset)
> +{
> +	return readl(info->elm_base + offset);
> +}
> +
> +/**
> + * elm_config - Configure ELM module
> + * @info:	elm info
> + */
> +static void elm_config(struct elm_info *info)

This is called "config", but there is no configuration information
passed which looks odd..

> +{
> +	u32 reg_val;
> +
> +	reg_val = (info->bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
> +	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
> +}

Is there a use case where BCH type needs to be changed after NAND has
been probed? You will have to erase any existing data written to NAND if
you change the ECC type. That sounds destructive enough to avoid such a
thing.

> +
> +/**
> + * elm_configure_page_mode - Enable/Disable page mode
> + * @info:	elm info
> + * @index:	index number of syndrome fragment vector
> + * @enable:	enable/disable flag for page mode
> + *
> + * Enable page mode for syndrome fragment index
> + */
> +static void elm_configure_page_mode(struct elm_info *info, int index,
> +		bool enable)
> +{
> +	u32 reg_val;
> +
> +	reg_val = elm_read_reg(info, ELM_PAGE_CTRL);
> +	if (enable)
> +		reg_val |= BIT(index);	/* enable page mode */
> +	else
> +		reg_val &= ~BIT(index);	/* disable page mode */
> +
> +	elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
> +}
> +
> +/**
> + * elm_load_syndrome - Load ELM syndrome reg
> + * @info:	elm info
> + * @err_vec:	elm error vectors
> + * @ecc:	buffer with calculated ecc
> + *
> + * Load syndrome fragment registers with calculated ecc in reverse order.
> + */
> +static void elm_load_syndrome(struct elm_info *info,
> +		struct elm_errorvec *err_vec, u8 *ecc)
> +{
> +	int i, offset;
> +	u32 val;
> +
> +	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
> +
> +		/* Check error reported */
> +		if (err_vec[i].error_reported) {
> +			elm_configure_page_mode(info, i, true);
> +			offset = ELM_SYNDROME_FRAGMENT_0 +
> +				SYNDROME_FRAGMENT_REG_SIZE * i;
> +
> +			/* BCH8 */
> +			if (info->bch_type) {
> +
> +				/* syndrome fragment 0 = ecc[9-12B] */
> +				val = cpu_to_be32(*(u32 *) &ecc[9]);
> +				elm_write_reg(info, offset, val);
> +
> +				/* syndrome fragment 1 = ecc[5-8B] */
> +				offset += 4;
> +				val = cpu_to_be32(*(u32 *) &ecc[5]);
> +				elm_write_reg(info, offset, val);
> +
> +				/* syndrome fragment 2 = ecc[1-4B] */
> +				offset += 4;
> +				val = cpu_to_be32(*(u32 *) &ecc[1]);
> +				elm_write_reg(info, offset, val);
> +
> +				/* syndrome fragment 3 = ecc[0B] */
> +				offset += 4;
> +				val = ecc[0];
> +				elm_write_reg(info, offset, val);
> +			} else {
> +				/* syndrome fragment 0 = ecc[20-52b] bits */
> +				val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
> +					((ecc[2] & 0xf) << 28);
> +				elm_write_reg(info, offset, val);
> +
> +				/* syndrome fragment 1 = ecc[0-20b] bits */
> +				offset += 4;
> +				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
> +				elm_write_reg(info, offset, val);
> +			}
> +		}
> +
> +		/* Update ecc pointer with ecc byte size */
> +		ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
> +	}
> +}
> +
> +/**
> + * elm_start_processing - start elm syndrome processing
> + * @info:	elm info
> + * @err_vec:	elm error vectors
> + *
> + * Set syndrome valid bit for syndrome fragment registers for which
> + * elm syndrome fragment registers are loaded. This enables elm module
> + * to start processing syndrome vectors.
> + */
> +static void elm_start_processing(struct elm_info *info,
> +		struct elm_errorvec *err_vec)
> +{
> +	int i, offset;
> +	u32 reg_val;
> +
> +	/*
> +	 * Set syndrome vector valid, so that ELM module
> +	 * will process it for vectors error is reported
> +	 */
> +	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
> +		if (err_vec[i].error_reported) {
> +			offset = ELM_SYNDROME_FRAGMENT_6 +
> +				SYNDROME_FRAGMENT_REG_SIZE * i;
> +			reg_val = elm_read_reg(info, offset);
> +			reg_val |= ELM_SYNDROME_VALID;
> +			elm_write_reg(info, offset, reg_val);
> +		}
> +	}
> +}
> +
> +/**
> + * elm_error_correction - locate correctable error position
> + * @info:	elm info
> + * @err_vec:	elm error vectors
> + *
> + * On completion of processing by elm module, error location status
> + * register updated with correctable/uncorrectable error information.
> + * In case of correctable errors, number of errors located from
> + * elm location status register & read the positions from
> + * elm error location register.
> + */
> +static void elm_error_correction(struct elm_info *info,
> +		struct elm_errorvec *err_vec)
> +{
> +	int i, j, errors = 0;
> +	int offset;
> +	u32 reg_val;
> +
> +	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
> +
> +		/* Check error reported */
> +		if (err_vec[i].error_reported) {
> +			offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;
> +			reg_val = elm_read_reg(info, offset);
> +
> +			/* Check correctable error or not */
> +			if (reg_val & ECC_CORRECTABLE_MASK) {
> +				offset = ELM_ERROR_LOCATION_0 +
> +					ERROR_LOCATION_SIZE * i;
> +
> +				/* Read count of correctable errors */
> +				err_vec[i].error_count = reg_val &
> +					ECC_NB_ERRORS_MASK;
> +
> +				/* Update the error locations in error vector */
> +				for (j = 0; j < err_vec[i].error_count; j++) {
> +
> +					reg_val = elm_read_reg(info, offset);
> +					err_vec[i].error_loc[j] = reg_val &
> +						ECC_ERROR_LOCATION_MASK;
> +
> +					/* Update error location register */
> +					offset += 4;
> +				}
> +
> +				errors += err_vec[i].error_count;
> +			} else {
> +				err_vec[i].error_uncorrectable = true;
> +			}
> +
> +			/* Clearing interrupts for processed error vectors */
> +			elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
> +
> +			/* Disable page mode */
> +			elm_configure_page_mode(info, i, false);
> +		}
> +	}
> +}
> +
> +/**
> + * elm_decode_bch_error_page - Locate error position
> + * @dev:	device pointer
> + * @ecc_calc:	calculated ECC bytes from GPMC
> + * @err_vec:	elm error vectors
> + *
> + * Called with one or more error reported vectors & vectors with
> + * error reported is updated in err_vec[].error_reported
> + *
> + */
> +void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
> +		struct elm_errorvec *err_vec)
> +{
> +	struct elm_info *info = dev_get_drvdata(dev);
> +	u32 reg_val;
> +
> +	/* Enable page mode interrupt */
> +	reg_val = elm_read_reg(info, ELM_IRQSTATUS);
> +	elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);
> +	elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);
> +
> +	/* Load valid ecc byte to syndrome fragment register */
> +	elm_load_syndrome(info, err_vec, ecc_calc);
> +
> +	/* Enable syndrome processing for which syndrome fragment is updated */
> +	elm_start_processing(info, err_vec);
> +
> +	/* Wait for ELM module to finish locating error correction */
> +	wait_for_completion(&info->elm_completion);
> +
> +	/* Disable page mode interrupt */
> +	reg_val = elm_read_reg(info, ELM_IRQENABLE);
> +	elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);
> +	elm_error_correction(info, err_vec);
> +}
> +EXPORT_SYMBOL(elm_decode_bch_error_page);
> +
> +static irqreturn_t elm_isr(int this_irq, void *dev_id)
> +{
> +	u32 reg_val;
> +	struct elm_info *info = dev_id;
> +
> +	reg_val = elm_read_reg(info, ELM_IRQSTATUS);
> +
> +	/* All error vectors processed */
> +	if (reg_val & INTR_STATUS_PAGE_VALID) {
> +		elm_write_reg(info, ELM_IRQSTATUS,
> +				reg_val & INTR_STATUS_PAGE_VALID);
> +		complete(&info->elm_completion);
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +struct device *elm_request(enum bch_ecc bch_type)
> +{
> +	struct elm_info *info;
> +
> +	list_for_each_entry(info, &elm_devices, list) {
> +		if (info && info->dev) {
> +				info->bch_type = bch_type;
> +				elm_config(info);
> +				return info->dev;
> +		}
> +	}

This will always return the first ELM device probed since you never
remove the allocated device from the list. I wonder why you really need
a list?

Thanks,
Sekhar
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
avinash philip Dec. 10, 2012, 6:43 a.m. UTC | #2
T24gRnJpLCBEZWMgMDcsIDIwMTIgYXQgMTY6MDc6MjMsIE5vcmksIFNla2hhciB3cm90ZToNCj4g
T24gMTEvMjkvMjAxMiA1OjE2IFBNLCBQaGlsaXAsIEF2aW5hc2ggd3JvdGU6DQo+ID4gVGhlIEVM
TSBoYXJkd2FyZSBtb2R1bGUgY2FuIGJlIHVzZWQgdG8gc3BlZWR1cCBCQ0ggNC84LzE2IEVDQyBz
Y2hlbWUNCj4gPiBlcnJvciBjb3JyZWN0aW9uLg0KPiA+IEZvciBub3cgb25seSA0ICYgOCBiaXQg
c3VwcG9ydCBpcyBhZGRlZA0KPiA+IA0KPiA+IFNpZ25lZC1vZmYtYnk6IFBoaWxpcCwgQXZpbmFz
aCA8YXZpbmFzaHBoaWxpcEB0aS5jb20+DQo+ID4gQ2M6IEdyYW50IExpa2VseSA8Z3JhbnQubGlr
ZWx5QHNlY3JldGxhYi5jYT4NCj4gPiBDYzogUm9iIEhlcnJpbmcgPHJvYi5oZXJyaW5nQGNhbHhl
ZGEuY29tPg0KPiA+IENjOiBSb2IgTGFuZGxleSA8cm9iQGxhbmRsZXkubmV0Pg0KDQpbLi4uXQ0K
LyoqDQo+ID4gKyAqIGVsbV9jb25maWcgLSBDb25maWd1cmUgRUxNIG1vZHVsZQ0KPiA+ICsgKiBA
aW5mbzoJZWxtIGluZm8NCj4gPiArICovDQo+ID4gK3N0YXRpYyB2b2lkIGVsbV9jb25maWcoc3Ry
dWN0IGVsbV9pbmZvICppbmZvKQ0KPiANCj4gVGhpcyBpcyBjYWxsZWQgImNvbmZpZyIsIGJ1dCB0
aGVyZSBpcyBubyBjb25maWd1cmF0aW9uIGluZm9ybWF0aW9uDQo+IHBhc3NlZCB3aGljaCBsb29r
cyBvZGQuLg0KDQpUaGUgY29uZmlnIGluZm9ybWF0aW9uIGlzIGJjaF90eXBlLCBlbmNhcHN1bGF0
ZWQgaW4gc3RydWN0IGVsbV9pbmZvLg0KDQo+IA0KPiA+ICt7DQo+ID4gKwl1MzIgcmVnX3ZhbDsN
Cj4gPiArDQo+ID4gKwlyZWdfdmFsID0gKGluZm8tPmJjaF90eXBlICYgRUNDX0JDSF9MRVZFTF9N
QVNLKSB8IChFTE1fRUNDX1NJWkUgPDwgMTYpOw0KPiA+ICsJZWxtX3dyaXRlX3JlZyhpbmZvLCBF
TE1fTE9DQVRJT05fQ09ORklHLCByZWdfdmFsKTsNCj4gPiArfQ0KPiANCj4gSXMgdGhlcmUgYSB1
c2UgY2FzZSB3aGVyZSBCQ0ggdHlwZSBuZWVkcyB0byBiZSBjaGFuZ2VkIGFmdGVyIE5BTkQgaGFz
DQo+IGJlZW4gcHJvYmVkPw0KDQpObywgSSB0aGluayBrZXJuZWwgaGFuZGxlcyB0aGUgZW50aXJl
IE5BTkQgcGFydCB3aXRoIGEgc2luZ2xlIGVjYyBsYXlvdXQuDQpIZW5jZSB0aGVyZSBpcyBubyBy
dW4gdGltZSBCQ0ggc3dpdGNoaW5nLiBCdXQgRUxNIGRyaXZlciBzaG91bGQgc3VwcG9ydCBCQ0gN
CjQgJiA4LiBTZWxlY3Rpb24gb2YgQkNIIGluZm9ybWF0aW9uIGNvbWVzIGZyb20gRFQgb2YgTkFO
RCBkcml2ZXIuDQoNCkFzIE5BTkQgZHJpdmVyIHN1cHBvcnRpbmcgQkNINCAmIDggZWNjIHNjaGVt
ZSBFTE0gbW9kdWxlIHN1cHBvcnQNCmNvbmZpZ3VyYXRpb24gb2YgYm90aC4gIENvbmZpZ3VyYXRp
b24gb2YgRUxNIG1vZHVsZSBzaG91bGQgZG9uZSBhcyBwYXJ0DQpvZiBOQU5EIGRyaXZlciBwcm9i
aW5nLg0KDQoNCj4gWW91IHdpbGwgaGF2ZSB0byBlcmFzZSBhbnkgZXhpc3RpbmcgZGF0YSB3cml0
dGVuIHRvIE5BTkQgaWYNCj4geW91IGNoYW5nZSB0aGUgRUNDIHR5cGUuIFRoYXQgc291bmRzIGRl
c3RydWN0aXZlIGVub3VnaCB0byBhdm9pZCBzdWNoIGENCj4gdGhpbmcuDQoNClRoZXJlIGlzIG5v
IHN1cHBvcnQgZm9yIEJDSCBzd2l0Y2hpbmcgYWZ0ZXIgTkFORCBkcml2ZXIgcHJvYmluZy4NCg0K
Wy4uLl0NCj4gPiArc3RydWN0IGRldmljZSAqZWxtX3JlcXVlc3QoZW51bSBiY2hfZWNjIGJjaF90
eXBlKQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgZWxtX2luZm8gKmluZm87DQo+ID4gKw0KPiA+ICsJ
bGlzdF9mb3JfZWFjaF9lbnRyeShpbmZvLCAmZWxtX2RldmljZXMsIGxpc3QpIHsNCj4gPiArCQlp
ZiAoaW5mbyAmJiBpbmZvLT5kZXYpIHsNCj4gPiArCQkJCWluZm8tPmJjaF90eXBlID0gYmNoX3R5
cGU7DQo+ID4gKwkJCQllbG1fY29uZmlnKGluZm8pOw0KPiA+ICsJCQkJcmV0dXJuIGluZm8tPmRl
djsNCj4gPiArCQl9DQo+ID4gKwl9DQo+IA0KPiBUaGlzIHdpbGwgYWx3YXlzIHJldHVybiB0aGUg
Zmlyc3QgRUxNIGRldmljZSBwcm9iZWQgc2luY2UgeW91IG5ldmVyDQo+IHJlbW92ZSB0aGUgYWxs
b2NhdGVkIGRldmljZSBmcm9tIHRoZSBsaXN0Lg0KDQpCdXQgbm93IEkgcmVhbGl6ZWQgdGhhdCwg
dGhlcmUgaXMgbm8gbWVjaGFuaXNtIG9mIGZyZWVpbmcgdGhlIHJlcXVlc3RlZA0KcmVzb3VyY2Uu
DQoNClNvIEkgd2lsbCBhZGQgbWVjaGFuaXNtIHRvIHJlcXVlc3QgRUxNIG1vZHVsZSBzdWNjZXNz
ZnVsbHkgb25seSBpZiBFTE0NCm1vZHVsZSBpcyBub3QgcmVxdWVzdGVkIGFscmVhZHkgYW5kIGFk
ZCBtZWNoYW5pc20gdG8gZnJlZSBpdCwgb24gTkFORA0KZHJpdmVyIG1vZHVsZSB1bmxvYWQgKGxv
YWRhYmxlIG1vZHVsZSBzdXBwb3J0KS4gVGhpcyB3YXkgRUxNIGRyaXZlcg0KY2FuIGFjaGlldmUg
bXVsdGkgaW5zdGFuY2Ugc3VwcG9ydC4NCg0KPiBJIHdvbmRlciB3aHkgeW91IHJlYWxseSBuZWVk
IGEgbGlzdD8NCg0KVGhlIHByaW1lIG1vdGl2YXRpb24gZm9yIHRoZSBsaXN0IGlzIHRoZSBkcml2
ZXIgc2hvdWxkIHN1cHBvcnQgbXVsdGkNCmluc3RhbmNlcyBvZiBFTE0gYnkgcmVtb3ZpbmcgZ2xv
YmFsIHN5bWJvbHMuDQoNClRoYW5rcw0KQXZpbmFzaA0KDQo+IA0KPiBUaGFua3MsDQo+IFNla2hh
cg0KPiANCg0K
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Grant Likely Dec. 11, 2012, 9:03 a.m. UTC | #3
On Thu, 29 Nov 2012 17:16:33 +0530, "Philip, Avinash" <avinashphilip@ti.com> wrote:
> The ELM hardware module can be used to speedup BCH 4/8/16 ECC scheme
> error correction.
> For now only 4 & 8 bit support is added
> 
> Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> ---
> Changes since v2:
> 	- Remove __devinit & __devexit annotations
> 
> Changes since v1:
> 	- Change build attribute to CONFIG_MTD_NAND_OMAP_BCH
> 	- Reduced indentation using by passing elm_info , offset
> 	  to elm_read & elm_write
> 	- Removed syndrome manipulation functions.
> 
> :000000 100644 0000000... b88ee83... A	Documentation/devicetree/bindings/mtd/elm.txt
> :100644 100644 395733a... 369a194... M	drivers/mtd/devices/Makefile
> :000000 100644 0000000... d2667f3... A	drivers/mtd/devices/elm.c
> :000000 100644 0000000... d4fce31... A	include/linux/platform_data/elm.h
>  Documentation/devicetree/bindings/mtd/elm.txt |   17 +
>  drivers/mtd/devices/Makefile                  |    4 +-
>  drivers/mtd/devices/elm.c                     |  418 +++++++++++++++++++++++++
>  include/linux/platform_data/elm.h             |   54 ++++
>  4 files changed, 493 insertions(+), 1 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt
> new file mode 100644
> index 0000000..b88ee83
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/elm.txt
> @@ -0,0 +1,17 @@
> +Error location module
> +
> +Required properties:
> +- compatible: Must be "ti,elm"

Compatible string is too generic. Need to specify a specific SoC here.
ie: "ti,omap3430-elm"

Otherwise the binding looks fine. I haven't reviewed the code though.

g.

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
avinash philip Dec. 11, 2012, 12:55 p.m. UTC | #4
On Tue, Dec 11, 2012 at 14:33:56, Grant Likely wrote:
> On Thu, 29 Nov 2012 17:16:33 +0530, "Philip, Avinash" <avinashphilip@ti.com> wrote:
> > The ELM hardware module can be used to speedup BCH 4/8/16 ECC scheme
> > error correction.
> > For now only 4 & 8 bit support is added
> > 
> > Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> > Cc: Grant Likely <grant.likely@secretlab.ca>
> > Cc: Rob Herring <rob.herring@calxeda.com>
> > Cc: Rob Landley <rob@landley.net>
> > ---
> > Changes since v2:
> > 	- Remove __devinit & __devexit annotations
> > 
> > Changes since v1:
> > 	- Change build attribute to CONFIG_MTD_NAND_OMAP_BCH
> > 	- Reduced indentation using by passing elm_info , offset
> > 	  to elm_read & elm_write
> > 	- Removed syndrome manipulation functions.
> > 
> > :000000 100644 0000000... b88ee83... A	Documentation/devicetree/bindings/mtd/elm.txt
> > :100644 100644 395733a... 369a194... M	drivers/mtd/devices/Makefile
> > :000000 100644 0000000... d2667f3... A	drivers/mtd/devices/elm.c
> > :000000 100644 0000000... d4fce31... A	include/linux/platform_data/elm.h
> >  Documentation/devicetree/bindings/mtd/elm.txt |   17 +
> >  drivers/mtd/devices/Makefile                  |    4 +-
> >  drivers/mtd/devices/elm.c                     |  418 +++++++++++++++++++++++++
> >  include/linux/platform_data/elm.h             |   54 ++++
> >  4 files changed, 493 insertions(+), 1 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt
> > new file mode 100644
> > index 0000000..b88ee83
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mtd/elm.txt
> > @@ -0,0 +1,17 @@
> > +Error location module
> > +
> > +Required properties:
> > +- compatible: Must be "ti,elm"
> 
> Compatible string is too generic. Need to specify a specific SoC here.
> ie: "ti,omap3430-elm"

I will change to "ti,am33xx-elm" in next version.

Thanks
Avinash


> 
> Otherwise the binding looks fine. I haven't reviewed the code though.
> 
> g.
> 
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sekhar Nori Dec. 12, 2012, 11:15 a.m. UTC | #5
On 12/10/2012 12:13 PM, Philip, Avinash wrote:
> On Fri, Dec 07, 2012 at 16:07:23, Nori, Sekhar wrote:
>> On 11/29/2012 5:16 PM, Philip, Avinash wrote:

[...]

>>> +struct device *elm_request(enum bch_ecc bch_type)
>>> +{
>>> +	struct elm_info *info;
>>> +
>>> +	list_for_each_entry(info, &elm_devices, list) {
>>> +		if (info && info->dev) {
>>> +				info->bch_type = bch_type;
>>> +				elm_config(info);
>>> +				return info->dev;
>>> +		}
>>> +	}
>>
>> This will always return the first ELM device probed since you never
>> remove the allocated device from the list.
> 
> But now I realized that, there is no mechanism of freeing the requested
> resource.

Right. You essentially want to assign an ELM instance to work with a
given instance of GPMC and that could be done statically too. Just pass
phandle of ELM node in GPMC DT data?

> So I will add mechanism to request ELM module successfully only if ELM
> module is not requested already and add mechanism to free it, on NAND
> driver module unload (loadable module support). This way ELM driver
> can achieve multi instance support.
> 
>> I wonder why you really need a list?
> 
> The prime motivation for the list is the driver should support multi
> instances of ELM by removing global symbols.

I still think a request/free API is bit too much for something that will
turn out to be a simple 1-to-1 match anyway. Can you please look at the
phandle suggestion above? I am no DT expert, but I think that will work
for your use case.

Thanks,
Sekhar
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt
new file mode 100644
index 0000000..b88ee83
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/elm.txt
@@ -0,0 +1,17 @@ 
+Error location module
+
+Required properties:
+- compatible: Must be "ti,elm"
+- reg: physical base address and size of the registers map.
+- interrupts: Interrupt number for the elm.
+- interrupt-parent: The parent interrupt controller
+
+Optional properties:
+- ti,hwmods: Name of the hwmod associated to the elm
+
+Example:
+elm: elm@0 {
+	compatible	= "ti,elm";
+	reg = <0x48080000 0x2000>;
+	interrupts = <4>;
+};
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 395733a..369a194 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -17,8 +17,10 @@  obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)	+= mtd_dataflash.o
 obj-$(CONFIG_MTD_M25P80)	+= m25p80.o
+obj-$(CONFIG_MTD_NAND_OMAP_BCH)	+= elm.o
 obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
 obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
 
-CFLAGS_docg3.o			+= -I$(src)
\ No newline at end of file
+
+CFLAGS_docg3.o			+= -I$(src)
diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c
new file mode 100644
index 0000000..d2667f3
--- /dev/null
+++ b/drivers/mtd/devices/elm.c
@@ -0,0 +1,418 @@ 
+/*
+ * Error Location Module
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_data/elm.h>
+
+#define ELM_IRQSTATUS			0x018
+#define ELM_IRQENABLE			0x01c
+#define ELM_LOCATION_CONFIG		0x020
+#define ELM_PAGE_CTRL			0x080
+#define ELM_SYNDROME_FRAGMENT_0		0x400
+#define ELM_SYNDROME_FRAGMENT_6		0x418
+#define ELM_LOCATION_STATUS		0x800
+#define ELM_ERROR_LOCATION_0		0x880
+
+/* ELM Interrupt Status Register */
+#define INTR_STATUS_PAGE_VALID		BIT(8)
+
+/* ELM Interrupt Enable Register */
+#define INTR_EN_PAGE_MASK		BIT(8)
+
+/* ELM Location Configuration Register */
+#define ECC_BCH_LEVEL_MASK		0x3
+
+/* ELM syndrome */
+#define ELM_SYNDROME_VALID		BIT(16)
+
+/* ELM_LOCATION_STATUS Register */
+#define ECC_CORRECTABLE_MASK		BIT(8)
+#define ECC_NB_ERRORS_MASK		0x1f
+
+/* ELM_ERROR_LOCATION_0-15 Registers */
+#define ECC_ERROR_LOCATION_MASK		0x1fff
+
+#define ELM_ECC_SIZE			0x7ff
+
+#define SYNDROME_FRAGMENT_REG_SIZE	0x40
+#define ERROR_LOCATION_SIZE		0x100
+
+struct elm_info {
+	struct device *dev;
+	void __iomem *elm_base;
+	struct completion elm_completion;
+	struct list_head list;
+	enum bch_ecc bch_type;
+};
+
+static LIST_HEAD(elm_devices);
+
+static void elm_write_reg(struct elm_info *info, int offset, u32 val)
+{
+	writel(val, info->elm_base + offset);
+}
+
+static u32 elm_read_reg(struct elm_info *info, int offset)
+{
+	return readl(info->elm_base + offset);
+}
+
+/**
+ * elm_config - Configure ELM module
+ * @info:	elm info
+ */
+static void elm_config(struct elm_info *info)
+{
+	u32 reg_val;
+
+	reg_val = (info->bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
+	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
+}
+
+/**
+ * elm_configure_page_mode - Enable/Disable page mode
+ * @info:	elm info
+ * @index:	index number of syndrome fragment vector
+ * @enable:	enable/disable flag for page mode
+ *
+ * Enable page mode for syndrome fragment index
+ */
+static void elm_configure_page_mode(struct elm_info *info, int index,
+		bool enable)
+{
+	u32 reg_val;
+
+	reg_val = elm_read_reg(info, ELM_PAGE_CTRL);
+	if (enable)
+		reg_val |= BIT(index);	/* enable page mode */
+	else
+		reg_val &= ~BIT(index);	/* disable page mode */
+
+	elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
+}
+
+/**
+ * elm_load_syndrome - Load ELM syndrome reg
+ * @info:	elm info
+ * @err_vec:	elm error vectors
+ * @ecc:	buffer with calculated ecc
+ *
+ * Load syndrome fragment registers with calculated ecc in reverse order.
+ */
+static void elm_load_syndrome(struct elm_info *info,
+		struct elm_errorvec *err_vec, u8 *ecc)
+{
+	int i, offset;
+	u32 val;
+
+	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+
+		/* Check error reported */
+		if (err_vec[i].error_reported) {
+			elm_configure_page_mode(info, i, true);
+			offset = ELM_SYNDROME_FRAGMENT_0 +
+				SYNDROME_FRAGMENT_REG_SIZE * i;
+
+			/* BCH8 */
+			if (info->bch_type) {
+
+				/* syndrome fragment 0 = ecc[9-12B] */
+				val = cpu_to_be32(*(u32 *) &ecc[9]);
+				elm_write_reg(info, offset, val);
+
+				/* syndrome fragment 1 = ecc[5-8B] */
+				offset += 4;
+				val = cpu_to_be32(*(u32 *) &ecc[5]);
+				elm_write_reg(info, offset, val);
+
+				/* syndrome fragment 2 = ecc[1-4B] */
+				offset += 4;
+				val = cpu_to_be32(*(u32 *) &ecc[1]);
+				elm_write_reg(info, offset, val);
+
+				/* syndrome fragment 3 = ecc[0B] */
+				offset += 4;
+				val = ecc[0];
+				elm_write_reg(info, offset, val);
+			} else {
+				/* syndrome fragment 0 = ecc[20-52b] bits */
+				val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
+					((ecc[2] & 0xf) << 28);
+				elm_write_reg(info, offset, val);
+
+				/* syndrome fragment 1 = ecc[0-20b] bits */
+				offset += 4;
+				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
+				elm_write_reg(info, offset, val);
+			}
+		}
+
+		/* Update ecc pointer with ecc byte size */
+		ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
+	}
+}
+
+/**
+ * elm_start_processing - start elm syndrome processing
+ * @info:	elm info
+ * @err_vec:	elm error vectors
+ *
+ * Set syndrome valid bit for syndrome fragment registers for which
+ * elm syndrome fragment registers are loaded. This enables elm module
+ * to start processing syndrome vectors.
+ */
+static void elm_start_processing(struct elm_info *info,
+		struct elm_errorvec *err_vec)
+{
+	int i, offset;
+	u32 reg_val;
+
+	/*
+	 * Set syndrome vector valid, so that ELM module
+	 * will process it for vectors error is reported
+	 */
+	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+		if (err_vec[i].error_reported) {
+			offset = ELM_SYNDROME_FRAGMENT_6 +
+				SYNDROME_FRAGMENT_REG_SIZE * i;
+			reg_val = elm_read_reg(info, offset);
+			reg_val |= ELM_SYNDROME_VALID;
+			elm_write_reg(info, offset, reg_val);
+		}
+	}
+}
+
+/**
+ * elm_error_correction - locate correctable error position
+ * @info:	elm info
+ * @err_vec:	elm error vectors
+ *
+ * On completion of processing by elm module, error location status
+ * register updated with correctable/uncorrectable error information.
+ * In case of correctable errors, number of errors located from
+ * elm location status register & read the positions from
+ * elm error location register.
+ */
+static void elm_error_correction(struct elm_info *info,
+		struct elm_errorvec *err_vec)
+{
+	int i, j, errors = 0;
+	int offset;
+	u32 reg_val;
+
+	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+
+		/* Check error reported */
+		if (err_vec[i].error_reported) {
+			offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;
+			reg_val = elm_read_reg(info, offset);
+
+			/* Check correctable error or not */
+			if (reg_val & ECC_CORRECTABLE_MASK) {
+				offset = ELM_ERROR_LOCATION_0 +
+					ERROR_LOCATION_SIZE * i;
+
+				/* Read count of correctable errors */
+				err_vec[i].error_count = reg_val &
+					ECC_NB_ERRORS_MASK;
+
+				/* Update the error locations in error vector */
+				for (j = 0; j < err_vec[i].error_count; j++) {
+
+					reg_val = elm_read_reg(info, offset);
+					err_vec[i].error_loc[j] = reg_val &
+						ECC_ERROR_LOCATION_MASK;
+
+					/* Update error location register */
+					offset += 4;
+				}
+
+				errors += err_vec[i].error_count;
+			} else {
+				err_vec[i].error_uncorrectable = true;
+			}
+
+			/* Clearing interrupts for processed error vectors */
+			elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
+
+			/* Disable page mode */
+			elm_configure_page_mode(info, i, false);
+		}
+	}
+}
+
+/**
+ * elm_decode_bch_error_page - Locate error position
+ * @dev:	device pointer
+ * @ecc_calc:	calculated ECC bytes from GPMC
+ * @err_vec:	elm error vectors
+ *
+ * Called with one or more error reported vectors & vectors with
+ * error reported is updated in err_vec[].error_reported
+ *
+ */
+void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
+		struct elm_errorvec *err_vec)
+{
+	struct elm_info *info = dev_get_drvdata(dev);
+	u32 reg_val;
+
+	/* Enable page mode interrupt */
+	reg_val = elm_read_reg(info, ELM_IRQSTATUS);
+	elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);
+	elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);
+
+	/* Load valid ecc byte to syndrome fragment register */
+	elm_load_syndrome(info, err_vec, ecc_calc);
+
+	/* Enable syndrome processing for which syndrome fragment is updated */
+	elm_start_processing(info, err_vec);
+
+	/* Wait for ELM module to finish locating error correction */
+	wait_for_completion(&info->elm_completion);
+
+	/* Disable page mode interrupt */
+	reg_val = elm_read_reg(info, ELM_IRQENABLE);
+	elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);
+	elm_error_correction(info, err_vec);
+}
+EXPORT_SYMBOL(elm_decode_bch_error_page);
+
+static irqreturn_t elm_isr(int this_irq, void *dev_id)
+{
+	u32 reg_val;
+	struct elm_info *info = dev_id;
+
+	reg_val = elm_read_reg(info, ELM_IRQSTATUS);
+
+	/* All error vectors processed */
+	if (reg_val & INTR_STATUS_PAGE_VALID) {
+		elm_write_reg(info, ELM_IRQSTATUS,
+				reg_val & INTR_STATUS_PAGE_VALID);
+		complete(&info->elm_completion);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+struct device *elm_request(enum bch_ecc bch_type)
+{
+	struct elm_info *info;
+
+	list_for_each_entry(info, &elm_devices, list) {
+		if (info && info->dev) {
+				info->bch_type = bch_type;
+				elm_config(info);
+				return info->dev;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(elm_request);
+
+static int elm_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res, *irq;
+	struct elm_info *info;
+
+	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	info->dev = &pdev->dev;
+
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no irq resource defined\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	info->elm_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!info->elm_base)
+		return -EADDRNOTAVAIL;
+
+	ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
+			pdev->name, info);
+	if (ret) {
+		dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start);
+		return ret;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	if (pm_runtime_get_sync(&pdev->dev)) {
+		ret = -EINVAL;
+		pm_runtime_disable(&pdev->dev);
+		dev_err(&pdev->dev, "can't enable clock\n");
+		return ret;
+	}
+
+	init_completion(&info->elm_completion);
+	INIT_LIST_HEAD(&info->list);
+	list_add(&info->list, &elm_devices);
+	platform_set_drvdata(pdev, info);
+	return ret;
+}
+
+static int elm_remove(struct platform_device *pdev)
+{
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+
+#ifdef CONFIG_OF
+static const struct of_device_id elm_of_match[] = {
+	{ .compatible = "ti,elm" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, elm_of_match);
+#endif
+
+static struct platform_driver elm_driver = {
+	.driver	= {
+		.name	= "elm",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(elm_of_match),
+	},
+	.probe	= elm_probe,
+	.remove	= elm_remove,
+};
+
+module_platform_driver(elm_driver);
+
+MODULE_DESCRIPTION("ELM driver for BCH error correction");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_ALIAS("platform: elm");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h
new file mode 100644
index 0000000..d4fce31
--- /dev/null
+++ b/include/linux/platform_data/elm.h
@@ -0,0 +1,54 @@ 
+/*
+ * BCH Error Location Module
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ELM_H
+#define __ELM_H
+
+enum bch_ecc {
+	BCH4_ECC = 0,
+	BCH8_ECC,
+};
+
+/* ELM support 8 error syndrome process */
+#define ERROR_VECTOR_MAX		8
+
+#define BCH8_ECC_OOB_BYTES		13
+#define BCH4_ECC_OOB_BYTES		7
+/* RBL requires 14 byte even though BCH8 uses only 13 byte */
+#define BCH8_SIZE			(BCH8_ECC_OOB_BYTES + 1)
+#define BCH4_SIZE			(BCH4_ECC_OOB_BYTES)
+
+/**
+ * struct elm_errorvec - error vector for elm
+ * @error_reported:		set true for vectors error is reported
+ *
+ * @error_count:		number of correctable errors in the sector
+ * @error_uncorrectable:	number of uncorrectable errors
+ * @error_loc:			buffer for error location
+ *
+ */
+struct elm_errorvec {
+	bool error_reported;
+	bool error_uncorrectable;
+	int error_count;
+	int error_loc[ERROR_VECTOR_MAX];
+};
+
+void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
+		struct elm_errorvec *err_vec);
+struct device *elm_request(enum bch_ecc bch_type);
+#endif /* __ELM_H */