162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  OneNAND driver for OMAP2 / OMAP3
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright © 2005-2006 Nokia Corporation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjölä
862306a36Sopenharmony_ci *  IRQ and DMA support written by Timo Teras
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1462306a36Sopenharmony_ci#include <linux/mtd/onenand.h>
1562306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/omap-gpmc.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2262306a36Sopenharmony_ci#include <linux/dmaengine.h>
2362306a36Sopenharmony_ci#include <linux/io.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <asm/mach/flash.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define DRIVER_NAME "omap2-onenand"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define ONENAND_BUFRAM_SIZE	(1024 * 5)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct omap2_onenand {
3462306a36Sopenharmony_ci	struct platform_device *pdev;
3562306a36Sopenharmony_ci	int gpmc_cs;
3662306a36Sopenharmony_ci	unsigned long phys_base;
3762306a36Sopenharmony_ci	struct gpio_desc *int_gpiod;
3862306a36Sopenharmony_ci	struct mtd_info mtd;
3962306a36Sopenharmony_ci	struct onenand_chip onenand;
4062306a36Sopenharmony_ci	struct completion irq_done;
4162306a36Sopenharmony_ci	struct completion dma_done;
4262306a36Sopenharmony_ci	struct dma_chan *dma_chan;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void omap2_onenand_dma_complete_func(void *completion)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	complete(completion);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct omap2_onenand *c = dev_id;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	complete(&c->irq_done);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return IRQ_HANDLED;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline unsigned short read_reg(struct omap2_onenand *c, int reg)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return readw(c->onenand.base + reg);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline void write_reg(struct omap2_onenand *c, unsigned short value,
6562306a36Sopenharmony_ci			     int reg)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	writew(value, c->onenand.base + reg);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int omap2_onenand_set_cfg(struct omap2_onenand *c,
7162306a36Sopenharmony_ci				 bool sr, bool sw,
7262306a36Sopenharmony_ci				 int latency, int burst_len)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	unsigned short reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	reg |= latency << ONENAND_SYS_CFG1_BRL_SHIFT;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	switch (burst_len) {
7962306a36Sopenharmony_ci	case 0:		/* continuous */
8062306a36Sopenharmony_ci		break;
8162306a36Sopenharmony_ci	case 4:
8262306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_BL_4;
8362306a36Sopenharmony_ci		break;
8462306a36Sopenharmony_ci	case 8:
8562306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_BL_8;
8662306a36Sopenharmony_ci		break;
8762306a36Sopenharmony_ci	case 16:
8862306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_BL_16;
8962306a36Sopenharmony_ci		break;
9062306a36Sopenharmony_ci	case 32:
9162306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_BL_32;
9262306a36Sopenharmony_ci		break;
9362306a36Sopenharmony_ci	default:
9462306a36Sopenharmony_ci		return -EINVAL;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (latency > 5)
9862306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_HF;
9962306a36Sopenharmony_ci	if (latency > 7)
10062306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_VHF;
10162306a36Sopenharmony_ci	if (sr)
10262306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_SYNC_READ;
10362306a36Sopenharmony_ci	if (sw)
10462306a36Sopenharmony_ci		reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	write_reg(c, reg, ONENAND_REG_SYS_CFG1);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int omap2_onenand_get_freq(int ver)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	switch ((ver >> 4) & 0xf) {
11462306a36Sopenharmony_ci	case 0:
11562306a36Sopenharmony_ci		return 40;
11662306a36Sopenharmony_ci	case 1:
11762306a36Sopenharmony_ci		return 54;
11862306a36Sopenharmony_ci	case 2:
11962306a36Sopenharmony_ci		return 66;
12062306a36Sopenharmony_ci	case 3:
12162306a36Sopenharmony_ci		return 83;
12262306a36Sopenharmony_ci	case 4:
12362306a36Sopenharmony_ci		return 104;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return -EINVAL;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n",
13262306a36Sopenharmony_ci	       msg, state, ctrl, intr);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void wait_warn(char *msg, int state, unsigned int ctrl,
13662306a36Sopenharmony_ci		      unsigned int intr)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x "
13962306a36Sopenharmony_ci	       "intr 0x%04x\n", msg, state, ctrl, intr);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int omap2_onenand_wait(struct mtd_info *mtd, int state)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
14562306a36Sopenharmony_ci	struct onenand_chip *this = mtd->priv;
14662306a36Sopenharmony_ci	unsigned int intr = 0;
14762306a36Sopenharmony_ci	unsigned int ctrl, ctrl_mask;
14862306a36Sopenharmony_ci	unsigned long timeout;
14962306a36Sopenharmony_ci	u32 syscfg;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (state == FL_RESETTING || state == FL_PREPARING_ERASE ||
15262306a36Sopenharmony_ci	    state == FL_VERIFYING_ERASE) {
15362306a36Sopenharmony_ci		int i = 21;
15462306a36Sopenharmony_ci		unsigned int intr_flags = ONENAND_INT_MASTER;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		switch (state) {
15762306a36Sopenharmony_ci		case FL_RESETTING:
15862306a36Sopenharmony_ci			intr_flags |= ONENAND_INT_RESET;
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		case FL_PREPARING_ERASE:
16162306a36Sopenharmony_ci			intr_flags |= ONENAND_INT_ERASE;
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci		case FL_VERIFYING_ERASE:
16462306a36Sopenharmony_ci			i = 101;
16562306a36Sopenharmony_ci			break;
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		while (--i) {
16962306a36Sopenharmony_ci			udelay(1);
17062306a36Sopenharmony_ci			intr = read_reg(c, ONENAND_REG_INTERRUPT);
17162306a36Sopenharmony_ci			if (intr & ONENAND_INT_MASTER)
17262306a36Sopenharmony_ci				break;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci		ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
17562306a36Sopenharmony_ci		if (ctrl & ONENAND_CTRL_ERROR) {
17662306a36Sopenharmony_ci			wait_err("controller error", state, ctrl, intr);
17762306a36Sopenharmony_ci			return -EIO;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		if ((intr & intr_flags) == intr_flags)
18062306a36Sopenharmony_ci			return 0;
18162306a36Sopenharmony_ci		/* Continue in wait for interrupt branch */
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (state != FL_READING) {
18562306a36Sopenharmony_ci		int result;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		/* Turn interrupts on */
18862306a36Sopenharmony_ci		syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
18962306a36Sopenharmony_ci		if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) {
19062306a36Sopenharmony_ci			syscfg |= ONENAND_SYS_CFG1_IOBE;
19162306a36Sopenharmony_ci			write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
19262306a36Sopenharmony_ci			/* Add a delay to let GPIO settle */
19362306a36Sopenharmony_ci			syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		reinit_completion(&c->irq_done);
19762306a36Sopenharmony_ci		result = gpiod_get_value(c->int_gpiod);
19862306a36Sopenharmony_ci		if (result < 0) {
19962306a36Sopenharmony_ci			ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
20062306a36Sopenharmony_ci			intr = read_reg(c, ONENAND_REG_INTERRUPT);
20162306a36Sopenharmony_ci			wait_err("gpio error", state, ctrl, intr);
20262306a36Sopenharmony_ci			return result;
20362306a36Sopenharmony_ci		} else if (result == 0) {
20462306a36Sopenharmony_ci			int retry_cnt = 0;
20562306a36Sopenharmony_ciretry:
20662306a36Sopenharmony_ci			if (!wait_for_completion_io_timeout(&c->irq_done,
20762306a36Sopenharmony_ci						msecs_to_jiffies(20))) {
20862306a36Sopenharmony_ci				/* Timeout after 20ms */
20962306a36Sopenharmony_ci				ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
21062306a36Sopenharmony_ci				if (ctrl & ONENAND_CTRL_ONGO &&
21162306a36Sopenharmony_ci				    !this->ongoing) {
21262306a36Sopenharmony_ci					/*
21362306a36Sopenharmony_ci					 * The operation seems to be still going
21462306a36Sopenharmony_ci					 * so give it some more time.
21562306a36Sopenharmony_ci					 */
21662306a36Sopenharmony_ci					retry_cnt += 1;
21762306a36Sopenharmony_ci					if (retry_cnt < 3)
21862306a36Sopenharmony_ci						goto retry;
21962306a36Sopenharmony_ci					intr = read_reg(c,
22062306a36Sopenharmony_ci							ONENAND_REG_INTERRUPT);
22162306a36Sopenharmony_ci					wait_err("timeout", state, ctrl, intr);
22262306a36Sopenharmony_ci					return -EIO;
22362306a36Sopenharmony_ci				}
22462306a36Sopenharmony_ci				intr = read_reg(c, ONENAND_REG_INTERRUPT);
22562306a36Sopenharmony_ci				if ((intr & ONENAND_INT_MASTER) == 0)
22662306a36Sopenharmony_ci					wait_warn("timeout", state, ctrl, intr);
22762306a36Sopenharmony_ci			}
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	} else {
23062306a36Sopenharmony_ci		int retry_cnt = 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		/* Turn interrupts off */
23362306a36Sopenharmony_ci		syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
23462306a36Sopenharmony_ci		syscfg &= ~ONENAND_SYS_CFG1_IOBE;
23562306a36Sopenharmony_ci		write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(20);
23862306a36Sopenharmony_ci		while (1) {
23962306a36Sopenharmony_ci			if (time_before(jiffies, timeout)) {
24062306a36Sopenharmony_ci				intr = read_reg(c, ONENAND_REG_INTERRUPT);
24162306a36Sopenharmony_ci				if (intr & ONENAND_INT_MASTER)
24262306a36Sopenharmony_ci					break;
24362306a36Sopenharmony_ci			} else {
24462306a36Sopenharmony_ci				/* Timeout after 20ms */
24562306a36Sopenharmony_ci				ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
24662306a36Sopenharmony_ci				if (ctrl & ONENAND_CTRL_ONGO) {
24762306a36Sopenharmony_ci					/*
24862306a36Sopenharmony_ci					 * The operation seems to be still going
24962306a36Sopenharmony_ci					 * so give it some more time.
25062306a36Sopenharmony_ci					 */
25162306a36Sopenharmony_ci					retry_cnt += 1;
25262306a36Sopenharmony_ci					if (retry_cnt < 3) {
25362306a36Sopenharmony_ci						timeout = jiffies +
25462306a36Sopenharmony_ci							  msecs_to_jiffies(20);
25562306a36Sopenharmony_ci						continue;
25662306a36Sopenharmony_ci					}
25762306a36Sopenharmony_ci				}
25862306a36Sopenharmony_ci				break;
25962306a36Sopenharmony_ci			}
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	intr = read_reg(c, ONENAND_REG_INTERRUPT);
26462306a36Sopenharmony_ci	ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (intr & ONENAND_INT_READ) {
26762306a36Sopenharmony_ci		int ecc = read_reg(c, ONENAND_REG_ECC_STATUS);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		if (ecc) {
27062306a36Sopenharmony_ci			unsigned int addr1, addr8;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci			addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1);
27362306a36Sopenharmony_ci			addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8);
27462306a36Sopenharmony_ci			if (ecc & ONENAND_ECC_2BIT_ALL) {
27562306a36Sopenharmony_ci				printk(KERN_ERR "onenand_wait: ECC error = "
27662306a36Sopenharmony_ci				       "0x%04x, addr1 %#x, addr8 %#x\n",
27762306a36Sopenharmony_ci				       ecc, addr1, addr8);
27862306a36Sopenharmony_ci				mtd->ecc_stats.failed++;
27962306a36Sopenharmony_ci				return -EBADMSG;
28062306a36Sopenharmony_ci			} else if (ecc & ONENAND_ECC_1BIT_ALL) {
28162306a36Sopenharmony_ci				printk(KERN_NOTICE "onenand_wait: correctable "
28262306a36Sopenharmony_ci				       "ECC error = 0x%04x, addr1 %#x, "
28362306a36Sopenharmony_ci				       "addr8 %#x\n", ecc, addr1, addr8);
28462306a36Sopenharmony_ci				mtd->ecc_stats.corrected++;
28562306a36Sopenharmony_ci			}
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci	} else if (state == FL_READING) {
28862306a36Sopenharmony_ci		wait_err("timeout", state, ctrl, intr);
28962306a36Sopenharmony_ci		return -EIO;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (ctrl & ONENAND_CTRL_ERROR) {
29362306a36Sopenharmony_ci		wait_err("controller error", state, ctrl, intr);
29462306a36Sopenharmony_ci		if (ctrl & ONENAND_CTRL_LOCK)
29562306a36Sopenharmony_ci			printk(KERN_ERR "onenand_wait: "
29662306a36Sopenharmony_ci					"Device is write protected!!!\n");
29762306a36Sopenharmony_ci		return -EIO;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	ctrl_mask = 0xFE9F;
30162306a36Sopenharmony_ci	if (this->ongoing)
30262306a36Sopenharmony_ci		ctrl_mask &= ~0x8000;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (ctrl & ctrl_mask)
30562306a36Sopenharmony_ci		wait_warn("unexpected controller status", state, ctrl, intr);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct onenand_chip *this = mtd->priv;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (ONENAND_CURRENT_BUFFERRAM(this)) {
31562306a36Sopenharmony_ci		if (area == ONENAND_DATARAM)
31662306a36Sopenharmony_ci			return this->writesize;
31762306a36Sopenharmony_ci		if (area == ONENAND_SPARERAM)
31862306a36Sopenharmony_ci			return mtd->oobsize;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return 0;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic inline int omap2_onenand_dma_transfer(struct omap2_onenand *c,
32562306a36Sopenharmony_ci					     dma_addr_t src, dma_addr_t dst,
32662306a36Sopenharmony_ci					     size_t count)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
32962306a36Sopenharmony_ci	dma_cookie_t cookie;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count,
33262306a36Sopenharmony_ci				       DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
33362306a36Sopenharmony_ci	if (!tx) {
33462306a36Sopenharmony_ci		dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n");
33562306a36Sopenharmony_ci		return -EIO;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	reinit_completion(&c->dma_done);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	tx->callback = omap2_onenand_dma_complete_func;
34162306a36Sopenharmony_ci	tx->callback_param = &c->dma_done;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	cookie = tx->tx_submit(tx);
34462306a36Sopenharmony_ci	if (dma_submit_error(cookie)) {
34562306a36Sopenharmony_ci		dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n");
34662306a36Sopenharmony_ci		return -EIO;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	dma_async_issue_pending(c->dma_chan);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (!wait_for_completion_io_timeout(&c->dma_done,
35262306a36Sopenharmony_ci					    msecs_to_jiffies(20))) {
35362306a36Sopenharmony_ci		dmaengine_terminate_sync(c->dma_chan);
35462306a36Sopenharmony_ci		return -ETIMEDOUT;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
36162306a36Sopenharmony_ci					unsigned char *buffer, int offset,
36262306a36Sopenharmony_ci					size_t count)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
36562306a36Sopenharmony_ci	struct onenand_chip *this = mtd->priv;
36662306a36Sopenharmony_ci	struct device *dev = &c->pdev->dev;
36762306a36Sopenharmony_ci	void *buf = (void *)buffer;
36862306a36Sopenharmony_ci	dma_addr_t dma_src, dma_dst;
36962306a36Sopenharmony_ci	int bram_offset, err;
37062306a36Sopenharmony_ci	size_t xtra;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
37362306a36Sopenharmony_ci	/*
37462306a36Sopenharmony_ci	 * If the buffer address is not DMA-able, len is not long enough to
37562306a36Sopenharmony_ci	 * make DMA transfers profitable or if invoked from panic_write()
37662306a36Sopenharmony_ci	 * fallback to PIO mode.
37762306a36Sopenharmony_ci	 */
37862306a36Sopenharmony_ci	if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 ||
37962306a36Sopenharmony_ci	    count < 384 || mtd->oops_panic_write)
38062306a36Sopenharmony_ci		goto out_copy;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	xtra = count & 3;
38362306a36Sopenharmony_ci	if (xtra) {
38462306a36Sopenharmony_ci		count -= xtra;
38562306a36Sopenharmony_ci		memcpy(buf + count, this->base + bram_offset + count, xtra);
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE);
38962306a36Sopenharmony_ci	dma_src = c->phys_base + bram_offset;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (dma_mapping_error(dev, dma_dst)) {
39262306a36Sopenharmony_ci		dev_err(dev, "Couldn't DMA map a %d byte buffer\n", count);
39362306a36Sopenharmony_ci		goto out_copy;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	err = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count);
39762306a36Sopenharmony_ci	dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE);
39862306a36Sopenharmony_ci	if (!err)
39962306a36Sopenharmony_ci		return 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	dev_err(dev, "timeout waiting for DMA\n");
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ciout_copy:
40462306a36Sopenharmony_ci	memcpy(buf, this->base + bram_offset, count);
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
40962306a36Sopenharmony_ci					 const unsigned char *buffer,
41062306a36Sopenharmony_ci					 int offset, size_t count)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
41362306a36Sopenharmony_ci	struct onenand_chip *this = mtd->priv;
41462306a36Sopenharmony_ci	struct device *dev = &c->pdev->dev;
41562306a36Sopenharmony_ci	void *buf = (void *)buffer;
41662306a36Sopenharmony_ci	dma_addr_t dma_src, dma_dst;
41762306a36Sopenharmony_ci	int bram_offset, err;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
42062306a36Sopenharmony_ci	/*
42162306a36Sopenharmony_ci	 * If the buffer address is not DMA-able, len is not long enough to
42262306a36Sopenharmony_ci	 * make DMA transfers profitable or if invoked from panic_write()
42362306a36Sopenharmony_ci	 * fallback to PIO mode.
42462306a36Sopenharmony_ci	 */
42562306a36Sopenharmony_ci	if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 ||
42662306a36Sopenharmony_ci	    count < 384 || mtd->oops_panic_write)
42762306a36Sopenharmony_ci		goto out_copy;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	dma_src = dma_map_single(dev, buf, count, DMA_TO_DEVICE);
43062306a36Sopenharmony_ci	dma_dst = c->phys_base + bram_offset;
43162306a36Sopenharmony_ci	if (dma_mapping_error(dev, dma_src)) {
43262306a36Sopenharmony_ci		dev_err(dev, "Couldn't DMA map a %d byte buffer\n", count);
43362306a36Sopenharmony_ci		goto out_copy;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	err = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count);
43762306a36Sopenharmony_ci	dma_unmap_page(dev, dma_src, count, DMA_TO_DEVICE);
43862306a36Sopenharmony_ci	if (!err)
43962306a36Sopenharmony_ci		return 0;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	dev_err(dev, "timeout waiting for DMA\n");
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciout_copy:
44462306a36Sopenharmony_ci	memcpy(this->base + bram_offset, buf, count);
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void omap2_onenand_shutdown(struct platform_device *pdev)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* With certain content in the buffer RAM, the OMAP boot ROM code
45362306a36Sopenharmony_ci	 * can recognize the flash chip incorrectly. Zero it out before
45462306a36Sopenharmony_ci	 * soft reset.
45562306a36Sopenharmony_ci	 */
45662306a36Sopenharmony_ci	memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic int omap2_onenand_probe(struct platform_device *pdev)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	u32 val;
46262306a36Sopenharmony_ci	dma_cap_mask_t mask;
46362306a36Sopenharmony_ci	int freq, latency, r;
46462306a36Sopenharmony_ci	struct resource *res;
46562306a36Sopenharmony_ci	struct omap2_onenand *c;
46662306a36Sopenharmony_ci	struct gpmc_onenand_info info;
46762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
46862306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	r = of_property_read_u32(np, "reg", &val);
47162306a36Sopenharmony_ci	if (r) {
47262306a36Sopenharmony_ci		dev_err(dev, "reg not found in DT\n");
47362306a36Sopenharmony_ci		return r;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	c = devm_kzalloc(dev, sizeof(struct omap2_onenand), GFP_KERNEL);
47762306a36Sopenharmony_ci	if (!c)
47862306a36Sopenharmony_ci		return -ENOMEM;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	init_completion(&c->irq_done);
48162306a36Sopenharmony_ci	init_completion(&c->dma_done);
48262306a36Sopenharmony_ci	c->gpmc_cs = val;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	c->onenand.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
48562306a36Sopenharmony_ci	if (IS_ERR(c->onenand.base))
48662306a36Sopenharmony_ci		return PTR_ERR(c->onenand.base);
48762306a36Sopenharmony_ci	c->phys_base = res->start;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN);
49062306a36Sopenharmony_ci	if (IS_ERR(c->int_gpiod)) {
49162306a36Sopenharmony_ci		/* Just try again if this happens */
49262306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(c->int_gpiod), "error getting gpio\n");
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (c->int_gpiod) {
49662306a36Sopenharmony_ci		r = devm_request_irq(dev, gpiod_to_irq(c->int_gpiod),
49762306a36Sopenharmony_ci				     omap2_onenand_interrupt,
49862306a36Sopenharmony_ci				     IRQF_TRIGGER_RISING, "onenand", c);
49962306a36Sopenharmony_ci		if (r)
50062306a36Sopenharmony_ci			return r;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		c->onenand.wait = omap2_onenand_wait;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	dma_cap_zero(mask);
50662306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, mask);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	c->dma_chan = dma_request_channel(mask, NULL, NULL);
50962306a36Sopenharmony_ci	if (c->dma_chan) {
51062306a36Sopenharmony_ci		c->onenand.read_bufferram = omap2_onenand_read_bufferram;
51162306a36Sopenharmony_ci		c->onenand.write_bufferram = omap2_onenand_write_bufferram;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	c->pdev = pdev;
51562306a36Sopenharmony_ci	c->mtd.priv = &c->onenand;
51662306a36Sopenharmony_ci	c->mtd.dev.parent = dev;
51762306a36Sopenharmony_ci	mtd_set_of_node(&c->mtd, dev->of_node);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	dev_info(dev, "initializing on CS%d (0x%08lx), va %p, %s mode\n",
52062306a36Sopenharmony_ci		 c->gpmc_cs, c->phys_base, c->onenand.base,
52162306a36Sopenharmony_ci		 c->dma_chan ? "DMA" : "PIO");
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	r = onenand_scan(&c->mtd, 1);
52462306a36Sopenharmony_ci	if (r < 0)
52562306a36Sopenharmony_ci		goto err_release_dma;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	freq = omap2_onenand_get_freq(c->onenand.version_id);
52862306a36Sopenharmony_ci	if (freq > 0) {
52962306a36Sopenharmony_ci		switch (freq) {
53062306a36Sopenharmony_ci		case 104:
53162306a36Sopenharmony_ci			latency = 7;
53262306a36Sopenharmony_ci			break;
53362306a36Sopenharmony_ci		case 83:
53462306a36Sopenharmony_ci			latency = 6;
53562306a36Sopenharmony_ci			break;
53662306a36Sopenharmony_ci		case 66:
53762306a36Sopenharmony_ci			latency = 5;
53862306a36Sopenharmony_ci			break;
53962306a36Sopenharmony_ci		case 56:
54062306a36Sopenharmony_ci			latency = 4;
54162306a36Sopenharmony_ci			break;
54262306a36Sopenharmony_ci		default:	/* 40 MHz or lower */
54362306a36Sopenharmony_ci			latency = 3;
54462306a36Sopenharmony_ci			break;
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		r = gpmc_omap_onenand_set_timings(dev, c->gpmc_cs,
54862306a36Sopenharmony_ci						  freq, latency, &info);
54962306a36Sopenharmony_ci		if (r)
55062306a36Sopenharmony_ci			goto err_release_onenand;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		r = omap2_onenand_set_cfg(c, info.sync_read, info.sync_write,
55362306a36Sopenharmony_ci					  latency, info.burst_len);
55462306a36Sopenharmony_ci		if (r)
55562306a36Sopenharmony_ci			goto err_release_onenand;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		if (info.sync_read || info.sync_write)
55862306a36Sopenharmony_ci			dev_info(dev, "optimized timings for %d MHz\n", freq);
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	r = mtd_device_register(&c->mtd, NULL, 0);
56262306a36Sopenharmony_ci	if (r)
56362306a36Sopenharmony_ci		goto err_release_onenand;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	platform_set_drvdata(pdev, c);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return 0;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cierr_release_onenand:
57062306a36Sopenharmony_ci	onenand_release(&c->mtd);
57162306a36Sopenharmony_cierr_release_dma:
57262306a36Sopenharmony_ci	if (c->dma_chan)
57362306a36Sopenharmony_ci		dma_release_channel(c->dma_chan);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return r;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic void omap2_onenand_remove(struct platform_device *pdev)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	onenand_release(&c->mtd);
58362306a36Sopenharmony_ci	if (c->dma_chan)
58462306a36Sopenharmony_ci		dma_release_channel(c->dma_chan);
58562306a36Sopenharmony_ci	omap2_onenand_shutdown(pdev);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic const struct of_device_id omap2_onenand_id_table[] = {
58962306a36Sopenharmony_ci	{ .compatible = "ti,omap2-onenand", },
59062306a36Sopenharmony_ci	{},
59162306a36Sopenharmony_ci};
59262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap2_onenand_id_table);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic struct platform_driver omap2_onenand_driver = {
59562306a36Sopenharmony_ci	.probe		= omap2_onenand_probe,
59662306a36Sopenharmony_ci	.remove_new	= omap2_onenand_remove,
59762306a36Sopenharmony_ci	.shutdown	= omap2_onenand_shutdown,
59862306a36Sopenharmony_ci	.driver		= {
59962306a36Sopenharmony_ci		.name	= DRIVER_NAME,
60062306a36Sopenharmony_ci		.of_match_table = omap2_onenand_id_table,
60162306a36Sopenharmony_ci	},
60262306a36Sopenharmony_ci};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cimodule_platform_driver(omap2_onenand_driver);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME);
60762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
60862306a36Sopenharmony_ciMODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>");
60962306a36Sopenharmony_ciMODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3");
610