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