10a572655SStefan Roese /* 20a572655SStefan Roese * MTD device concatenation layer 30a572655SStefan Roese * 40a572655SStefan Roese * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> 50a572655SStefan Roese * 60a572655SStefan Roese * NAND support by Christian Gan <cgan@iders.ca> 70a572655SStefan Roese * 80a572655SStefan Roese * This code is GPL 90a572655SStefan Roese */ 100a572655SStefan Roese 110a572655SStefan Roese #include <linux/mtd/mtd.h> 127b15e2bbSMike Frysinger #include <linux/compat.h> 130a572655SStefan Roese #include <linux/mtd/concat.h> 140a572655SStefan Roese #include <ubi_uboot.h> 150a572655SStefan Roese 160a572655SStefan Roese /* 170a572655SStefan Roese * Our storage structure: 180a572655SStefan Roese * Subdev points to an array of pointers to struct mtd_info objects 190a572655SStefan Roese * which is allocated along with this structure 200a572655SStefan Roese * 210a572655SStefan Roese */ 220a572655SStefan Roese struct mtd_concat { 230a572655SStefan Roese struct mtd_info mtd; 240a572655SStefan Roese int num_subdev; 250a572655SStefan Roese struct mtd_info **subdev; 260a572655SStefan Roese }; 270a572655SStefan Roese 280a572655SStefan Roese /* 290a572655SStefan Roese * how to calculate the size required for the above structure, 300a572655SStefan Roese * including the pointer array subdev points to: 310a572655SStefan Roese */ 320a572655SStefan Roese #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ 330a572655SStefan Roese ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) 340a572655SStefan Roese 350a572655SStefan Roese /* 360a572655SStefan Roese * Given a pointer to the MTD object in the mtd_concat structure, 370a572655SStefan Roese * we can retrieve the pointer to that structure with this macro. 380a572655SStefan Roese */ 390a572655SStefan Roese #define CONCAT(x) ((struct mtd_concat *)(x)) 400a572655SStefan Roese 410a572655SStefan Roese /* 420a572655SStefan Roese * MTD methods which look up the relevant subdevice, translate the 430a572655SStefan Roese * effective address and pass through to the subdevice. 440a572655SStefan Roese */ 450a572655SStefan Roese 460a572655SStefan Roese static int 470a572655SStefan Roese concat_read(struct mtd_info *mtd, loff_t from, size_t len, 480a572655SStefan Roese size_t * retlen, u_char * buf) 490a572655SStefan Roese { 500a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 510a572655SStefan Roese int ret = 0, err; 520a572655SStefan Roese int i; 530a572655SStefan Roese 540a572655SStefan Roese *retlen = 0; 550a572655SStefan Roese 560a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 570a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 580a572655SStefan Roese size_t size, retsize; 590a572655SStefan Roese 600a572655SStefan Roese if (from >= subdev->size) { 610a572655SStefan Roese /* Not destined for this subdev */ 620a572655SStefan Roese size = 0; 630a572655SStefan Roese from -= subdev->size; 640a572655SStefan Roese continue; 650a572655SStefan Roese } 660a572655SStefan Roese if (from + len > subdev->size) 670a572655SStefan Roese /* First part goes into this subdev */ 680a572655SStefan Roese size = subdev->size - from; 690a572655SStefan Roese else 700a572655SStefan Roese /* Entire transaction goes into this subdev */ 710a572655SStefan Roese size = len; 720a572655SStefan Roese 73*dfe64e2cSSergey Lapin err = mtd_read(subdev, from, size, &retsize, buf); 740a572655SStefan Roese 750a572655SStefan Roese /* Save information about bitflips! */ 760a572655SStefan Roese if (unlikely(err)) { 77*dfe64e2cSSergey Lapin if (mtd_is_eccerr(err)) { 780a572655SStefan Roese mtd->ecc_stats.failed++; 790a572655SStefan Roese ret = err; 80*dfe64e2cSSergey Lapin } else if (mtd_is_bitflip(err)) { 810a572655SStefan Roese mtd->ecc_stats.corrected++; 820a572655SStefan Roese /* Do not overwrite -EBADMSG !! */ 830a572655SStefan Roese if (!ret) 840a572655SStefan Roese ret = err; 850a572655SStefan Roese } else 860a572655SStefan Roese return err; 870a572655SStefan Roese } 880a572655SStefan Roese 890a572655SStefan Roese *retlen += retsize; 900a572655SStefan Roese len -= size; 910a572655SStefan Roese if (len == 0) 920a572655SStefan Roese return ret; 930a572655SStefan Roese 940a572655SStefan Roese buf += size; 950a572655SStefan Roese from = 0; 960a572655SStefan Roese } 970a572655SStefan Roese return -EINVAL; 980a572655SStefan Roese } 990a572655SStefan Roese 1000a572655SStefan Roese static int 1010a572655SStefan Roese concat_write(struct mtd_info *mtd, loff_t to, size_t len, 1020a572655SStefan Roese size_t * retlen, const u_char * buf) 1030a572655SStefan Roese { 1040a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 1050a572655SStefan Roese int err = -EINVAL; 1060a572655SStefan Roese int i; 1070a572655SStefan Roese 1080a572655SStefan Roese *retlen = 0; 1090a572655SStefan Roese 1100a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 1110a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 1120a572655SStefan Roese size_t size, retsize; 1130a572655SStefan Roese 1140a572655SStefan Roese if (to >= subdev->size) { 1150a572655SStefan Roese size = 0; 1160a572655SStefan Roese to -= subdev->size; 1170a572655SStefan Roese continue; 1180a572655SStefan Roese } 1190a572655SStefan Roese if (to + len > subdev->size) 1200a572655SStefan Roese size = subdev->size - to; 1210a572655SStefan Roese else 1220a572655SStefan Roese size = len; 1230a572655SStefan Roese 124*dfe64e2cSSergey Lapin err = mtd_write(subdev, to, size, &retsize, buf); 1250a572655SStefan Roese if (err) 1260a572655SStefan Roese break; 1270a572655SStefan Roese 1280a572655SStefan Roese *retlen += retsize; 1290a572655SStefan Roese len -= size; 1300a572655SStefan Roese if (len == 0) 1310a572655SStefan Roese break; 1320a572655SStefan Roese 1330a572655SStefan Roese err = -EINVAL; 1340a572655SStefan Roese buf += size; 1350a572655SStefan Roese to = 0; 1360a572655SStefan Roese } 1370a572655SStefan Roese return err; 1380a572655SStefan Roese } 1390a572655SStefan Roese 1400a572655SStefan Roese static int 1410a572655SStefan Roese concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) 1420a572655SStefan Roese { 1430a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 1440a572655SStefan Roese struct mtd_oob_ops devops = *ops; 1450a572655SStefan Roese int i, err, ret = 0; 1460a572655SStefan Roese 1470a572655SStefan Roese ops->retlen = ops->oobretlen = 0; 1480a572655SStefan Roese 1490a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 1500a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 1510a572655SStefan Roese 1520a572655SStefan Roese if (from >= subdev->size) { 1530a572655SStefan Roese from -= subdev->size; 1540a572655SStefan Roese continue; 1550a572655SStefan Roese } 1560a572655SStefan Roese 1570a572655SStefan Roese /* partial read ? */ 1580a572655SStefan Roese if (from + devops.len > subdev->size) 1590a572655SStefan Roese devops.len = subdev->size - from; 1600a572655SStefan Roese 161*dfe64e2cSSergey Lapin err = mtd_read_oob(subdev, from, &devops); 1620a572655SStefan Roese ops->retlen += devops.retlen; 1630a572655SStefan Roese ops->oobretlen += devops.oobretlen; 1640a572655SStefan Roese 1650a572655SStefan Roese /* Save information about bitflips! */ 1660a572655SStefan Roese if (unlikely(err)) { 167*dfe64e2cSSergey Lapin if (mtd_is_eccerr(err)) { 1680a572655SStefan Roese mtd->ecc_stats.failed++; 1690a572655SStefan Roese ret = err; 170*dfe64e2cSSergey Lapin } else if (mtd_is_bitflip(err)) { 1710a572655SStefan Roese mtd->ecc_stats.corrected++; 1720a572655SStefan Roese /* Do not overwrite -EBADMSG !! */ 1730a572655SStefan Roese if (!ret) 1740a572655SStefan Roese ret = err; 1750a572655SStefan Roese } else 1760a572655SStefan Roese return err; 1770a572655SStefan Roese } 1780a572655SStefan Roese 1790a572655SStefan Roese if (devops.datbuf) { 1800a572655SStefan Roese devops.len = ops->len - ops->retlen; 1810a572655SStefan Roese if (!devops.len) 1820a572655SStefan Roese return ret; 1830a572655SStefan Roese devops.datbuf += devops.retlen; 1840a572655SStefan Roese } 1850a572655SStefan Roese if (devops.oobbuf) { 1860a572655SStefan Roese devops.ooblen = ops->ooblen - ops->oobretlen; 1870a572655SStefan Roese if (!devops.ooblen) 1880a572655SStefan Roese return ret; 1890a572655SStefan Roese devops.oobbuf += ops->oobretlen; 1900a572655SStefan Roese } 1910a572655SStefan Roese 1920a572655SStefan Roese from = 0; 1930a572655SStefan Roese } 1940a572655SStefan Roese return -EINVAL; 1950a572655SStefan Roese } 1960a572655SStefan Roese 1970a572655SStefan Roese static int 1980a572655SStefan Roese concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) 1990a572655SStefan Roese { 2000a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 2010a572655SStefan Roese struct mtd_oob_ops devops = *ops; 2020a572655SStefan Roese int i, err; 2030a572655SStefan Roese 2040a572655SStefan Roese if (!(mtd->flags & MTD_WRITEABLE)) 2050a572655SStefan Roese return -EROFS; 2060a572655SStefan Roese 2070a572655SStefan Roese ops->retlen = 0; 2080a572655SStefan Roese 2090a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 2100a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 2110a572655SStefan Roese 2120a572655SStefan Roese if (to >= subdev->size) { 2130a572655SStefan Roese to -= subdev->size; 2140a572655SStefan Roese continue; 2150a572655SStefan Roese } 2160a572655SStefan Roese 2170a572655SStefan Roese /* partial write ? */ 2180a572655SStefan Roese if (to + devops.len > subdev->size) 2190a572655SStefan Roese devops.len = subdev->size - to; 2200a572655SStefan Roese 221*dfe64e2cSSergey Lapin err = mtd_write_oob(subdev, to, &devops); 2220a572655SStefan Roese ops->retlen += devops.retlen; 2230a572655SStefan Roese if (err) 2240a572655SStefan Roese return err; 2250a572655SStefan Roese 2260a572655SStefan Roese if (devops.datbuf) { 2270a572655SStefan Roese devops.len = ops->len - ops->retlen; 2280a572655SStefan Roese if (!devops.len) 2290a572655SStefan Roese return 0; 2300a572655SStefan Roese devops.datbuf += devops.retlen; 2310a572655SStefan Roese } 2320a572655SStefan Roese if (devops.oobbuf) { 2330a572655SStefan Roese devops.ooblen = ops->ooblen - ops->oobretlen; 2340a572655SStefan Roese if (!devops.ooblen) 2350a572655SStefan Roese return 0; 2360a572655SStefan Roese devops.oobbuf += devops.oobretlen; 2370a572655SStefan Roese } 2380a572655SStefan Roese to = 0; 2390a572655SStefan Roese } 2400a572655SStefan Roese return -EINVAL; 2410a572655SStefan Roese } 2420a572655SStefan Roese 2430a572655SStefan Roese static void concat_erase_callback(struct erase_info *instr) 2440a572655SStefan Roese { 2450a572655SStefan Roese /* Nothing to do here in U-Boot */ 2460a572655SStefan Roese } 2470a572655SStefan Roese 2480a572655SStefan Roese static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) 2490a572655SStefan Roese { 2500a572655SStefan Roese int err; 2510a572655SStefan Roese wait_queue_head_t waitq; 2520a572655SStefan Roese DECLARE_WAITQUEUE(wait, current); 2530a572655SStefan Roese 2540a572655SStefan Roese /* 2550a572655SStefan Roese * This code was stol^H^H^H^Hinspired by mtdchar.c 2560a572655SStefan Roese */ 2570a572655SStefan Roese init_waitqueue_head(&waitq); 2580a572655SStefan Roese 2590a572655SStefan Roese erase->mtd = mtd; 2600a572655SStefan Roese erase->callback = concat_erase_callback; 2610a572655SStefan Roese erase->priv = (unsigned long) &waitq; 2620a572655SStefan Roese 2630a572655SStefan Roese /* 2640a572655SStefan Roese * FIXME: Allow INTERRUPTIBLE. Which means 2650a572655SStefan Roese * not having the wait_queue head on the stack. 2660a572655SStefan Roese */ 267*dfe64e2cSSergey Lapin err = mtd_erase(mtd, erase); 2680a572655SStefan Roese if (!err) { 2690a572655SStefan Roese set_current_state(TASK_UNINTERRUPTIBLE); 2700a572655SStefan Roese add_wait_queue(&waitq, &wait); 2710a572655SStefan Roese if (erase->state != MTD_ERASE_DONE 2720a572655SStefan Roese && erase->state != MTD_ERASE_FAILED) 2730a572655SStefan Roese schedule(); 2740a572655SStefan Roese remove_wait_queue(&waitq, &wait); 2750a572655SStefan Roese set_current_state(TASK_RUNNING); 2760a572655SStefan Roese 2770a572655SStefan Roese err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; 2780a572655SStefan Roese } 2790a572655SStefan Roese return err; 2800a572655SStefan Roese } 2810a572655SStefan Roese 2820a572655SStefan Roese static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) 2830a572655SStefan Roese { 2840a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 2850a572655SStefan Roese struct mtd_info *subdev; 2860a572655SStefan Roese int i, err; 2870a572655SStefan Roese uint64_t length, offset = 0; 2880a572655SStefan Roese struct erase_info *erase; 2890a572655SStefan Roese 2900a572655SStefan Roese /* 2910a572655SStefan Roese * Check for proper erase block alignment of the to-be-erased area. 2920a572655SStefan Roese * It is easier to do this based on the super device's erase 2930a572655SStefan Roese * region info rather than looking at each particular sub-device 2940a572655SStefan Roese * in turn. 2950a572655SStefan Roese */ 2960a572655SStefan Roese if (!concat->mtd.numeraseregions) { 2970a572655SStefan Roese /* the easy case: device has uniform erase block size */ 2980a572655SStefan Roese if (instr->addr & (concat->mtd.erasesize - 1)) 2990a572655SStefan Roese return -EINVAL; 3000a572655SStefan Roese if (instr->len & (concat->mtd.erasesize - 1)) 3010a572655SStefan Roese return -EINVAL; 3020a572655SStefan Roese } else { 3030a572655SStefan Roese /* device has variable erase size */ 3040a572655SStefan Roese struct mtd_erase_region_info *erase_regions = 3050a572655SStefan Roese concat->mtd.eraseregions; 3060a572655SStefan Roese 3070a572655SStefan Roese /* 3080a572655SStefan Roese * Find the erase region where the to-be-erased area begins: 3090a572655SStefan Roese */ 3100a572655SStefan Roese for (i = 0; i < concat->mtd.numeraseregions && 3110a572655SStefan Roese instr->addr >= erase_regions[i].offset; i++) ; 3120a572655SStefan Roese --i; 3130a572655SStefan Roese 3140a572655SStefan Roese /* 3150a572655SStefan Roese * Now erase_regions[i] is the region in which the 3160a572655SStefan Roese * to-be-erased area begins. Verify that the starting 3170a572655SStefan Roese * offset is aligned to this region's erase size: 3180a572655SStefan Roese */ 3190a572655SStefan Roese if (instr->addr & (erase_regions[i].erasesize - 1)) 3200a572655SStefan Roese return -EINVAL; 3210a572655SStefan Roese 3220a572655SStefan Roese /* 3230a572655SStefan Roese * now find the erase region where the to-be-erased area ends: 3240a572655SStefan Roese */ 3250a572655SStefan Roese for (; i < concat->mtd.numeraseregions && 3260a572655SStefan Roese (instr->addr + instr->len) >= erase_regions[i].offset; 3270a572655SStefan Roese ++i) ; 3280a572655SStefan Roese --i; 3290a572655SStefan Roese /* 3300a572655SStefan Roese * check if the ending offset is aligned to this region's erase size 3310a572655SStefan Roese */ 3320a572655SStefan Roese if ((instr->addr + instr->len) & (erase_regions[i].erasesize - 3330a572655SStefan Roese 1)) 3340a572655SStefan Roese return -EINVAL; 3350a572655SStefan Roese } 3360a572655SStefan Roese 3370a572655SStefan Roese /* make a local copy of instr to avoid modifying the caller's struct */ 3380a572655SStefan Roese erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); 3390a572655SStefan Roese 3400a572655SStefan Roese if (!erase) 3410a572655SStefan Roese return -ENOMEM; 3420a572655SStefan Roese 3430a572655SStefan Roese *erase = *instr; 3440a572655SStefan Roese length = instr->len; 3450a572655SStefan Roese 3460a572655SStefan Roese /* 3470a572655SStefan Roese * find the subdevice where the to-be-erased area begins, adjust 3480a572655SStefan Roese * starting offset to be relative to the subdevice start 3490a572655SStefan Roese */ 3500a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 3510a572655SStefan Roese subdev = concat->subdev[i]; 3520a572655SStefan Roese if (subdev->size <= erase->addr) { 3530a572655SStefan Roese erase->addr -= subdev->size; 3540a572655SStefan Roese offset += subdev->size; 3550a572655SStefan Roese } else { 3560a572655SStefan Roese break; 3570a572655SStefan Roese } 3580a572655SStefan Roese } 3590a572655SStefan Roese 3600a572655SStefan Roese /* must never happen since size limit has been verified above */ 3610a572655SStefan Roese BUG_ON(i >= concat->num_subdev); 3620a572655SStefan Roese 3630a572655SStefan Roese /* now do the erase: */ 3640a572655SStefan Roese err = 0; 3650a572655SStefan Roese for (; length > 0; i++) { 3660a572655SStefan Roese /* loop for all subdevices affected by this request */ 3670a572655SStefan Roese subdev = concat->subdev[i]; /* get current subdevice */ 3680a572655SStefan Roese 3690a572655SStefan Roese /* limit length to subdevice's size: */ 3700a572655SStefan Roese if (erase->addr + length > subdev->size) 3710a572655SStefan Roese erase->len = subdev->size - erase->addr; 3720a572655SStefan Roese else 3730a572655SStefan Roese erase->len = length; 3740a572655SStefan Roese 3750a572655SStefan Roese length -= erase->len; 3760a572655SStefan Roese if ((err = concat_dev_erase(subdev, erase))) { 3770a572655SStefan Roese /* sanity check: should never happen since 3780a572655SStefan Roese * block alignment has been checked above */ 3790a572655SStefan Roese BUG_ON(err == -EINVAL); 3800a572655SStefan Roese if (erase->fail_addr != MTD_FAIL_ADDR_UNKNOWN) 3810a572655SStefan Roese instr->fail_addr = erase->fail_addr + offset; 3820a572655SStefan Roese break; 3830a572655SStefan Roese } 3840a572655SStefan Roese /* 3850a572655SStefan Roese * erase->addr specifies the offset of the area to be 3860a572655SStefan Roese * erased *within the current subdevice*. It can be 3870a572655SStefan Roese * non-zero only the first time through this loop, i.e. 3880a572655SStefan Roese * for the first subdevice where blocks need to be erased. 3890a572655SStefan Roese * All the following erases must begin at the start of the 3900a572655SStefan Roese * current subdevice, i.e. at offset zero. 3910a572655SStefan Roese */ 3920a572655SStefan Roese erase->addr = 0; 3930a572655SStefan Roese offset += subdev->size; 3940a572655SStefan Roese } 3950a572655SStefan Roese instr->state = erase->state; 3960a572655SStefan Roese kfree(erase); 3970a572655SStefan Roese if (err) 3980a572655SStefan Roese return err; 3990a572655SStefan Roese 4000a572655SStefan Roese if (instr->callback) 4010a572655SStefan Roese instr->callback(instr); 4020a572655SStefan Roese return 0; 4030a572655SStefan Roese } 4040a572655SStefan Roese 4050a572655SStefan Roese static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 4060a572655SStefan Roese { 4070a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 4080a572655SStefan Roese int i, err = -EINVAL; 4090a572655SStefan Roese 4100a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 4110a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 4120a572655SStefan Roese uint64_t size; 4130a572655SStefan Roese 4140a572655SStefan Roese if (ofs >= subdev->size) { 4150a572655SStefan Roese size = 0; 4160a572655SStefan Roese ofs -= subdev->size; 4170a572655SStefan Roese continue; 4180a572655SStefan Roese } 4190a572655SStefan Roese if (ofs + len > subdev->size) 4200a572655SStefan Roese size = subdev->size - ofs; 4210a572655SStefan Roese else 4220a572655SStefan Roese size = len; 4230a572655SStefan Roese 424*dfe64e2cSSergey Lapin err = mtd_lock(subdev, ofs, size); 4250a572655SStefan Roese 4260a572655SStefan Roese if (err) 4270a572655SStefan Roese break; 4280a572655SStefan Roese 4290a572655SStefan Roese len -= size; 4300a572655SStefan Roese if (len == 0) 4310a572655SStefan Roese break; 4320a572655SStefan Roese 4330a572655SStefan Roese err = -EINVAL; 4340a572655SStefan Roese ofs = 0; 4350a572655SStefan Roese } 4360a572655SStefan Roese 4370a572655SStefan Roese return err; 4380a572655SStefan Roese } 4390a572655SStefan Roese 4400a572655SStefan Roese static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 4410a572655SStefan Roese { 4420a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 4430a572655SStefan Roese int i, err = 0; 4440a572655SStefan Roese 4450a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 4460a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 4470a572655SStefan Roese uint64_t size; 4480a572655SStefan Roese 4490a572655SStefan Roese if (ofs >= subdev->size) { 4500a572655SStefan Roese size = 0; 4510a572655SStefan Roese ofs -= subdev->size; 4520a572655SStefan Roese continue; 4530a572655SStefan Roese } 4540a572655SStefan Roese if (ofs + len > subdev->size) 4550a572655SStefan Roese size = subdev->size - ofs; 4560a572655SStefan Roese else 4570a572655SStefan Roese size = len; 4580a572655SStefan Roese 459*dfe64e2cSSergey Lapin err = mtd_unlock(subdev, ofs, size); 4600a572655SStefan Roese 4610a572655SStefan Roese if (err) 4620a572655SStefan Roese break; 4630a572655SStefan Roese 4640a572655SStefan Roese len -= size; 4650a572655SStefan Roese if (len == 0) 4660a572655SStefan Roese break; 4670a572655SStefan Roese 4680a572655SStefan Roese err = -EINVAL; 4690a572655SStefan Roese ofs = 0; 4700a572655SStefan Roese } 4710a572655SStefan Roese 4720a572655SStefan Roese return err; 4730a572655SStefan Roese } 4740a572655SStefan Roese 4750a572655SStefan Roese static void concat_sync(struct mtd_info *mtd) 4760a572655SStefan Roese { 4770a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 4780a572655SStefan Roese int i; 4790a572655SStefan Roese 4800a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 4810a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 482*dfe64e2cSSergey Lapin mtd_sync(subdev); 4830a572655SStefan Roese } 4840a572655SStefan Roese } 4850a572655SStefan Roese 4860a572655SStefan Roese static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs) 4870a572655SStefan Roese { 4880a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 4890a572655SStefan Roese int i, res = 0; 4900a572655SStefan Roese 491*dfe64e2cSSergey Lapin if (!mtd_can_have_bb(concat->subdev[0])) 4920a572655SStefan Roese return res; 4930a572655SStefan Roese 4940a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 4950a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 4960a572655SStefan Roese 4970a572655SStefan Roese if (ofs >= subdev->size) { 4980a572655SStefan Roese ofs -= subdev->size; 4990a572655SStefan Roese continue; 5000a572655SStefan Roese } 5010a572655SStefan Roese 502*dfe64e2cSSergey Lapin res = mtd_block_isbad(subdev, ofs); 5030a572655SStefan Roese break; 5040a572655SStefan Roese } 5050a572655SStefan Roese 5060a572655SStefan Roese return res; 5070a572655SStefan Roese } 5080a572655SStefan Roese 5090a572655SStefan Roese static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) 5100a572655SStefan Roese { 5110a572655SStefan Roese struct mtd_concat *concat = CONCAT(mtd); 5120a572655SStefan Roese int i, err = -EINVAL; 5130a572655SStefan Roese 514*dfe64e2cSSergey Lapin if (!mtd_can_have_bb(concat->subdev[0])) 5150a572655SStefan Roese return 0; 5160a572655SStefan Roese 5170a572655SStefan Roese for (i = 0; i < concat->num_subdev; i++) { 5180a572655SStefan Roese struct mtd_info *subdev = concat->subdev[i]; 5190a572655SStefan Roese 5200a572655SStefan Roese if (ofs >= subdev->size) { 5210a572655SStefan Roese ofs -= subdev->size; 5220a572655SStefan Roese continue; 5230a572655SStefan Roese } 5240a572655SStefan Roese 525*dfe64e2cSSergey Lapin err = mtd_block_markbad(subdev, ofs); 5260a572655SStefan Roese if (!err) 5270a572655SStefan Roese mtd->ecc_stats.badblocks++; 5280a572655SStefan Roese break; 5290a572655SStefan Roese } 5300a572655SStefan Roese 5310a572655SStefan Roese return err; 5320a572655SStefan Roese } 5330a572655SStefan Roese 5340a572655SStefan Roese /* 5350a572655SStefan Roese * This function constructs a virtual MTD device by concatenating 5360a572655SStefan Roese * num_devs MTD devices. A pointer to the new device object is 5370a572655SStefan Roese * stored to *new_dev upon success. This function does _not_ 5380a572655SStefan Roese * register any devices: this is the caller's responsibility. 5390a572655SStefan Roese */ 5400a572655SStefan Roese struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ 5410a572655SStefan Roese int num_devs, /* number of subdevices */ 5420a572655SStefan Roese const char *name) 5430a572655SStefan Roese { /* name for the new device */ 5440a572655SStefan Roese int i; 5450a572655SStefan Roese size_t size; 5460a572655SStefan Roese struct mtd_concat *concat; 5470a572655SStefan Roese uint32_t max_erasesize, curr_erasesize; 5480a572655SStefan Roese int num_erase_region; 5490a572655SStefan Roese 5500a572655SStefan Roese debug("Concatenating MTD devices:\n"); 5510a572655SStefan Roese for (i = 0; i < num_devs; i++) 5520a572655SStefan Roese debug("(%d): \"%s\"\n", i, subdev[i]->name); 5530a572655SStefan Roese debug("into device \"%s\"\n", name); 5540a572655SStefan Roese 5550a572655SStefan Roese /* allocate the device structure */ 5560a572655SStefan Roese size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); 5570a572655SStefan Roese concat = kzalloc(size, GFP_KERNEL); 5580a572655SStefan Roese if (!concat) { 5590a572655SStefan Roese printk 5600a572655SStefan Roese ("memory allocation error while creating concatenated device \"%s\"\n", 5610a572655SStefan Roese name); 5620a572655SStefan Roese return NULL; 5630a572655SStefan Roese } 5640a572655SStefan Roese concat->subdev = (struct mtd_info **) (concat + 1); 5650a572655SStefan Roese 5660a572655SStefan Roese /* 5670a572655SStefan Roese * Set up the new "super" device's MTD object structure, check for 5680a572655SStefan Roese * incompatibilites between the subdevices. 5690a572655SStefan Roese */ 5700a572655SStefan Roese concat->mtd.type = subdev[0]->type; 5710a572655SStefan Roese concat->mtd.flags = subdev[0]->flags; 5720a572655SStefan Roese concat->mtd.size = subdev[0]->size; 5730a572655SStefan Roese concat->mtd.erasesize = subdev[0]->erasesize; 5740a572655SStefan Roese concat->mtd.writesize = subdev[0]->writesize; 5750a572655SStefan Roese concat->mtd.subpage_sft = subdev[0]->subpage_sft; 5760a572655SStefan Roese concat->mtd.oobsize = subdev[0]->oobsize; 5770a572655SStefan Roese concat->mtd.oobavail = subdev[0]->oobavail; 578*dfe64e2cSSergey Lapin if (subdev[0]->_read_oob) 579*dfe64e2cSSergey Lapin concat->mtd._read_oob = concat_read_oob; 580*dfe64e2cSSergey Lapin if (subdev[0]->_write_oob) 581*dfe64e2cSSergey Lapin concat->mtd._write_oob = concat_write_oob; 582*dfe64e2cSSergey Lapin if (subdev[0]->_block_isbad) 583*dfe64e2cSSergey Lapin concat->mtd._block_isbad = concat_block_isbad; 584*dfe64e2cSSergey Lapin if (subdev[0]->_block_markbad) 585*dfe64e2cSSergey Lapin concat->mtd._block_markbad = concat_block_markbad; 5860a572655SStefan Roese 5870a572655SStefan Roese concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; 5880a572655SStefan Roese 5890a572655SStefan Roese concat->subdev[0] = subdev[0]; 5900a572655SStefan Roese 5910a572655SStefan Roese for (i = 1; i < num_devs; i++) { 5920a572655SStefan Roese if (concat->mtd.type != subdev[i]->type) { 5930a572655SStefan Roese kfree(concat); 5940a572655SStefan Roese printk("Incompatible device type on \"%s\"\n", 5950a572655SStefan Roese subdev[i]->name); 5960a572655SStefan Roese return NULL; 5970a572655SStefan Roese } 5980a572655SStefan Roese if (concat->mtd.flags != subdev[i]->flags) { 5990a572655SStefan Roese /* 6000a572655SStefan Roese * Expect all flags except MTD_WRITEABLE to be 6010a572655SStefan Roese * equal on all subdevices. 6020a572655SStefan Roese */ 6030a572655SStefan Roese if ((concat->mtd.flags ^ subdev[i]-> 6040a572655SStefan Roese flags) & ~MTD_WRITEABLE) { 6050a572655SStefan Roese kfree(concat); 6060a572655SStefan Roese printk("Incompatible device flags on \"%s\"\n", 6070a572655SStefan Roese subdev[i]->name); 6080a572655SStefan Roese return NULL; 6090a572655SStefan Roese } else 6100a572655SStefan Roese /* if writeable attribute differs, 6110a572655SStefan Roese make super device writeable */ 6120a572655SStefan Roese concat->mtd.flags |= 6130a572655SStefan Roese subdev[i]->flags & MTD_WRITEABLE; 6140a572655SStefan Roese } 6150a572655SStefan Roese 6160a572655SStefan Roese concat->mtd.size += subdev[i]->size; 6170a572655SStefan Roese concat->mtd.ecc_stats.badblocks += 6180a572655SStefan Roese subdev[i]->ecc_stats.badblocks; 6190a572655SStefan Roese if (concat->mtd.writesize != subdev[i]->writesize || 6200a572655SStefan Roese concat->mtd.subpage_sft != subdev[i]->subpage_sft || 6210a572655SStefan Roese concat->mtd.oobsize != subdev[i]->oobsize || 622*dfe64e2cSSergey Lapin !concat->mtd._read_oob != !subdev[i]->_read_oob || 623*dfe64e2cSSergey Lapin !concat->mtd._write_oob != !subdev[i]->_write_oob) { 6240a572655SStefan Roese kfree(concat); 6250a572655SStefan Roese printk("Incompatible OOB or ECC data on \"%s\"\n", 6260a572655SStefan Roese subdev[i]->name); 6270a572655SStefan Roese return NULL; 6280a572655SStefan Roese } 6290a572655SStefan Roese concat->subdev[i] = subdev[i]; 6300a572655SStefan Roese 6310a572655SStefan Roese } 6320a572655SStefan Roese 6330a572655SStefan Roese concat->mtd.ecclayout = subdev[0]->ecclayout; 6340a572655SStefan Roese 6350a572655SStefan Roese concat->num_subdev = num_devs; 6360a572655SStefan Roese concat->mtd.name = name; 6370a572655SStefan Roese 638*dfe64e2cSSergey Lapin concat->mtd._erase = concat_erase; 639*dfe64e2cSSergey Lapin concat->mtd._read = concat_read; 640*dfe64e2cSSergey Lapin concat->mtd._write = concat_write; 641*dfe64e2cSSergey Lapin concat->mtd._sync = concat_sync; 642*dfe64e2cSSergey Lapin concat->mtd._lock = concat_lock; 643*dfe64e2cSSergey Lapin concat->mtd._unlock = concat_unlock; 6440a572655SStefan Roese 6450a572655SStefan Roese /* 6460a572655SStefan Roese * Combine the erase block size info of the subdevices: 6470a572655SStefan Roese * 6480a572655SStefan Roese * first, walk the map of the new device and see how 6490a572655SStefan Roese * many changes in erase size we have 6500a572655SStefan Roese */ 6510a572655SStefan Roese max_erasesize = curr_erasesize = subdev[0]->erasesize; 6520a572655SStefan Roese num_erase_region = 1; 6530a572655SStefan Roese for (i = 0; i < num_devs; i++) { 6540a572655SStefan Roese if (subdev[i]->numeraseregions == 0) { 6550a572655SStefan Roese /* current subdevice has uniform erase size */ 6560a572655SStefan Roese if (subdev[i]->erasesize != curr_erasesize) { 6570a572655SStefan Roese /* if it differs from the last subdevice's erase size, count it */ 6580a572655SStefan Roese ++num_erase_region; 6590a572655SStefan Roese curr_erasesize = subdev[i]->erasesize; 6600a572655SStefan Roese if (curr_erasesize > max_erasesize) 6610a572655SStefan Roese max_erasesize = curr_erasesize; 6620a572655SStefan Roese } 6630a572655SStefan Roese } else { 6640a572655SStefan Roese /* current subdevice has variable erase size */ 6650a572655SStefan Roese int j; 6660a572655SStefan Roese for (j = 0; j < subdev[i]->numeraseregions; j++) { 6670a572655SStefan Roese 6680a572655SStefan Roese /* walk the list of erase regions, count any changes */ 6690a572655SStefan Roese if (subdev[i]->eraseregions[j].erasesize != 6700a572655SStefan Roese curr_erasesize) { 6710a572655SStefan Roese ++num_erase_region; 6720a572655SStefan Roese curr_erasesize = 6730a572655SStefan Roese subdev[i]->eraseregions[j]. 6740a572655SStefan Roese erasesize; 6750a572655SStefan Roese if (curr_erasesize > max_erasesize) 6760a572655SStefan Roese max_erasesize = curr_erasesize; 6770a572655SStefan Roese } 6780a572655SStefan Roese } 6790a572655SStefan Roese } 6800a572655SStefan Roese } 6810a572655SStefan Roese 6820a572655SStefan Roese if (num_erase_region == 1) { 6830a572655SStefan Roese /* 6840a572655SStefan Roese * All subdevices have the same uniform erase size. 6850a572655SStefan Roese * This is easy: 6860a572655SStefan Roese */ 6870a572655SStefan Roese concat->mtd.erasesize = curr_erasesize; 6880a572655SStefan Roese concat->mtd.numeraseregions = 0; 6890a572655SStefan Roese } else { 6900a572655SStefan Roese uint64_t tmp64; 6910a572655SStefan Roese 6920a572655SStefan Roese /* 6930a572655SStefan Roese * erase block size varies across the subdevices: allocate 6940a572655SStefan Roese * space to store the data describing the variable erase regions 6950a572655SStefan Roese */ 6960a572655SStefan Roese struct mtd_erase_region_info *erase_region_p; 6970a572655SStefan Roese uint64_t begin, position; 6980a572655SStefan Roese 6990a572655SStefan Roese concat->mtd.erasesize = max_erasesize; 7000a572655SStefan Roese concat->mtd.numeraseregions = num_erase_region; 7010a572655SStefan Roese concat->mtd.eraseregions = erase_region_p = 7020a572655SStefan Roese kmalloc(num_erase_region * 7030a572655SStefan Roese sizeof (struct mtd_erase_region_info), GFP_KERNEL); 7040a572655SStefan Roese if (!erase_region_p) { 7050a572655SStefan Roese kfree(concat); 7060a572655SStefan Roese printk 7070a572655SStefan Roese ("memory allocation error while creating erase region list" 7080a572655SStefan Roese " for device \"%s\"\n", name); 7090a572655SStefan Roese return NULL; 7100a572655SStefan Roese } 7110a572655SStefan Roese 7120a572655SStefan Roese /* 7130a572655SStefan Roese * walk the map of the new device once more and fill in 7140a572655SStefan Roese * in erase region info: 7150a572655SStefan Roese */ 7160a572655SStefan Roese curr_erasesize = subdev[0]->erasesize; 7170a572655SStefan Roese begin = position = 0; 7180a572655SStefan Roese for (i = 0; i < num_devs; i++) { 7190a572655SStefan Roese if (subdev[i]->numeraseregions == 0) { 7200a572655SStefan Roese /* current subdevice has uniform erase size */ 7210a572655SStefan Roese if (subdev[i]->erasesize != curr_erasesize) { 7220a572655SStefan Roese /* 7230a572655SStefan Roese * fill in an mtd_erase_region_info structure for the area 7240a572655SStefan Roese * we have walked so far: 7250a572655SStefan Roese */ 7260a572655SStefan Roese erase_region_p->offset = begin; 7270a572655SStefan Roese erase_region_p->erasesize = 7280a572655SStefan Roese curr_erasesize; 7290a572655SStefan Roese tmp64 = position - begin; 7300a572655SStefan Roese do_div(tmp64, curr_erasesize); 7310a572655SStefan Roese erase_region_p->numblocks = tmp64; 7320a572655SStefan Roese begin = position; 7330a572655SStefan Roese 7340a572655SStefan Roese curr_erasesize = subdev[i]->erasesize; 7350a572655SStefan Roese ++erase_region_p; 7360a572655SStefan Roese } 7370a572655SStefan Roese position += subdev[i]->size; 7380a572655SStefan Roese } else { 7390a572655SStefan Roese /* current subdevice has variable erase size */ 7400a572655SStefan Roese int j; 7410a572655SStefan Roese for (j = 0; j < subdev[i]->numeraseregions; j++) { 7420a572655SStefan Roese /* walk the list of erase regions, count any changes */ 7430a572655SStefan Roese if (subdev[i]->eraseregions[j]. 7440a572655SStefan Roese erasesize != curr_erasesize) { 7450a572655SStefan Roese erase_region_p->offset = begin; 7460a572655SStefan Roese erase_region_p->erasesize = 7470a572655SStefan Roese curr_erasesize; 7480a572655SStefan Roese tmp64 = position - begin; 7490a572655SStefan Roese do_div(tmp64, curr_erasesize); 7500a572655SStefan Roese erase_region_p->numblocks = tmp64; 7510a572655SStefan Roese begin = position; 7520a572655SStefan Roese 7530a572655SStefan Roese curr_erasesize = 7540a572655SStefan Roese subdev[i]->eraseregions[j]. 7550a572655SStefan Roese erasesize; 7560a572655SStefan Roese ++erase_region_p; 7570a572655SStefan Roese } 7580a572655SStefan Roese position += 7590a572655SStefan Roese subdev[i]->eraseregions[j]. 7600a572655SStefan Roese numblocks * (uint64_t)curr_erasesize; 7610a572655SStefan Roese } 7620a572655SStefan Roese } 7630a572655SStefan Roese } 7640a572655SStefan Roese /* Now write the final entry */ 7650a572655SStefan Roese erase_region_p->offset = begin; 7660a572655SStefan Roese erase_region_p->erasesize = curr_erasesize; 7670a572655SStefan Roese tmp64 = position - begin; 7680a572655SStefan Roese do_div(tmp64, curr_erasesize); 7690a572655SStefan Roese erase_region_p->numblocks = tmp64; 7700a572655SStefan Roese } 7710a572655SStefan Roese 7720a572655SStefan Roese return &concat->mtd; 7730a572655SStefan Roese } 774