diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-02 19:33:54 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-02 19:33:54 -0500 |
commit | 48476df99894492a0f7239f2f3c9a2dde4ff38e2 (patch) | |
tree | 5a1b80f20449968f0de6e5bfbcda5e360e31ba1f /drivers/mtd/devices | |
parent | 37cae6ad4c484030fa972241533c32730ec79b7d (diff) | |
parent | 24dea0c9feccf699749f860fa2f4ccd84d30390d (diff) |
Merge tag 'for-linus-20130301' of git://git.infradead.org/linux-mtd
Pull MTD update from David Woodhouse:
"Fairly unexciting MTD merge for 3.9:
- misc clean-ups in the MTD command-line partitioning parser
(cmdlinepart)
- add flash locking support for STmicro chips serial flash chips, as
well as for CFI command set 2 chips.
- new driver for the ELM error correction HW module found in various
TI chips, enable the OMAP NAND driver to use the ELM HW error
correction
- added number of new serial flash IDs
- various fixes and improvements in the gpmi NAND driver
- bcm47xx NAND driver improvements
- make the mtdpart module actually removable"
* tag 'for-linus-20130301' of git://git.infradead.org/linux-mtd: (45 commits)
mtd: map: BUG() in non handled cases
mtd: bcm47xxnflash: use pr_fmt for module prefix in messages
mtd: davinci_nand: Use managed resources
mtd: mtd_torturetest can cause stack overflows
mtd: physmap_of: Convert device allocation to managed devm_kzalloc()
mtd: at91: atmel_nand: for PMECC, add code to check the ONFI parameter ECC requirement.
mtd: atmel_nand: make pmecc-cap, pmecc-sector-size in dts is optional.
mtd: atmel_nand: avoid to report an error when lookup table offset is 0.
mtd: bcm47xxsflash: adjust names of bus-specific functions
mtd: bcm47xxpart: improve probing of nvram partition
mtd: bcm47xxpart: add support for other erase sizes
mtd: bcm47xxnflash: register this as normal driver
mtd: bcm47xxnflash: fix message
mtd: bcm47xxsflash: register this as normal driver
mtd: bcm47xxsflash: write number of written bytes
mtd: gpmi: add sanity check for the ECC
mtd: gpmi: set the Golois Field bit for mx6q's BCH
mtd: devices: elm: Removes <xx> literals in elm DT node
mtd: gpmi: fix a dereferencing freed memory error
mtd: fix the wrong timeo for panic_nand_wait()
...
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | drivers/mtd/devices/Makefile | 4 | ||||
-rw-r--r-- | drivers/mtd/devices/bcm47xxsflash.c | 55 | ||||
-rw-r--r-- | drivers/mtd/devices/bcm47xxsflash.h | 15 | ||||
-rw-r--r-- | drivers/mtd/devices/elm.c | 404 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 100 |
5 files changed, 560 insertions, 18 deletions
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 395733a30ef4..369a1943ca25 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -17,8 +17,10 @@ obj-$(CONFIG_MTD_LART) += lart.o | |||
17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
19 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 19 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
20 | obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o | ||
20 | obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o | 21 | obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o |
21 | obj-$(CONFIG_MTD_SST25L) += sst25l.o | 22 | obj-$(CONFIG_MTD_SST25L) += sst25l.o |
22 | obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o | 23 | obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o |
23 | 24 | ||
24 | CFLAGS_docg3.o += -I$(src) \ No newline at end of file | 25 | |
26 | CFLAGS_docg3.o += -I$(src) | ||
diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 4714584aa993..95266285acb1 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/platform_device.h> | 5 | #include <linux/platform_device.h> |
6 | #include <linux/bcma/bcma.h> | 6 | #include <linux/bcma/bcma.h> |
7 | 7 | ||
8 | #include "bcm47xxsflash.h" | ||
9 | |||
8 | MODULE_LICENSE("GPL"); | 10 | MODULE_LICENSE("GPL"); |
9 | MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); | 11 | MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); |
10 | 12 | ||
@@ -13,26 +15,28 @@ static const char *probes[] = { "bcm47xxpart", NULL }; | |||
13 | static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, | 15 | static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, |
14 | size_t *retlen, u_char *buf) | 16 | size_t *retlen, u_char *buf) |
15 | { | 17 | { |
16 | struct bcma_sflash *sflash = mtd->priv; | 18 | struct bcm47xxsflash *b47s = mtd->priv; |
17 | 19 | ||
18 | /* Check address range */ | 20 | /* Check address range */ |
19 | if ((from + len) > mtd->size) | 21 | if ((from + len) > mtd->size) |
20 | return -EINVAL; | 22 | return -EINVAL; |
21 | 23 | ||
22 | memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(sflash->window + from), | 24 | memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), |
23 | len); | 25 | len); |
26 | *retlen = len; | ||
24 | 27 | ||
25 | return len; | 28 | return len; |
26 | } | 29 | } |
27 | 30 | ||
28 | static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash, | 31 | static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) |
29 | struct mtd_info *mtd) | ||
30 | { | 32 | { |
31 | mtd->priv = sflash; | 33 | struct mtd_info *mtd = &b47s->mtd; |
34 | |||
35 | mtd->priv = b47s; | ||
32 | mtd->name = "bcm47xxsflash"; | 36 | mtd->name = "bcm47xxsflash"; |
33 | mtd->owner = THIS_MODULE; | 37 | mtd->owner = THIS_MODULE; |
34 | mtd->type = MTD_ROM; | 38 | mtd->type = MTD_ROM; |
35 | mtd->size = sflash->size; | 39 | mtd->size = b47s->size; |
36 | mtd->_read = bcm47xxsflash_read; | 40 | mtd->_read = bcm47xxsflash_read; |
37 | 41 | ||
38 | /* TODO: implement writing support and verify/change following code */ | 42 | /* TODO: implement writing support and verify/change following code */ |
@@ -40,19 +44,30 @@ static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash, | |||
40 | mtd->writebufsize = mtd->writesize = 1; | 44 | mtd->writebufsize = mtd->writesize = 1; |
41 | } | 45 | } |
42 | 46 | ||
43 | static int bcm47xxsflash_probe(struct platform_device *pdev) | 47 | /************************************************** |
48 | * BCMA | ||
49 | **************************************************/ | ||
50 | |||
51 | static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) | ||
44 | { | 52 | { |
45 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); | 53 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); |
54 | struct bcm47xxsflash *b47s; | ||
46 | int err; | 55 | int err; |
47 | 56 | ||
48 | sflash->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); | 57 | b47s = kzalloc(sizeof(*b47s), GFP_KERNEL); |
49 | if (!sflash->mtd) { | 58 | if (!b47s) { |
50 | err = -ENOMEM; | 59 | err = -ENOMEM; |
51 | goto out; | 60 | goto out; |
52 | } | 61 | } |
53 | bcm47xxsflash_fill_mtd(sflash, sflash->mtd); | 62 | sflash->priv = b47s; |
54 | 63 | ||
55 | err = mtd_device_parse_register(sflash->mtd, probes, NULL, NULL, 0); | 64 | b47s->window = sflash->window; |
65 | b47s->blocksize = sflash->blocksize; | ||
66 | b47s->numblocks = sflash->numblocks; | ||
67 | b47s->size = sflash->size; | ||
68 | bcm47xxsflash_fill_mtd(b47s); | ||
69 | |||
70 | err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); | ||
56 | if (err) { | 71 | if (err) { |
57 | pr_err("Failed to register MTD device: %d\n", err); | 72 | pr_err("Failed to register MTD device: %d\n", err); |
58 | goto err_dev_reg; | 73 | goto err_dev_reg; |
@@ -61,34 +76,40 @@ static int bcm47xxsflash_probe(struct platform_device *pdev) | |||
61 | return 0; | 76 | return 0; |
62 | 77 | ||
63 | err_dev_reg: | 78 | err_dev_reg: |
64 | kfree(sflash->mtd); | 79 | kfree(&b47s->mtd); |
65 | out: | 80 | out: |
66 | return err; | 81 | return err; |
67 | } | 82 | } |
68 | 83 | ||
69 | static int bcm47xxsflash_remove(struct platform_device *pdev) | 84 | static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) |
70 | { | 85 | { |
71 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); | 86 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); |
87 | struct bcm47xxsflash *b47s = sflash->priv; | ||
72 | 88 | ||
73 | mtd_device_unregister(sflash->mtd); | 89 | mtd_device_unregister(&b47s->mtd); |
74 | kfree(sflash->mtd); | 90 | kfree(b47s); |
75 | 91 | ||
76 | return 0; | 92 | return 0; |
77 | } | 93 | } |
78 | 94 | ||
79 | static struct platform_driver bcma_sflash_driver = { | 95 | static struct platform_driver bcma_sflash_driver = { |
80 | .remove = bcm47xxsflash_remove, | 96 | .probe = bcm47xxsflash_bcma_probe, |
97 | .remove = bcm47xxsflash_bcma_remove, | ||
81 | .driver = { | 98 | .driver = { |
82 | .name = "bcma_sflash", | 99 | .name = "bcma_sflash", |
83 | .owner = THIS_MODULE, | 100 | .owner = THIS_MODULE, |
84 | }, | 101 | }, |
85 | }; | 102 | }; |
86 | 103 | ||
104 | /************************************************** | ||
105 | * Init | ||
106 | **************************************************/ | ||
107 | |||
87 | static int __init bcm47xxsflash_init(void) | 108 | static int __init bcm47xxsflash_init(void) |
88 | { | 109 | { |
89 | int err; | 110 | int err; |
90 | 111 | ||
91 | err = platform_driver_probe(&bcma_sflash_driver, bcm47xxsflash_probe); | 112 | err = platform_driver_register(&bcma_sflash_driver); |
92 | if (err) | 113 | if (err) |
93 | pr_err("Failed to register BCMA serial flash driver: %d\n", | 114 | pr_err("Failed to register BCMA serial flash driver: %d\n", |
94 | err); | 115 | err); |
diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h new file mode 100644 index 000000000000..ebf6f710e23c --- /dev/null +++ b/drivers/mtd/devices/bcm47xxsflash.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef __BCM47XXSFLASH_H | ||
2 | #define __BCM47XXSFLASH_H | ||
3 | |||
4 | #include <linux/mtd/mtd.h> | ||
5 | |||
6 | struct bcm47xxsflash { | ||
7 | u32 window; | ||
8 | u32 blocksize; | ||
9 | u16 numblocks; | ||
10 | u32 size; | ||
11 | |||
12 | struct mtd_info mtd; | ||
13 | }; | ||
14 | |||
15 | #endif /* BCM47XXSFLASH */ | ||
diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c new file mode 100644 index 000000000000..2ec5da9ee248 --- /dev/null +++ b/drivers/mtd/devices/elm.c | |||
@@ -0,0 +1,404 @@ | |||
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 | #include <linux/platform_device.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/pm_runtime.h> | ||
24 | #include <linux/platform_data/elm.h> | ||
25 | |||
26 | #define ELM_IRQSTATUS 0x018 | ||
27 | #define ELM_IRQENABLE 0x01c | ||
28 | #define ELM_LOCATION_CONFIG 0x020 | ||
29 | #define ELM_PAGE_CTRL 0x080 | ||
30 | #define ELM_SYNDROME_FRAGMENT_0 0x400 | ||
31 | #define ELM_SYNDROME_FRAGMENT_6 0x418 | ||
32 | #define ELM_LOCATION_STATUS 0x800 | ||
33 | #define ELM_ERROR_LOCATION_0 0x880 | ||
34 | |||
35 | /* ELM Interrupt Status Register */ | ||
36 | #define INTR_STATUS_PAGE_VALID BIT(8) | ||
37 | |||
38 | /* ELM Interrupt Enable Register */ | ||
39 | #define INTR_EN_PAGE_MASK BIT(8) | ||
40 | |||
41 | /* ELM Location Configuration Register */ | ||
42 | #define ECC_BCH_LEVEL_MASK 0x3 | ||
43 | |||
44 | /* ELM syndrome */ | ||
45 | #define ELM_SYNDROME_VALID BIT(16) | ||
46 | |||
47 | /* ELM_LOCATION_STATUS Register */ | ||
48 | #define ECC_CORRECTABLE_MASK BIT(8) | ||
49 | #define ECC_NB_ERRORS_MASK 0x1f | ||
50 | |||
51 | /* ELM_ERROR_LOCATION_0-15 Registers */ | ||
52 | #define ECC_ERROR_LOCATION_MASK 0x1fff | ||
53 | |||
54 | #define ELM_ECC_SIZE 0x7ff | ||
55 | |||
56 | #define SYNDROME_FRAGMENT_REG_SIZE 0x40 | ||
57 | #define ERROR_LOCATION_SIZE 0x100 | ||
58 | |||
59 | struct elm_info { | ||
60 | struct device *dev; | ||
61 | void __iomem *elm_base; | ||
62 | struct completion elm_completion; | ||
63 | struct list_head list; | ||
64 | enum bch_ecc bch_type; | ||
65 | }; | ||
66 | |||
67 | static LIST_HEAD(elm_devices); | ||
68 | |||
69 | static void elm_write_reg(struct elm_info *info, int offset, u32 val) | ||
70 | { | ||
71 | writel(val, info->elm_base + offset); | ||
72 | } | ||
73 | |||
74 | static u32 elm_read_reg(struct elm_info *info, int offset) | ||
75 | { | ||
76 | return readl(info->elm_base + offset); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * elm_config - Configure ELM module | ||
81 | * @dev: ELM device | ||
82 | * @bch_type: Type of BCH ecc | ||
83 | */ | ||
84 | void elm_config(struct device *dev, enum bch_ecc bch_type) | ||
85 | { | ||
86 | u32 reg_val; | ||
87 | struct elm_info *info = dev_get_drvdata(dev); | ||
88 | |||
89 | reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); | ||
90 | elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); | ||
91 | info->bch_type = bch_type; | ||
92 | } | ||
93 | EXPORT_SYMBOL(elm_config); | ||
94 | |||
95 | /** | ||
96 | * elm_configure_page_mode - Enable/Disable page mode | ||
97 | * @info: elm info | ||
98 | * @index: index number of syndrome fragment vector | ||
99 | * @enable: enable/disable flag for page mode | ||
100 | * | ||
101 | * Enable page mode for syndrome fragment index | ||
102 | */ | ||
103 | static void elm_configure_page_mode(struct elm_info *info, int index, | ||
104 | bool enable) | ||
105 | { | ||
106 | u32 reg_val; | ||
107 | |||
108 | reg_val = elm_read_reg(info, ELM_PAGE_CTRL); | ||
109 | if (enable) | ||
110 | reg_val |= BIT(index); /* enable page mode */ | ||
111 | else | ||
112 | reg_val &= ~BIT(index); /* disable page mode */ | ||
113 | |||
114 | elm_write_reg(info, ELM_PAGE_CTRL, reg_val); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * elm_load_syndrome - Load ELM syndrome reg | ||
119 | * @info: elm info | ||
120 | * @err_vec: elm error vectors | ||
121 | * @ecc: buffer with calculated ecc | ||
122 | * | ||
123 | * Load syndrome fragment registers with calculated ecc in reverse order. | ||
124 | */ | ||
125 | static void elm_load_syndrome(struct elm_info *info, | ||
126 | struct elm_errorvec *err_vec, u8 *ecc) | ||
127 | { | ||
128 | int i, offset; | ||
129 | u32 val; | ||
130 | |||
131 | for (i = 0; i < ERROR_VECTOR_MAX; i++) { | ||
132 | |||
133 | /* Check error reported */ | ||
134 | if (err_vec[i].error_reported) { | ||
135 | elm_configure_page_mode(info, i, true); | ||
136 | offset = ELM_SYNDROME_FRAGMENT_0 + | ||
137 | SYNDROME_FRAGMENT_REG_SIZE * i; | ||
138 | |||
139 | /* BCH8 */ | ||
140 | if (info->bch_type) { | ||
141 | |||
142 | /* syndrome fragment 0 = ecc[9-12B] */ | ||
143 | val = cpu_to_be32(*(u32 *) &ecc[9]); | ||
144 | elm_write_reg(info, offset, val); | ||
145 | |||
146 | /* syndrome fragment 1 = ecc[5-8B] */ | ||
147 | offset += 4; | ||
148 | val = cpu_to_be32(*(u32 *) &ecc[5]); | ||
149 | elm_write_reg(info, offset, val); | ||
150 | |||
151 | /* syndrome fragment 2 = ecc[1-4B] */ | ||
152 | offset += 4; | ||
153 | val = cpu_to_be32(*(u32 *) &ecc[1]); | ||
154 | elm_write_reg(info, offset, val); | ||
155 | |||
156 | /* syndrome fragment 3 = ecc[0B] */ | ||
157 | offset += 4; | ||
158 | val = ecc[0]; | ||
159 | elm_write_reg(info, offset, val); | ||
160 | } else { | ||
161 | /* syndrome fragment 0 = ecc[20-52b] bits */ | ||
162 | val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | | ||
163 | ((ecc[2] & 0xf) << 28); | ||
164 | elm_write_reg(info, offset, val); | ||
165 | |||
166 | /* syndrome fragment 1 = ecc[0-20b] bits */ | ||
167 | offset += 4; | ||
168 | val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; | ||
169 | elm_write_reg(info, offset, val); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /* Update ecc pointer with ecc byte size */ | ||
174 | ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * elm_start_processing - start elm syndrome processing | ||
180 | * @info: elm info | ||
181 | * @err_vec: elm error vectors | ||
182 | * | ||
183 | * Set syndrome valid bit for syndrome fragment registers for which | ||
184 | * elm syndrome fragment registers are loaded. This enables elm module | ||
185 | * to start processing syndrome vectors. | ||
186 | */ | ||
187 | static void elm_start_processing(struct elm_info *info, | ||
188 | struct elm_errorvec *err_vec) | ||
189 | { | ||
190 | int i, offset; | ||
191 | u32 reg_val; | ||
192 | |||
193 | /* | ||
194 | * Set syndrome vector valid, so that ELM module | ||
195 | * will process it for vectors error is reported | ||
196 | */ | ||
197 | for (i = 0; i < ERROR_VECTOR_MAX; i++) { | ||
198 | if (err_vec[i].error_reported) { | ||
199 | offset = ELM_SYNDROME_FRAGMENT_6 + | ||
200 | SYNDROME_FRAGMENT_REG_SIZE * i; | ||
201 | reg_val = elm_read_reg(info, offset); | ||
202 | reg_val |= ELM_SYNDROME_VALID; | ||
203 | elm_write_reg(info, offset, reg_val); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * elm_error_correction - locate correctable error position | ||
210 | * @info: elm info | ||
211 | * @err_vec: elm error vectors | ||
212 | * | ||
213 | * On completion of processing by elm module, error location status | ||
214 | * register updated with correctable/uncorrectable error information. | ||
215 | * In case of correctable errors, number of errors located from | ||
216 | * elm location status register & read the positions from | ||
217 | * elm error location register. | ||
218 | */ | ||
219 | static void elm_error_correction(struct elm_info *info, | ||
220 | struct elm_errorvec *err_vec) | ||
221 | { | ||
222 | int i, j, errors = 0; | ||
223 | int offset; | ||
224 | u32 reg_val; | ||
225 | |||
226 | for (i = 0; i < ERROR_VECTOR_MAX; i++) { | ||
227 | |||
228 | /* Check error reported */ | ||
229 | if (err_vec[i].error_reported) { | ||
230 | offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; | ||
231 | reg_val = elm_read_reg(info, offset); | ||
232 | |||
233 | /* Check correctable error or not */ | ||
234 | if (reg_val & ECC_CORRECTABLE_MASK) { | ||
235 | offset = ELM_ERROR_LOCATION_0 + | ||
236 | ERROR_LOCATION_SIZE * i; | ||
237 | |||
238 | /* Read count of correctable errors */ | ||
239 | err_vec[i].error_count = reg_val & | ||
240 | ECC_NB_ERRORS_MASK; | ||
241 | |||
242 | /* Update the error locations in error vector */ | ||
243 | for (j = 0; j < err_vec[i].error_count; j++) { | ||
244 | |||
245 | reg_val = elm_read_reg(info, offset); | ||
246 | err_vec[i].error_loc[j] = reg_val & | ||
247 | ECC_ERROR_LOCATION_MASK; | ||
248 | |||
249 | /* Update error location register */ | ||
250 | offset += 4; | ||
251 | } | ||
252 | |||
253 | errors += err_vec[i].error_count; | ||
254 | } else { | ||
255 | err_vec[i].error_uncorrectable = true; | ||
256 | } | ||
257 | |||
258 | /* Clearing interrupts for processed error vectors */ | ||
259 | elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); | ||
260 | |||
261 | /* Disable page mode */ | ||
262 | elm_configure_page_mode(info, i, false); | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * elm_decode_bch_error_page - Locate error position | ||
269 | * @dev: device pointer | ||
270 | * @ecc_calc: calculated ECC bytes from GPMC | ||
271 | * @err_vec: elm error vectors | ||
272 | * | ||
273 | * Called with one or more error reported vectors & vectors with | ||
274 | * error reported is updated in err_vec[].error_reported | ||
275 | */ | ||
276 | void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, | ||
277 | struct elm_errorvec *err_vec) | ||
278 | { | ||
279 | struct elm_info *info = dev_get_drvdata(dev); | ||
280 | u32 reg_val; | ||
281 | |||
282 | /* Enable page mode interrupt */ | ||
283 | reg_val = elm_read_reg(info, ELM_IRQSTATUS); | ||
284 | elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); | ||
285 | elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); | ||
286 | |||
287 | /* Load valid ecc byte to syndrome fragment register */ | ||
288 | elm_load_syndrome(info, err_vec, ecc_calc); | ||
289 | |||
290 | /* Enable syndrome processing for which syndrome fragment is updated */ | ||
291 | elm_start_processing(info, err_vec); | ||
292 | |||
293 | /* Wait for ELM module to finish locating error correction */ | ||
294 | wait_for_completion(&info->elm_completion); | ||
295 | |||
296 | /* Disable page mode interrupt */ | ||
297 | reg_val = elm_read_reg(info, ELM_IRQENABLE); | ||
298 | elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); | ||
299 | elm_error_correction(info, err_vec); | ||
300 | } | ||
301 | EXPORT_SYMBOL(elm_decode_bch_error_page); | ||
302 | |||
303 | static irqreturn_t elm_isr(int this_irq, void *dev_id) | ||
304 | { | ||
305 | u32 reg_val; | ||
306 | struct elm_info *info = dev_id; | ||
307 | |||
308 | reg_val = elm_read_reg(info, ELM_IRQSTATUS); | ||
309 | |||
310 | /* All error vectors processed */ | ||
311 | if (reg_val & INTR_STATUS_PAGE_VALID) { | ||
312 | elm_write_reg(info, ELM_IRQSTATUS, | ||
313 | reg_val & INTR_STATUS_PAGE_VALID); | ||
314 | complete(&info->elm_completion); | ||
315 | return IRQ_HANDLED; | ||
316 | } | ||
317 | |||
318 | return IRQ_NONE; | ||
319 | } | ||
320 | |||
321 | static int elm_probe(struct platform_device *pdev) | ||
322 | { | ||
323 | int ret = 0; | ||
324 | struct resource *res, *irq; | ||
325 | struct elm_info *info; | ||
326 | |||
327 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||
328 | if (!info) { | ||
329 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
330 | return -ENOMEM; | ||
331 | } | ||
332 | |||
333 | info->dev = &pdev->dev; | ||
334 | |||
335 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
336 | if (!irq) { | ||
337 | dev_err(&pdev->dev, "no irq resource defined\n"); | ||
338 | return -ENODEV; | ||
339 | } | ||
340 | |||
341 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
342 | if (!res) { | ||
343 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
344 | return -ENODEV; | ||
345 | } | ||
346 | |||
347 | info->elm_base = devm_request_and_ioremap(&pdev->dev, res); | ||
348 | if (!info->elm_base) | ||
349 | return -EADDRNOTAVAIL; | ||
350 | |||
351 | ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, | ||
352 | pdev->name, info); | ||
353 | if (ret) { | ||
354 | dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | pm_runtime_enable(&pdev->dev); | ||
359 | if (pm_runtime_get_sync(&pdev->dev)) { | ||
360 | ret = -EINVAL; | ||
361 | pm_runtime_disable(&pdev->dev); | ||
362 | dev_err(&pdev->dev, "can't enable clock\n"); | ||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | init_completion(&info->elm_completion); | ||
367 | INIT_LIST_HEAD(&info->list); | ||
368 | list_add(&info->list, &elm_devices); | ||
369 | platform_set_drvdata(pdev, info); | ||
370 | return ret; | ||
371 | } | ||
372 | |||
373 | static int elm_remove(struct platform_device *pdev) | ||
374 | { | ||
375 | pm_runtime_put_sync(&pdev->dev); | ||
376 | pm_runtime_disable(&pdev->dev); | ||
377 | platform_set_drvdata(pdev, NULL); | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | #ifdef CONFIG_OF | ||
382 | static const struct of_device_id elm_of_match[] = { | ||
383 | { .compatible = "ti,am3352-elm" }, | ||
384 | {}, | ||
385 | }; | ||
386 | MODULE_DEVICE_TABLE(of, elm_of_match); | ||
387 | #endif | ||
388 | |||
389 | static struct platform_driver elm_driver = { | ||
390 | .driver = { | ||
391 | .name = "elm", | ||
392 | .owner = THIS_MODULE, | ||
393 | .of_match_table = of_match_ptr(elm_of_match), | ||
394 | }, | ||
395 | .probe = elm_probe, | ||
396 | .remove = elm_remove, | ||
397 | }; | ||
398 | |||
399 | module_platform_driver(elm_driver); | ||
400 | |||
401 | MODULE_DESCRIPTION("ELM driver for BCH error correction"); | ||
402 | MODULE_AUTHOR("Texas Instruments"); | ||
403 | MODULE_ALIAS("platform: elm"); | ||
404 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4eeeb2d7f6ea..5b6b0728be21 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -565,6 +565,96 @@ time_out: | |||
565 | return ret; | 565 | return ret; |
566 | } | 566 | } |
567 | 567 | ||
568 | static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
569 | { | ||
570 | struct m25p *flash = mtd_to_m25p(mtd); | ||
571 | uint32_t offset = ofs; | ||
572 | uint8_t status_old, status_new; | ||
573 | int res = 0; | ||
574 | |||
575 | mutex_lock(&flash->lock); | ||
576 | /* Wait until finished previous command */ | ||
577 | if (wait_till_ready(flash)) { | ||
578 | res = 1; | ||
579 | goto err; | ||
580 | } | ||
581 | |||
582 | status_old = read_sr(flash); | ||
583 | |||
584 | if (offset < flash->mtd.size-(flash->mtd.size/2)) | ||
585 | status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; | ||
586 | else if (offset < flash->mtd.size-(flash->mtd.size/4)) | ||
587 | status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; | ||
588 | else if (offset < flash->mtd.size-(flash->mtd.size/8)) | ||
589 | status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; | ||
590 | else if (offset < flash->mtd.size-(flash->mtd.size/16)) | ||
591 | status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; | ||
592 | else if (offset < flash->mtd.size-(flash->mtd.size/32)) | ||
593 | status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; | ||
594 | else if (offset < flash->mtd.size-(flash->mtd.size/64)) | ||
595 | status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; | ||
596 | else | ||
597 | status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; | ||
598 | |||
599 | /* Only modify protection if it will not unlock other areas */ | ||
600 | if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) > | ||
601 | (status_old&(SR_BP2|SR_BP1|SR_BP0))) { | ||
602 | write_enable(flash); | ||
603 | if (write_sr(flash, status_new) < 0) { | ||
604 | res = 1; | ||
605 | goto err; | ||
606 | } | ||
607 | } | ||
608 | |||
609 | err: mutex_unlock(&flash->lock); | ||
610 | return res; | ||
611 | } | ||
612 | |||
613 | static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
614 | { | ||
615 | struct m25p *flash = mtd_to_m25p(mtd); | ||
616 | uint32_t offset = ofs; | ||
617 | uint8_t status_old, status_new; | ||
618 | int res = 0; | ||
619 | |||
620 | mutex_lock(&flash->lock); | ||
621 | /* Wait until finished previous command */ | ||
622 | if (wait_till_ready(flash)) { | ||
623 | res = 1; | ||
624 | goto err; | ||
625 | } | ||
626 | |||
627 | status_old = read_sr(flash); | ||
628 | |||
629 | if (offset+len > flash->mtd.size-(flash->mtd.size/64)) | ||
630 | status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0); | ||
631 | else if (offset+len > flash->mtd.size-(flash->mtd.size/32)) | ||
632 | status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; | ||
633 | else if (offset+len > flash->mtd.size-(flash->mtd.size/16)) | ||
634 | status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; | ||
635 | else if (offset+len > flash->mtd.size-(flash->mtd.size/8)) | ||
636 | status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; | ||
637 | else if (offset+len > flash->mtd.size-(flash->mtd.size/4)) | ||
638 | status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; | ||
639 | else if (offset+len > flash->mtd.size-(flash->mtd.size/2)) | ||
640 | status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; | ||
641 | else | ||
642 | status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; | ||
643 | |||
644 | /* Only modify protection if it will not lock other areas */ | ||
645 | if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) < | ||
646 | (status_old&(SR_BP2|SR_BP1|SR_BP0))) { | ||
647 | write_enable(flash); | ||
648 | if (write_sr(flash, status_new) < 0) { | ||
649 | res = 1; | ||
650 | goto err; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | err: mutex_unlock(&flash->lock); | ||
655 | return res; | ||
656 | } | ||
657 | |||
568 | /****************************************************************************/ | 658 | /****************************************************************************/ |
569 | 659 | ||
570 | /* | 660 | /* |
@@ -642,6 +732,10 @@ static const struct spi_device_id m25p_ids[] = { | |||
642 | /* Everspin */ | 732 | /* Everspin */ |
643 | { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, | 733 | { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, |
644 | 734 | ||
735 | /* GigaDevice */ | ||
736 | { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, | ||
737 | { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, | ||
738 | |||
645 | /* Intel/Numonyx -- xxxs33b */ | 739 | /* Intel/Numonyx -- xxxs33b */ |
646 | { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, | 740 | { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, |
647 | { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, | 741 | { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, |
@@ -899,6 +993,12 @@ static int m25p_probe(struct spi_device *spi) | |||
899 | flash->mtd._erase = m25p80_erase; | 993 | flash->mtd._erase = m25p80_erase; |
900 | flash->mtd._read = m25p80_read; | 994 | flash->mtd._read = m25p80_read; |
901 | 995 | ||
996 | /* flash protection support for STmicro chips */ | ||
997 | if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { | ||
998 | flash->mtd._lock = m25p80_lock; | ||
999 | flash->mtd._unlock = m25p80_unlock; | ||
1000 | } | ||
1001 | |||
902 | /* sst flash chips use AAI word program */ | 1002 | /* sst flash chips use AAI word program */ |
903 | if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) | 1003 | if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) |
904 | flash->mtd._write = sst_write; | 1004 | flash->mtd._write = sst_write; |