aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/onenand
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r--drivers/mtd/onenand/onenand_base.c132
1 files changed, 76 insertions, 56 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 7bd6ad3ff30a..894ebadc3e69 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -2183,69 +2183,33 @@ static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allo
2183} 2183}
2184 2184
2185/** 2185/**
2186 * onenand_erase - [MTD Interface] erase block(s) 2186 * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase
2187 * @param mtd MTD device structure 2187 * @param mtd MTD device structure
2188 * @param instr erase instruction 2188 * @param instr erase instruction
2189 * @param region erase region
2190 * @param block_size erase block size
2189 * 2191 *
2190 * Erase one ore more blocks 2192 * Erase one or more blocks one block at a time
2191 */ 2193 */
2192static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) 2194static int onenand_block_by_block_erase(struct mtd_info *mtd,
2195 struct erase_info *instr,
2196 struct mtd_erase_region_info *region,
2197 unsigned int block_size)
2193{ 2198{
2194 struct onenand_chip *this = mtd->priv; 2199 struct onenand_chip *this = mtd->priv;
2195 unsigned int block_size;
2196 loff_t addr = instr->addr; 2200 loff_t addr = instr->addr;
2197 loff_t len = instr->len; 2201 int len = instr->len;
2198 int ret = 0, i;
2199 struct mtd_erase_region_info *region = NULL;
2200 loff_t region_end = 0; 2202 loff_t region_end = 0;
2203 int ret = 0;
2201 2204
2202 DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); 2205 if (region) {
2203 2206 /* region is set for Flex-OneNAND */
2204 /* Do not allow erase past end of device */
2205 if (unlikely((len + addr) > mtd->size)) {
2206 printk(KERN_ERR "%s: Erase past end of device\n", __func__);
2207 return -EINVAL;
2208 }
2209
2210 if (FLEXONENAND(this)) {
2211 /* Find the eraseregion of this address */
2212 i = flexonenand_region(mtd, addr);
2213 region = &mtd->eraseregions[i];
2214
2215 block_size = region->erasesize;
2216 region_end = region->offset + region->erasesize * region->numblocks; 2207 region_end = region->offset + region->erasesize * region->numblocks;
2217
2218 /* Start address within region must align on block boundary.
2219 * Erase region's start offset is always block start address.
2220 */
2221 if (unlikely((addr - region->offset) & (block_size - 1))) {
2222 printk(KERN_ERR "%s: Unaligned address\n", __func__);
2223 return -EINVAL;
2224 }
2225 } else {
2226 block_size = 1 << this->erase_shift;
2227
2228 /* Start address must align on block boundary */
2229 if (unlikely(addr & (block_size - 1))) {
2230 printk(KERN_ERR "%s: Unaligned address\n", __func__);
2231 return -EINVAL;
2232 }
2233 }
2234
2235 /* Length must align on block boundary */
2236 if (unlikely(len & (block_size - 1))) {
2237 printk(KERN_ERR "%s: Length not block aligned\n", __func__);
2238 return -EINVAL;
2239 } 2208 }
2240 2209
2241 instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
2242
2243 /* Grab the lock and see if the device is available */
2244 onenand_get_device(mtd, FL_ERASING);
2245
2246 /* Loop through the blocks */
2247 instr->state = MTD_ERASING; 2210 instr->state = MTD_ERASING;
2248 2211
2212 /* Loop through the blocks */
2249 while (len) { 2213 while (len) {
2250 cond_resched(); 2214 cond_resched();
2251 2215
@@ -2255,7 +2219,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
2255 "at addr 0x%012llx\n", 2219 "at addr 0x%012llx\n",
2256 __func__, (unsigned long long) addr); 2220 __func__, (unsigned long long) addr);
2257 instr->state = MTD_ERASE_FAILED; 2221 instr->state = MTD_ERASE_FAILED;
2258 goto erase_exit; 2222 return -EIO;
2259 } 2223 }
2260 2224
2261 this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); 2225 this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
@@ -2269,7 +2233,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
2269 __func__, onenand_block(this, addr)); 2233 __func__, onenand_block(this, addr));
2270 instr->state = MTD_ERASE_FAILED; 2234 instr->state = MTD_ERASE_FAILED;
2271 instr->fail_addr = addr; 2235 instr->fail_addr = addr;
2272 goto erase_exit; 2236 return -EIO;
2273 } 2237 }
2274 2238
2275 len -= block_size; 2239 len -= block_size;
@@ -2287,24 +2251,80 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
2287 /* FIXME: This should be handled at MTD partitioning level. */ 2251 /* FIXME: This should be handled at MTD partitioning level. */
2288 printk(KERN_ERR "%s: Unaligned address\n", 2252 printk(KERN_ERR "%s: Unaligned address\n",
2289 __func__); 2253 __func__);
2290 goto erase_exit; 2254 return -EIO;
2291 } 2255 }
2292 } 2256 }
2257 }
2258 return 0;
2259}
2260
2261/**
2262 * onenand_erase - [MTD Interface] erase block(s)
2263 * @param mtd MTD device structure
2264 * @param instr erase instruction
2265 *
2266 * Erase one or more blocks
2267 */
2268static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
2269{
2270 struct onenand_chip *this = mtd->priv;
2271 unsigned int block_size;
2272 loff_t addr = instr->addr;
2273 loff_t len = instr->len;
2274 int ret = 0;
2275 struct mtd_erase_region_info *region = NULL;
2276 loff_t region_offset = 0;
2277
2278 DEBUG(MTD_DEBUG_LEVEL3, "%s: start=0x%012llx, len=%llu\n", __func__,
2279 (unsigned long long) instr->addr, (unsigned long long) instr->len);
2280
2281 /* Do not allow erase past end of device */
2282 if (unlikely((len + addr) > mtd->size)) {
2283 printk(KERN_ERR "%s: Erase past end of device\n", __func__);
2284 return -EINVAL;
2285 }
2286
2287 if (FLEXONENAND(this)) {
2288 /* Find the eraseregion of this address */
2289 int i = flexonenand_region(mtd, addr);
2290
2291 region = &mtd->eraseregions[i];
2292 block_size = region->erasesize;
2293
2294 /* Start address within region must align on block boundary.
2295 * Erase region's start offset is always block start address.
2296 */
2297 region_offset = region->offset;
2298 } else
2299 block_size = 1 << this->erase_shift;
2300
2301 /* Start address must align on block boundary */
2302 if (unlikely((addr - region_offset) & (block_size - 1))) {
2303 printk(KERN_ERR "%s: Unaligned address\n", __func__);
2304 return -EINVAL;
2305 }
2293 2306
2307 /* Length must align on block boundary */
2308 if (unlikely(len & (block_size - 1))) {
2309 printk(KERN_ERR "%s: Length not block aligned\n", __func__);
2310 return -EINVAL;
2294 } 2311 }
2295 2312
2296 instr->state = MTD_ERASE_DONE; 2313 instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
2297 2314
2298erase_exit: 2315 /* Grab the lock and see if the device is available */
2316 onenand_get_device(mtd, FL_ERASING);
2299 2317
2300 ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; 2318 ret = onenand_block_by_block_erase(mtd, instr, region, block_size);
2301 2319
2302 /* Deselect and wake up anyone waiting on the device */ 2320 /* Deselect and wake up anyone waiting on the device */
2303 onenand_release_device(mtd); 2321 onenand_release_device(mtd);
2304 2322
2305 /* Do call back function */ 2323 /* Do call back function */
2306 if (!ret) 2324 if (!ret) {
2325 instr->state = MTD_ERASE_DONE;
2307 mtd_erase_callback(instr); 2326 mtd_erase_callback(instr);
2327 }
2308 2328
2309 return ret; 2329 return ret;
2310} 2330}