1e29c22f5SKyungmin Park /* 2e29c22f5SKyungmin Park * Core registration and callback routines for MTD 3e29c22f5SKyungmin Park * drivers and users. 4e29c22f5SKyungmin Park * 5e29c22f5SKyungmin Park * This program is free software; you can redistribute it and/or modify 6e29c22f5SKyungmin Park * it under the terms of the GNU General Public License version 2 as 7e29c22f5SKyungmin Park * published by the Free Software Foundation. 8e29c22f5SKyungmin Park */ 9e29c22f5SKyungmin Park 10e29c22f5SKyungmin Park #include <linux/mtd/mtd.h> 117b15e2bbSMike Frysinger #include <linux/compat.h> 12e29c22f5SKyungmin Park #include <ubi_uboot.h> 13e29c22f5SKyungmin Park 14e29c22f5SKyungmin Park struct mtd_info *mtd_table[MAX_MTD_DEVICES]; 15e29c22f5SKyungmin Park 16e29c22f5SKyungmin Park int add_mtd_device(struct mtd_info *mtd) 17e29c22f5SKyungmin Park { 18e29c22f5SKyungmin Park int i; 19e29c22f5SKyungmin Park 20e29c22f5SKyungmin Park BUG_ON(mtd->writesize == 0); 21e29c22f5SKyungmin Park 22e29c22f5SKyungmin Park for (i = 0; i < MAX_MTD_DEVICES; i++) 23e29c22f5SKyungmin Park if (!mtd_table[i]) { 24e29c22f5SKyungmin Park mtd_table[i] = mtd; 25e29c22f5SKyungmin Park mtd->index = i; 26e29c22f5SKyungmin Park mtd->usecount = 0; 27e29c22f5SKyungmin Park 28*dfe64e2cSSergey Lapin /* default value if not set by driver */ 29*dfe64e2cSSergey Lapin if (mtd->bitflip_threshold == 0) 30*dfe64e2cSSergey Lapin mtd->bitflip_threshold = mtd->ecc_strength; 31*dfe64e2cSSergey Lapin 32*dfe64e2cSSergey Lapin 33e29c22f5SKyungmin Park /* No need to get a refcount on the module containing 34e29c22f5SKyungmin Park the notifier, since we hold the mtd_table_mutex */ 35e29c22f5SKyungmin Park 36e29c22f5SKyungmin Park /* We _know_ we aren't being removed, because 37e29c22f5SKyungmin Park our caller is still holding us here. So none 38e29c22f5SKyungmin Park of this try_ nonsense, and no bitching about it 39e29c22f5SKyungmin Park either. :) */ 40e29c22f5SKyungmin Park return 0; 41e29c22f5SKyungmin Park } 42e29c22f5SKyungmin Park 43e29c22f5SKyungmin Park return 1; 44e29c22f5SKyungmin Park } 45e29c22f5SKyungmin Park 46e29c22f5SKyungmin Park /** 47e29c22f5SKyungmin Park * del_mtd_device - unregister an MTD device 48e29c22f5SKyungmin Park * @mtd: pointer to MTD device info structure 49e29c22f5SKyungmin Park * 50e29c22f5SKyungmin Park * Remove a device from the list of MTD devices present in the system, 51e29c22f5SKyungmin Park * and notify each currently active MTD 'user' of its departure. 52e29c22f5SKyungmin Park * Returns zero on success or 1 on failure, which currently will happen 53e29c22f5SKyungmin Park * if the requested device does not appear to be present in the list. 54e29c22f5SKyungmin Park */ 55e29c22f5SKyungmin Park int del_mtd_device(struct mtd_info *mtd) 56e29c22f5SKyungmin Park { 57e29c22f5SKyungmin Park int ret; 58e29c22f5SKyungmin Park 59e29c22f5SKyungmin Park if (mtd_table[mtd->index] != mtd) { 60e29c22f5SKyungmin Park ret = -ENODEV; 61e29c22f5SKyungmin Park } else if (mtd->usecount) { 62e29c22f5SKyungmin Park printk(KERN_NOTICE "Removing MTD device #%d (%s)" 63e29c22f5SKyungmin Park " with use count %d\n", 64e29c22f5SKyungmin Park mtd->index, mtd->name, mtd->usecount); 65e29c22f5SKyungmin Park ret = -EBUSY; 66e29c22f5SKyungmin Park } else { 67e29c22f5SKyungmin Park /* No need to get a refcount on the module containing 68e29c22f5SKyungmin Park * the notifier, since we hold the mtd_table_mutex */ 69e29c22f5SKyungmin Park mtd_table[mtd->index] = NULL; 70e29c22f5SKyungmin Park 71e29c22f5SKyungmin Park ret = 0; 72e29c22f5SKyungmin Park } 73e29c22f5SKyungmin Park 74e29c22f5SKyungmin Park return ret; 75e29c22f5SKyungmin Park } 76e29c22f5SKyungmin Park 77e29c22f5SKyungmin Park /** 78e29c22f5SKyungmin Park * get_mtd_device - obtain a validated handle for an MTD device 79e29c22f5SKyungmin Park * @mtd: last known address of the required MTD device 80e29c22f5SKyungmin Park * @num: internal device number of the required MTD device 81e29c22f5SKyungmin Park * 82e29c22f5SKyungmin Park * Given a number and NULL address, return the num'th entry in the device 83e29c22f5SKyungmin Park * table, if any. Given an address and num == -1, search the device table 84e29c22f5SKyungmin Park * for a device with that address and return if it's still present. Given 85e29c22f5SKyungmin Park * both, return the num'th driver only if its address matches. Return 86e29c22f5SKyungmin Park * error code if not. 87e29c22f5SKyungmin Park */ 88e29c22f5SKyungmin Park struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) 89e29c22f5SKyungmin Park { 90e29c22f5SKyungmin Park struct mtd_info *ret = NULL; 91e29c22f5SKyungmin Park int i, err = -ENODEV; 92e29c22f5SKyungmin Park 93e29c22f5SKyungmin Park if (num == -1) { 94e29c22f5SKyungmin Park for (i = 0; i < MAX_MTD_DEVICES; i++) 95e29c22f5SKyungmin Park if (mtd_table[i] == mtd) 96e29c22f5SKyungmin Park ret = mtd_table[i]; 97e29c22f5SKyungmin Park } else if (num < MAX_MTD_DEVICES) { 98e29c22f5SKyungmin Park ret = mtd_table[num]; 99e29c22f5SKyungmin Park if (mtd && mtd != ret) 100e29c22f5SKyungmin Park ret = NULL; 101e29c22f5SKyungmin Park } 102e29c22f5SKyungmin Park 103e29c22f5SKyungmin Park if (!ret) 104e29c22f5SKyungmin Park goto out_unlock; 105e29c22f5SKyungmin Park 106e29c22f5SKyungmin Park ret->usecount++; 107e29c22f5SKyungmin Park return ret; 108e29c22f5SKyungmin Park 109e29c22f5SKyungmin Park out_unlock: 110e29c22f5SKyungmin Park return ERR_PTR(err); 111e29c22f5SKyungmin Park } 112e29c22f5SKyungmin Park 113e29c22f5SKyungmin Park /** 114e29c22f5SKyungmin Park * get_mtd_device_nm - obtain a validated handle for an MTD device by 115e29c22f5SKyungmin Park * device name 116e29c22f5SKyungmin Park * @name: MTD device name to open 117e29c22f5SKyungmin Park * 118e29c22f5SKyungmin Park * This function returns MTD device description structure in case of 119e29c22f5SKyungmin Park * success and an error code in case of failure. 120e29c22f5SKyungmin Park */ 121e29c22f5SKyungmin Park struct mtd_info *get_mtd_device_nm(const char *name) 122e29c22f5SKyungmin Park { 123e29c22f5SKyungmin Park int i, err = -ENODEV; 124e29c22f5SKyungmin Park struct mtd_info *mtd = NULL; 125e29c22f5SKyungmin Park 126e29c22f5SKyungmin Park for (i = 0; i < MAX_MTD_DEVICES; i++) { 127e29c22f5SKyungmin Park if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { 128e29c22f5SKyungmin Park mtd = mtd_table[i]; 129e29c22f5SKyungmin Park break; 130e29c22f5SKyungmin Park } 131e29c22f5SKyungmin Park } 132e29c22f5SKyungmin Park 133e29c22f5SKyungmin Park if (!mtd) 134e29c22f5SKyungmin Park goto out_unlock; 135e29c22f5SKyungmin Park 136e29c22f5SKyungmin Park mtd->usecount++; 137e29c22f5SKyungmin Park return mtd; 138e29c22f5SKyungmin Park 139e29c22f5SKyungmin Park out_unlock: 140e29c22f5SKyungmin Park return ERR_PTR(err); 141e29c22f5SKyungmin Park } 142e29c22f5SKyungmin Park 143e29c22f5SKyungmin Park void put_mtd_device(struct mtd_info *mtd) 144e29c22f5SKyungmin Park { 145e29c22f5SKyungmin Park int c; 146e29c22f5SKyungmin Park 147e29c22f5SKyungmin Park c = --mtd->usecount; 148e29c22f5SKyungmin Park BUG_ON(c < 0); 149e29c22f5SKyungmin Park } 1504ba692fbSBen Gardiner 1514ba692fbSBen Gardiner #if defined(CONFIG_CMD_MTDPARTS_SPREAD) 1524ba692fbSBen Gardiner /** 1534ba692fbSBen Gardiner * mtd_get_len_incl_bad 1544ba692fbSBen Gardiner * 1554ba692fbSBen Gardiner * Check if length including bad blocks fits into device. 1564ba692fbSBen Gardiner * 1574ba692fbSBen Gardiner * @param mtd an MTD device 1584ba692fbSBen Gardiner * @param offset offset in flash 1594ba692fbSBen Gardiner * @param length image length 1604ba692fbSBen Gardiner * @return image length including bad blocks in *len_incl_bad and whether or not 1614ba692fbSBen Gardiner * the length returned was truncated in *truncated 1624ba692fbSBen Gardiner */ 1634ba692fbSBen Gardiner void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, 1644ba692fbSBen Gardiner const uint64_t length, uint64_t *len_incl_bad, 1654ba692fbSBen Gardiner int *truncated) 1664ba692fbSBen Gardiner { 1674ba692fbSBen Gardiner *truncated = 0; 1684ba692fbSBen Gardiner *len_incl_bad = 0; 1694ba692fbSBen Gardiner 1704ba692fbSBen Gardiner if (!mtd->block_isbad) { 1714ba692fbSBen Gardiner *len_incl_bad = length; 1724ba692fbSBen Gardiner return; 1734ba692fbSBen Gardiner } 1744ba692fbSBen Gardiner 1754ba692fbSBen Gardiner uint64_t len_excl_bad = 0; 1764ba692fbSBen Gardiner uint64_t block_len; 1774ba692fbSBen Gardiner 1784ba692fbSBen Gardiner while (len_excl_bad < length) { 17936650ca9SScott Wood if (offset >= mtd->size) { 18036650ca9SScott Wood *truncated = 1; 18136650ca9SScott Wood return; 18236650ca9SScott Wood } 18336650ca9SScott Wood 1844ba692fbSBen Gardiner block_len = mtd->erasesize - (offset & (mtd->erasesize - 1)); 1854ba692fbSBen Gardiner 1864ba692fbSBen Gardiner if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1))) 1874ba692fbSBen Gardiner len_excl_bad += block_len; 1884ba692fbSBen Gardiner 1894ba692fbSBen Gardiner *len_incl_bad += block_len; 1904ba692fbSBen Gardiner offset += block_len; 1914ba692fbSBen Gardiner } 1924ba692fbSBen Gardiner } 1934ba692fbSBen Gardiner #endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ 194*dfe64e2cSSergey Lapin 195*dfe64e2cSSergey Lapin /* 196*dfe64e2cSSergey Lapin * Erase is an asynchronous operation. Device drivers are supposed 197*dfe64e2cSSergey Lapin * to call instr->callback() whenever the operation completes, even 198*dfe64e2cSSergey Lapin * if it completes with a failure. 199*dfe64e2cSSergey Lapin * Callers are supposed to pass a callback function and wait for it 200*dfe64e2cSSergey Lapin * to be called before writing to the block. 201*dfe64e2cSSergey Lapin */ 202*dfe64e2cSSergey Lapin int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) 203*dfe64e2cSSergey Lapin { 204*dfe64e2cSSergey Lapin if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr) 205*dfe64e2cSSergey Lapin return -EINVAL; 206*dfe64e2cSSergey Lapin if (!(mtd->flags & MTD_WRITEABLE)) 207*dfe64e2cSSergey Lapin return -EROFS; 208*dfe64e2cSSergey Lapin instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; 209*dfe64e2cSSergey Lapin if (!instr->len) { 210*dfe64e2cSSergey Lapin instr->state = MTD_ERASE_DONE; 211*dfe64e2cSSergey Lapin mtd_erase_callback(instr); 212*dfe64e2cSSergey Lapin return 0; 213*dfe64e2cSSergey Lapin } 214*dfe64e2cSSergey Lapin return mtd->_erase(mtd, instr); 215*dfe64e2cSSergey Lapin } 216*dfe64e2cSSergey Lapin 217*dfe64e2cSSergey Lapin int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, 218*dfe64e2cSSergey Lapin u_char *buf) 219*dfe64e2cSSergey Lapin { 220*dfe64e2cSSergey Lapin if (from < 0 || from > mtd->size || len > mtd->size - from) 221*dfe64e2cSSergey Lapin return -EINVAL; 222*dfe64e2cSSergey Lapin if (!len) 223*dfe64e2cSSergey Lapin return 0; 224*dfe64e2cSSergey Lapin return mtd->_read(mtd, from, len, retlen, buf); 225*dfe64e2cSSergey Lapin } 226*dfe64e2cSSergey Lapin 227*dfe64e2cSSergey Lapin int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, 228*dfe64e2cSSergey Lapin const u_char *buf) 229*dfe64e2cSSergey Lapin { 230*dfe64e2cSSergey Lapin *retlen = 0; 231*dfe64e2cSSergey Lapin if (to < 0 || to > mtd->size || len > mtd->size - to) 232*dfe64e2cSSergey Lapin return -EINVAL; 233*dfe64e2cSSergey Lapin if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE)) 234*dfe64e2cSSergey Lapin return -EROFS; 235*dfe64e2cSSergey Lapin if (!len) 236*dfe64e2cSSergey Lapin return 0; 237*dfe64e2cSSergey Lapin return mtd->_write(mtd, to, len, retlen, buf); 238*dfe64e2cSSergey Lapin } 239*dfe64e2cSSergey Lapin 240*dfe64e2cSSergey Lapin /* 241*dfe64e2cSSergey Lapin * In blackbox flight recorder like scenarios we want to make successful writes 242*dfe64e2cSSergey Lapin * in interrupt context. panic_write() is only intended to be called when its 243*dfe64e2cSSergey Lapin * known the kernel is about to panic and we need the write to succeed. Since 244*dfe64e2cSSergey Lapin * the kernel is not going to be running for much longer, this function can 245*dfe64e2cSSergey Lapin * break locks and delay to ensure the write succeeds (but not sleep). 246*dfe64e2cSSergey Lapin */ 247*dfe64e2cSSergey Lapin int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, 248*dfe64e2cSSergey Lapin const u_char *buf) 249*dfe64e2cSSergey Lapin { 250*dfe64e2cSSergey Lapin *retlen = 0; 251*dfe64e2cSSergey Lapin if (!mtd->_panic_write) 252*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 253*dfe64e2cSSergey Lapin if (to < 0 || to > mtd->size || len > mtd->size - to) 254*dfe64e2cSSergey Lapin return -EINVAL; 255*dfe64e2cSSergey Lapin if (!(mtd->flags & MTD_WRITEABLE)) 256*dfe64e2cSSergey Lapin return -EROFS; 257*dfe64e2cSSergey Lapin if (!len) 258*dfe64e2cSSergey Lapin return 0; 259*dfe64e2cSSergey Lapin return mtd->_panic_write(mtd, to, len, retlen, buf); 260*dfe64e2cSSergey Lapin } 261*dfe64e2cSSergey Lapin 262*dfe64e2cSSergey Lapin int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) 263*dfe64e2cSSergey Lapin { 264*dfe64e2cSSergey Lapin ops->retlen = ops->oobretlen = 0; 265*dfe64e2cSSergey Lapin if (!mtd->_read_oob) 266*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 267*dfe64e2cSSergey Lapin return mtd->_read_oob(mtd, from, ops); 268*dfe64e2cSSergey Lapin } 269*dfe64e2cSSergey Lapin 270*dfe64e2cSSergey Lapin /* 271*dfe64e2cSSergey Lapin * Method to access the protection register area, present in some flash 272*dfe64e2cSSergey Lapin * devices. The user data is one time programmable but the factory data is read 273*dfe64e2cSSergey Lapin * only. 274*dfe64e2cSSergey Lapin */ 275*dfe64e2cSSergey Lapin int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, 276*dfe64e2cSSergey Lapin size_t len) 277*dfe64e2cSSergey Lapin { 278*dfe64e2cSSergey Lapin if (!mtd->_get_fact_prot_info) 279*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 280*dfe64e2cSSergey Lapin if (!len) 281*dfe64e2cSSergey Lapin return 0; 282*dfe64e2cSSergey Lapin return mtd->_get_fact_prot_info(mtd, buf, len); 283*dfe64e2cSSergey Lapin } 284*dfe64e2cSSergey Lapin 285*dfe64e2cSSergey Lapin int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, 286*dfe64e2cSSergey Lapin size_t *retlen, u_char *buf) 287*dfe64e2cSSergey Lapin { 288*dfe64e2cSSergey Lapin *retlen = 0; 289*dfe64e2cSSergey Lapin if (!mtd->_read_fact_prot_reg) 290*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 291*dfe64e2cSSergey Lapin if (!len) 292*dfe64e2cSSergey Lapin return 0; 293*dfe64e2cSSergey Lapin return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); 294*dfe64e2cSSergey Lapin } 295*dfe64e2cSSergey Lapin 296*dfe64e2cSSergey Lapin int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, 297*dfe64e2cSSergey Lapin size_t len) 298*dfe64e2cSSergey Lapin { 299*dfe64e2cSSergey Lapin if (!mtd->_get_user_prot_info) 300*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 301*dfe64e2cSSergey Lapin if (!len) 302*dfe64e2cSSergey Lapin return 0; 303*dfe64e2cSSergey Lapin return mtd->_get_user_prot_info(mtd, buf, len); 304*dfe64e2cSSergey Lapin } 305*dfe64e2cSSergey Lapin 306*dfe64e2cSSergey Lapin int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, 307*dfe64e2cSSergey Lapin size_t *retlen, u_char *buf) 308*dfe64e2cSSergey Lapin { 309*dfe64e2cSSergey Lapin *retlen = 0; 310*dfe64e2cSSergey Lapin if (!mtd->_read_user_prot_reg) 311*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 312*dfe64e2cSSergey Lapin if (!len) 313*dfe64e2cSSergey Lapin return 0; 314*dfe64e2cSSergey Lapin return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); 315*dfe64e2cSSergey Lapin } 316*dfe64e2cSSergey Lapin 317*dfe64e2cSSergey Lapin int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, 318*dfe64e2cSSergey Lapin size_t *retlen, u_char *buf) 319*dfe64e2cSSergey Lapin { 320*dfe64e2cSSergey Lapin *retlen = 0; 321*dfe64e2cSSergey Lapin if (!mtd->_write_user_prot_reg) 322*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 323*dfe64e2cSSergey Lapin if (!len) 324*dfe64e2cSSergey Lapin return 0; 325*dfe64e2cSSergey Lapin return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); 326*dfe64e2cSSergey Lapin } 327*dfe64e2cSSergey Lapin 328*dfe64e2cSSergey Lapin int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) 329*dfe64e2cSSergey Lapin { 330*dfe64e2cSSergey Lapin if (!mtd->_lock_user_prot_reg) 331*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 332*dfe64e2cSSergey Lapin if (!len) 333*dfe64e2cSSergey Lapin return 0; 334*dfe64e2cSSergey Lapin return mtd->_lock_user_prot_reg(mtd, from, len); 335*dfe64e2cSSergey Lapin } 336*dfe64e2cSSergey Lapin 337*dfe64e2cSSergey Lapin /* Chip-supported device locking */ 338*dfe64e2cSSergey Lapin int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 339*dfe64e2cSSergey Lapin { 340*dfe64e2cSSergey Lapin if (!mtd->_lock) 341*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 342*dfe64e2cSSergey Lapin if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) 343*dfe64e2cSSergey Lapin return -EINVAL; 344*dfe64e2cSSergey Lapin if (!len) 345*dfe64e2cSSergey Lapin return 0; 346*dfe64e2cSSergey Lapin return mtd->_lock(mtd, ofs, len); 347*dfe64e2cSSergey Lapin } 348*dfe64e2cSSergey Lapin 349*dfe64e2cSSergey Lapin int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 350*dfe64e2cSSergey Lapin { 351*dfe64e2cSSergey Lapin if (!mtd->_unlock) 352*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 353*dfe64e2cSSergey Lapin if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) 354*dfe64e2cSSergey Lapin return -EINVAL; 355*dfe64e2cSSergey Lapin if (!len) 356*dfe64e2cSSergey Lapin return 0; 357*dfe64e2cSSergey Lapin return mtd->_unlock(mtd, ofs, len); 358*dfe64e2cSSergey Lapin } 359*dfe64e2cSSergey Lapin 360*dfe64e2cSSergey Lapin int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) 361*dfe64e2cSSergey Lapin { 362*dfe64e2cSSergey Lapin if (!mtd->_block_isbad) 363*dfe64e2cSSergey Lapin return 0; 364*dfe64e2cSSergey Lapin if (ofs < 0 || ofs > mtd->size) 365*dfe64e2cSSergey Lapin return -EINVAL; 366*dfe64e2cSSergey Lapin return mtd->_block_isbad(mtd, ofs); 367*dfe64e2cSSergey Lapin } 368*dfe64e2cSSergey Lapin 369*dfe64e2cSSergey Lapin int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) 370*dfe64e2cSSergey Lapin { 371*dfe64e2cSSergey Lapin if (!mtd->_block_markbad) 372*dfe64e2cSSergey Lapin return -EOPNOTSUPP; 373*dfe64e2cSSergey Lapin if (ofs < 0 || ofs > mtd->size) 374*dfe64e2cSSergey Lapin return -EINVAL; 375*dfe64e2cSSergey Lapin if (!(mtd->flags & MTD_WRITEABLE)) 376*dfe64e2cSSergey Lapin return -EROFS; 377*dfe64e2cSSergey Lapin return mtd->_block_markbad(mtd, ofs); 378*dfe64e2cSSergey Lapin } 379*dfe64e2cSSergey Lapin 380