197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Direct MTD block device access 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> 5a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 2000-2003 Nicolas Pitre <nico@fluxnic.net> 6a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * 7a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * This program is free software; you can redistribute it and/or modify 8a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * it under the terms of the GNU General Public License as published by 9a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * the Free Software Foundation; either version 2 of the License, or 10a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * (at your option) any later version. 11a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * 12a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * This program is distributed in the hope that it will be useful, 13a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * but WITHOUT ANY WARRANTY; without even the implied warranty of 14a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * GNU General Public License for more details. 16a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * 17a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * You should have received a copy of the GNU General Public License 18a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * along with this program; if not, write to the Free Software 19a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 2515fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/kernel.h> 2615fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/module.h> 2715fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/sched.h> 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 2915fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/types.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h> 3115fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/blktrans.h> 3448b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar#include <linux/mutex.h> 3548b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 37cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchingsstruct mtdblk_dev { 38cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtd_blktrans_dev mbd; 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int count; 4048b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar struct mutex cache_mutex; 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *cache_data; 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long cache_offset; 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int cache_size; 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; 45cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings}; 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 477578ca927b1a2a0445c9a3166d05462b9ffd4c06Axel Linstatic DEFINE_MUTEX(mtdblks_lock); 48d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Cache stuff... 5197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Since typical flash erasable sectors are much larger than what Linux's 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * buffer cache can handle, we must implement read-modify-write on flash 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * sectors for each block write requests. To avoid over-erasing flash sectors 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and to speed things up, we locally cache a whole flash sector while it is 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * being written to until a different sector is required. 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void erase_callback(struct erase_info *done) 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(wait_q); 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6597894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int erase_write (struct mtd_info *mtd, unsigned long pos, 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int len, const char *buf) 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct erase_info erase; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_queue_head_t wait_q; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t retlen; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * First, let's erase the flash block. 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds init_waitqueue_head(&wait_q); 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.mtd = mtd; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.callback = erase_callback; 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.addr = pos; 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.len = len; 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.priv = (u_long)&wait_q; 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&wait_q, &wait); 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 887e1f0dc0551b99acb5e8fa161a7ac401994d57d8Artem Bityutskiy ret = mtd_erase(mtd, &erase); 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) { 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_RUNNING); 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&wait_q, &wait); 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "on \"%s\" failed\n", 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pos, len, mtd->name); 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); /* Wait for erase to finish. */ 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&wait_q, &wait); 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 102dff1550986a4c0e2a4e857c9085ef3cb66b2cec5Matthias Kaehlcke * Next, write the data to flash. 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 105eda95cbf75193808f62948fb0142ba0901d8bee2Artem Bityutskiy ret = mtd_write(mtd, pos, len, &retlen, buf); 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen != len) 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int write_cached_data (struct mtdblk_dev *mtdblk) 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 116cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtd_info *mtd = mtdblk->mbd.mtd; 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state != STATE_DIRTY) 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 122289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("mtdblock: writing cached data for \"%s\" " 12397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner "at 0x%lx, size 0x%x\n", mtd->name, 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset, mtdblk->cache_size); 12597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 12697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner ret = erase_write (mtd, mtdblk->cache_offset, 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_size, mtdblk->cache_data); 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 13225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * Here we could arguably set the cache state to STATE_CLEAN. 13397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * However this could lead to inconsistency since we will not 13497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * be notified if this content is altered on the flash by other 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * means. Let's declare it empty and leave buffering tasks to 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the buffer cache instead. 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_EMPTY; 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14397894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int len, const char *buf) 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 146cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtd_info *mtd = mtdblk->mbd.mtd; 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int sect_size = mtdblk->cache_size; 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t retlen; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 151289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n", 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name, pos, len); 15397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!sect_size) 155eda95cbf75193808f62948fb0142ba0901d8bee2Artem Bityutskiy return mtd_write(mtd, pos, len, &retlen, buf); 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len > 0) { 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long sect_start = (pos/sect_size)*sect_size; 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int offset = pos - sect_start; 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int size = sect_size - offset; 16197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner if( size > len ) 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = len; 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size == sect_size) { 16597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner /* 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We are covering a whole sector. Thus there is no 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * need to bother with the cache while it may still be 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * useful for other partial writes. 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = erase_write (mtd, pos, size, buf); 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Partial sector: need to use the cache */ 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state == STATE_DIRTY && 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset != sect_start) { 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = write_cached_data(mtdblk); 17997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner if (ret) 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state == STATE_EMPTY || 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset != sect_start) { 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* fill the cache with the current sector */ 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_EMPTY; 187329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy ret = mtd_read(mtd, sect_start, sect_size, 188329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy &retlen, mtdblk->cache_data); 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen != sect_size) 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset = sect_start; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_size = sect_size; 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_CLEAN; 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* write data to our local cache */ 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy (mtdblk->cache_data + offset, buf, size); 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_DIRTY; 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf += size; 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pos += size; 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= size; 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 21397894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int len, char *buf) 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 216cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtd_info *mtd = mtdblk->mbd.mtd; 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int sect_size = mtdblk->cache_size; 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t retlen; 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 221289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n", 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name, pos, len); 22397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!sect_size) 225329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy return mtd_read(mtd, pos, len, &retlen, buf); 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len > 0) { 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long sect_start = (pos/sect_size)*sect_size; 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int offset = pos - sect_start; 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int size = sect_size - offset; 23197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner if (size > len) 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = len; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check if the requested data is already cached 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the requested amount of data from our internal cache if it 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * contains what we want, otherwise we read the data directly 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * from flash. 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state != STATE_EMPTY && 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset == sect_start) { 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy (buf, mtdblk->cache_data + offset, size); 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 244329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy ret = mtd_read(mtd, pos, size, &retlen, buf); 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen != size) 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf += size; 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pos += size; 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= size; 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_readsect(struct mtd_blktrans_dev *dev, 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long block, char *buf) 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 262cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return do_cached_read(mtdblk, block<<9, 512, buf); 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_writesect(struct mtd_blktrans_dev *dev, 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long block, char *buf) 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 269cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { 271cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize); 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!mtdblk->cache_data) 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINTR; 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* -EINTR is not really correct, but it is the best match 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * documented in man 2 write for all cases. We could also 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * return -EAGAIN sometimes, but why bother? 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return do_cached_write(mtdblk, block<<9, 512, buf); 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_open(struct mtd_blktrans_dev *mbd) 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 284cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 286289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("mtdblock_open\n"); 28797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 288d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke mutex_lock(&mtdblks_lock); 289cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings if (mtdblk->count) { 290cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings mtdblk->count++; 291d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke mutex_unlock(&mtdblks_lock); 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 29497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK, it's not open. Create cache info for it */ 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->count = 1; 29748b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_init(&mtdblk->cache_mutex); 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_EMPTY; 299cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) { 300cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings mtdblk->cache_size = mbd->mtd->erasesize; 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_data = NULL; 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 304d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke mutex_unlock(&mtdblks_lock); 30597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 306289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("ok\n"); 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_release(struct mtd_blktrans_dev *mbd) 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 313cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 315289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("mtdblock_release\n"); 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 317d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke mutex_lock(&mtdblks_lock); 318d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke 31948b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_lock(&mtdblk->cache_mutex); 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds write_cached_data(mtdblk); 32148b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_unlock(&mtdblk->cache_mutex); 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!--mtdblk->count) { 32470d5098a4b1551864dd7df43f67b7f606a1a6438Alexander Stein /* 32570d5098a4b1551864dd7df43f67b7f606a1a6438Alexander Stein * It was the last usage. Free the cache, but only sync if 32670d5098a4b1551864dd7df43f67b7f606a1a6438Alexander Stein * opened for writing. 32770d5098a4b1551864dd7df43f67b7f606a1a6438Alexander Stein */ 32870d5098a4b1551864dd7df43f67b7f606a1a6438Alexander Stein if (mbd->file_mode & FMODE_WRITE) 32970d5098a4b1551864dd7df43f67b7f606a1a6438Alexander Stein mtd_sync(mbd->mtd); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vfree(mtdblk->cache_data); 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 332d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke 333d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke mutex_unlock(&mtdblks_lock); 334d676c11727815761e41a81b00c054b4bec452ae5Matthias Kaehlcke 335289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("ok\n"); 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 33897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner} 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_flush(struct mtd_blktrans_dev *dev) 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 342cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 34448b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_lock(&mtdblk->cache_mutex); 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds write_cached_data(mtdblk); 34648b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_unlock(&mtdblk->cache_mutex); 347327cf2922b4edf0439b219469722d2a502e37349Artem Bityutskiy mtd_sync(dev->mtd); 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 353cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev) 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 358cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings dev->mbd.mtd = mtd; 359cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings dev->mbd.devnum = mtd->index; 360191876729901d0c8dab8a331f9a1e4b73a56457bRichard Purdie 361cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings dev->mbd.size = mtd->size >> 9; 362cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings dev->mbd.tr = tr; 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!(mtd->flags & MTD_WRITEABLE)) 365cbfe93e9cedfcd59689bad9e67f57ef67545e5a0Ben Hutchings dev->mbd.readonly = 1; 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 367298304f1a554d44cf13391e531ced3cde69a8ce4Maxim Levitsky if (add_mtd_blktrans_dev(&dev->mbd)) 368298304f1a554d44cf13391e531ced3cde69a8ce4Maxim Levitsky kfree(dev); 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds del_mtd_blktrans_dev(dev); 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_blktrans_ops mtdblock_tr = { 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "mtdblock", 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .major = 31, 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .part_bits = 0, 380191876729901d0c8dab8a331f9a1e4b73a56457bRichard Purdie .blksize = 512, 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = mtdblock_open, 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .flush = mtdblock_flush, 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .release = mtdblock_release, 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .readsect = mtdblock_readsect, 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .writesect = mtdblock_writesect, 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .add_mtd = mtdblock_add_mtd, 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .remove_dev = mtdblock_remove_dev, 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .owner = THIS_MODULE, 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_mtdblock(void) 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return register_mtd_blktrans(&mtdblock_tr); 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_mtdblock(void) 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds deregister_mtd_blktrans(&mtdblock_tr); 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_mtdblock); 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_mtdblock); 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 4062f82af08fcc7dc01a7e98a49a5995a77e32a2925Nicolas PitreMODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net> et al."); 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices"); 408