* [PATCH] ARM: OMAP: OneNAND for OMAP3 @ 2008-08-01 8:11 Adrian Hunter 2008-08-01 16:45 ` Paul Walmsley 2008-08-04 14:02 ` Tony Lindgren 0 siblings, 2 replies; 9+ messages in thread From: Adrian Hunter @ 2008-08-01 8:11 UTC (permalink / raw) To: linux-omap Update OneNAND support for OMAP3. Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> --- arch/arm/mach-omap2/board-n800-flash.c | 240 ++++++++++++++++++++++++-------- arch/arm/mach-omap2/gpmc.c | 5 + drivers/mtd/onenand/omap2.c | 186 +++++++++++++++++++++++-- include/asm-arm/arch-omap/gpmc.h | 4 + include/asm-arm/arch-omap/onenand.h | 3 + include/linux/mtd/onenand_regs.h | 2 + 6 files changed, 367 insertions(+), 73 deletions(-) diff --git a/arch/arm/mach-omap2/board-n800-flash.c b/arch/arm/mach-omap2/board-n800-flash.c index f7403b9..cd98be5 100644 --- a/arch/arm/mach-omap2/board-n800-flash.c +++ b/arch/arm/mach-omap2/board-n800-flash.c @@ -19,13 +19,17 @@ #include <asm/arch/board.h> #include <asm/arch/gpmc.h> -static struct mtd_partition n800_partitions[8]; +struct mtd_partition n800_partitions[ONENAND_MAX_PARTITIONS]; static int n800_onenand_setup(void __iomem *, int freq); static struct omap_onenand_platform_data n800_onenand_data = { .cs = 0, +#ifdef CONFIG_ARCH_OMAP3430 + .gpio_irq = 65, +#else .gpio_irq = 26, +#endif .parts = n800_partitions, .nr_parts = 0, /* filled later */ .onenand_setup = n800_onenand_setup, @@ -39,6 +43,55 @@ static struct platform_device n800_onenand_device = { }, }; +static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) +{ + struct gpmc_timings t; + + const int t_cer = 15; + const int t_avdp = 12; + const int t_aavdh = 7; + const int t_ce = 76; + const int t_aa = 76; + const int t_oe = 20; + const int t_cez = 20; /* max of t_cez, t_oez */ + const int t_ds = 30; + const int t_wpl = 40; + const int t_wph = 30; + + memset(&t, 0, sizeof(t)); + t.sync_clk = 0; + t.cs_on = 0; + t.adv_on = 0; + + /* Read */ + t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); + t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh); + t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa); + t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce)); + t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe)); + t.oe_off = t.access + gpmc_round_ns_to_ticks(1); + t.cs_rd_off = t.oe_off; + t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez); + + /* Write */ + t.adv_wr_off = t.adv_rd_off; + t.we_on = t.oe_on; +#ifdef CONFIG_ARCH_OMAP3430 + t.wr_data_mux_bus = t.we_on; + t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); +#endif + t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); + t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); + t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); + + /* Configure GPMC for asynchronous read */ + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, + GPMC_CONFIG1_DEVICESIZE_16 | + GPMC_CONFIG1_MUXADDDATA); + + return gpmc_cs_set_timings(cs, &t); +} + static unsigned short omap2_onenand_readw(void __iomem *addr) { return readw(addr); @@ -49,54 +102,124 @@ static void omap2_onenand_writew(unsigned short value, void __iomem *addr) writew(value, addr); } +static void set_onenand_cfg(void __iomem *onenand_base, int latency, + int sync_write, int hf) +{ + u32 reg; + + reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1); + reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); + reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | + ONENAND_SYS_CFG1_SYNC_READ | + ONENAND_SYS_CFG1_BL_16; + if (sync_write) + reg |= ONENAND_SYS_CFG1_SYNC_WRITE; + else + reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE; + if (hf) + reg |= ONENAND_SYS_CFG1_HF; + else + reg &= ~ONENAND_SYS_CFG1_HF; + omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); +} + static int omap2_onenand_set_sync_mode(int cs, void __iomem *onenand_base, int freq) { struct gpmc_timings t; - int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_avdp, t_wpl, t_wea; + const int t_cer = 15; + const int t_avdp = 12; + const int t_cez = 20; /* max of t_cez, t_oez */ +#ifdef CONFIG_ARCH_OMAP3430 + const int t_ds = 30; +#endif + const int t_wpl = 40; + const int t_wph = 30; + int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; - int err; + int err, ticks_cez, sync_write = 0, first_time = 0, hf = 0; u32 reg; -again: + if (!freq) { + /* Very first call freq is not known */ + err = omap2_onenand_set_async_mode(cs, onenand_base); + if (err) + return err; + reg = omap2_onenand_readw(onenand_base+ONENAND_REG_VERSION_ID); + switch ((reg >> 4) & 0xf) { + case 0: + freq = 40; + break; + case 1: + freq = 54; + break; + case 2: + freq = 66; + break; + case 3: + freq = 83; + break; + case 4: + freq = 104; + break; + default: + freq = 54; + break; + } + first_time = 1; + } + switch (freq) { case 83: min_gpmc_clk_period = 12; /* 83 MHz */ - t_ces = 5; - t_avds = 5; - t_avdh = 6; - t_avdp = 12; - t_wpl = 40; - t_wea = 15; + t_ces = 5; + t_avds = 4; + t_avdh = 2; + t_ach = 6; + t_aavdh = 6; + t_rdyo = 9; +#ifdef CONFIG_ARCH_OMAP3430 + sync_write = 1; +#endif break; case 66: min_gpmc_clk_period = 15; /* 66 MHz */ - t_ces = 6; - t_avds = 5; - t_avdh = 6; - t_avdp = 12; - t_wpl = 40; - t_wea = 15; + t_ces = 6; + t_avds = 5; + t_avdh = 2; + t_ach = 6; + t_aavdh = 6; + t_rdyo = 11; +#ifdef CONFIG_ARCH_OMAP3430 + sync_write = 1; +#endif break; default: min_gpmc_clk_period = 18; /* 54 MHz */ - t_ces = 7; - t_avds = 7; - t_avdh = 7; - t_avdp = 12; - t_wpl = 40; - t_wea = 15; + t_ces = 7; + t_avds = 7; + t_avdh = 7; + t_ach = 9; + t_aavdh = 7; + t_rdyo = 15; break; } tick_ns = gpmc_ticks_to_ns(1); div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period); gpmc_clk_ns = gpmc_ticks_to_ns(div); - if (gpmc_clk_ns >= 25) /* 40 MHz*/ + if (gpmc_clk_ns < 15) /* >66Mhz */ + hf = 1; + if (hf) + latency = 6; + else if (gpmc_clk_ns >= 25) /* 40 MHz*/ latency = 3; else latency = 4; + if (first_time) + set_onenand_cfg(onenand_base, latency, sync_write, hf); + if (div == 1) { reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); reg |= (1 << 7); @@ -121,7 +244,7 @@ again: gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg); } - /* Set syncronous read timings */ + /* Set synchronous read timings */ memset(&t, 0, sizeof(t)); t.sync_clk = min_gpmc_clk_period; t.cs_on = 0; @@ -132,28 +255,52 @@ again: /* Read */ t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); - t.oe_on = t.adv_rd_off; + t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); t.oe_off = t.access + gpmc_round_ns_to_ticks(1); t.cs_rd_off = t.oe_off; - t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + div); + ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div; + t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + + ticks_cez); /* Write */ - t.adv_wr_off = t.adv_on + gpmc_round_ns_to_ticks(t_avdp); - t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_avdh); - t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); - t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(1); - t.wr_cycle = t.we_off + gpmc_round_ns_to_ticks(t_wea); + if (sync_write) { + t.adv_wr_off = t.adv_rd_off; + t.we_on = 0; + t.we_off = t.cs_rd_off; + t.cs_wr_off = t.cs_rd_off; + t.wr_cycle = t.rd_cycle; +#ifdef CONFIG_ARCH_OMAP3430 + t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset + + gpmc_ns_to_ticks(min_gpmc_clk_period + + t_rdyo)); + t.wr_access = t.access; +#endif + } else { + t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); + t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh); + t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); + t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); + t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); +#ifdef CONFIG_ARCH_OMAP3430 + t.wr_data_mux_bus = t.we_on; + t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); +#endif + } /* Configure GPMC for synchronous read */ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, GPMC_CONFIG1_WRAPBURST_SUPP | GPMC_CONFIG1_READMULTIPLE_SUPP | GPMC_CONFIG1_READTYPE_SYNC | + (sync_write?GPMC_CONFIG1_WRITEMULTIPLE_SUPP:0) | + (sync_write?GPMC_CONFIG1_WRITETYPE_SYNC:0) | GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) | GPMC_CONFIG1_PAGE_LEN(2) | +#ifndef CONFIG_ARCH_OMAP3430 GPMC_CONFIG1_WAIT_READ_MON | GPMC_CONFIG1_WAIT_PIN_SEL(0) | +#endif GPMC_CONFIG1_DEVICESIZE_16 | GPMC_CONFIG1_DEVICETYPE_NOR | GPMC_CONFIG1_MUXADDDATA); @@ -162,39 +309,12 @@ again: if (err) return err; - if (!freq) { - /* Very first call freq is not known */ - reg = omap2_onenand_readw(onenand_base + ONENAND_REG_VERSION_ID); - switch ((reg >> 4) & 0xf) { - case 0: - freq = 40; - break; - case 1: - freq = 54; - break; - case 2: - freq = 66; - break; - case 3: - freq = 83; - break; - } - if (freq && freq != 54) - goto again; - } - - /* Configure OneNAND for sync read */ - reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1); - reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); - reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | - ONENAND_SYS_CFG1_SYNC_READ | - ONENAND_SYS_CFG1_BL_16; - omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); + set_onenand_cfg(onenand_base, latency, sync_write, hf); return 0; } -static int n800_onenand_setup(void __iomem *onenand_base, int freq) +int n800_onenand_setup(void __iomem *onenand_base, int freq) { struct omap_onenand_platform_data *datap = &n800_onenand_data; struct device *dev = &n800_onenand_device.dev; diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 87368a8..b46c6be 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -232,6 +232,11 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access); +#ifdef CONFIG_ARCH_OMAP3430 + GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus); + GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access); +#endif + /* caller is expected to have initialized CONFIG1 to cover * at least sync vs async */ diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index ba83900..378ee17 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -180,32 +180,36 @@ retry: interrupt = omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT); ctrl = omap2_onenand_readw(info->onenand.base + ONENAND_REG_CTRL_STATUS); - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); - if (ctrl & ONENAND_CTRL_LOCK) - printk(KERN_ERR "onenand_erase: Device is write protected!!!\n"); - return -EIO; - } - - if (ctrl & 0xFE9F) - printk(KERN_WARNING "onenand_wait: unexpected controller status = 0x%04x state = %d interrupt = 0x%04x\n", ctrl, state, interrupt); - if (interrupt & ONENAND_INT_READ) { int ecc = omap2_onenand_readw(info->onenand.base + ONENAND_REG_ECC_STATUS); if (ecc) { + unsigned addr1 = omap2_onenand_readw(info->onenand.base + ONENAND_REG_START_ADDRESS1); + unsigned addr8 = omap2_onenand_readw(info->onenand.base + ONENAND_REG_START_ADDRESS8); + if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); + printk(KERN_ERR "onenand_wait: ECC error = 0x%04x, addr1 %#x, addr8 %#x\n", ecc, addr1, addr8); mtd->ecc_stats.failed++; return -EBADMSG; - } else if (ecc & ONENAND_ECC_1BIT_ALL) - printk(KERN_NOTICE "onenand_wait: correctable ECC error = 0x%04x\n", ecc); + } else if (ecc & ONENAND_ECC_1BIT_ALL) { + printk(KERN_NOTICE "onenand_wait: correctable ECC error = 0x%04x, addr1 %#x, addr8 %#x\n", ecc, addr1, addr8); mtd->ecc_stats.corrected++; + } } } else if (state == FL_READING) { printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); return -EIO; } + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); + if (ctrl & ONENAND_CTRL_LOCK) + printk(KERN_ERR "onenand_erase: Device is write protected!!!\n"); + return -EIO; + } + + if (ctrl & 0xFE9F) + printk(KERN_WARNING "onenand_wait: unexpected controller status = 0x%04x state = %d interrupt = 0x%04x\n", ctrl, state, interrupt); + return 0; } @@ -223,6 +227,155 @@ static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area) return 0; } +#if defined(CONFIG_ARCH_OMAP3) + +static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + dma_addr_t dma_src, dma_dst; + int bram_offset; + unsigned long timeout; + void *buf = (void *)buffer; + size_t xtra; + volatile unsigned *done; + + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) + goto out_copy; + + if (buf >= high_memory) { + struct page *p1; + + if (((size_t)buf & PAGE_MASK) != + ((size_t)(buf + count - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(buf); + if (!p1) + goto out_copy; + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); + } + + xtra = count & 3; + if (xtra) { + count -= xtra; + memcpy(buf + count, this->base + bram_offset + count, xtra); + } + + dma_src = info->phys_base + bram_offset; + dma_dst = dma_map_single(&info->pdev->dev, buf, count, DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dst)) { + dev_err(&info->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", + count); + goto out_copy; + } + + omap_set_dma_transfer_params(info->dma_channel, OMAP_DMA_DATA_TYPE_S32, + count >> 2, 1, 0, 0, 0); + omap_set_dma_src_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_src, 0, 0); + omap_set_dma_dest_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_dst, 0, 0); + + INIT_COMPLETION(info->dma_done); + omap_start_dma(info->dma_channel); + + timeout = jiffies + msecs_to_jiffies(20); + done = &info->dma_done.done; + while (time_before(jiffies, timeout)) + if (*done) + break; + + dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (!*done) { + dev_err(&info->pdev->dev, "timeout waiting for DMA\n"); + goto out_copy; + } + + return 0; + +out_copy: + memcpy(buf, this->base + bram_offset, count); + return 0; +} + +static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + dma_addr_t dma_src, dma_dst; + int bram_offset; + unsigned long timeout; + void *buf = (void *)buffer; + volatile unsigned *done; + + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) + goto out_copy; + + /* panic_write() may be in an interrupt context */ + if (in_interrupt()) + goto out_copy; + + if (buf >= high_memory) { + struct page *p1; + + if (((size_t)buf & PAGE_MASK) != + ((size_t)(buf + count - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(buf); + if (!p1) + goto out_copy; + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); + } + + dma_src = dma_map_single(&info->pdev->dev, buf, count, DMA_TO_DEVICE); + dma_dst = info->phys_base + bram_offset; + if (dma_mapping_error(dma_dst)) { + dev_err(&info->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", + count); + return -1; + } + + omap_set_dma_transfer_params(info->dma_channel, OMAP_DMA_DATA_TYPE_S32, + count >> 2, 1, 0, 0, 0); + omap_set_dma_src_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_src, 0, 0); + omap_set_dma_dest_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_dst, 0, 0); + + INIT_COMPLETION(info->dma_done); + omap_start_dma(info->dma_channel); + + timeout = jiffies + msecs_to_jiffies(20); + done = &info->dma_done.done; + while (time_before(jiffies, timeout)) + if (*done) + break; + + dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + + if (!*done) { + dev_err(&info->pdev->dev, "timeout waiting for DMA\n"); + goto out_copy; + } + + return 0; + +out_copy: + memcpy(this->base + bram_offset, buf, count); + return 0; +} + +#else + static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count) @@ -310,6 +463,8 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, return 0; } +#endif + static struct platform_driver omap2_onenand_driver; static int __adjust_timing(struct device *dev, void *data) @@ -439,8 +594,13 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) if (info->dma_channel >= 0) { info->onenand.wait = omap2_onenand_wait; +#if defined(CONFIG_ARCH_OMAP3) + info->onenand.read_bufferram = omap3_onenand_read_bufferram; + info->onenand.write_bufferram = omap3_onenand_write_bufferram; +#else info->onenand.read_bufferram = omap2_onenand_read_bufferram; info->onenand.write_bufferram = omap2_onenand_write_bufferram; +#endif } if ((r = onenand_scan(&info->mtd, 1)) < 0) diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h index 57fec44..ef6c4c8 100644 --- a/include/asm-arm/arch-omap/gpmc.h +++ b/include/asm-arm/arch-omap/gpmc.h @@ -84,6 +84,10 @@ struct gpmc_timings { u16 access; /* Start-cycle to first data valid delay */ u16 rd_cycle; /* Total read cycle time */ u16 wr_cycle; /* Total write cycle time */ + + /* The following are only on OMAP3430 */ + u16 wr_access; /* WRACCESSTIME */ + u16 wr_data_mux_bus; /* WRDATAONADMUXBUS */ }; /* Structure to save gpmc cs context */ diff --git a/include/asm-arm/arch-omap/onenand.h b/include/asm-arm/arch-omap/onenand.h index f0f28d3..e302371 100644 --- a/include/asm-arm/arch-omap/onenand.h +++ b/include/asm-arm/arch-omap/onenand.h @@ -21,3 +21,6 @@ struct omap_onenand_platform_data { }; int omap2_onenand_rephase(void); + +#define ONENAND_MAX_PARTITIONS 8 + diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index d1b310c..0c6bbe2 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -152,6 +152,8 @@ #define ONENAND_SYS_CFG1_INT (1 << 6) #define ONENAND_SYS_CFG1_IOBE (1 << 5) #define ONENAND_SYS_CFG1_RDY_CONF (1 << 4) +#define ONENAND_SYS_CFG1_HF (1 << 2) +#define ONENAND_SYS_CFG1_SYNC_WRITE (1 << 1) /* * Controller Status Register F240h (R) -- 1.5.4.3 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] ARM: OMAP: OneNAND for OMAP3 2008-08-01 8:11 [PATCH] ARM: OMAP: OneNAND for OMAP3 Adrian Hunter @ 2008-08-01 16:45 ` Paul Walmsley 2008-08-04 6:56 ` Adrian Hunter 2008-08-04 14:02 ` Tony Lindgren 1 sibling, 1 reply; 9+ messages in thread From: Paul Walmsley @ 2008-08-01 16:45 UTC (permalink / raw) To: Adrian Hunter; +Cc: linux-omap Hello Adrian, On Fri, 1 Aug 2008, Adrian Hunter wrote: > Update OneNAND support for OMAP3. a few quick comments. > + reg = > omap2_onenand_readw(onenand_base+ONENAND_REG_VERSION_ID); Just a minor nit - please use spaces around binary & ternary operators per CodingStyle. > + (sync_write?GPMC_CONFIG1_WRITEMULTIPLE_SUPP:0) | > + (sync_write?GPMC_CONFIG1_WRITETYPE_SYNC:0) | as above. > diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c > index ba83900..378ee17 100644 > --- a/drivers/mtd/onenand/omap2.c > +++ b/drivers/mtd/onenand/omap2.c > @@ -223,6 +227,155 @@ static inline int omap2_onenand_bufferram_offset(struct > mtd_info *mtd, int area) > return 0; > } > > +#if defined(CONFIG_ARCH_OMAP3) > + > +static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, > + unsigned char *buffer, int offset, > + size_t count) > +{ > + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, > mtd); > + struct onenand_chip *this = mtd->priv; > + dma_addr_t dma_src, dma_dst; > + int bram_offset; > + unsigned long timeout; > + void *buf = (void *)buffer; > + size_t xtra; > + volatile unsigned *done; The way this volatile is used doesn't look right... > + INIT_COMPLETION(info->dma_done); > + omap_start_dma(info->dma_channel); > + > + timeout = jiffies + msecs_to_jiffies(20); > + done = &info->dma_done.done; So the volatile here appears to apply to the address of 'done', but this address does not change, correct? Only the value of 'done' itself changes. > + while (time_before(jiffies, timeout)) > + if (*done) > + break; Can this be replaced with wait_for_completion_timeout() or something similar? > + dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); > + > + if (!*done) { > + dev_err(&info->pdev->dev, "timeout waiting for DMA\n"); > + goto out_copy; > + } ... > +static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, > + const unsigned char *buffer, int > offset, > + size_t count) > +{ > + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, > mtd); > + struct onenand_chip *this = mtd->priv; > + dma_addr_t dma_src, dma_dst; > + int bram_offset; > + unsigned long timeout; > + void *buf = (void *)buffer; > + volatile unsigned *done; Same comments in this function per volatile. - Paul ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] ARM: OMAP: OneNAND for OMAP3 2008-08-01 16:45 ` Paul Walmsley @ 2008-08-04 6:56 ` Adrian Hunter 0 siblings, 0 replies; 9+ messages in thread From: Adrian Hunter @ 2008-08-04 6:56 UTC (permalink / raw) To: Paul Walmsley; +Cc: linux-omap Paul Walmsley wrote: > Hello Adrian, > > On Fri, 1 Aug 2008, Adrian Hunter wrote: > >> Update OneNAND support for OMAP3. > > a few quick comments. > Thanks for looking at the code. >> + reg = >> omap2_onenand_readw(onenand_base+ONENAND_REG_VERSION_ID); > > Just a minor nit - please use spaces around binary & ternary operators per > CodingStyle. > >> + (sync_write?GPMC_CONFIG1_WRITEMULTIPLE_SUPP:0) | >> + (sync_write?GPMC_CONFIG1_WRITETYPE_SYNC:0) | > > as above. > >> diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c >> index ba83900..378ee17 100644 >> --- a/drivers/mtd/onenand/omap2.c >> +++ b/drivers/mtd/onenand/omap2.c >> @@ -223,6 +227,155 @@ static inline int omap2_onenand_bufferram_offset(struct >> mtd_info *mtd, int area) >> return 0; >> } >> >> +#if defined(CONFIG_ARCH_OMAP3) >> + >> +static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, >> + unsigned char *buffer, int offset, >> + size_t count) >> +{ >> + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, >> mtd); >> + struct onenand_chip *this = mtd->priv; >> + dma_addr_t dma_src, dma_dst; >> + int bram_offset; >> + unsigned long timeout; >> + void *buf = (void *)buffer; >> + size_t xtra; >> + volatile unsigned *done; > > The way this volatile is used doesn't look right... > Well, it is correct. >> + INIT_COMPLETION(info->dma_done); >> + omap_start_dma(info->dma_channel); >> + >> + timeout = jiffies + msecs_to_jiffies(20); >> + done = &info->dma_done.done; > > So the volatile here appears to apply to the address of 'done', but this > address does not change, correct? Only the value of 'done' itself > changes. > No, the volatile applies to the "unsigned" not the "*". >> + while (time_before(jiffies, timeout)) >> + if (*done) >> + break; > > Can this be replaced with wait_for_completion_timeout() or something > similar? > No. Performance testing showed that a context switch here is too expensive. It is better to spin. >> + dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); >> + >> + if (!*done) { >> + dev_err(&info->pdev->dev, "timeout waiting for DMA\n"); >> + goto out_copy; >> + } > > ... > >> +static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, >> + const unsigned char *buffer, int >> offset, >> + size_t count) >> +{ >> + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, >> mtd); >> + struct onenand_chip *this = mtd->priv; >> + dma_addr_t dma_src, dma_dst; >> + int bram_offset; >> + unsigned long timeout; >> + void *buf = (void *)buffer; >> + volatile unsigned *done; > > Same comments in this function per volatile. > > > - Paul > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] ARM: OMAP: OneNAND for OMAP3 2008-08-01 8:11 [PATCH] ARM: OMAP: OneNAND for OMAP3 Adrian Hunter 2008-08-01 16:45 ` Paul Walmsley @ 2008-08-04 14:02 ` Tony Lindgren 2008-08-06 6:53 ` Adrian Hunter 1 sibling, 1 reply; 9+ messages in thread From: Tony Lindgren @ 2008-08-04 14:02 UTC (permalink / raw) To: Adrian Hunter; +Cc: linux-omap Hi, Will push today as this will make the driver more usable. Also few comments below if you feel like improving things in the long run. * Adrian Hunter <ext-adrian.hunter@nokia.com> [080801 11:10]: > Update OneNAND support for OMAP3. > > Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> > --- > arch/arm/mach-omap2/board-n800-flash.c | 240 ++++++++++++++++++++++++-------- > arch/arm/mach-omap2/gpmc.c | 5 + > drivers/mtd/onenand/omap2.c | 186 +++++++++++++++++++++++-- > include/asm-arm/arch-omap/gpmc.h | 4 + > include/asm-arm/arch-omap/onenand.h | 3 + > include/linux/mtd/onenand_regs.h | 2 + > 6 files changed, 367 insertions(+), 73 deletions(-) > > diff --git a/arch/arm/mach-omap2/board-n800-flash.c b/arch/arm/mach-omap2/board-n800-flash.c > index f7403b9..cd98be5 100644 > --- a/arch/arm/mach-omap2/board-n800-flash.c > +++ b/arch/arm/mach-omap2/board-n800-flash.c > @@ -19,13 +19,17 @@ > #include <asm/arch/board.h> > #include <asm/arch/gpmc.h> > > -static struct mtd_partition n800_partitions[8]; > +struct mtd_partition n800_partitions[ONENAND_MAX_PARTITIONS]; > > static int n800_onenand_setup(void __iomem *, int freq); > > static struct omap_onenand_platform_data n800_onenand_data = { > .cs = 0, > +#ifdef CONFIG_ARCH_OMAP3430 > + .gpio_irq = 65, > +#else > .gpio_irq = 26, > +#endif > .parts = n800_partitions, > .nr_parts = 0, /* filled later */ > .onenand_setup = n800_onenand_setup, The gpio number is board specific, so that information should come in platform_data from board-*.c files. > @@ -39,6 +43,55 @@ static struct platform_device n800_onenand_device = { > }, > }; > > +static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) > +{ > + struct gpmc_timings t; > + > + const int t_cer = 15; > + const int t_avdp = 12; > + const int t_aavdh = 7; > + const int t_ce = 76; > + const int t_aa = 76; > + const int t_oe = 20; > + const int t_cez = 20; /* max of t_cez, t_oez */ > + const int t_ds = 30; > + const int t_wpl = 40; > + const int t_wph = 30; > + > + memset(&t, 0, sizeof(t)); > + t.sync_clk = 0; > + t.cs_on = 0; > + t.adv_on = 0; > + > + /* Read */ > + t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); > + t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh); > + t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa); > + t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce)); > + t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe)); > + t.oe_off = t.access + gpmc_round_ns_to_ticks(1); > + t.cs_rd_off = t.oe_off; > + t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez); > + > + /* Write */ > + t.adv_wr_off = t.adv_rd_off; > + t.we_on = t.oe_on; > +#ifdef CONFIG_ARCH_OMAP3430 > + t.wr_data_mux_bus = t.we_on; > + t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); > +#endif > + t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); > + t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); > + t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); > + > + /* Configure GPMC for asynchronous read */ > + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, > + GPMC_CONFIG1_DEVICESIZE_16 | > + GPMC_CONFIG1_MUXADDDATA); > + > + return gpmc_cs_set_timings(cs, &t); > +} > + > static unsigned short omap2_onenand_readw(void __iomem *addr) > { > return readw(addr); How about using cpu_is_omap34xx() instead of the ifdefs where possible through the code? It should be possible to compile in support for 24xx and 34xx to the same kernel soonish. Tony > @@ -49,54 +102,124 @@ static void omap2_onenand_writew(unsigned short value, void __iomem *addr) > writew(value, addr); > } > > +static void set_onenand_cfg(void __iomem *onenand_base, int latency, > + int sync_write, int hf) > +{ > + u32 reg; > + > + reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1); > + reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); > + reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | > + ONENAND_SYS_CFG1_SYNC_READ | > + ONENAND_SYS_CFG1_BL_16; > + if (sync_write) > + reg |= ONENAND_SYS_CFG1_SYNC_WRITE; > + else > + reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE; > + if (hf) > + reg |= ONENAND_SYS_CFG1_HF; > + else > + reg &= ~ONENAND_SYS_CFG1_HF; > + omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); > +} > + > static int omap2_onenand_set_sync_mode(int cs, void __iomem *onenand_base, > int freq) > { > struct gpmc_timings t; > - int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_avdp, t_wpl, t_wea; > + const int t_cer = 15; > + const int t_avdp = 12; > + const int t_cez = 20; /* max of t_cez, t_oez */ > +#ifdef CONFIG_ARCH_OMAP3430 > + const int t_ds = 30; > +#endif > + const int t_wpl = 40; > + const int t_wph = 30; > + int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; > int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; > - int err; > + int err, ticks_cez, sync_write = 0, first_time = 0, hf = 0; > u32 reg; > > -again: > + if (!freq) { > + /* Very first call freq is not known */ > + err = omap2_onenand_set_async_mode(cs, onenand_base); > + if (err) > + return err; > + reg = omap2_onenand_readw(onenand_base+ONENAND_REG_VERSION_ID); > + switch ((reg >> 4) & 0xf) { > + case 0: > + freq = 40; > + break; > + case 1: > + freq = 54; > + break; > + case 2: > + freq = 66; > + break; > + case 3: > + freq = 83; > + break; > + case 4: > + freq = 104; > + break; > + default: > + freq = 54; > + break; > + } > + first_time = 1; > + } > + > switch (freq) { > case 83: > min_gpmc_clk_period = 12; /* 83 MHz */ > - t_ces = 5; > - t_avds = 5; > - t_avdh = 6; > - t_avdp = 12; > - t_wpl = 40; > - t_wea = 15; > + t_ces = 5; > + t_avds = 4; > + t_avdh = 2; > + t_ach = 6; > + t_aavdh = 6; > + t_rdyo = 9; > +#ifdef CONFIG_ARCH_OMAP3430 > + sync_write = 1; > +#endif > break; > case 66: > min_gpmc_clk_period = 15; /* 66 MHz */ > - t_ces = 6; > - t_avds = 5; > - t_avdh = 6; > - t_avdp = 12; > - t_wpl = 40; > - t_wea = 15; > + t_ces = 6; > + t_avds = 5; > + t_avdh = 2; > + t_ach = 6; > + t_aavdh = 6; > + t_rdyo = 11; > +#ifdef CONFIG_ARCH_OMAP3430 > + sync_write = 1; > +#endif > break; > default: > min_gpmc_clk_period = 18; /* 54 MHz */ > - t_ces = 7; > - t_avds = 7; > - t_avdh = 7; > - t_avdp = 12; > - t_wpl = 40; > - t_wea = 15; > + t_ces = 7; > + t_avds = 7; > + t_avdh = 7; > + t_ach = 9; > + t_aavdh = 7; > + t_rdyo = 15; > break; > } > > tick_ns = gpmc_ticks_to_ns(1); > div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period); > gpmc_clk_ns = gpmc_ticks_to_ns(div); > - if (gpmc_clk_ns >= 25) /* 40 MHz*/ > + if (gpmc_clk_ns < 15) /* >66Mhz */ > + hf = 1; > + if (hf) > + latency = 6; > + else if (gpmc_clk_ns >= 25) /* 40 MHz*/ > latency = 3; > else > latency = 4; > > + if (first_time) > + set_onenand_cfg(onenand_base, latency, sync_write, hf); > + > if (div == 1) { > reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); > reg |= (1 << 7); > @@ -121,7 +244,7 @@ again: > gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg); > } > > - /* Set syncronous read timings */ > + /* Set synchronous read timings */ > memset(&t, 0, sizeof(t)); > t.sync_clk = min_gpmc_clk_period; > t.cs_on = 0; > @@ -132,28 +255,52 @@ again: > > /* Read */ > t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); > - t.oe_on = t.adv_rd_off; > + t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); > t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); > t.oe_off = t.access + gpmc_round_ns_to_ticks(1); > t.cs_rd_off = t.oe_off; > - t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + div); > + ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div; > + t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + > + ticks_cez); > > /* Write */ > - t.adv_wr_off = t.adv_on + gpmc_round_ns_to_ticks(t_avdp); > - t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_avdh); > - t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); > - t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(1); > - t.wr_cycle = t.we_off + gpmc_round_ns_to_ticks(t_wea); > + if (sync_write) { > + t.adv_wr_off = t.adv_rd_off; > + t.we_on = 0; > + t.we_off = t.cs_rd_off; > + t.cs_wr_off = t.cs_rd_off; > + t.wr_cycle = t.rd_cycle; > +#ifdef CONFIG_ARCH_OMAP3430 > + t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset + > + gpmc_ns_to_ticks(min_gpmc_clk_period + > + t_rdyo)); > + t.wr_access = t.access; > +#endif > + } else { > + t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); > + t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh); > + t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); > + t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); > + t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); > +#ifdef CONFIG_ARCH_OMAP3430 > + t.wr_data_mux_bus = t.we_on; > + t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); > +#endif > + } > > /* Configure GPMC for synchronous read */ > gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, > GPMC_CONFIG1_WRAPBURST_SUPP | > GPMC_CONFIG1_READMULTIPLE_SUPP | > GPMC_CONFIG1_READTYPE_SYNC | > + (sync_write?GPMC_CONFIG1_WRITEMULTIPLE_SUPP:0) | > + (sync_write?GPMC_CONFIG1_WRITETYPE_SYNC:0) | > GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) | > GPMC_CONFIG1_PAGE_LEN(2) | > +#ifndef CONFIG_ARCH_OMAP3430 > GPMC_CONFIG1_WAIT_READ_MON | > GPMC_CONFIG1_WAIT_PIN_SEL(0) | > +#endif > GPMC_CONFIG1_DEVICESIZE_16 | > GPMC_CONFIG1_DEVICETYPE_NOR | > GPMC_CONFIG1_MUXADDDATA); > @@ -162,39 +309,12 @@ again: > if (err) > return err; > > - if (!freq) { > - /* Very first call freq is not known */ > - reg = omap2_onenand_readw(onenand_base + ONENAND_REG_VERSION_ID); > - switch ((reg >> 4) & 0xf) { > - case 0: > - freq = 40; > - break; > - case 1: > - freq = 54; > - break; > - case 2: > - freq = 66; > - break; > - case 3: > - freq = 83; > - break; > - } > - if (freq && freq != 54) > - goto again; > - } > - > - /* Configure OneNAND for sync read */ > - reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1); > - reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); > - reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | > - ONENAND_SYS_CFG1_SYNC_READ | > - ONENAND_SYS_CFG1_BL_16; > - omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); > + set_onenand_cfg(onenand_base, latency, sync_write, hf); > > return 0; > } > > -static int n800_onenand_setup(void __iomem *onenand_base, int freq) > +int n800_onenand_setup(void __iomem *onenand_base, int freq) > { > struct omap_onenand_platform_data *datap = &n800_onenand_data; > struct device *dev = &n800_onenand_device.dev; > diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c > index 87368a8..b46c6be 100644 > --- a/arch/arm/mach-omap2/gpmc.c > +++ b/arch/arm/mach-omap2/gpmc.c > @@ -232,6 +232,11 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) > > GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access); > > +#ifdef CONFIG_ARCH_OMAP3430 > + GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus); > + GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access); > +#endif > + > /* caller is expected to have initialized CONFIG1 to cover > * at least sync vs async > */ > diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c > index ba83900..378ee17 100644 > --- a/drivers/mtd/onenand/omap2.c > +++ b/drivers/mtd/onenand/omap2.c > @@ -180,32 +180,36 @@ retry: > interrupt = omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT); > ctrl = omap2_onenand_readw(info->onenand.base + ONENAND_REG_CTRL_STATUS); > > - if (ctrl & ONENAND_CTRL_ERROR) { > - printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); > - if (ctrl & ONENAND_CTRL_LOCK) > - printk(KERN_ERR "onenand_erase: Device is write protected!!!\n"); > - return -EIO; > - } > - > - if (ctrl & 0xFE9F) > - printk(KERN_WARNING "onenand_wait: unexpected controller status = 0x%04x state = %d interrupt = 0x%04x\n", ctrl, state, interrupt); > - > if (interrupt & ONENAND_INT_READ) { > int ecc = omap2_onenand_readw(info->onenand.base + ONENAND_REG_ECC_STATUS); > if (ecc) { > + unsigned addr1 = omap2_onenand_readw(info->onenand.base + ONENAND_REG_START_ADDRESS1); > + unsigned addr8 = omap2_onenand_readw(info->onenand.base + ONENAND_REG_START_ADDRESS8); > + > if (ecc & ONENAND_ECC_2BIT_ALL) { > - printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); > + printk(KERN_ERR "onenand_wait: ECC error = 0x%04x, addr1 %#x, addr8 %#x\n", ecc, addr1, addr8); > mtd->ecc_stats.failed++; > return -EBADMSG; > - } else if (ecc & ONENAND_ECC_1BIT_ALL) > - printk(KERN_NOTICE "onenand_wait: correctable ECC error = 0x%04x\n", ecc); > + } else if (ecc & ONENAND_ECC_1BIT_ALL) { > + printk(KERN_NOTICE "onenand_wait: correctable ECC error = 0x%04x, addr1 %#x, addr8 %#x\n", ecc, addr1, addr8); > mtd->ecc_stats.corrected++; > + } > } > } else if (state == FL_READING) { > printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); > return -EIO; > } > > + if (ctrl & ONENAND_CTRL_ERROR) { > + printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); > + if (ctrl & ONENAND_CTRL_LOCK) > + printk(KERN_ERR "onenand_erase: Device is write protected!!!\n"); > + return -EIO; > + } > + > + if (ctrl & 0xFE9F) > + printk(KERN_WARNING "onenand_wait: unexpected controller status = 0x%04x state = %d interrupt = 0x%04x\n", ctrl, state, interrupt); > + > return 0; > } > > @@ -223,6 +227,155 @@ static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area) > return 0; > } > > +#if defined(CONFIG_ARCH_OMAP3) > + > +static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, > + unsigned char *buffer, int offset, > + size_t count) > +{ > + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); > + struct onenand_chip *this = mtd->priv; > + dma_addr_t dma_src, dma_dst; > + int bram_offset; > + unsigned long timeout; > + void *buf = (void *)buffer; > + size_t xtra; > + volatile unsigned *done; > + > + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; > + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) > + goto out_copy; > + > + if (buf >= high_memory) { > + struct page *p1; > + > + if (((size_t)buf & PAGE_MASK) != > + ((size_t)(buf + count - 1) & PAGE_MASK)) > + goto out_copy; > + p1 = vmalloc_to_page(buf); > + if (!p1) > + goto out_copy; > + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); > + } > + > + xtra = count & 3; > + if (xtra) { > + count -= xtra; > + memcpy(buf + count, this->base + bram_offset + count, xtra); > + } > + > + dma_src = info->phys_base + bram_offset; > + dma_dst = dma_map_single(&info->pdev->dev, buf, count, DMA_FROM_DEVICE); > + if (dma_mapping_error(dma_dst)) { > + dev_err(&info->pdev->dev, > + "Couldn't DMA map a %d byte buffer\n", > + count); > + goto out_copy; > + } > + > + omap_set_dma_transfer_params(info->dma_channel, OMAP_DMA_DATA_TYPE_S32, > + count >> 2, 1, 0, 0, 0); > + omap_set_dma_src_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, > + dma_src, 0, 0); > + omap_set_dma_dest_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, > + dma_dst, 0, 0); > + > + INIT_COMPLETION(info->dma_done); > + omap_start_dma(info->dma_channel); > + > + timeout = jiffies + msecs_to_jiffies(20); > + done = &info->dma_done.done; > + while (time_before(jiffies, timeout)) > + if (*done) > + break; > + > + dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); > + > + if (!*done) { > + dev_err(&info->pdev->dev, "timeout waiting for DMA\n"); > + goto out_copy; > + } > + > + return 0; > + > +out_copy: > + memcpy(buf, this->base + bram_offset, count); > + return 0; > +} > + > +static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, > + const unsigned char *buffer, int offset, > + size_t count) > +{ > + struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); > + struct onenand_chip *this = mtd->priv; > + dma_addr_t dma_src, dma_dst; > + int bram_offset; > + unsigned long timeout; > + void *buf = (void *)buffer; > + volatile unsigned *done; > + > + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; > + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) > + goto out_copy; > + > + /* panic_write() may be in an interrupt context */ > + if (in_interrupt()) > + goto out_copy; > + > + if (buf >= high_memory) { > + struct page *p1; > + > + if (((size_t)buf & PAGE_MASK) != > + ((size_t)(buf + count - 1) & PAGE_MASK)) > + goto out_copy; > + p1 = vmalloc_to_page(buf); > + if (!p1) > + goto out_copy; > + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); > + } > + > + dma_src = dma_map_single(&info->pdev->dev, buf, count, DMA_TO_DEVICE); > + dma_dst = info->phys_base + bram_offset; > + if (dma_mapping_error(dma_dst)) { > + dev_err(&info->pdev->dev, > + "Couldn't DMA map a %d byte buffer\n", > + count); > + return -1; > + } > + > + omap_set_dma_transfer_params(info->dma_channel, OMAP_DMA_DATA_TYPE_S32, > + count >> 2, 1, 0, 0, 0); > + omap_set_dma_src_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, > + dma_src, 0, 0); > + omap_set_dma_dest_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, > + dma_dst, 0, 0); > + > + INIT_COMPLETION(info->dma_done); > + omap_start_dma(info->dma_channel); > + > + timeout = jiffies + msecs_to_jiffies(20); > + done = &info->dma_done.done; > + while (time_before(jiffies, timeout)) > + if (*done) > + break; > + > + dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_TO_DEVICE); > + > + if (!*done) { > + dev_err(&info->pdev->dev, "timeout waiting for DMA\n"); > + goto out_copy; > + } > + > + return 0; > + > +out_copy: > + memcpy(this->base + bram_offset, buf, count); > + return 0; > +} > + > +#else > + > static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, > unsigned char *buffer, int offset, > size_t count) > @@ -310,6 +463,8 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, > return 0; > } > > +#endif > + > static struct platform_driver omap2_onenand_driver; > > static int __adjust_timing(struct device *dev, void *data) > @@ -439,8 +594,13 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) > > if (info->dma_channel >= 0) { > info->onenand.wait = omap2_onenand_wait; > +#if defined(CONFIG_ARCH_OMAP3) > + info->onenand.read_bufferram = omap3_onenand_read_bufferram; > + info->onenand.write_bufferram = omap3_onenand_write_bufferram; > +#else > info->onenand.read_bufferram = omap2_onenand_read_bufferram; > info->onenand.write_bufferram = omap2_onenand_write_bufferram; > +#endif > } > > if ((r = onenand_scan(&info->mtd, 1)) < 0) > diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h > index 57fec44..ef6c4c8 100644 > --- a/include/asm-arm/arch-omap/gpmc.h > +++ b/include/asm-arm/arch-omap/gpmc.h > @@ -84,6 +84,10 @@ struct gpmc_timings { > u16 access; /* Start-cycle to first data valid delay */ > u16 rd_cycle; /* Total read cycle time */ > u16 wr_cycle; /* Total write cycle time */ > + > + /* The following are only on OMAP3430 */ > + u16 wr_access; /* WRACCESSTIME */ > + u16 wr_data_mux_bus; /* WRDATAONADMUXBUS */ > }; > > /* Structure to save gpmc cs context */ > diff --git a/include/asm-arm/arch-omap/onenand.h b/include/asm-arm/arch-omap/onenand.h > index f0f28d3..e302371 100644 > --- a/include/asm-arm/arch-omap/onenand.h > +++ b/include/asm-arm/arch-omap/onenand.h > @@ -21,3 +21,6 @@ struct omap_onenand_platform_data { > }; > > int omap2_onenand_rephase(void); > + > +#define ONENAND_MAX_PARTITIONS 8 > + > diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h > index d1b310c..0c6bbe2 100644 > --- a/include/linux/mtd/onenand_regs.h > +++ b/include/linux/mtd/onenand_regs.h > @@ -152,6 +152,8 @@ > #define ONENAND_SYS_CFG1_INT (1 << 6) > #define ONENAND_SYS_CFG1_IOBE (1 << 5) > #define ONENAND_SYS_CFG1_RDY_CONF (1 << 4) > +#define ONENAND_SYS_CFG1_HF (1 << 2) > +#define ONENAND_SYS_CFG1_SYNC_WRITE (1 << 1) > > /* > * Controller Status Register F240h (R) > -- > 1.5.4.3 > -- > To unsubscribe from this list: send the line "unsubscribe linux-omap" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] ARM: OMAP: OneNAND for OMAP3 2008-08-04 14:02 ` Tony Lindgren @ 2008-08-06 6:53 ` Adrian Hunter 2008-08-06 6:55 ` [PATCH 1/3] " Adrian Hunter ` (3 more replies) 0 siblings, 4 replies; 9+ messages in thread From: Adrian Hunter @ 2008-08-06 6:53 UTC (permalink / raw) To: Tony Lindgren; +Cc: linux-omap I have split the patch into 3 patches, tidied up drivers/mtd/onenand/omap2.c ready to be sent to MTD (which I will do shortly), and made multi-omap changes ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] ARM: OMAP: OneNAND for OMAP3 2008-08-06 6:53 ` Adrian Hunter @ 2008-08-06 6:55 ` Adrian Hunter 2008-08-06 6:56 ` [PATCH 2/3] ARM: OMAP: Add fields to GPMC " Adrian Hunter ` (2 subsequent siblings) 3 siblings, 0 replies; 9+ messages in thread From: Adrian Hunter @ 2008-08-06 6:55 UTC (permalink / raw) To: Tony Lindgren; +Cc: linux-omap Update OneNAND support for OMAP3. Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> --- drivers/mtd/onenand/omap2.c | 588 +++++++++++++++++++++++++++++-------------- 1 files changed, 404 insertions(+), 184 deletions(-) diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index ba83900..b1f0df1 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -1,7 +1,7 @@ /* * linux/drivers/mtd/onenand/omap2.c * - * OneNAND driver for OMAP2 + * OneNAND driver for OMAP2 / OMAP3 * * Copyright (C) 2005-2006 Nokia Corporation * @@ -47,7 +47,7 @@ #include <asm/arch/board.h> -#define DRIVER_NAME "omap2-onenand" +#define DRIVER_NAME "omap2-onenand" #define ONENAND_IO_SIZE SZ_128K #define ONENAND_BUFRAM_SIZE (1024 * 5) @@ -67,36 +67,50 @@ struct omap2_onenand { int (*setup)(void __iomem *base, int freq); }; -static unsigned short omap2_onenand_readw(void __iomem *addr) +static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data) { - return readw(addr); + struct omap2_onenand *c = data; + + complete(&c->dma_done); } -static void omap2_onenand_writew(unsigned short value, void __iomem *addr) +static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id) { - writew(value, addr); + struct omap2_onenand *c = dev_id; + + complete(&c->irq_done); + + return IRQ_HANDLED; } -static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data) +static inline unsigned short read_reg(struct omap2_onenand *c, int reg) { - struct omap2_onenand *info = data; - - complete(&info->dma_done); + return readw(c->onenand.base + reg); } -static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id) +static inline void write_reg(struct omap2_onenand *c, unsigned short value, + int reg) { - struct omap2_onenand *info = dev_id; + writew(value, c->onenand.base + reg); +} - complete(&info->irq_done); +static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr) +{ + printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n", + msg, state, ctrl, intr); +} - return IRQ_HANDLED; +static void wait_warn(char *msg, int state, unsigned int ctrl, + unsigned int intr) +{ + printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x " + "intr 0x%04x\n", msg, state, ctrl, intr); } static int omap2_onenand_wait(struct mtd_info *mtd, int state) { - struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); - unsigned int interrupt = 0; + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); + unsigned int intr = 0; unsigned int ctrl; unsigned long timeout; u32 syscfg; @@ -106,17 +120,17 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) for (i = 0; i < 20; i++) { udelay(1); - interrupt = omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT); - if (interrupt & ONENAND_INT_MASTER) + intr = read_reg(c, ONENAND_REG_INTERRUPT); + if (intr & ONENAND_INT_MASTER) break; } - ctrl = omap2_onenand_readw(info->onenand.base + ONENAND_REG_CTRL_STATUS); + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_ERR "onenand_wait: reset error! ctrl 0x%04x intr 0x%04x\n", ctrl, interrupt); + wait_err("controller error", state, ctrl, intr); return -EIO; } - if (!(interrupt & ONENAND_INT_RESET)) { - printk(KERN_ERR "onenand_wait: reset timeout! ctrl 0x%04x intr 0x%04x\n", ctrl, interrupt); + if (!(intr & ONENAND_INT_RESET)) { + wait_err("timeout", state, ctrl, intr); return -EIO; } return 0; @@ -124,88 +138,103 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) if (state != FL_READING) { int result; + /* Turn interrupts on */ - syscfg = omap2_onenand_readw(info->onenand.base + ONENAND_REG_SYS_CFG1); + syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); syscfg |= ONENAND_SYS_CFG1_IOBE; - omap2_onenand_writew(syscfg, info->onenand.base + ONENAND_REG_SYS_CFG1); + write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); - INIT_COMPLETION(info->irq_done); - if (info->gpio_irq) { - result = omap_get_gpio_datain(info->gpio_irq); + INIT_COMPLETION(c->irq_done); + if (c->gpio_irq) { + result = omap_get_gpio_datain(c->gpio_irq); if (result == -1) { - ctrl = omap2_onenand_readw(info->onenand.base + ONENAND_REG_CTRL_STATUS); - printk(KERN_ERR "onenand_wait: gpio error, state = %d, ctrl = 0x%04x\n", state, ctrl); + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + intr = read_reg(c, ONENAND_REG_INTERRUPT); + wait_err("gpio error", state, ctrl, intr); return -EIO; } - } else { + } else result = 0; - } if (result == 0) { int retry_cnt = 0; retry: - result = wait_for_completion_timeout(&info->irq_done, + result = wait_for_completion_timeout(&c->irq_done, msecs_to_jiffies(20)); if (result == 0) { /* Timeout after 20ms */ - ctrl = omap2_onenand_readw(info->onenand.base + ONENAND_REG_CTRL_STATUS); + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ONGO) { - /* The operation seems to be still going - so give it some more time */ + /* + * The operation seems to be still going + * so give it some more time. + */ retry_cnt += 1; if (retry_cnt < 3) goto retry; - interrupt = omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT); - printk(KERN_ERR "onenand_wait: timeout state=%d ctrl=0x%04x intr=0x%04x\n", state, ctrl, interrupt); + intr = read_reg(c, + ONENAND_REG_INTERRUPT); + wait_err("timeout", state, ctrl, intr); return -EIO; } - interrupt = omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT); - if ((interrupt & ONENAND_INT_MASTER) == 0) - printk(KERN_WARNING "onenand_wait: timeout state=%d ctrl=0x%04x intr=0x%04x\n", state, ctrl, interrupt); + intr = read_reg(c, ONENAND_REG_INTERRUPT); + if ((intr & ONENAND_INT_MASTER) == 0) + wait_warn("timeout", state, ctrl, intr); } } } else { /* Turn interrupts off */ - syscfg = omap2_onenand_readw(info->onenand.base + ONENAND_REG_SYS_CFG1); + syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); syscfg &= ~ONENAND_SYS_CFG1_IOBE; - omap2_onenand_writew(syscfg, info->onenand.base + ONENAND_REG_SYS_CFG1); + write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); timeout = jiffies + msecs_to_jiffies(20); while (time_before(jiffies, timeout)) { - if (omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT) & - ONENAND_INT_MASTER) + intr = read_reg(c, ONENAND_REG_INTERRUPT); + if (intr & ONENAND_INT_MASTER) break; } } - /* To get correct interrupt status in timeout case */ - interrupt = omap2_onenand_readw(info->onenand.base + ONENAND_REG_INTERRUPT); - ctrl = omap2_onenand_readw(info->onenand.base + ONENAND_REG_CTRL_STATUS); + intr = read_reg(c, ONENAND_REG_INTERRUPT); + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); - if (ctrl & ONENAND_CTRL_LOCK) - printk(KERN_ERR "onenand_erase: Device is write protected!!!\n"); - return -EIO; - } + if (intr & ONENAND_INT_READ) { + int ecc = read_reg(c, ONENAND_REG_ECC_STATUS); - if (ctrl & 0xFE9F) - printk(KERN_WARNING "onenand_wait: unexpected controller status = 0x%04x state = %d interrupt = 0x%04x\n", ctrl, state, interrupt); - - if (interrupt & ONENAND_INT_READ) { - int ecc = omap2_onenand_readw(info->onenand.base + ONENAND_REG_ECC_STATUS); if (ecc) { + unsigned int addr1, addr8; + + addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1); + addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8); if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); + printk(KERN_ERR "onenand_wait: ECC error = " + "0x%04x, addr1 %#x, addr8 %#x\n", + ecc, addr1, addr8); mtd->ecc_stats.failed++; return -EBADMSG; - } else if (ecc & ONENAND_ECC_1BIT_ALL) - printk(KERN_NOTICE "onenand_wait: correctable ECC error = 0x%04x\n", ecc); + } else if (ecc & ONENAND_ECC_1BIT_ALL) { + printk(KERN_NOTICE "onenand_wait: correctable " + "ECC error = 0x%04x, addr1 %#x, " + "addr8 %#x\n", ecc, addr1, addr8); mtd->ecc_stats.corrected++; + } } } else if (state == FL_READING) { - printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + wait_err("timeout", state, ctrl, intr); + return -EIO; + } + + if (ctrl & ONENAND_CTRL_ERROR) { + wait_err("controller error", state, ctrl, intr); + if (ctrl & ONENAND_CTRL_LOCK) + printk(KERN_ERR "onenand_wait: " + "Device is write protected!!!\n"); return -EIO; } + if (ctrl & 0xFE9F) + wait_warn("unexpected controller status", state, ctrl, intr); + return 0; } @@ -223,106 +252,283 @@ static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area) return 0; } +#if defined(CONFIG_ARCH_OMAP3) || defined(MULTI_OMAP2) + +static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + dma_addr_t dma_src, dma_dst; + int bram_offset; + unsigned long timeout; + void *buf = (void *)buffer; + size_t xtra; + volatile unsigned *done; + + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) + goto out_copy; + + if (buf >= high_memory) { + struct page *p1; + + if (((size_t)buf & PAGE_MASK) != + ((size_t)(buf + count - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(buf); + if (!p1) + goto out_copy; + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); + } + + xtra = count & 3; + if (xtra) { + count -= xtra; + memcpy(buf + count, this->base + bram_offset + count, xtra); + } + + dma_src = c->phys_base + bram_offset; + dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dst)) { + dev_err(&c->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", + count); + goto out_copy; + } + + omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32, + count >> 2, 1, 0, 0, 0); + omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_src, 0, 0); + omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_dst, 0, 0); + + INIT_COMPLETION(c->dma_done); + omap_start_dma(c->dma_channel); + + timeout = jiffies + msecs_to_jiffies(20); + done = &c->dma_done.done; + while (time_before(jiffies, timeout)) + if (*done) + break; + + dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (!*done) { + dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); + goto out_copy; + } + + return 0; + +out_copy: + memcpy(buf, this->base + bram_offset, count); + return 0; +} + +static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, + int offset, size_t count) +{ + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + dma_addr_t dma_src, dma_dst; + int bram_offset; + unsigned long timeout; + void *buf = (void *)buffer; + volatile unsigned *done; + + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) + goto out_copy; + + /* panic_write() may be in an interrupt context */ + if (in_interrupt()) + goto out_copy; + + if (buf >= high_memory) { + struct page *p1; + + if (((size_t)buf & PAGE_MASK) != + ((size_t)(buf + count - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(buf); + if (!p1) + goto out_copy; + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); + } + + dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); + dma_dst = c->phys_base + bram_offset; + if (dma_mapping_error(dma_dst)) { + dev_err(&c->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", + count); + return -1; + } + + omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32, + count >> 2, 1, 0, 0, 0); + omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_src, 0, 0); + omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dma_dst, 0, 0); + + INIT_COMPLETION(c->dma_done); + omap_start_dma(c->dma_channel); + + timeout = jiffies + msecs_to_jiffies(20); + done = &c->dma_done.done; + while (time_before(jiffies, timeout)) + if (*done) + break; + + dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + + if (!*done) { + dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); + goto out_copy; + } + + return 0; + +out_copy: + memcpy(this->base + bram_offset, buf, count); + return 0; +} + +#else + +int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count); + +int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, + int offset, size_t count); + +#endif + +#if defined(CONFIG_ARCH_OMAP2) || defined(MULTI_OMAP2) + static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count) { - struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); struct onenand_chip *this = mtd->priv; dma_addr_t dma_src, dma_dst; int bram_offset; bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; - if (1 || (info->dma_channel < 0) || ((void *) buffer >= (void *) high_memory) || - (bram_offset & 3) || (((unsigned int) buffer) & 3) || - (count < 1024) || (count & 3)) { - memcpy(buffer, (void *)(this->base + bram_offset), count); + /* DMA is not used. Revisit PM requirements before enabling it. */ + if (1 || (c->dma_channel < 0) || + ((void *) buffer >= (void *) high_memory) || (bram_offset & 3) || + (((unsigned int) buffer) & 3) || (count < 1024) || (count & 3)) { + memcpy(buffer, (__force void *)(this->base + bram_offset), + count); return 0; } - dma_src = info->phys_base + bram_offset; - dma_dst = dma_map_single(&info->pdev->dev, buffer, count, DMA_FROM_DEVICE); + dma_src = c->phys_base + bram_offset; + dma_dst = dma_map_single(&c->pdev->dev, buffer, count, + DMA_FROM_DEVICE); if (dma_mapping_error(dma_dst)) { - dev_err(&info->pdev->dev, + dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); return -1; } - omap_set_dma_transfer_params(info->dma_channel, OMAP_DMA_DATA_TYPE_S32, + omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32, count / 4, 1, 0, 0, 0); - omap_set_dma_src_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_src, 0, 0); - omap_set_dma_dest_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_dst, 0, 0); - INIT_COMPLETION(info->dma_done); - omap2_block_sleep(); - omap_start_dma(info->dma_channel); - wait_for_completion(&info->dma_done); - omap2_allow_sleep(); + INIT_COMPLETION(c->dma_done); + omap_start_dma(c->dma_channel); + wait_for_completion(&c->dma_done); - dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); return 0; } static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, int offset, - size_t count) + const unsigned char *buffer, + int offset, size_t count) { - struct omap2_onenand *info = container_of(mtd, struct omap2_onenand, mtd); + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); struct onenand_chip *this = mtd->priv; dma_addr_t dma_src, dma_dst; int bram_offset; bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; - if (1 || (info->dma_channel < 0) || ((void *) buffer >= (void *) high_memory) || - (bram_offset & 3) || (((unsigned int) buffer) & 3) || - (count < 1024) || (count & 3)) { - memcpy((void *)(this->base + bram_offset), buffer, count); + /* DMA is not used. Revisit PM requirements before enabling it. */ + if (1 || (c->dma_channel < 0) || + ((void *) buffer >= (void *) high_memory) || (bram_offset & 3) || + (((unsigned int) buffer) & 3) || (count < 1024) || (count & 3)) { + memcpy((__force void *)(this->base + bram_offset), buffer, + count); return 0; } - dma_src = dma_map_single(&info->pdev->dev, (void *) buffer, count, + dma_src = dma_map_single(&c->pdev->dev, (void *) buffer, count, DMA_TO_DEVICE); - dma_dst = info->phys_base + bram_offset; + dma_dst = c->phys_base + bram_offset; if (dma_mapping_error(dma_dst)) { - dev_err(&info->pdev->dev, + dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); return -1; } - omap_set_dma_transfer_params(info->dma_channel, OMAP_DMA_DATA_TYPE_S16, + omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S16, count / 2, 1, 0, 0, 0); - omap_set_dma_src_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_src, 0, 0); - omap_set_dma_dest_params(info->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_dst, 0, 0); - INIT_COMPLETION(info->dma_done); - omap_start_dma(info->dma_channel); - wait_for_completion(&info->dma_done); + INIT_COMPLETION(c->dma_done); + omap_start_dma(c->dma_channel); + wait_for_completion(&c->dma_done); - dma_unmap_single(&info->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); return 0; } +#else + +int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count); + +int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, + int offset, size_t count); + +#endif + static struct platform_driver omap2_onenand_driver; static int __adjust_timing(struct device *dev, void *data) { int ret = 0; - struct omap2_onenand *info; + struct omap2_onenand *c; - info = dev_get_drvdata(dev); + c = dev_get_drvdata(dev); - BUG_ON(info->setup == NULL); + BUG_ON(c->setup == NULL); /* DMA is not in use so this is all that is needed */ - ret = info->setup(info->onenand.base, info->freq); + /* Revisit for OMAP3! */ + ret = c->setup(c->onenand.base, c->freq); return ret; } @@ -335,19 +541,19 @@ int omap2_onenand_rephase(void) static void __devexit omap2_onenand_shutdown(struct platform_device *pdev) { - struct omap2_onenand *info = dev_get_drvdata(&pdev->dev); + struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); /* With certain content in the buffer RAM, the OMAP boot ROM code * can recognize the flash chip incorrectly. Zero it out before * soft reset. */ - memset(info->onenand.base, 0, ONENAND_BUFRAM_SIZE); + memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE); } static int __devinit omap2_onenand_probe(struct platform_device *pdev) { struct omap_onenand_platform_data *pdata; - struct omap2_onenand *info; + struct omap2_onenand *c; int r; pdata = pdev->dev.platform_data; @@ -356,173 +562,187 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) return -ENODEV; } - info = kzalloc(sizeof(struct omap2_onenand), GFP_KERNEL); - if (!info) + c = kzalloc(sizeof(struct omap2_onenand), GFP_KERNEL); + if (!c) return -ENOMEM; - init_completion(&info->irq_done); - init_completion(&info->dma_done); - info->gpmc_cs = pdata->cs; - info->gpio_irq = pdata->gpio_irq; - info->dma_channel = pdata->dma_channel; - if (info->dma_channel < 0) { + init_completion(&c->irq_done); + init_completion(&c->dma_done); + c->gpmc_cs = pdata->cs; + c->gpio_irq = pdata->gpio_irq; + c->dma_channel = pdata->dma_channel; + if (c->dma_channel < 0) { /* if -1, don't use DMA */ - info->gpio_irq = 0; + c->gpio_irq = 0; } - r = gpmc_cs_request(info->gpmc_cs, ONENAND_IO_SIZE, &info->phys_base); + r = gpmc_cs_request(c->gpmc_cs, ONENAND_IO_SIZE, &c->phys_base); if (r < 0) { dev_err(&pdev->dev, "Cannot request GPMC CS\n"); goto err_kfree; } - if (request_mem_region(info->phys_base, ONENAND_IO_SIZE, + if (request_mem_region(c->phys_base, ONENAND_IO_SIZE, pdev->dev.driver->name) == NULL) { - dev_err(&pdev->dev, "Cannot reserve memory region at 0x%08lx, size: 0x%x\n", - info->phys_base, ONENAND_IO_SIZE); + dev_err(&pdev->dev, "Cannot reserve memory region at 0x%08lx, " + "size: 0x%x\n", c->phys_base, ONENAND_IO_SIZE); r = -EBUSY; goto err_free_cs; } - info->onenand.base = ioremap(info->phys_base, ONENAND_IO_SIZE); - if (info->onenand.base == NULL) { + c->onenand.base = ioremap(c->phys_base, ONENAND_IO_SIZE); + if (c->onenand.base == NULL) { r = -ENOMEM; goto err_release_mem_region; } if (pdata->onenand_setup != NULL) { - r = pdata->onenand_setup(info->onenand.base, info->freq); + r = pdata->onenand_setup(c->onenand.base, c->freq); if (r < 0) { - dev_err(&pdev->dev, "Onenand platform setup failed: %d\n", r); + dev_err(&pdev->dev, "Onenand platform setup failed: " + "%d\n", r); goto err_iounmap; } - info->setup = pdata->onenand_setup; + c->setup = pdata->onenand_setup; } - if (info->gpio_irq) { - if ((r = omap_request_gpio(info->gpio_irq)) < 0) { - dev_err(&pdev->dev, "Failed to request GPIO%d for OneNAND\n", - info->gpio_irq); + if (c->gpio_irq) { + if ((r = omap_request_gpio(c->gpio_irq)) < 0) { + dev_err(&pdev->dev, "Failed to request GPIO%d for " + "OneNAND\n", c->gpio_irq); goto err_iounmap; } - omap_set_gpio_direction(info->gpio_irq, 1); + omap_set_gpio_direction(c->gpio_irq, 1); - if ((r = request_irq(OMAP_GPIO_IRQ(info->gpio_irq), + if ((r = request_irq(OMAP_GPIO_IRQ(c->gpio_irq), omap2_onenand_interrupt, IRQF_TRIGGER_RISING, - pdev->dev.driver->name, info)) < 0) + pdev->dev.driver->name, c)) < 0) goto err_release_gpio; } - if (info->dma_channel >= 0) { + if (c->dma_channel >= 0) { r = omap_request_dma(0, pdev->dev.driver->name, - omap2_onenand_dma_cb, (void *) info, - &info->dma_channel); + omap2_onenand_dma_cb, (void *) c, + &c->dma_channel); if (r == 0) { - omap_set_dma_write_mode(info->dma_channel, OMAP_DMA_WRITE_NON_POSTED); - omap_set_dma_src_data_pack(info->dma_channel, 1); - omap_set_dma_src_burst_mode(info->dma_channel, OMAP_DMA_DATA_BURST_8); - omap_set_dma_dest_data_pack(info->dma_channel, 1); - omap_set_dma_dest_burst_mode(info->dma_channel, OMAP_DMA_DATA_BURST_8); + omap_set_dma_write_mode(c->dma_channel, + OMAP_DMA_WRITE_NON_POSTED); + omap_set_dma_src_data_pack(c->dma_channel, 1); + omap_set_dma_src_burst_mode(c->dma_channel, + OMAP_DMA_DATA_BURST_8); + omap_set_dma_dest_data_pack(c->dma_channel, 1); + omap_set_dma_dest_burst_mode(c->dma_channel, + OMAP_DMA_DATA_BURST_8); } else { dev_info(&pdev->dev, - "failed to allocate DMA for OneNAND, using PIO instead\n"); - info->dma_channel = -1; + "failed to allocate DMA for OneNAND, " + "using PIO instead\n"); + c->dma_channel = -1; } } - dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual base %p\n", - info->gpmc_cs, info->phys_base, info->onenand.base); + dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual " + "base %p\n", c->gpmc_cs, c->phys_base, + c->onenand.base); - info->pdev = pdev; - info->mtd.name = pdev->dev.bus_id; - info->mtd.priv = &info->onenand; - info->mtd.owner = THIS_MODULE; + c->pdev = pdev; + c->mtd.name = pdev->dev.bus_id; + c->mtd.priv = &c->onenand; + c->mtd.owner = THIS_MODULE; - if (info->dma_channel >= 0) { - info->onenand.wait = omap2_onenand_wait; - info->onenand.read_bufferram = omap2_onenand_read_bufferram; - info->onenand.write_bufferram = omap2_onenand_write_bufferram; + if (c->dma_channel >= 0) { + struct onenand_chip *this = &c->onenand; + + this->wait = omap2_onenand_wait; + if (cpu_is_omap34xx()) { + this->read_bufferram = omap3_onenand_read_bufferram; + this->write_bufferram = omap3_onenand_write_bufferram; + } else { + this->read_bufferram = omap2_onenand_read_bufferram; + this->write_bufferram = omap2_onenand_write_bufferram; + } } - if ((r = onenand_scan(&info->mtd, 1)) < 0) + if ((r = onenand_scan(&c->mtd, 1)) < 0) goto err_release_dma; - switch ((info->onenand.version_id >> 4) & 0xf) { + switch ((c->onenand.version_id >> 4) & 0xf) { case 0: - info->freq = 40; + c->freq = 40; break; case 1: - info->freq = 54; + c->freq = 54; break; case 2: - info->freq = 66; + c->freq = 66; break; case 3: - info->freq = 83; + c->freq = 83; break; } #ifdef CONFIG_MTD_PARTITIONS if (pdata->parts != NULL) - r = add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + r = add_mtd_partitions(&c->mtd, pdata->parts, + pdata->nr_parts); else #endif - r = add_mtd_device(&info->mtd); + r = add_mtd_device(&c->mtd); if (r < 0) goto err_release_onenand; - platform_set_drvdata(pdev, info); + platform_set_drvdata(pdev, c); return 0; err_release_onenand: - onenand_release(&info->mtd); + onenand_release(&c->mtd); err_release_dma: - if (info->dma_channel != -1) - omap_free_dma(info->dma_channel); - if (info->gpio_irq) - free_irq(OMAP_GPIO_IRQ(info->gpio_irq), info); + if (c->dma_channel != -1) + omap_free_dma(c->dma_channel); + if (c->gpio_irq) + free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c); err_release_gpio: - if (info->gpio_irq) - omap_free_gpio(info->gpio_irq); + if (c->gpio_irq) + omap_free_gpio(c->gpio_irq); err_iounmap: - iounmap(info->onenand.base); + iounmap(c->onenand.base); err_release_mem_region: - release_mem_region(info->phys_base, ONENAND_IO_SIZE); + release_mem_region(c->phys_base, ONENAND_IO_SIZE); err_free_cs: - gpmc_cs_free(info->gpmc_cs); + gpmc_cs_free(c->gpmc_cs); err_kfree: - kfree(info); + kfree(c); return r; } static int __devexit omap2_onenand_remove(struct platform_device *pdev) { - struct omap2_onenand *info = dev_get_drvdata(&pdev->dev); + struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); - BUG_ON(info == NULL); + BUG_ON(c == NULL); #ifdef CONFIG_MTD_PARTITIONS - if (info->parts) - del_mtd_partitions(&info->mtd); + if (c->parts) + del_mtd_partitions(&c->mtd); else - del_mtd_device(&info->mtd); + del_mtd_device(&c->mtd); #else - del_mtd_device(&info->mtd); + del_mtd_device(&c->mtd); #endif - onenand_release(&info->mtd); - if (info->dma_channel != -1) - omap_free_dma(info->dma_channel); + onenand_release(&c->mtd); + if (c->dma_channel != -1) + omap_free_dma(c->dma_channel); omap2_onenand_shutdown(pdev); platform_set_drvdata(pdev, NULL); - if (info->gpio_irq) { - free_irq(OMAP_GPIO_IRQ(info->gpio_irq), info); - omap_free_gpio(info->gpio_irq); + if (c->gpio_irq) { + free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c); + omap_free_gpio(c->gpio_irq); } - iounmap(info->onenand.base); - release_mem_region(info->phys_base, ONENAND_IO_SIZE); - kfree(info); + iounmap(c->onenand.base); + release_mem_region(c->phys_base, ONENAND_IO_SIZE); + kfree(c); return 0; } @@ -539,7 +759,7 @@ static struct platform_driver omap2_onenand_driver = { static int __init omap2_onenand_init(void) { - printk(KERN_INFO "OMAP2 OneNAND driver initializing\n"); + printk(KERN_INFO "OneNAND driver initializing\n"); return platform_driver_register(&omap2_onenand_driver); } @@ -554,4 +774,4 @@ module_exit(omap2_onenand_exit); MODULE_ALIAS(DRIVER_NAME); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>"); -MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2"); +MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3"); -- 1.5.4.3 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/3] ARM: OMAP: Add fields to GPMC for OMAP3 2008-08-06 6:53 ` Adrian Hunter 2008-08-06 6:55 ` [PATCH 1/3] " Adrian Hunter @ 2008-08-06 6:56 ` Adrian Hunter 2008-08-06 6:57 ` [PATCH 3/3] ARM: OMAP: Update OneNAND support Adrian Hunter 2008-08-08 8:48 ` [PATCH] ARM: OMAP: OneNAND for OMAP3 Tony Lindgren 3 siblings, 0 replies; 9+ messages in thread From: Adrian Hunter @ 2008-08-06 6:56 UTC (permalink / raw) To: Tony Lindgren; +Cc: linux-omap Add fields wr_access and wr_data_mux_bus. Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> --- arch/arm/mach-omap2/gpmc.c | 5 +++++ include/asm-arm/arch-omap/gpmc.h | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 83984f7..3edcc17 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -217,6 +217,11 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access); + if (cpu_is_omap34xx()) { + GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus); + GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access); + } + /* caller is expected to have initialized CONFIG1 to cover * at least sync vs async */ diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h index 53dc246..2aa1920 100644 --- a/include/asm-arm/arch-omap/gpmc.h +++ b/include/asm-arm/arch-omap/gpmc.h @@ -84,6 +84,10 @@ struct gpmc_timings { u16 access; /* Start-cycle to first data valid delay */ u16 rd_cycle; /* Total read cycle time */ u16 wr_cycle; /* Total write cycle time */ + + /* The following are only on OMAP3430 */ + u16 wr_access; /* WRACCESSTIME */ + u16 wr_data_mux_bus; /* WRDATAONADMUXBUS */ }; extern unsigned int gpmc_ns_to_ticks(unsigned int time_ns); -- 1.5.4.3 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/3] ARM: OMAP: Update OneNAND support 2008-08-06 6:53 ` Adrian Hunter 2008-08-06 6:55 ` [PATCH 1/3] " Adrian Hunter 2008-08-06 6:56 ` [PATCH 2/3] ARM: OMAP: Add fields to GPMC " Adrian Hunter @ 2008-08-06 6:57 ` Adrian Hunter 2008-08-08 8:48 ` [PATCH] ARM: OMAP: OneNAND for OMAP3 Tony Lindgren 3 siblings, 0 replies; 9+ messages in thread From: Adrian Hunter @ 2008-08-06 6:57 UTC (permalink / raw) To: Tony Lindgren; +Cc: linux-omap Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> --- arch/arm/mach-omap2/board-n800-flash.c | 241 +++++++++++++++++++++++--------- include/asm-arm/arch-omap/onenand.h | 3 + include/linux/mtd/onenand_regs.h | 2 + 3 files changed, 182 insertions(+), 64 deletions(-) diff --git a/arch/arm/mach-omap2/board-n800-flash.c b/arch/arm/mach-omap2/board-n800-flash.c index f7403b9..fbf83b3 100644 --- a/arch/arm/mach-omap2/board-n800-flash.c +++ b/arch/arm/mach-omap2/board-n800-flash.c @@ -19,13 +19,12 @@ #include <asm/arch/board.h> #include <asm/arch/gpmc.h> -static struct mtd_partition n800_partitions[8]; +struct mtd_partition n800_partitions[ONENAND_MAX_PARTITIONS]; -static int n800_onenand_setup(void __iomem *, int freq); +int n800_onenand_setup(void __iomem *, int freq); static struct omap_onenand_platform_data n800_onenand_data = { .cs = 0, - .gpio_irq = 26, .parts = n800_partitions, .nr_parts = 0, /* filled later */ .onenand_setup = n800_onenand_setup, @@ -39,6 +38,55 @@ static struct platform_device n800_onenand_device = { }, }; +static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) +{ + struct gpmc_timings t; + + const int t_cer = 15; + const int t_avdp = 12; + const int t_aavdh = 7; + const int t_ce = 76; + const int t_aa = 76; + const int t_oe = 20; + const int t_cez = 20; /* max of t_cez, t_oez */ + const int t_ds = 30; + const int t_wpl = 40; + const int t_wph = 30; + + memset(&t, 0, sizeof(t)); + t.sync_clk = 0; + t.cs_on = 0; + t.adv_on = 0; + + /* Read */ + t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); + t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh); + t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa); + t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce)); + t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe)); + t.oe_off = t.access + gpmc_round_ns_to_ticks(1); + t.cs_rd_off = t.oe_off; + t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez); + + /* Write */ + t.adv_wr_off = t.adv_rd_off; + t.we_on = t.oe_on; + if (cpu_is_omap34xx()) { + t.wr_data_mux_bus = t.we_on; + t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); + } + t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); + t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); + t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); + + /* Configure GPMC for asynchronous read */ + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, + GPMC_CONFIG1_DEVICESIZE_16 | + GPMC_CONFIG1_MUXADDDATA); + + return gpmc_cs_set_timings(cs, &t); +} + static unsigned short omap2_onenand_readw(void __iomem *addr) { return readw(addr); @@ -49,54 +97,121 @@ static void omap2_onenand_writew(unsigned short value, void __iomem *addr) writew(value, addr); } +static void set_onenand_cfg(void __iomem *onenand_base, int latency, + int sync_write, int hf) +{ + u32 reg; + + reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1); + reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); + reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | + ONENAND_SYS_CFG1_SYNC_READ | + ONENAND_SYS_CFG1_BL_16; + if (sync_write) + reg |= ONENAND_SYS_CFG1_SYNC_WRITE; + else + reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE; + if (hf) + reg |= ONENAND_SYS_CFG1_HF; + else + reg &= ~ONENAND_SYS_CFG1_HF; + omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); +} + static int omap2_onenand_set_sync_mode(int cs, void __iomem *onenand_base, int freq) { struct gpmc_timings t; - int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_avdp, t_wpl, t_wea; + const int t_cer = 15; + const int t_avdp = 12; + const int t_cez = 20; /* max of t_cez, t_oez */ + const int t_ds = 30; + const int t_wpl = 40; + const int t_wph = 30; + int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; - int err; + int err, ticks_cez, sync_write = 0, first_time = 0, hf = 0; u32 reg; -again: + if (!freq) { + /* Very first call freq is not known */ + err = omap2_onenand_set_async_mode(cs, onenand_base); + if (err) + return err; + reg = omap2_onenand_readw(onenand_base + + ONENAND_REG_VERSION_ID); + switch ((reg >> 4) & 0xf) { + case 0: + freq = 40; + break; + case 1: + freq = 54; + break; + case 2: + freq = 66; + break; + case 3: + freq = 83; + break; + case 4: + freq = 104; + break; + default: + freq = 54; + break; + } + first_time = 1; + } + switch (freq) { case 83: min_gpmc_clk_period = 12; /* 83 MHz */ - t_ces = 5; - t_avds = 5; - t_avdh = 6; - t_avdp = 12; - t_wpl = 40; - t_wea = 15; + t_ces = 5; + t_avds = 4; + t_avdh = 2; + t_ach = 6; + t_aavdh = 6; + t_rdyo = 9; + if (cpu_is_omap34xx()) + sync_write = 1; break; case 66: min_gpmc_clk_period = 15; /* 66 MHz */ - t_ces = 6; - t_avds = 5; - t_avdh = 6; - t_avdp = 12; - t_wpl = 40; - t_wea = 15; + t_ces = 6; + t_avds = 5; + t_avdh = 2; + t_ach = 6; + t_aavdh = 6; + t_rdyo = 11; + if (cpu_is_omap34xx()) + sync_write = 1; break; default: min_gpmc_clk_period = 18; /* 54 MHz */ - t_ces = 7; - t_avds = 7; - t_avdh = 7; - t_avdp = 12; - t_wpl = 40; - t_wea = 15; + t_ces = 7; + t_avds = 7; + t_avdh = 7; + t_ach = 9; + t_aavdh = 7; + t_rdyo = 15; break; } tick_ns = gpmc_ticks_to_ns(1); div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period); gpmc_clk_ns = gpmc_ticks_to_ns(div); - if (gpmc_clk_ns >= 25) /* 40 MHz*/ + if (gpmc_clk_ns < 15) /* >66Mhz */ + hf = 1; + if (hf) + latency = 6; + else if (gpmc_clk_ns >= 25) /* 40 MHz*/ latency = 3; else latency = 4; + if (first_time) + set_onenand_cfg(onenand_base, latency, sync_write, hf); + if (div == 1) { reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); reg |= (1 << 7); @@ -121,7 +236,7 @@ again: gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg); } - /* Set syncronous read timings */ + /* Set synchronous read timings */ memset(&t, 0, sizeof(t)); t.sync_clk = min_gpmc_clk_period; t.cs_on = 0; @@ -132,28 +247,51 @@ again: /* Read */ t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); - t.oe_on = t.adv_rd_off; + t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); t.oe_off = t.access + gpmc_round_ns_to_ticks(1); t.cs_rd_off = t.oe_off; - t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + div); + ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div; + t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + + ticks_cez); /* Write */ - t.adv_wr_off = t.adv_on + gpmc_round_ns_to_ticks(t_avdp); - t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_avdh); - t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); - t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(1); - t.wr_cycle = t.we_off + gpmc_round_ns_to_ticks(t_wea); + if (sync_write) { + t.adv_wr_off = t.adv_rd_off; + t.we_on = 0; + t.we_off = t.cs_rd_off; + t.cs_wr_off = t.cs_rd_off; + t.wr_cycle = t.rd_cycle; + if (cpu_is_omap34xx()) { + t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset + + gpmc_ns_to_ticks(min_gpmc_clk_period + + t_rdyo)); + t.wr_access = t.access; + } + } else { + t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); + t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh); + t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); + t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); + t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); + if (cpu_is_omap34xx()) { + t.wr_data_mux_bus = t.we_on; + t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); + } + } /* Configure GPMC for synchronous read */ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, GPMC_CONFIG1_WRAPBURST_SUPP | GPMC_CONFIG1_READMULTIPLE_SUPP | GPMC_CONFIG1_READTYPE_SYNC | + (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) | + (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) | GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) | GPMC_CONFIG1_PAGE_LEN(2) | - GPMC_CONFIG1_WAIT_READ_MON | - GPMC_CONFIG1_WAIT_PIN_SEL(0) | + (cpu_is_omap34xx() ? 0 : + (GPMC_CONFIG1_WAIT_READ_MON | + GPMC_CONFIG1_WAIT_PIN_SEL(0))) | GPMC_CONFIG1_DEVICESIZE_16 | GPMC_CONFIG1_DEVICETYPE_NOR | GPMC_CONFIG1_MUXADDDATA); @@ -162,39 +300,12 @@ again: if (err) return err; - if (!freq) { - /* Very first call freq is not known */ - reg = omap2_onenand_readw(onenand_base + ONENAND_REG_VERSION_ID); - switch ((reg >> 4) & 0xf) { - case 0: - freq = 40; - break; - case 1: - freq = 54; - break; - case 2: - freq = 66; - break; - case 3: - freq = 83; - break; - } - if (freq && freq != 54) - goto again; - } - - /* Configure OneNAND for sync read */ - reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1); - reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); - reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | - ONENAND_SYS_CFG1_SYNC_READ | - ONENAND_SYS_CFG1_BL_16; - omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); + set_onenand_cfg(onenand_base, latency, sync_write, hf); return 0; } -static int n800_onenand_setup(void __iomem *onenand_base, int freq) +int n800_onenand_setup(void __iomem *onenand_base, int freq) { struct omap_onenand_platform_data *datap = &n800_onenand_data; struct device *dev = &n800_onenand_device.dev; @@ -213,6 +324,8 @@ void __init n800_flash_init(void) const struct omap_partition_config *part; int i = 0; + n800_onenand_data.gpio_irq = cpu_is_omap34xx() ? 65 : 26; + while ((part = omap_get_nr_config(OMAP_TAG_PARTITION, struct omap_partition_config, i)) != NULL) { struct mtd_partition *mpart; diff --git a/include/asm-arm/arch-omap/onenand.h b/include/asm-arm/arch-omap/onenand.h index f0f28d3..e302371 100644 --- a/include/asm-arm/arch-omap/onenand.h +++ b/include/asm-arm/arch-omap/onenand.h @@ -21,3 +21,6 @@ struct omap_onenand_platform_data { }; int omap2_onenand_rephase(void); + +#define ONENAND_MAX_PARTITIONS 8 + diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index d1b310c..0c6bbe2 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -152,6 +152,8 @@ #define ONENAND_SYS_CFG1_INT (1 << 6) #define ONENAND_SYS_CFG1_IOBE (1 << 5) #define ONENAND_SYS_CFG1_RDY_CONF (1 << 4) +#define ONENAND_SYS_CFG1_HF (1 << 2) +#define ONENAND_SYS_CFG1_SYNC_WRITE (1 << 1) /* * Controller Status Register F240h (R) -- 1.5.4.3 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] ARM: OMAP: OneNAND for OMAP3 2008-08-06 6:53 ` Adrian Hunter ` (2 preceding siblings ...) 2008-08-06 6:57 ` [PATCH 3/3] ARM: OMAP: Update OneNAND support Adrian Hunter @ 2008-08-08 8:48 ` Tony Lindgren 3 siblings, 0 replies; 9+ messages in thread From: Tony Lindgren @ 2008-08-08 8:48 UTC (permalink / raw) To: Adrian Hunter; +Cc: linux-omap * Adrian Hunter <ext-adrian.hunter@nokia.com> [080806 09:52]: > I have split the patch into 3 patches, tidied up drivers/mtd/onenand/omap2.c > ready to be sent to MTD (which I will do shortly), and made multi-omap changes Thanks. I'll push these to l-o tree today. Tony ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-08-08 8:48 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-08-01 8:11 [PATCH] ARM: OMAP: OneNAND for OMAP3 Adrian Hunter 2008-08-01 16:45 ` Paul Walmsley 2008-08-04 6:56 ` Adrian Hunter 2008-08-04 14:02 ` Tony Lindgren 2008-08-06 6:53 ` Adrian Hunter 2008-08-06 6:55 ` [PATCH 1/3] " Adrian Hunter 2008-08-06 6:56 ` [PATCH 2/3] ARM: OMAP: Add fields to GPMC " Adrian Hunter 2008-08-06 6:57 ` [PATCH 3/3] ARM: OMAP: Update OneNAND support Adrian Hunter 2008-08-08 8:48 ` [PATCH] ARM: OMAP: OneNAND for OMAP3 Tony Lindgren
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox