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> 11e29c22f5SKyungmin Park #include <linux/mtd/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 28e29c22f5SKyungmin Park /* No need to get a refcount on the module containing 29e29c22f5SKyungmin Park the notifier, since we hold the mtd_table_mutex */ 30e29c22f5SKyungmin Park 31e29c22f5SKyungmin Park /* We _know_ we aren't being removed, because 32e29c22f5SKyungmin Park our caller is still holding us here. So none 33e29c22f5SKyungmin Park of this try_ nonsense, and no bitching about it 34e29c22f5SKyungmin Park either. :) */ 35e29c22f5SKyungmin Park return 0; 36e29c22f5SKyungmin Park } 37e29c22f5SKyungmin Park 38e29c22f5SKyungmin Park return 1; 39e29c22f5SKyungmin Park } 40e29c22f5SKyungmin Park 41e29c22f5SKyungmin Park /** 42e29c22f5SKyungmin Park * del_mtd_device - unregister an MTD device 43e29c22f5SKyungmin Park * @mtd: pointer to MTD device info structure 44e29c22f5SKyungmin Park * 45e29c22f5SKyungmin Park * Remove a device from the list of MTD devices present in the system, 46e29c22f5SKyungmin Park * and notify each currently active MTD 'user' of its departure. 47e29c22f5SKyungmin Park * Returns zero on success or 1 on failure, which currently will happen 48e29c22f5SKyungmin Park * if the requested device does not appear to be present in the list. 49e29c22f5SKyungmin Park */ 50e29c22f5SKyungmin Park int del_mtd_device(struct mtd_info *mtd) 51e29c22f5SKyungmin Park { 52e29c22f5SKyungmin Park int ret; 53e29c22f5SKyungmin Park 54e29c22f5SKyungmin Park if (mtd_table[mtd->index] != mtd) { 55e29c22f5SKyungmin Park ret = -ENODEV; 56e29c22f5SKyungmin Park } else if (mtd->usecount) { 57e29c22f5SKyungmin Park printk(KERN_NOTICE "Removing MTD device #%d (%s)" 58e29c22f5SKyungmin Park " with use count %d\n", 59e29c22f5SKyungmin Park mtd->index, mtd->name, mtd->usecount); 60e29c22f5SKyungmin Park ret = -EBUSY; 61e29c22f5SKyungmin Park } else { 62e29c22f5SKyungmin Park /* No need to get a refcount on the module containing 63e29c22f5SKyungmin Park * the notifier, since we hold the mtd_table_mutex */ 64e29c22f5SKyungmin Park mtd_table[mtd->index] = NULL; 65e29c22f5SKyungmin Park 66e29c22f5SKyungmin Park ret = 0; 67e29c22f5SKyungmin Park } 68e29c22f5SKyungmin Park 69e29c22f5SKyungmin Park return ret; 70e29c22f5SKyungmin Park } 71e29c22f5SKyungmin Park 72e29c22f5SKyungmin Park /** 73e29c22f5SKyungmin Park * get_mtd_device - obtain a validated handle for an MTD device 74e29c22f5SKyungmin Park * @mtd: last known address of the required MTD device 75e29c22f5SKyungmin Park * @num: internal device number of the required MTD device 76e29c22f5SKyungmin Park * 77e29c22f5SKyungmin Park * Given a number and NULL address, return the num'th entry in the device 78e29c22f5SKyungmin Park * table, if any. Given an address and num == -1, search the device table 79e29c22f5SKyungmin Park * for a device with that address and return if it's still present. Given 80e29c22f5SKyungmin Park * both, return the num'th driver only if its address matches. Return 81e29c22f5SKyungmin Park * error code if not. 82e29c22f5SKyungmin Park */ 83e29c22f5SKyungmin Park struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) 84e29c22f5SKyungmin Park { 85e29c22f5SKyungmin Park struct mtd_info *ret = NULL; 86e29c22f5SKyungmin Park int i, err = -ENODEV; 87e29c22f5SKyungmin Park 88e29c22f5SKyungmin Park if (num == -1) { 89e29c22f5SKyungmin Park for (i = 0; i < MAX_MTD_DEVICES; i++) 90e29c22f5SKyungmin Park if (mtd_table[i] == mtd) 91e29c22f5SKyungmin Park ret = mtd_table[i]; 92e29c22f5SKyungmin Park } else if (num < MAX_MTD_DEVICES) { 93e29c22f5SKyungmin Park ret = mtd_table[num]; 94e29c22f5SKyungmin Park if (mtd && mtd != ret) 95e29c22f5SKyungmin Park ret = NULL; 96e29c22f5SKyungmin Park } 97e29c22f5SKyungmin Park 98e29c22f5SKyungmin Park if (!ret) 99e29c22f5SKyungmin Park goto out_unlock; 100e29c22f5SKyungmin Park 101e29c22f5SKyungmin Park ret->usecount++; 102e29c22f5SKyungmin Park return ret; 103e29c22f5SKyungmin Park 104e29c22f5SKyungmin Park out_unlock: 105e29c22f5SKyungmin Park return ERR_PTR(err); 106e29c22f5SKyungmin Park } 107e29c22f5SKyungmin Park 108e29c22f5SKyungmin Park /** 109e29c22f5SKyungmin Park * get_mtd_device_nm - obtain a validated handle for an MTD device by 110e29c22f5SKyungmin Park * device name 111e29c22f5SKyungmin Park * @name: MTD device name to open 112e29c22f5SKyungmin Park * 113e29c22f5SKyungmin Park * This function returns MTD device description structure in case of 114e29c22f5SKyungmin Park * success and an error code in case of failure. 115e29c22f5SKyungmin Park */ 116e29c22f5SKyungmin Park struct mtd_info *get_mtd_device_nm(const char *name) 117e29c22f5SKyungmin Park { 118e29c22f5SKyungmin Park int i, err = -ENODEV; 119e29c22f5SKyungmin Park struct mtd_info *mtd = NULL; 120e29c22f5SKyungmin Park 121e29c22f5SKyungmin Park for (i = 0; i < MAX_MTD_DEVICES; i++) { 122e29c22f5SKyungmin Park if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { 123e29c22f5SKyungmin Park mtd = mtd_table[i]; 124e29c22f5SKyungmin Park break; 125e29c22f5SKyungmin Park } 126e29c22f5SKyungmin Park } 127e29c22f5SKyungmin Park 128e29c22f5SKyungmin Park if (!mtd) 129e29c22f5SKyungmin Park goto out_unlock; 130e29c22f5SKyungmin Park 131e29c22f5SKyungmin Park mtd->usecount++; 132e29c22f5SKyungmin Park return mtd; 133e29c22f5SKyungmin Park 134e29c22f5SKyungmin Park out_unlock: 135e29c22f5SKyungmin Park return ERR_PTR(err); 136e29c22f5SKyungmin Park } 137e29c22f5SKyungmin Park 138e29c22f5SKyungmin Park void put_mtd_device(struct mtd_info *mtd) 139e29c22f5SKyungmin Park { 140e29c22f5SKyungmin Park int c; 141e29c22f5SKyungmin Park 142e29c22f5SKyungmin Park c = --mtd->usecount; 143e29c22f5SKyungmin Park BUG_ON(c < 0); 144e29c22f5SKyungmin Park } 1454ba692fbSBen Gardiner 1464ba692fbSBen Gardiner #if defined(CONFIG_CMD_MTDPARTS_SPREAD) 1474ba692fbSBen Gardiner /** 1484ba692fbSBen Gardiner * mtd_get_len_incl_bad 1494ba692fbSBen Gardiner * 1504ba692fbSBen Gardiner * Check if length including bad blocks fits into device. 1514ba692fbSBen Gardiner * 1524ba692fbSBen Gardiner * @param mtd an MTD device 1534ba692fbSBen Gardiner * @param offset offset in flash 1544ba692fbSBen Gardiner * @param length image length 1554ba692fbSBen Gardiner * @return image length including bad blocks in *len_incl_bad and whether or not 1564ba692fbSBen Gardiner * the length returned was truncated in *truncated 1574ba692fbSBen Gardiner */ 1584ba692fbSBen Gardiner void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, 1594ba692fbSBen Gardiner const uint64_t length, uint64_t *len_incl_bad, 1604ba692fbSBen Gardiner int *truncated) 1614ba692fbSBen Gardiner { 1624ba692fbSBen Gardiner *truncated = 0; 1634ba692fbSBen Gardiner *len_incl_bad = 0; 1644ba692fbSBen Gardiner 1654ba692fbSBen Gardiner if (!mtd->block_isbad) { 1664ba692fbSBen Gardiner *len_incl_bad = length; 1674ba692fbSBen Gardiner return; 1684ba692fbSBen Gardiner } 1694ba692fbSBen Gardiner 1704ba692fbSBen Gardiner uint64_t len_excl_bad = 0; 1714ba692fbSBen Gardiner uint64_t block_len; 1724ba692fbSBen Gardiner 1734ba692fbSBen Gardiner while (len_excl_bad < length) { 174*36650ca9SScott Wood if (offset >= mtd->size) { 175*36650ca9SScott Wood *truncated = 1; 176*36650ca9SScott Wood return; 177*36650ca9SScott Wood } 178*36650ca9SScott Wood 1794ba692fbSBen Gardiner block_len = mtd->erasesize - (offset & (mtd->erasesize - 1)); 1804ba692fbSBen Gardiner 1814ba692fbSBen Gardiner if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1))) 1824ba692fbSBen Gardiner len_excl_bad += block_len; 1834ba692fbSBen Gardiner 1844ba692fbSBen Gardiner *len_incl_bad += block_len; 1854ba692fbSBen Gardiner offset += block_len; 1864ba692fbSBen Gardiner } 1874ba692fbSBen Gardiner } 1884ba692fbSBen Gardiner #endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ 189