All pastes #1815120 Raw Edit

mbm

public text v1 · immutable
#1815120 ·published 2010-02-28 00:44 UTC
rendered paste body
/* * This modules maps additional mtd partions of the Motorola Milestone * * Copyright (C) 2010 Janne Grunau * Copyright (C) 2010 Mike Baker * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. * */#include <linux/module.h>#include <linux/platform_device.h>#include <linux/err.h>#include <linux/mtd/mtd.h>#include <linux/mtd/partitions.h>#include <linux/mtd/nand.h>#include <linux/sched.h>#define mtd_hack_part(pname, pstart, pend)	.name       = pname,         \						.offset     = pstart,        \						.size       = pend - pstart, \struct mtd_partition part[] = {	{ mtd_hack_part("h_hack",	0x00000000, 0x00620000) },};/** * nand_check_wp - [GENERIC] check if the chip is write protected * @mtd:	MTD device structure * Check, if the device is write protected * * The function expects, that the device is already selected */static int nand_check_wp(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	/* Check the WP bit */	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);	return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;}/** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd:	MTD device structure * @ofs:	offset from device start * @getchip:	0, if the chip is already selected * @allowbbt:	1, if its allowed to access the bbt area * * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,			       int allowbbt){	struct nand_chip *chip = mtd->priv;	if (!chip->bbt)		return chip->block_bad(mtd, ofs, getchip);	/* Return info from the table */	return nand_isbad_bbt(mtd, ofs, allowbbt);}/** * nand_get_device - [GENERIC] Get chip for selected access * @chip:	the nand chip descriptor * @mtd:	MTD device structure * @new_state:	the state which is requested * * Get the device and lock it for exclusive access */static intnand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state){	spinlock_t *lock = &chip->controller->lock;	wait_queue_head_t *wq = &chip->controller->wq;	DECLARE_WAITQUEUE(wait, current); retry:	spin_lock(lock);	/* Hardware controller shared among independend devices */	/* Hardware controller shared among independend devices */	if (!chip->controller->active)		chip->controller->active = chip;	if (chip->controller->active == chip && chip->state == FL_READY) {		chip->state = new_state;		spin_unlock(lock);		return 0;	}	if (new_state == FL_PM_SUSPENDED) {		spin_unlock(lock);		return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;	}	set_current_state(TASK_UNINTERRUPTIBLE);	add_wait_queue(wq, &wait);	spin_unlock(lock);	schedule();	remove_wait_queue(wq, &wait);	goto retry;}/** * nand_release_device - [GENERIC] release chip * @mtd:	MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */static void nand_release_device(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	/* De-select the NAND device */	chip->select_chip(mtd, -1);	/* Release the controller and the chip */	spin_lock(&chip->controller->lock);	chip->controller->active = NULL;	chip->state = FL_READY;	wake_up(&chip->controller->wq);	spin_unlock(&chip->controller->lock);}#define BBT_PAGE_MASK	0xffffff3f/** * nand_erase_nand - [Internal] erase block(s) * @mtd:	MTD device structure * @instr:	erase instruction * * Erase one ore more blocks */int nand_erase_nand_hack(struct mtd_info *mtd, struct erase_info *instr){	int allowbbt = 0;	int page, status, pages_per_block, ret, chipnr;	struct nand_chip *chip = mtd->priv;	loff_t rewrite_bbt[NAND_MAX_CHIPS]={0};	unsigned int bbt_masked_page = 0xffffffff;	loff_t len;	DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n",	      (unsigned long long)instr->addr, (unsigned long long)instr->len);#if 0	/* if attempt to erase nand flash physical block# 0,	 * dump stack and panic the phone ...	*/	if (!(instr->addr)) {		dump_stack();		panic("%s: nand flash physical block: 0x%12llx\n", __func__,		  (unsigned long long)instr->addr);	}#endif	/* Start address must align on block boundary */	if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");		return -EINVAL;	}	/* Length must align on block boundary */	if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "		      "Length not block aligned\n");		return -EINVAL;	}	/* Do not allow erase past end of device */	if ((instr->len + instr->addr) > mtd->size) {		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "		      "Erase past end of device\n");		return -EINVAL;	}	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;	/* Grab the lock and see if the device is available */	nand_get_device(chip, mtd, FL_ERASING);	/* Shift to get first page */	page = (int)(instr->addr >> chip->page_shift);	chipnr = (int)(instr->addr >> chip->chip_shift);	/* Calculate pages in each block */	pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);	/* Select the NAND device */	chip->select_chip(mtd, chipnr);	/* Check, if it is write protected */	if (nand_check_wp(mtd)) {		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "		      "Device is write protected!!!\n");		instr->state = MTD_ERASE_FAILED;		goto erase_exit;	}	/*	 * If BBT requires refresh, set the BBT page mask to see if the BBT	 * should be rewritten. Otherwise the mask is set to 0xffffffff which	 * can not be matched. This is also done when the bbt is actually	 * erased to avoid recusrsive updates	 */	if (chip->options & BBT_AUTO_REFRESH && !allowbbt)		bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;	/* Loop through the pages */	len = instr->len;	instr->state = MTD_ERASING;	while (len) {		/*		 * heck if we have a bad block, we do not erase bad blocks !		 */		if (nand_block_checkbad(mtd, ((loff_t) page) <<					chip->page_shift, 0, allowbbt)) {			printk(KERN_WARNING "nand_erase: attempt to erase a "			       "bad block at page 0x%08x\n", page);			instr->state = MTD_ERASE_FAILED;			goto erase_exit;		}		/*		 * Invalidate the page cache, if we erase the block which		 * contains the current cached page		 */		if (page <= chip->pagebuf && chip->pagebuf <		    (page + pages_per_block))			chip->pagebuf = -1;		chip->erase_cmd(mtd, page & chip->pagemask);		status = chip->waitfunc(mtd, chip);		/*		 * See if operation failed and additional status checks are		 * available		 */		if ((status & NAND_STATUS_FAIL) && (chip->errstat))			status = chip->errstat(mtd, chip, FL_ERASING,					       status, page);		/* See if block erase succeeded */		if (status & NAND_STATUS_FAIL) {			DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "			      "Failed erase, page 0x%08x\n", page);			instr->state = MTD_ERASE_FAILED;			instr->fail_addr =				((loff_t)page << chip->page_shift);			goto erase_exit;		}		/*		 * If BBT requires refresh, set the BBT rewrite flag to the		 * page being erased		 */		if (bbt_masked_page != 0xffffffff &&		    (page & BBT_PAGE_MASK) == bbt_masked_page)			    rewrite_bbt[chipnr] =					((loff_t)page << chip->page_shift);		/* Increment page address and decrement length */		len -= (1 << chip->phys_erase_shift);		page += pages_per_block;		/* Check, if we cross a chip boundary */		if (len && !(page & chip->pagemask)) {			chipnr++;			chip->select_chip(mtd, -1);			chip->select_chip(mtd, chipnr);			/*			 * If BBT requires refresh and BBT-PERCHIP, set the BBT			 * page mask to see if this BBT should be rewritten			 */			if (bbt_masked_page != 0xffffffff &&			    (chip->bbt_td->options & NAND_BBT_PERCHIP))				bbt_masked_page = chip->bbt_td->pages[chipnr] &					BBT_PAGE_MASK;		}	}	instr->state = MTD_ERASE_DONE; erase_exit:	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;	/* Deselect and wake up anyone waiting on the device */	nand_release_device(mtd);	/* Do call back function */	if (!ret)		mtd_erase_callback(instr);	/*	 * If BBT requires refresh and erase was successful, rewrite any	 * selected bad block tables	 */	if (bbt_masked_page == 0xffffffff || ret)		return ret;	for (chipnr = 0; chipnr < chip->numchips; chipnr++) {		if (!rewrite_bbt[chipnr])			continue;		/* update the BBT for chip */		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "		      "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr],		      chip->bbt_td->pages[chipnr]);		nand_update_bbt(mtd, rewrite_bbt[chipnr]);	}	/* Return more or less happy */	return ret;}static int create_missing_flash_parts(struct device *dev, void *data){	struct mtd_info *mtd = NULL;	printk(KERN_INFO "mtd-hack: device %s\n", dev->init_name);	mtd = dev_get_drvdata(dev);	if (!mtd)		return -1;        printk(KERN_INFO "mtd-hack: mtd name %s, type %d, size %llu\n",                mtd->name, mtd->type, mtd->size);	add_mtd_partitions(mtd, part, sizeof(part)/sizeof(struct mtd_partition));		mtd->erase = nand_erase_nand_hack;	return 0;}static int __init mtd_init(void){	struct device_driver *devdrv;	int err = 0;	//	struct device_driver *driver_find(const char *name, struct bus_type *bus);	devdrv = driver_find("omap2-nand", &platform_bus_type);	printk(KERN_INFO "mtd-hack: found driver %s modname %s\n", devdrv->name, devdrv->mod_name);	//	int driver_for_each_device(struct device_driver *drv, struct device *start,	//	                           void *data, int (*fn)(struct device *, void *))	err = driver_for_each_device(devdrv, NULL, NULL, create_missing_flash_parts);	printk(KERN_INFO "mtd hack loaded");	return 0;} module_init(mtd_init);