* [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip @ 2010-06-19 5:08 Lars-Peter Clausen 2010-06-19 5:08 ` [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver Lars-Peter Clausen 2010-06-20 9:26 ` [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Thomas Bogendoerfer 0 siblings, 2 replies; 12+ messages in thread From: Lars-Peter Clausen @ 2010-06-19 5:08 UTC (permalink / raw) To: Ralf Baechle Cc: linux-mips, Paul Gortmaker, Lars-Peter Clausen, Mark Brown, Samuel Ortiz, alsa-devel, Alessandro Zummo, Greg Kroah-Hartman, linux-mmc, linux-kernel, lm-sensors, David Brownell, linux-mtd, linux-fbdev, Anton Vorontsov, rtc-linux, Andrew Morton, David Woodhouse, linux-usb, Liam Girdwood Foreword: Ralf suggested that it might be a good idea in order to allow for reasonable testing and to avoid build failures due to two-way dependencies in different parts of the kernel, that he applies all the patches once they have been acked by their respective maintainers, feeds them into -next and eventually sends the whole series to Linus. One exception will be the ASoC patches which will, due to major changes in the ASoC subsystem, go through the ASoC tree. So if you are a maintainer for one of the subsystem touched by this series and would rather see the patch going through your tree (given the patch is ok) please tell. This patch series adds support for the Ingenic JZ4740 System-on-a-Chip. The JZ4740 has a mostly MIPS32 compatible core (no on cpu timers) and many on chip peripherals like RTC, NAND, MMC, OHCI, UDC, ADC, I2C, SPI, AC97, I2S, I2S Codec, UART and LCD controller. The JZ4740 is mostly used in eBooks, PMPs and hand-held consoles. This series contains patches for the Qi Ben NanoNote clamshell device as the inital supported device. Changes since v1: There have been some minor changes since v1, mostly code cleanup and some functional changes. One bigger change is that there is now a MFD driver for the ADC core which does IRQ demultiplexing for the ADC unit and synchronizes access to shared registers between the different users of the ADC core. The patch adding a defconfig for the Qi LB60 has been dropped. A detailed list of changes is present in each patch. - Lars Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Anton Vorontsov <cbouatmailru@gmail.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: David Woodhouse <dwmw2@infradead.org> Cc: Greg Kroah-Hartman <gregkh@suse.de> Cc: Liam Girdwood <lrg@slimlogic.co.uk> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com> Cc: Paul Gortmaker <p_gortmaker@yahoo.com> Cc: Samuel Ortiz <sameo@linux.intel.com> Cc: alsa-devel@alsa-project.org Cc: linux-fbdev@vger.kernel.org Cc: linux-mmc@vger.kernel.org Cc: linux-mtd@lists.infradead.org Cc: linux-usb@vger.kernel.org Cc: lm-sensors@lm-sensors.org Cc: rtc-linux@googlegroups.com Lars-Peter Clausen (26): MIPS: Add base support for Ingenic JZ4740 System-on-a-Chip MIPS: jz4740: Add IRQ handler code MIPS: JZ4740: Add clock API support. MIPS: JZ4740: Add timer support MIPS: JZ4740: Add clocksource/clockevent support. MIPS: JZ4740: Add power-management and system reset support MIPS: JZ4740: Add setup code MIPS: JZ4740: Add gpio support MIPS: JZ4740: Add DMA support. MIPS: JZ4740: Add PWM support MIPS: JZ4740: Add serial support MIPS: JZ4740: Add prom support MIPS: JZ4740: Add platform devices MIPS: JZ4740: Add Kbuild files RTC: Add JZ4740 RTC driver fbdev: Add JZ4740 framebuffer driver MTD: Nand: Add JZ4740 NAND driver MMC: Add JZ4740 mmc driver USB: Add JZ4740 ohci support alsa: ASoC: Add JZ4740 codec driver alsa: ASoC: Add JZ4740 ASoC support MFD: Add JZ4740 ADC driver hwmon: Add JZ4740 ADC driver power: Add JZ4740 battery driver. MIPS: JZ4740: Add qi_lb60 board support alsa: ASoC: JZ4740: Add qi_lb60 board driver arch/mips/Kbuild.platforms | 1 + arch/mips/Kconfig | 13 + arch/mips/include/asm/bootinfo.h | 6 + arch/mips/include/asm/cpu.h | 9 +- arch/mips/include/asm/mach-jz4740/base.h | 26 + arch/mips/include/asm/mach-jz4740/clock.h | 28 + arch/mips/include/asm/mach-jz4740/dma.h | 90 +++ arch/mips/include/asm/mach-jz4740/gpio.h | 398 +++++++++++ arch/mips/include/asm/mach-jz4740/irq.h | 57 ++ arch/mips/include/asm/mach-jz4740/platform.h | 36 + arch/mips/include/asm/mach-jz4740/timer.h | 22 + arch/mips/include/asm/mach-jz4740/war.h | 25 + arch/mips/jz4740/Kconfig | 12 + arch/mips/jz4740/Makefile | 20 + arch/mips/jz4740/Platform | 5 + arch/mips/jz4740/board-qi_lb60.c | 483 +++++++++++++ arch/mips/jz4740/clock-debugfs.c | 109 +++ arch/mips/jz4740/clock.c | 920 ++++++++++++++++++++++++ arch/mips/jz4740/clock.h | 76 ++ arch/mips/jz4740/dma.c | 289 ++++++++ arch/mips/jz4740/gpio.c | 601 ++++++++++++++++ arch/mips/jz4740/irq.c | 169 +++++ arch/mips/jz4740/irq.h | 21 + arch/mips/jz4740/platform.c | 284 ++++++++ arch/mips/jz4740/pm.c | 56 ++ arch/mips/jz4740/prom.c | 68 ++ arch/mips/jz4740/pwm.c | 169 +++++ arch/mips/jz4740/reset.c | 79 ++ arch/mips/jz4740/reset.h | 7 + arch/mips/jz4740/serial.c | 33 + arch/mips/jz4740/serial.h | 21 + arch/mips/jz4740/setup.c | 29 + arch/mips/jz4740/time.c | 144 ++++ arch/mips/jz4740/timer.c | 48 ++ arch/mips/jz4740/timer.h | 136 ++++ arch/mips/kernel/cpu-probe.c | 20 + arch/mips/mm/tlbex.c | 5 + drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/jz4740-hwmon.c | 206 ++++++ drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/jz4740-adc.c | 392 ++++++++++ drivers/mmc/host/Kconfig | 8 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/jz4740_mmc.c | 993 ++++++++++++++++++++++++++ drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/jz4740_nand.c | 474 ++++++++++++ drivers/power/Kconfig | 11 + drivers/power/Makefile | 1 + drivers/power/jz4740-battery.c | 445 ++++++++++++ drivers/rtc/Kconfig | 11 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-jz4740.c | 341 +++++++++ drivers/usb/Kconfig | 1 + drivers/usb/host/ohci-hcd.c | 5 + drivers/usb/host/ohci-jz4740.c | 276 +++++++ drivers/video/Kconfig | 9 + drivers/video/Makefile | 1 + drivers/video/jz4740_fb.c | 817 +++++++++++++++++++++ include/linux/jz4740-adc.h | 32 + include/linux/jz4740_fb.h | 58 ++ include/linux/mmc/jz4740_mmc.h | 15 + include/linux/mtd/jz4740_nand.h | 34 + include/linux/power/jz4740-battery.h | 24 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/jz4740-codec.c | 514 +++++++++++++ sound/soc/codecs/jz4740-codec.h | 20 + sound/soc/jz4740/Kconfig | 23 + sound/soc/jz4740/Makefile | 13 + sound/soc/jz4740/jz4740-i2s.c | 540 ++++++++++++++ sound/soc/jz4740/jz4740-i2s.h | 18 + sound/soc/jz4740/jz4740-pcm.c | 373 ++++++++++ sound/soc/jz4740/jz4740-pcm.h | 22 + sound/soc/jz4740/qi_lb60.c | 167 +++++ 79 files changed, 10396 insertions(+), 1 deletions(-) create mode 100644 arch/mips/include/asm/mach-jz4740/base.h create mode 100644 arch/mips/include/asm/mach-jz4740/clock.h create mode 100644 arch/mips/include/asm/mach-jz4740/dma.h create mode 100644 arch/mips/include/asm/mach-jz4740/gpio.h create mode 100644 arch/mips/include/asm/mach-jz4740/irq.h create mode 100644 arch/mips/include/asm/mach-jz4740/platform.h create mode 100644 arch/mips/include/asm/mach-jz4740/timer.h create mode 100644 arch/mips/include/asm/mach-jz4740/war.h create mode 100644 arch/mips/jz4740/Kconfig create mode 100644 arch/mips/jz4740/Makefile create mode 100644 arch/mips/jz4740/Platform create mode 100644 arch/mips/jz4740/board-qi_lb60.c create mode 100644 arch/mips/jz4740/clock-debugfs.c create mode 100644 arch/mips/jz4740/clock.c create mode 100644 arch/mips/jz4740/clock.h create mode 100644 arch/mips/jz4740/dma.c create mode 100644 arch/mips/jz4740/gpio.c create mode 100644 arch/mips/jz4740/irq.c create mode 100644 arch/mips/jz4740/irq.h create mode 100644 arch/mips/jz4740/platform.c create mode 100644 arch/mips/jz4740/pm.c create mode 100644 arch/mips/jz4740/prom.c create mode 100644 arch/mips/jz4740/pwm.c create mode 100644 arch/mips/jz4740/reset.c create mode 100644 arch/mips/jz4740/reset.h create mode 100644 arch/mips/jz4740/serial.c create mode 100644 arch/mips/jz4740/serial.h create mode 100644 arch/mips/jz4740/setup.c create mode 100644 arch/mips/jz4740/time.c create mode 100644 arch/mips/jz4740/timer.c create mode 100644 arch/mips/jz4740/timer.h create mode 100644 drivers/hwmon/jz4740-hwmon.c create mode 100644 drivers/mfd/jz4740-adc.c create mode 100644 drivers/mmc/host/jz4740_mmc.c create mode 100644 drivers/mtd/nand/jz4740_nand.c create mode 100644 drivers/power/jz4740-battery.c create mode 100644 drivers/rtc/rtc-jz4740.c create mode 100644 drivers/usb/host/ohci-jz4740.c create mode 100644 drivers/video/jz4740_fb.c create mode 100644 include/linux/jz4740-adc.h create mode 100644 include/linux/jz4740_fb.h create mode 100644 include/linux/mmc/jz4740_mmc.h create mode 100644 include/linux/mtd/jz4740_nand.h create mode 100644 include/linux/power/jz4740-battery.h create mode 100644 sound/soc/codecs/jz4740-codec.c create mode 100644 sound/soc/codecs/jz4740-codec.h create mode 100644 sound/soc/jz4740/Kconfig create mode 100644 sound/soc/jz4740/Makefile create mode 100644 sound/soc/jz4740/jz4740-i2s.c create mode 100644 sound/soc/jz4740/jz4740-i2s.h create mode 100644 sound/soc/jz4740/jz4740-pcm.c create mode 100644 sound/soc/jz4740/jz4740-pcm.h create mode 100644 sound/soc/jz4740/qi_lb60.c ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver 2010-06-19 5:08 [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Lars-Peter Clausen @ 2010-06-19 5:08 ` Lars-Peter Clausen 2010-07-08 6:06 ` Artem Bityutskiy 2010-07-17 12:15 ` [PATCH v3] " Lars-Peter Clausen 2010-06-20 9:26 ` [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Thomas Bogendoerfer 1 sibling, 2 replies; 12+ messages in thread From: Lars-Peter Clausen @ 2010-06-19 5:08 UTC (permalink / raw) To: Ralf Baechle Cc: linux-mips, Lars-Peter Clausen, David Woodhouse, linux-kernel, linux-mtd This patch adds support for the NAND controller on JZ4740 SoCs. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Cc: David Woodhouse <dwmw2@infradead.org> Cc: linux-mtd@lists.infradead.org --- Changes since v1 - JZ4740: Remove debug macro - Fix platform driver remove callback - Add custom nand read/write callback since we need to support more then 64 ecc bytes --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/jz4740_nand.c | 474 +++++++++++++++++++++++++++++++++++++++ include/linux/mtd/jz4740_nand.h | 34 +++ 4 files changed, 515 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/jz4740_nand.c create mode 100644 include/linux/mtd/jz4740_nand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ffc3720..362d177 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -526,4 +526,10 @@ config MTD_NAND_NUC900 This enables the driver for the NAND Flash on evaluation board based on w90p910 / NUC9xx. +config MTD_NAND_JZ4740 + tristate "Support for JZ4740 SoC NAND controller" + depends on MACH_JZ4740 + help + Enables support for NAND Flash on JZ4740 SoC based boards. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e8ab884..ac83dcd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -46,5 +46,6 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o +obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 0000000..8c55f8a --- /dev/null +++ b/drivers/mtd/nand/jz4740_nand.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC NAND controller driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <linux/mtd/jz4740_nand.h> +#include <linux/gpio.h> + +#define JZ_REG_NAND_CTRL 0x50 +#define JZ_REG_NAND_ECC_CTRL 0x100 +#define JZ_REG_NAND_DATA 0x104 +#define JZ_REG_NAND_PAR0 0x108 +#define JZ_REG_NAND_PAR1 0x10C +#define JZ_REG_NAND_PAR2 0x110 +#define JZ_REG_NAND_IRQ_STAT 0x114 +#define JZ_REG_NAND_IRQ_CTRL 0x118 +#define JZ_REG_NAND_ERR(x) (0x11C + (x << 2)) + +#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) +#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) +#define JZ_NAND_ECC_CTRL_RS BIT(2) +#define JZ_NAND_ECC_CTRL_RESET BIT(1) +#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) + +#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) +#define JZ_NAND_STATUS_PAD_FINISH BIT(4) +#define JZ_NAND_STATUS_DEC_FINISH BIT(3) +#define JZ_NAND_STATUS_ENC_FINISH BIT(2) +#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) +#define JZ_NAND_STATUS_ERROR BIT(0) + +#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT(x << 1) +#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT((x << 1) + 1) + +#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000) +#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000) +#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000) + +struct jz_nand { + struct mtd_info mtd; + struct nand_chip chip; + void __iomem *base; + struct resource *mem; + + struct jz_nand_platform_data *pdata; + bool is_reading; +}; + +static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) +{ + return container_of(mtd, struct jz_nand, mtd); +} + +static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd->priv; + uint32_t reg; + + if (ctrl & NAND_CTRL_CHANGE) { + BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); + if (ctrl & NAND_ALE) + chip->IO_ADDR_W = JZ_NAND_ADDR_ADDR; + else if (ctrl & NAND_CLE) + chip->IO_ADDR_W = JZ_NAND_CMD_ADDR; + else + chip->IO_ADDR_W = JZ_NAND_DATA_ADDR; + + reg = readl(nand->base + JZ_REG_NAND_CTRL); + if (ctrl & NAND_NCE) + reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); + else + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); + writel(reg, nand->base + JZ_REG_NAND_CTRL); + } + if (dat != NAND_CMD_NONE) + writeb(dat, chip->IO_ADDR_W); +} + +static int jz_nand_dev_ready(struct mtd_info *mtd) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + return gpio_get_value_cansleep(nand->pdata->busy_gpio); +} + +static void jz_nand_hwctl(struct mtd_info *mtd, int mode) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg; + + writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + + reg |= JZ_NAND_ECC_CTRL_RESET; + reg |= JZ_NAND_ECC_CTRL_ENABLE; + reg |= JZ_NAND_ECC_CTRL_RS; + + switch (mode) { + case NAND_ECC_READ: + reg &= ~JZ_NAND_ECC_CTRL_ENCODING; + nand->is_reading = true; + break; + case NAND_ECC_WRITE: + reg |= JZ_NAND_ECC_CTRL_ENCODING; + nand->is_reading = false; + break; + default: + break; + } + + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); +} + +static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg, status; + int i; + static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, + 0x8b, 0xff, 0xb7, 0x6f}; + + if (nand->is_reading) + return 0; + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_ENC_FINISH)); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + for (i = 0; i < 9; ++i) + ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); + + /* If the written data is completly 0xff, we also want to write 0xff as + * ecc, otherwise we will get in trouble when doing subpage writes. */ + if (memcmp(ecc_code, empty_block_ecc, 9) == 0) + memset(ecc_code, 0xff, 9); + + return 0; +} + +static void correct_data(uint8_t *dat, int index, int mask) +{ + int offset = index & 0x7; + uint16_t data; + + index += (index >> 3); + + data = dat[index]; + data |= dat[index+1] << 8; + + mask ^= (data >> offset) & 0x1ff; + data &= ~(0x1ff << offset); + data |= (mask << offset); + + dat[index] = data & 0xff; + dat[index+1] = (data >> 8) & 0xff; +} + +static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + int i, error_count, index; + uint32_t reg, status, error; + uint32_t t; + + t = read_ecc[0]; + + if (t == 0xff) { + for (i = 1; i < 9; ++i) + t &= read_ecc[i]; + + t &= dat[0]; + t &= dat[nand->chip.ecc.size / 2]; + t &= dat[nand->chip.ecc.size - 1]; + + if (t == 0xff) { + for (i = 1; i < nand->chip.ecc.size - 1; ++i) + t &= dat[i]; + if (t == 0xff) + return 0; + } + } + + for (i = 0; i < 9; ++i) + writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg |= JZ_NAND_ECC_CTRL_PAR_READY; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_DEC_FINISH)); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + if (status & JZ_NAND_STATUS_ERROR) { + if (status & JZ_NAND_STATUS_UNCOR_ERROR) + return -1; + + error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; + + for (i = 0; i < error_count; ++i) { + error = readl(nand->base + JZ_REG_NAND_ERR(i)); + index = ((error >> 16) & 0x1ff) - 1; + if (index >= 0 && index < 512) + correct_data(dat, index, error & 0x1ff); + } + + return error_count; + } + + return 0; +} + + +/* Copy paste of nand_read_page_hwecc_oob_first except for different eccpos + * handling. The ecc area is for 4k chips 72 bytes long and thus does not fit + * into the eccpos array. */ +static int jz_nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + unsigned int ecc_offset = chip->page_shift; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + stat = chip->ecc.correct(mtd, p, &chip->oob_poi[i], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/* Copy-and-paste of nand_write_page_hwecc with different eccpos handling. */ +static void jz_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + unsigned int ecc_offset = chip->page_shift; + + for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &chip->oob_poi[i]); + } + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static const char *part_probes[] = {"cmdline", NULL}; +#endif + +static int __devinit jz_nand_probe(struct platform_device *pdev) +{ + int ret; + struct jz_nand *nand; + struct nand_chip *chip; + struct mtd_info *mtd; + struct jz_nand_platform_data *pdata = pdev->dev.platform_data; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partition_info; + int num_partitions = 0; +#endif + + nand = kzalloc(sizeof(*nand), GFP_KERNEL); + if (!nand) { + dev_err(&pdev->dev, "Failed to allocate device structure.\n"); + return -ENOMEM; + } + + nand->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!nand->mem) { + dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); + ret = -ENOENT; + goto err_free; + } + + nand->mem = request_mem_region(nand->mem->start, + resource_size(nand->mem), pdev->name); + if (!nand->mem) { + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + ret = -EBUSY; + goto err_free; + } + + nand->base = ioremap(nand->mem->start, resource_size(nand->mem)); + if (!nand->base) { + dev_err(&pdev->dev, "Failed to ioremap mmio memory region\n"); + ret = -EBUSY; + goto err_release_mem; + } + + if (pdata && gpio_is_valid(pdata->busy_gpio)) { + ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request busy gpio %d: %d\n", + pdata->busy_gpio, ret); + goto err_iounmap; + } + } + + mtd = &nand->mtd; + chip = &nand->chip; + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->name = "jz4740-nand"; + + chip->ecc.hwctl = jz_nand_hwctl; + chip->ecc.calculate = jz_nand_calculate_ecc_rs; + chip->ecc.correct = jz_nand_correct_ecc_rs; + chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; + chip->ecc.size = 512; + chip->ecc.bytes = 9; + + chip->ecc.read_page = jz_nand_read_page_hwecc_oob_first; + chip->ecc.write_page = jz_nand_write_page_hwecc; + + if (pdata) + chip->ecc.layout = pdata->ecc_layout; + + chip->chip_delay = 50; + chip->cmd_ctrl = jz_nand_cmd_ctrl; + + if (pdata && gpio_is_valid(pdata->busy_gpio)) + chip->dev_ready = jz_nand_dev_ready; + + chip->IO_ADDR_R = JZ_NAND_DATA_ADDR; + chip->IO_ADDR_W = JZ_NAND_DATA_ADDR; + + nand->pdata = pdata; + platform_set_drvdata(pdev, nand); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; + } + + if (pdata && pdata->ident_callback) { + pdata->ident_callback(pdev, chip, &pdata->partitions, + &pdata->num_partitions); + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; + } + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + num_partitions = parse_mtd_partitions(mtd, part_probes, + &partition_info, 0); +#endif + if (num_partitions <= 0 && pdata) { + num_partitions = pdata->num_partitions; + partition_info = pdata->partitions; + } + + if (num_partitions > 0) + ret = add_mtd_partitions(mtd, partition_info, num_partitions); + else +#endif + ret = add_mtd_device(mtd); + + if (ret) { + dev_err(&pdev->dev, "Failed to add mtd device\n"); + goto err_nand_release; + } + + dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); + + return 0; +err_nand_release: + nand_release(&nand->mtd); +err_gpio_free: + platform_set_drvdata(pdev, NULL); + gpio_free(pdata->busy_gpio); +err_iounmap: + iounmap(nand->base); +err_release_mem: + release_mem_region(nand->mem->start, resource_size(nand->mem)); +err_free: + kfree(nand); + return ret; +} + +static int __devexit jz_nand_remove(struct platform_device *pdev) +{ + struct jz_nand *nand = platform_get_drvdata(pdev); + + nand_release(&nand->mtd); + + iounmap(nand->base); + release_mem_region(nand->mem->start, resource_size(nand->mem)); + + platform_set_drvdata(pdev, NULL); + kfree(nand); + + return 0; +} + +struct platform_driver jz_nand_driver = { + .probe = jz_nand_probe, + .remove = __devexit_p(jz_nand_remove), + .driver = { + .name = "jz4740-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init jz_nand_init(void) +{ + return platform_driver_register(&jz_nand_driver); +} +module_init(jz_nand_init); + +static void __exit jz_nand_exit(void) +{ + platform_driver_unregister(&jz_nand_driver); +} +module_exit(jz_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); +MODULE_ALIAS("platform:jz4740-nand"); diff --git a/include/linux/mtd/jz4740_nand.h b/include/linux/mtd/jz4740_nand.h new file mode 100644 index 0000000..379f9b6 --- /dev/null +++ b/include/linux/mtd/jz4740_nand.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC NAND controller driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __JZ_NAND_H__ +#define __JZ_NAND_H__ + +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +struct jz_nand_platform_data { + int num_partitions; + struct mtd_partition *partitions; + + struct nand_ecclayout *ecc_layout; + + unsigned int busy_gpio; + + void (*ident_callback)(struct platform_device *, struct nand_chip *, + struct mtd_partition **, int *num_partitions); +}; + +#endif -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver 2010-06-19 5:08 ` [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver Lars-Peter Clausen @ 2010-07-08 6:06 ` Artem Bityutskiy 2010-07-08 13:20 ` Lars-Peter Clausen 2010-07-17 12:15 ` [PATCH v3] " Lars-Peter Clausen 1 sibling, 1 reply; 12+ messages in thread From: Artem Bityutskiy @ 2010-07-08 6:06 UTC (permalink / raw) To: Lars-Peter Clausen Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle On Sat, 2010-06-19 at 07:08 +0200, Lars-Peter Clausen wrote: > diff --git a/include/linux/mtd/jz4740_nand.h b/include/linux/mtd/jz4740_nand.h > new file mode 100644 > index 0000000..379f9b6 > --- /dev/null > +++ b/include/linux/mtd/jz4740_nand.h > @@ -0,0 +1,34 @@ > +/* > + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> > + * JZ4740 SoC NAND controller driver > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 675 Mass Ave, Cambridge, MA 02139, USA. > + * > + */ > + > +#ifndef __JZ_NAND_H__ > +#define __JZ_NAND_H__ > + > +#include <linux/mtd/nand.h> > +#include <linux/mtd/partitions.h> > + > +struct jz_nand_platform_data { > + int num_partitions; > + struct mtd_partition *partitions; > + > + struct nand_ecclayout *ecc_layout; > + > + unsigned int busy_gpio; > + > + void (*ident_callback)(struct platform_device *, struct nand_chip *, > + struct mtd_partition **, int *num_partitions); > +}; > + > +#endif Do you really have to add your platform data strucutre to "inlculde/mtd" ? That is quite global namespace, and ideally only things like user-space interface and "public" interface of the MTD subsystem should live there. Can you keep this somewhere in mips architecture directory? -- Best Regards, Artem Bityutskiy (Артём Битюцкий) ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver 2010-07-08 6:06 ` Artem Bityutskiy @ 2010-07-08 13:20 ` Lars-Peter Clausen 2010-07-08 13:19 ` Artem Bityutskiy 0 siblings, 1 reply; 12+ messages in thread From: Lars-Peter Clausen @ 2010-07-08 13:20 UTC (permalink / raw) To: dedekind1 Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle Artem Bityutskiy wrote: > On Sat, 2010-06-19 at 07:08 +0200, Lars-Peter Clausen wrote: >> diff --git a/include/linux/mtd/jz4740_nand.h b/include/linux/mtd/jz4740_nand.h >> new file mode 100644 >> index 0000000..379f9b6 >> --- /dev/null >> +++ b/include/linux/mtd/jz4740_nand.h >> @@ -0,0 +1,34 @@ >> +/* >> + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> >> + * JZ4740 SoC NAND controller driver >> + * >> + * This program is free software; you can redistribute it and/or modify it >> + * under the terms of the GNU General Public License as published by the >> + * Free Software Foundation; either version 2 of the License, or (at your >> + * option) any later version. >> + * >> + * You should have received a copy of the GNU General Public License along >> + * with this program; if not, write to the Free Software Foundation, Inc., >> + * 675 Mass Ave, Cambridge, MA 02139, USA. >> + * >> + */ >> + >> +#ifndef __JZ_NAND_H__ >> +#define __JZ_NAND_H__ >> + >> +#include <linux/mtd/nand.h> >> +#include <linux/mtd/partitions.h> >> + >> +struct jz_nand_platform_data { >> + int num_partitions; >> + struct mtd_partition *partitions; >> + >> + struct nand_ecclayout *ecc_layout; >> + >> + unsigned int busy_gpio; >> + >> + void (*ident_callback)(struct platform_device *, struct nand_chip *, >> + struct mtd_partition **, int *num_partitions); >> +}; >> + >> +#endif > > Do you really have to add your platform data strucutre to > "inlculde/mtd" ? That is quite global namespace, and ideally only things > like user-space interface and "public" interface of the MTD subsystem > should live there. > > Can you keep this somewhere in mips architecture directory? Hi Hm, ok, I see. I'll move it to arch/mips/include/asm/mach-jz4740/ then. But I guess I should move the headers for all the other jz4740 driver to the same directory as well. On the other hand I'm wondering where on would put headers for non platform specific drivers? - Lars ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver 2010-07-08 13:20 ` Lars-Peter Clausen @ 2010-07-08 13:19 ` Artem Bityutskiy 2010-07-08 14:02 ` Lars-Peter Clausen 0 siblings, 1 reply; 12+ messages in thread From: Artem Bityutskiy @ 2010-07-08 13:19 UTC (permalink / raw) To: Lars-Peter Clausen Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle On Thu, 2010-07-08 at 15:20 +0200, Lars-Peter Clausen wrote: > On the other hand I'm wondering where on would put headers for non platform specific > drivers? If we are talking about MTD, then drivers/mtd ? -- Best Regards, Artem Bityutskiy (Артём Битюцкий) ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver 2010-07-08 13:19 ` Artem Bityutskiy @ 2010-07-08 14:02 ` Lars-Peter Clausen 2010-07-08 14:14 ` Artem Bityutskiy 0 siblings, 1 reply; 12+ messages in thread From: Lars-Peter Clausen @ 2010-07-08 14:02 UTC (permalink / raw) To: dedekind1 Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle Artem Bityutskiy wrote: > On Thu, 2010-07-08 at 15:20 +0200, Lars-Peter Clausen wrote: >> On the other hand I'm wondering where on would put headers for non platform specific >> drivers? > > If we are talking about MTD, then drivers/mtd ? > No, what I meant was header defining platform data structs and such. And what I wanted to get at is an answer to why driver header files are put in different directories while the driver files themselves are all keep in the same directory. (drivers of the same subsystem that is) - Lars ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver 2010-07-08 14:02 ` Lars-Peter Clausen @ 2010-07-08 14:14 ` Artem Bityutskiy 0 siblings, 0 replies; 12+ messages in thread From: Artem Bityutskiy @ 2010-07-08 14:14 UTC (permalink / raw) To: Lars-Peter Clausen Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle On Thu, 2010-07-08 at 16:02 +0200, Lars-Peter Clausen wrote: > Artem Bityutskiy wrote: > > On Thu, 2010-07-08 at 15:20 +0200, Lars-Peter Clausen wrote: > >> On the other hand I'm wondering where on would put headers for non platform specific > >> drivers? > > > > If we are talking about MTD, then drivers/mtd ? > > > No, what I meant was header defining platform data structs and such. > And what I wanted to get at is an answer to why driver header files are put in > different directories while the driver files themselves are all keep in the same > directory. (drivers of the same subsystem that is) To be frank I do not know, I did not look at the whole picture, just at the MTD part :-) -- Best Regards, Artem Bityutskiy (Артём Битюцкий) ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3] MTD: Nand: Add JZ4740 NAND driver 2010-06-19 5:08 ` [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver Lars-Peter Clausen 2010-07-08 6:06 ` Artem Bityutskiy @ 2010-07-17 12:15 ` Lars-Peter Clausen 2010-07-18 16:54 ` Artem Bityutskiy 1 sibling, 1 reply; 12+ messages in thread From: Lars-Peter Clausen @ 2010-07-17 12:15 UTC (permalink / raw) To: Ralf Baechle Cc: linux-mips, Lars-Peter Clausen, David Woodhouse, linux-kernel, linux-mtd This patch adds support for the NAND controller on JZ4740 SoCs. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Cc: David Woodhouse <dwmw2@infradead.org> Cc: linux-mtd@lists.infradead.org -- Changes since v1 - JZ4740: Remove debug macro - Fix platform driver remove callback - Add custom nand read/write callback since we need to support more then 64 ecc bytes Changes since v2 - Fix potential deadlock that can happen when the hardware is broken - Move include file from include/linux/mtd/ to arch/mips/include/asm/mach-jz4740/ - {Enable,Disable} NAND-chip in {probe,remove} - Supply memory bank address through platform resource --- arch/mips/include/asm/mach-jz4740/jz4740_nand.h | 34 ++ drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/jz4740_nand.c | 516 +++++++++++++++++++++++ 4 files changed, 557 insertions(+), 0 deletions(-) create mode 100644 arch/mips/include/asm/mach-jz4740/jz4740_nand.h create mode 100644 drivers/mtd/nand/jz4740_nand.c diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h new file mode 100644 index 0000000..bb5b9a4 --- /dev/null +++ b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC NAND controller driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __ASM_MACH_JZ4740_JZ4740_NAND_H__ +#define __ASM_MACH_JZ4740_JZ4740_NAND_H__ + +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +struct jz_nand_platform_data { + int num_partitions; + struct mtd_partition *partitions; + + struct nand_ecclayout *ecc_layout; + + unsigned int busy_gpio; + + void (*ident_callback)(struct platform_device *, struct nand_chip *, + struct mtd_partition **, int *num_partitions); +}; + +#endif diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ffc3720..362d177 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -526,4 +526,10 @@ config MTD_NAND_NUC900 This enables the driver for the NAND Flash on evaluation board based on w90p910 / NUC9xx. +config MTD_NAND_JZ4740 + tristate "Support for JZ4740 SoC NAND controller" + depends on MACH_JZ4740 + help + Enables support for NAND Flash on JZ4740 SoC based boards. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e8ab884..ac83dcd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -46,5 +46,6 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o +obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 0000000..67343fc --- /dev/null +++ b/drivers/mtd/nand/jz4740_nand.c @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC NAND controller driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <linux/gpio.h> + +#include <asm/mach-jz4740/jz4740_nand.h> + +#define JZ_REG_NAND_CTRL 0x50 +#define JZ_REG_NAND_ECC_CTRL 0x100 +#define JZ_REG_NAND_DATA 0x104 +#define JZ_REG_NAND_PAR0 0x108 +#define JZ_REG_NAND_PAR1 0x10C +#define JZ_REG_NAND_PAR2 0x110 +#define JZ_REG_NAND_IRQ_STAT 0x114 +#define JZ_REG_NAND_IRQ_CTRL 0x118 +#define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) + +#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) +#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) +#define JZ_NAND_ECC_CTRL_RS BIT(2) +#define JZ_NAND_ECC_CTRL_RESET BIT(1) +#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) + +#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) +#define JZ_NAND_STATUS_PAD_FINISH BIT(4) +#define JZ_NAND_STATUS_DEC_FINISH BIT(3) +#define JZ_NAND_STATUS_ENC_FINISH BIT(2) +#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) +#define JZ_NAND_STATUS_ERROR BIT(0) + +#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) +#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) + +#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 +#define JZ_NAND_MEM_CMD_OFFSET 0x08000 + +struct jz_nand { + struct mtd_info mtd; + struct nand_chip chip; + void __iomem *base; + struct resource *mem; + + void __iomem *bank_base; + struct resource *bank_mem; + + struct jz_nand_platform_data *pdata; + bool is_reading; +}; + +static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) +{ + return container_of(mtd, struct jz_nand, mtd); +} + +static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd->priv; + uint32_t reg; + + if (ctrl & NAND_CTRL_CHANGE) { + BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); + if (ctrl & NAND_ALE) + chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; + else if (ctrl & NAND_CLE) + chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; + else + chip->IO_ADDR_W = nand->bank_base; + + reg = readl(nand->base + JZ_REG_NAND_CTRL); + if (ctrl & NAND_NCE) + reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); + else + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); + writel(reg, nand->base + JZ_REG_NAND_CTRL); + } + if (dat != NAND_CMD_NONE) + writeb(dat, chip->IO_ADDR_W); +} + +static int jz_nand_dev_ready(struct mtd_info *mtd) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + return gpio_get_value_cansleep(nand->pdata->busy_gpio); +} + +static void jz_nand_hwctl(struct mtd_info *mtd, int mode) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg; + + writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + + reg |= JZ_NAND_ECC_CTRL_RESET; + reg |= JZ_NAND_ECC_CTRL_ENABLE; + reg |= JZ_NAND_ECC_CTRL_RS; + + switch (mode) { + case NAND_ECC_READ: + reg &= ~JZ_NAND_ECC_CTRL_ENCODING; + nand->is_reading = true; + break; + case NAND_ECC_WRITE: + reg |= JZ_NAND_ECC_CTRL_ENCODING; + nand->is_reading = false; + break; + default: + break; + } + + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); +} + +static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg, status; + int i; + unsigned int timeout = 1000; + static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, + 0x8b, 0xff, 0xb7, 0x6f}; + + if (nand->is_reading) + return 0; + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); + + if (timeout == 0) + return -1; + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + for (i = 0; i < 9; ++i) + ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); + + /* If the written data is completly 0xff, we also want to write 0xff as + * ecc, otherwise we will get in trouble when doing subpage writes. */ + if (memcmp(ecc_code, empty_block_ecc, 9) == 0) + memset(ecc_code, 0xff, 9); + + return 0; +} + +static void jz_nand_correct_data(uint8_t *dat, int index, int mask) +{ + int offset = index & 0x7; + uint16_t data; + + index += (index >> 3); + + data = dat[index]; + data |= dat[index+1] << 8; + + mask ^= (data >> offset) & 0x1ff; + data &= ~(0x1ff << offset); + data |= (mask << offset); + + dat[index] = data & 0xff; + dat[index+1] = (data >> 8) & 0xff; +} + +static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + int i, error_count, index; + uint32_t reg, status, error; + uint32_t t; + unsigned int timeout = 1000; + + t = read_ecc[0]; + + if (t == 0xff) { + for (i = 1; i < 9; ++i) + t &= read_ecc[i]; + + t &= dat[0]; + t &= dat[nand->chip.ecc.size / 2]; + t &= dat[nand->chip.ecc.size - 1]; + + if (t == 0xff) { + for (i = 1; i < nand->chip.ecc.size - 1; ++i) + t &= dat[i]; + if (t == 0xff) + return 0; + } + } + + for (i = 0; i < 9; ++i) + writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg |= JZ_NAND_ECC_CTRL_PAR_READY; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); + + if (timeout == 0) + return -1; + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + if (status & JZ_NAND_STATUS_ERROR) { + if (status & JZ_NAND_STATUS_UNCOR_ERROR) + return -1; + + error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; + + for (i = 0; i < error_count; ++i) { + error = readl(nand->base + JZ_REG_NAND_ERR(i)); + index = ((error >> 16) & 0x1ff) - 1; + if (index >= 0 && index < 512) + jz_nand_correct_data(dat, index, error & 0x1ff); + } + + return error_count; + } + + return 0; +} + + +/* Copy paste of nand_read_page_hwecc_oob_first except for different eccpos + * handling. The ecc area is for 4k chips 72 bytes long and thus does not fit + * into the eccpos array. */ +static int jz_nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + unsigned int ecc_offset = chip->page_shift; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + stat = chip->ecc.correct(mtd, p, &chip->oob_poi[i], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/* Copy-and-paste of nand_write_page_hwecc with different eccpos handling. */ +static void jz_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + unsigned int ecc_offset = chip->page_shift; + + for (i = ecc_offset; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &chip->oob_poi[i]); + } + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static const char *part_probes[] = {"cmdline", NULL}; +#endif + +static int jz_nand_ioremap_resource(struct platform_device *pdev, + const char *name, struct resource **res, void __iomem **base) +{ + int ret; + + *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!*res) { + dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); + ret = -ENXIO; + goto err; + } + + *res = request_mem_region((*res)->start, resource_size(*res), + pdev->name); + if (!*res) { + dev_err(&pdev->dev, "Failed to request %s memory region\n", name); + ret = -EBUSY; + goto err; + } + + *base = ioremap((*res)->start, resource_size(*res)); + if (!*base) { + dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); + ret = -EBUSY; + goto err_release_mem; + } + + return 0; + +err_release_mem: + release_mem_region((*res)->start, resource_size(*res)); +err: + *res = NULL; + *base = NULL; + return ret; +} + +static int __devinit jz_nand_probe(struct platform_device *pdev) +{ + int ret; + struct jz_nand *nand; + struct nand_chip *chip; + struct mtd_info *mtd; + struct jz_nand_platform_data *pdata = pdev->dev.platform_data; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partition_info; + int num_partitions = 0; +#endif + + nand = kzalloc(sizeof(*nand), GFP_KERNEL); + if (!nand) { + dev_err(&pdev->dev, "Failed to allocate device structure.\n"); + return -ENOMEM; + } + + ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); + if (ret) + goto err_free; + ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, + &nand->bank_base); + if (ret) + goto err_iounmap_mmio; + + if (pdata && gpio_is_valid(pdata->busy_gpio)) { + ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request busy gpio %d: %d\n", + pdata->busy_gpio, ret); + goto err_iounmap_mem; + } + } + + mtd = &nand->mtd; + chip = &nand->chip; + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->name = "jz4740-nand"; + + chip->ecc.hwctl = jz_nand_hwctl; + chip->ecc.calculate = jz_nand_calculate_ecc_rs; + chip->ecc.correct = jz_nand_correct_ecc_rs; + chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; + chip->ecc.size = 512; + chip->ecc.bytes = 9; + + chip->ecc.read_page = jz_nand_read_page_hwecc_oob_first; + chip->ecc.write_page = jz_nand_write_page_hwecc; + + if (pdata) + chip->ecc.layout = pdata->ecc_layout; + + chip->chip_delay = 50; + chip->cmd_ctrl = jz_nand_cmd_ctrl; + + if (pdata && gpio_is_valid(pdata->busy_gpio)) + chip->dev_ready = jz_nand_dev_ready; + + chip->IO_ADDR_R = nand->bank_base; + chip->IO_ADDR_W = nand->bank_base; + + nand->pdata = pdata; + platform_set_drvdata(pdev, nand); + + writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; + } + + if (pdata && pdata->ident_callback) { + pdata->ident_callback(pdev, chip, &pdata->partitions, + &pdata->num_partitions); + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; + } + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + num_partitions = parse_mtd_partitions(mtd, part_probes, + &partition_info, 0); +#endif + if (num_partitions <= 0 && pdata) { + num_partitions = pdata->num_partitions; + partition_info = pdata->partitions; + } + + if (num_partitions > 0) + ret = add_mtd_partitions(mtd, partition_info, num_partitions); + else +#endif + ret = add_mtd_device(mtd); + + if (ret) { + dev_err(&pdev->dev, "Failed to add mtd device\n"); + goto err_nand_release; + } + + dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); + + return 0; + +err_nand_release: + nand_release(&nand->mtd); +err_gpio_free: + platform_set_drvdata(pdev, NULL); + gpio_free(pdata->busy_gpio); +err_iounmap_mem: + iounmap(nand->bank_base); +err_iounmap_mmio: + iounmap(nand->base); +err_free: + kfree(nand); + return ret; +} + +static int __devexit jz_nand_remove(struct platform_device *pdev) +{ + struct jz_nand *nand = platform_get_drvdata(pdev); + + nand_release(&nand->mtd); + + /* Deassert and disable all chips */ + writel(0, nand->base + JZ_REG_NAND_CTRL); + + iounmap(nand->bank_base); + release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); + iounmap(nand->base); + release_mem_region(nand->mem->start, resource_size(nand->mem)); + + platform_set_drvdata(pdev, NULL); + kfree(nand); + + return 0; +} + +struct platform_driver jz_nand_driver = { + .probe = jz_nand_probe, + .remove = __devexit_p(jz_nand_remove), + .driver = { + .name = "jz4740-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init jz_nand_init(void) +{ + return platform_driver_register(&jz_nand_driver); +} +module_init(jz_nand_init); + +static void __exit jz_nand_exit(void) +{ + platform_driver_unregister(&jz_nand_driver); +} +module_exit(jz_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); +MODULE_ALIAS("platform:jz4740-nand"); -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3] MTD: Nand: Add JZ4740 NAND driver 2010-07-17 12:15 ` [PATCH v3] " Lars-Peter Clausen @ 2010-07-18 16:54 ` Artem Bityutskiy 2010-07-18 17:02 ` Lars-Peter Clausen 0 siblings, 1 reply; 12+ messages in thread From: Artem Bityutskiy @ 2010-07-18 16:54 UTC (permalink / raw) To: Lars-Peter Clausen Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle On Sat, 2010-07-17 at 14:15 +0200, Lars-Peter Clausen wrote: > This patch adds support for the NAND controller on JZ4740 SoCs. > > Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> > Cc: David Woodhouse <dwmw2@infradead.org> > Cc: linux-mtd@lists.infradead.org > Do you expect this patch to go in via the MTD tree? I guess it might be better if it was MIPS tree? -- Best Regards, Artem Bityutskiy (Артём Битюцкий) ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3] MTD: Nand: Add JZ4740 NAND driver 2010-07-18 16:54 ` Artem Bityutskiy @ 2010-07-18 17:02 ` Lars-Peter Clausen 0 siblings, 0 replies; 12+ messages in thread From: Lars-Peter Clausen @ 2010-07-18 17:02 UTC (permalink / raw) To: dedekind1 Cc: linux-mips, linux-mtd, David Woodhouse, linux-kernel, Ralf Baechle Artem Bityutskiy wrote: > On Sat, 2010-07-17 at 14:15 +0200, Lars-Peter Clausen wrote: >> This patch adds support for the NAND controller on JZ4740 SoCs. >> >> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> >> Cc: David Woodhouse <dwmw2@infradead.org> >> Cc: linux-mtd@lists.infradead.org >> > > Do you expect this patch to go in via the MTD tree? I guess it might be > better if it was MIPS tree? Hi Yes, letting it go through the MIPS tree is the plan. - Lars ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip 2010-06-19 5:08 [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Lars-Peter Clausen 2010-06-19 5:08 ` [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver Lars-Peter Clausen @ 2010-06-20 9:26 ` Thomas Bogendoerfer 2010-06-21 2:56 ` Xiangfu Liu 1 sibling, 1 reply; 12+ messages in thread From: Thomas Bogendoerfer @ 2010-06-20 9:26 UTC (permalink / raw) To: Lars-Peter Clausen Cc: linux-mips, Paul Gortmaker, David Brownell, Mark Brown, Samuel Ortiz, alsa-devel, Alessandro Zummo, Greg Kroah-Hartman, linux-mmc, linux-kernel, Ralf Baechle, lm-sensors, linux-usb, linux-mtd, linux-fbdev, Anton Vorontsov, rtc-linux, Andrew Morton, David Woodhouse, Liam Girdwood On Sat, Jun 19, 2010 at 07:08:05AM +0200, Lars-Peter Clausen wrote: > This patch series adds support for the Ingenic JZ4740 System-on-a-Chip. great stuff. I have a JZ4730 based netbook, for which I started magling the provided sources quite some time ago, but I didn't reach the point of submitting patches... there are a lot of common stuff between JZ4730 and JZ4740 so IMHO it would be a good thing not to nail everthing to JZ4740 namewise. It might also a good idea to select something like arch/mips/jzrisc as base directory, put the factored out code there and add JZ4730/JZ4740 in either seperate files or directories. Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip 2010-06-20 9:26 ` [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Thomas Bogendoerfer @ 2010-06-21 2:56 ` Xiangfu Liu 0 siblings, 0 replies; 12+ messages in thread From: Xiangfu Liu @ 2010-06-21 2:56 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: linux-mips, Paul Gortmaker, Lars-Peter Clausen, Mark Brown, Samuel Ortiz, alsa-devel, Alessandro Zummo, Greg Kroah-Hartman, linux-mmc, linux-kernel, Ralf Baechle, lm-sensors, David Brownell, linux-mtd, linux-fbdev, Anton Vorontsov, rtc-linux, Andrew Morton, David Woodhouse, linux-usb, Liam Girdwood On 06/20/2010 05:26 PM, Thomas Bogendoerfer wrote: > great stuff. I have a JZ4730 based netbook, for which I started magling > the provided sources quite some time ago, but I didn't reach the > point of submitting patches... there are a lot of common stuff between > JZ4730 and JZ4740 so IMHO it would be a good thing not to nail > everthing to JZ4740 namewise. It might also a good idea to select > something like arch/mips/jzrisc as base directory, put the Hi Thomas I would advice "xburst" instead jzrisc. because the Ingenic call their cpu "XBurst" series. like: XBurst JZ4740, XBurst JZ4750 ... > factored out code there and add JZ4730/JZ4740 in either seperate > files or directories. -- Best Regards Xiangfu Liu http://www.openmobilefree.net ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2010-07-18 17:02 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-06-19 5:08 [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Lars-Peter Clausen 2010-06-19 5:08 ` [PATCH v2 17/26] MTD: Nand: Add JZ4740 NAND driver Lars-Peter Clausen 2010-07-08 6:06 ` Artem Bityutskiy 2010-07-08 13:20 ` Lars-Peter Clausen 2010-07-08 13:19 ` Artem Bityutskiy 2010-07-08 14:02 ` Lars-Peter Clausen 2010-07-08 14:14 ` Artem Bityutskiy 2010-07-17 12:15 ` [PATCH v3] " Lars-Peter Clausen 2010-07-18 16:54 ` Artem Bityutskiy 2010-07-18 17:02 ` Lars-Peter Clausen 2010-06-20 9:26 ` [PATCH v2 00/26] Add support for the Ingenic JZ4740 System-on-a-Chip Thomas Bogendoerfer 2010-06-21 2:56 ` Xiangfu Liu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).