Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 103e40f6 authored by David Woodhouse's avatar David Woodhouse
Browse files
parents 95b93a0c f4f91ac3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ static int __devinit generic_onenand_probe(struct device *dev)
	}

	info->onenand.mmcontrol = pdata->mmcontrol;
	info->onenand.irq = platform_get_irq(pdev, 0);

	info->mtd.name = pdev->dev.bus_id;
	info->mtd.priv = &info->onenand;
+167 −21
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
@@ -330,15 +331,123 @@ static int onenand_wait(struct mtd_info *mtd, int state)

	if (interrupt & ONENAND_INT_READ) {
		ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
		if (ecc & ONENAND_ECC_2BIT_ALL) {
		if (ecc) {
			DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
			return -EBADMSG;
			if (ecc & ONENAND_ECC_2BIT_ALL)
				mtd->ecc_stats.failed++;
			else if (ecc & ONENAND_ECC_1BIT_ALL)
				mtd->ecc_stats.corrected++;
		}
	}

	return 0;
}

/*
 * onenand_interrupt - [DEFAULT] onenand interrupt handler
 * @param irq		onenand interrupt number
 * @param dev_id	interrupt data
 *
 * complete the work
 */
static irqreturn_t onenand_interrupt(int irq, void *data)
{
	struct onenand_chip *this = (struct onenand_chip *) data;

	/* To handle shared interrupt */
	if (!this->complete.done)
		complete(&this->complete);

	return IRQ_HANDLED;
}

/*
 * onenand_interrupt_wait - [DEFAULT] wait until the command is done
 * @param mtd		MTD device structure
 * @param state		state to select the max. timeout value
 *
 * Wait for command done.
 */
static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
{
	struct onenand_chip *this = mtd->priv;

	/* To prevent soft lockup */
	touch_softlockup_watchdog();

	wait_for_completion(&this->complete);

	return onenand_wait(mtd, state);
}

/*
 * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
 * @param mtd		MTD device structure
 * @param state		state to select the max. timeout value
 *
 * Try interrupt based wait (It is used one-time)
 */
static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
{
	struct onenand_chip *this = mtd->priv;
	unsigned long remain, timeout;

	/* We use interrupt wait first */
	this->wait = onenand_interrupt_wait;

	/* To prevent soft lockup */
	touch_softlockup_watchdog();

	timeout = msecs_to_jiffies(100);
	remain = wait_for_completion_timeout(&this->complete, timeout);
	if (!remain) {
		printk(KERN_INFO "OneNAND: There's no interrupt. "
				"We use the normal wait\n");

		/* Release the irq */
		free_irq(this->irq, this);
		
		this->wait = onenand_wait;
	}

	return onenand_wait(mtd, state);
}

/*
 * onenand_setup_wait - [OneNAND Interface] setup onenand wait method
 * @param mtd		MTD device structure
 *
 * There's two method to wait onenand work
 * 1. polling - read interrupt status register
 * 2. interrupt - use the kernel interrupt method
 */
static void onenand_setup_wait(struct mtd_info *mtd)
{
	struct onenand_chip *this = mtd->priv;
	int syscfg;

	init_completion(&this->complete);

	if (this->irq <= 0) {
		this->wait = onenand_wait;
		return;
	}

	if (request_irq(this->irq, &onenand_interrupt,
				IRQF_SHARED, "onenand", this)) {
		/* If we can't get irq, use the normal wait */
		this->wait = onenand_wait;
		return;
	}

	/* Enable interrupt */
	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
	syscfg |= ONENAND_SYS_CFG1_IOBE;
	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);

	this->wait = onenand_try_interrupt_wait;
}

/**
 * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
 * @param mtd		MTD data structure
@@ -609,6 +718,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
	size_t *retlen, u_char *buf)
{
	struct onenand_chip *this = mtd->priv;
	struct mtd_ecc_stats stats;
	int read = 0, column;
	int thislen;
	int ret = 0;
@@ -627,6 +737,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,

	/* TODO handling oob */

	stats = mtd->ecc_stats;
	while (read < len) {
		thislen = min_t(int, mtd->writesize, len - read);

@@ -668,7 +779,11 @@ out:
	 * retlen == desired len and result == -EBADMSG
	 */
	*retlen = read;
	return ret;

	if (mtd->ecc_stats.failed - stats.failed)
		return -EBADMSG;

	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}

/**
@@ -1129,7 +1244,6 @@ static void onenand_sync(struct mtd_info *mtd)
	onenand_release_device(mtd);
}


/**
 * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
 * @param mtd		MTD device structure
@@ -1196,32 +1310,38 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
}

/**
 * onenand_unlock - [MTD Interface] Unlock block(s)
 * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
 * @param mtd		MTD device structure
 * @param ofs		offset relative to mtd start
 * @param len		number of bytes to unlock
 * @param len		number of bytes to lock or unlock
 *
 * Unlock one or more blocks
 * Lock or unlock one or more blocks
 */
static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
{
	struct onenand_chip *this = mtd->priv;
	int start, end, block, value, status;
	int wp_status_mask;

	start = ofs >> this->erase_shift;
	end = len >> this->erase_shift;

	if (cmd == ONENAND_CMD_LOCK)
		wp_status_mask = ONENAND_WP_LS;
	else
		wp_status_mask = ONENAND_WP_US;

	/* Continuous lock scheme */
	if (this->options & ONENAND_HAS_CONT_LOCK) {
		/* Set start block address */
		this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
		/* Set end block address */
		this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
		/* Write unlock command */
		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
		/* Write lock command */
		this->command(mtd, cmd, 0, 0);

		/* There's no return value */
		this->wait(mtd, FL_UNLOCKING);
		this->wait(mtd, FL_LOCKING);

		/* Sanity check */
		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
@@ -1230,7 +1350,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)

		/* Check lock status */
		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
		if (!(status & ONENAND_WP_US))
		if (!(status & wp_status_mask))
			printk(KERN_ERR "wp status = 0x%x\n", status);

		return 0;
@@ -1246,11 +1366,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
		/* Set start block address */
		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
		/* Write unlock command */
		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
		/* Write lock command */
		this->command(mtd, cmd, 0, 0);

		/* There's no return value */
		this->wait(mtd, FL_UNLOCKING);
		this->wait(mtd, FL_LOCKING);

		/* Sanity check */
		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
@@ -1259,13 +1379,39 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)

		/* Check lock status */
		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
		if (!(status & ONENAND_WP_US))
		if (!(status & wp_status_mask))
			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
	}

	return 0;
}

/**
 * onenand_lock - [MTD Interface] Lock block(s)
 * @param mtd		MTD device structure
 * @param ofs		offset relative to mtd start
 * @param len		number of bytes to unlock
 *
 * Lock one or more blocks
 */
static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
	return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
}

/**
 * onenand_unlock - [MTD Interface] Unlock block(s)
 * @param mtd		MTD device structure
 * @param ofs		offset relative to mtd start
 * @param len		number of bytes to unlock
 *
 * Unlock one or more blocks
 */
static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
	return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
}

/**
 * onenand_check_lock_status - [OneNAND Interface] Check lock status
 * @param this		onenand chip data structure
@@ -1310,7 +1456,7 @@ static int onenand_unlock_all(struct mtd_info *mtd)
		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);

		/* There's no return value */
		this->wait(mtd, FL_UNLOCKING);
		this->wait(mtd, FL_LOCKING);

		/* Sanity check */
		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
@@ -1334,7 +1480,7 @@ static int onenand_unlock_all(struct mtd_info *mtd)
		return 0;
	}

	mtd->unlock(mtd, 0x0, this->chipsize);
	onenand_unlock(mtd, 0x0, this->chipsize);

	return 0;
}
@@ -1846,7 +1992,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
	if (!this->command)
		this->command = onenand_command;
	if (!this->wait)
		this->wait = onenand_wait;
		onenand_setup_wait(mtd);

	if (!this->read_bufferram)
		this->read_bufferram = onenand_read_bufferram;
@@ -1922,7 +2068,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
	mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
#endif
	mtd->sync = onenand_sync;
	mtd->lock = NULL;
	mtd->lock = onenand_lock;
	mtd->unlock = onenand_unlock;
	mtd->suspend = onenand_suspend;
	mtd->resume = onenand_resume;
+1 −0
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
				bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
				printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
					i >> 1, (unsigned int) from);
				mtd->ecc_stats.badblocks++;
				break;
			}
		}
+4 −1
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#define __LINUX_MTD_ONENAND_H

#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/mtd/onenand_regs.h>
#include <linux/mtd/bbm.h>

@@ -33,7 +34,6 @@ typedef enum {
	FL_WRITING,
	FL_ERASING,
	FL_SYNCING,
	FL_UNLOCKING,
	FL_LOCKING,
	FL_RESETING,
	FL_OTPING,
@@ -120,6 +120,9 @@ struct onenand_chip {
	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
	int (*scan_bbt)(struct mtd_info *mtd);

	struct completion	complete;
	int			irq;

	spinlock_t		chip_lock;
	wait_queue_head_t	wq;
	onenand_state_t		state;
+1 −0
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@
 * ECC Status Reigser FF00h (R)
 */
#define ONENAND_ECC_1BIT		(1 << 0)
#define ONENAND_ECC_1BIT_ALL		(0x5555)
#define ONENAND_ECC_2BIT		(1 << 1)
#define ONENAND_ECC_2BIT_ALL		(0xAAAA)