📄 omap-nand-flash.c
字号:
/* * drivers/mtd/nand/omap-nand-flash.c * * Copyright (c) 2004 Texas Instruments * Jian Zhang <jzhang@ti.com> * Copyright (c) 2004 David Brownell * * Derived from drivers/mtd/autcpu12.c * * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Overview: * This is a device driver for the NAND flash device found on the * TI H4/H3/H2 boards.This supports 16-bit 32MiB Samsung k9f5616 chip on H3/H2 * and 16-bit 64MiB Samsung k9k1216 on H4 board. * * History: * * 2004 Jian Zhang Initial release. * 2004-11-02 Syed Mohammed Khasim Modified the driver to add support for OMAP 2420 NAND controller. * */#include <linux/slab.h>#include <linux/init.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/partitions.h>#include <asm/io.h>#include <asm/arch/hardware.h>#include <asm/arch/gpio.h>#include <asm/arch/mux.h>#include <asm/sizes.h>#include <asm/mach-types.h>#ifdef CONFIG_ARCH_OMAP24XX#include <asm/arch/platform.h>#endif#define H3_NAND_RB_GPIO_PIN 10#define H2_NAND_RB_GPIO_PIN 10#ifdef CONFIG_ARCH_OMAP24XX/* Function prototype declaration */static void omap_nandwpoff(void);static void omap_nandwpon(void);/* GPMC Base address */volatile unsigned long GPMC_BASE;#endif/* * MTD structure for H3 board */static struct mtd_info *omap_nand_mtd = NULL;static unsigned long omap_nand_flash_base;/* * Define partitions for flash devices */#ifdef CONFIG_MTD_PARTITIONSstatic struct mtd_partition static_partition[] = { {.name = "Booting Image", .offset = 0,#ifdef CONFIG_ARCH_OMAP24XX .size = 256 * 1024,#endif#ifdef CONFIG_ARCH_OMAP16XX .size = 64 * 1024,#endif .mask_flags = MTD_WRITEABLE /* force read-only */ }, {.name = "U-Boot", .offset = MTDPART_OFS_APPEND, .size = 256 * 1024, .mask_flags = MTD_WRITEABLE /* force read-only */ }, {.name = "U-Boot Environment", .offset = MTDPART_OFS_APPEND, .size = 192 * 1024}, {.name = "Kernel", .offset = MTDPART_OFS_APPEND, .size = 2 * SZ_1M}, {.name = "File System", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_APPEND, },};#define NUM_PARTITIONS 5#endif/* H2/H3 maps two address LSBs to CLE and ALE; MSBs make CS_2B */#define MASK_CLE 0x02#define MASK_ALE 0x04const char *part_probes[] = { "cmdlinepart", NULL, };/* * hardware specific access to control-lines*/static void omap_nand_hwcontrol(struct mtd_info *mtd, int cmd){ register struct nand_chip *this = mtd->priv;#ifdef CONFIG_ARCH_OMAP24XX/* * Point the IO_ADDR to DATA and ADDRESS registers instead of chip address */ switch (cmd) { case NAND_CTL_SETCLE: this->IO_ADDR_W = GPMC_BASE + OFFSET_NAND_COMMAND_0; this->IO_ADDR_R = GPMC_BASE + OFFSET_NAND_DATA_0; break; case NAND_CTL_SETALE: this->IO_ADDR_W = GPMC_BASE + OFFSET_NAND_ADDRESS_0; this->IO_ADDR_R = GPMC_BASE + OFFSET_NAND_DATA_0; break; case NAND_CTL_CLRCLE: this->IO_ADDR_W = GPMC_BASE + OFFSET_NAND_DATA_0; this->IO_ADDR_R = GPMC_BASE + OFFSET_NAND_DATA_0; break; case NAND_CTL_CLRALE: this->IO_ADDR_W = GPMC_BASE + OFFSET_NAND_DATA_0; this->IO_ADDR_R = GPMC_BASE + OFFSET_NAND_DATA_0; break; }#endif#ifdef CONFIG_ARCH_OMAP16XX this->IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); switch (cmd) { case NAND_CTL_SETCLE: this->IO_ADDR_W |= MASK_CLE; break; case NAND_CTL_SETALE: this->IO_ADDR_W |= MASK_ALE; break; }#endif}#ifdef CONFIG_ARCH_OMAP24XX/* * omap_nandwpoff - to switch off the write protect * */static void omap_nandwpoff(){ unsigned long config = readw(GPMC_BASE + OFFSET_CONFIG); config |= NAND_WP_BIT; writew(config, (GPMC_BASE + OFFSET_CONFIG));}/* * omap_nandwpon - to switch on the write protect * */static void omap_nandwpon(){ unsigned long config = readw(GPMC_BASE + OFFSET_CONFIG); config &= ~(NAND_WP_BIT); writew(config, (GPMC_BASE + OFFSET_CONFIG));}/* * omap_nand_write_word - write one word to the NAND controller * @mtd: MTD device structure * @word: data word to write * */static void omap_nand_write_word(struct mtd_info *mtd, u16 word){ struct nand_chip *this = mtd->priv; writew(word, this->IO_ADDR_W);}/* * omap_nand_read_word - read one word from the NAND controller * @mtd: MTD device structure * */static u16 omap_nand_read_word(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; return readw(this->IO_ADDR_R);}/* * omap_nand_write_byte16 - write one byte endianess aware to the NAND controller * @mtd: MTD device structure * @byte: pointer to data byte to write * */static void omap_nand_write_byte16(struct mtd_info *mtd, u_char byte){ struct nand_chip *this = mtd->priv; writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);}/* * omap_nand_read_byte16 - read one byte endianess aware from the NAND controller * @mtd: MTD device structure * */static u_char omap_nand_read_byte16(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));}/* * omap_nand_write_buf - write buffer to NAND controller * @mtd: MTD device structure * @buf: data buffer * @len: number of bytes to write * */static void omap_nand_write_buf(struct mtd_info *mtd, const u_char * buf, int len){ int i, j; struct nand_chip *this = mtd->priv; union data_tx { u16 data_16; u_char data[2]; } write_data; for (i = 0, j = 0; i < (len / 2); i++) { write_data.data[0] = buf[j++]; write_data.data[1] = buf[j++]; writew(le16_to_cpu((u16) write_data.data_16), this->IO_ADDR_W); while (GPMC_BUF_EMPTY == (readw(GPMC_BASE + OFFSET_STATUS) & GPMC_BUF_FULL)) ; }}/* * omap_nand_read_buf - read data from NAND controller into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read * */static void omap_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len){ int i, j; union data_rx { u16 data_16; u_char data[2]; } read_data; struct nand_chip *this = mtd->priv; for (i = 0, j = 0; i < (len / 2); i++) { read_data.data_16 = cpu_to_le16(readw(this->IO_ADDR_R)); buf[j++] = read_data.data[0]; buf[j++] = read_data.data[1]; }}/* * omap_nand_verify_buf - Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare * */static int omap_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, int len){ int i, j; struct nand_chip *this = mtd->priv; union data_rx { u16 data_16; u_char data[2]; } read_data; for (i = 0, j = 0; i < len / 2; i++) { read_data.data[0] = buf[j++]; read_data.data[1] = buf[j++]; if (read_data.data_16 != cpu_to_le16(readw(this->IO_ADDR_R))) return -EFAULT; } return 0;}#endif#ifdef CONFIG_ARCH_OMAP16XX/* * chip busy R/B detection */static int omap_nand_ready(struct mtd_info *mtd){ return omap_get_gpio_datain(H3_NAND_RB_GPIO_PIN);}#endif/* Scan to find existance of the device at omap_nand_flash_base. This also allocates oob and data internal buffers */static int probe_nand_chip(void){ struct nand_chip *this; this = (struct nand_chip *)(&omap_nand_mtd[1]); /* Initialize structures */ memset((char *)this, 0, sizeof(struct nand_chip)); this->IO_ADDR_R = omap_nand_flash_base; this->IO_ADDR_W = omap_nand_flash_base; this->options = NAND_SAMSUNG_LP_OPTIONS; this->hwcontrol = omap_nand_hwcontrol;#ifdef CONFIG_ARCH_OMAP24XX this->write_word = omap_nand_write_word; this->read_word = omap_nand_read_word; this->write_byte = omap_nand_write_byte16; this->read_byte = omap_nand_read_byte16; this->write_buf = omap_nand_write_buf; this->read_buf = omap_nand_read_buf; this->verify_buf = omap_nand_verify_buf;#endif this->eccmode = NAND_ECC_SOFT; /* try 16-bit chip first */ this->options |= NAND_BUSWIDTH_16; if (nand_scan(omap_nand_mtd, 1)) { if (machine_is_omap_h3()) return -ENXIO; /* then try 8-bit chip for H2 */ memset((char *)this, 0, sizeof(struct nand_chip)); this->IO_ADDR_R = omap_nand_flash_base; this->IO_ADDR_W = omap_nand_flash_base; this->options = NAND_SAMSUNG_LP_OPTIONS; this->hwcontrol = omap_nand_hwcontrol; this->eccmode = NAND_ECC_SOFT; if (nand_scan(omap_nand_mtd, 1)) { return -ENXIO; } } return 0;}/* * Main initialization routine */int __init omap_nand_init(void){ struct nand_chip *this; struct mtd_partition *dynamic_partition = 0; int err = 0;#ifdef CONFIG_ARCH_OMAP24XX unsigned long config1_0; GPMC_BASE = (unsigned long)ioremap_nocache(OMAP2420_GPMC_BASE, 0x23C); /* * Enable the Read and Write pin monitoring in NAND controller * this is like R/B in 1710 */ config1_0 = *(unsigned long *)(GPMC_BASE + OFFSET_CONFIG1_0); config1_0 |= WR_RD_PIN_MONITORING; *(unsigned long *)(GPMC_BASE + OFFSET_CONFIG1_0) = config1_0; /* Write protect off */ omap_nandwpoff();#endif /* Allocate memory for MTD device structure and private data */ omap_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!omap_nand_mtd) { printk(KERN_WARNING "Unable to allocate NAND MTD device structure.\n"); err = -ENOMEM; goto out; } /* Get pointer to private data */ this = (struct nand_chip *)(&omap_nand_mtd[1]); /* Initialize structures */ memset((char *)omap_nand_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip)); /* Link the private data with the MTD structure */ omap_nand_mtd->priv = this;#ifdef CONFIG_ARCH_OMAP24XX this->chip_delay = 0;#endif#ifdef CONFIG_ARCH_OMAP16XX if (machine_is_omap_h3()) { if (omap_request_gpio(H3_NAND_RB_GPIO_PIN) != 0) { printk(KERN_ERR "NAND: Unable to get GPIO pin for R/B, use delay\n"); /* 15 us command delay time */ this->chip_delay = 15; } else { /* GPIO10 for input. it is in GPIO1 module */ omap_set_gpio_direction(H3_NAND_RB_GPIO_PIN, 1); /* GPIO10 Func_MUX_CTRL reg bit 29:27, Configure V2 to mode1 as GPIO */ /* GPIO10 pullup/down register, Enable pullup on GPIO10 */ omap_cfg_reg(V2_1710_GPIO10); this->dev_ready = omap_nand_ready; } } if (machine_is_omap_h2()) { /* FIXME On H2, R/B is also tied to GPIO10. Not work yet */ /* it will wrongly mark block bad when erasing */ this->chip_delay = 15; omap_cfg_reg(L3_1610_FLASH_CS2B_OE); omap_cfg_reg(M8_1610_FLASH_CS2B_WE); }#endif /* try the first address */ omap_nand_flash_base = (unsigned long)ioremap(OMAP_NAND_FLASH_START1, SZ_4K); if (probe_nand_chip()) { /* try the second address */ iounmap((void *)omap_nand_flash_base); omap_nand_flash_base = (unsigned long)ioremap(OMAP_NAND_FLASH_START2, SZ_4K); if (probe_nand_chip()) { iounmap((void *)omap_nand_flash_base); err = -ENXIO; goto out_mtd; } } /* * The NAND chip on H4 is 64 M So the case is added for handling 64M */ /* Register the partitions */ switch (omap_nand_mtd->size) { case SZ_64M:#ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(omap_nand_mtd, part_probes, &dynamic_partition, 0); if (err > 0) err = add_mtd_partitions(omap_nand_mtd, dynamic_partition, err); else err = add_mtd_partitions(omap_nand_mtd, static_partition, NUM_PARTITIONS);#else err = add_mtd_device(omap_nand_mtd);#endif if (err) goto out_buf; break; case SZ_32M:#ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(omap_nand_mtd, part_probes, &dynamic_partition, 0); if (err > 0) err = add_mtd_partitions(omap_nand_mtd, dynamic_partition, err); else err = add_mtd_partitions(omap_nand_mtd, static_partition, NUM_PARTITIONS);#else err = add_mtd_device(omap_nand_mtd);#endif if (err) goto out_buf; break; default:{ printk(KERN_WARNING "Unsupported Nand device\n"); err = -ENXIO; goto out_buf; } } goto out; out_buf: nand_release(omap_nand_mtd); if (this->dev_ready) {#ifdef CONFIG_ARCH_OMAP16XX if (machine_is_omap_h3()) omap_free_gpio(H3_NAND_RB_GPIO_PIN);#endif } iounmap((void *)omap_nand_flash_base); out_mtd: kfree(omap_nand_mtd); /* Un map the Physical addresses allocated for GPMC */#ifdef CONFIG_ARCH_OMAP24XX omap_nandwpon(); iounmap((void *)GPMC_BASE);#endif out: return err;}module_init(omap_nand_init);/* * Clean up routine */static void __exit omap_nand_cleanup(void){ struct nand_chip *this = omap_nand_mtd->priv; if (this->dev_ready) {#ifdef CONFIG_ARCH_OMAP16XX if (machine_is_omap_h3()) omap_free_gpio(H3_NAND_RB_GPIO_PIN);#endif } /* nand_release frees MTD partitions, MTD structure and nand internal buffers */ nand_release(omap_nand_mtd); kfree(omap_nand_mtd);#ifdef CONFIG_ARCH_OMAP24XX omap_nandwpon(); iounmap((void *)GPMC_BASE);#endif iounmap((void *)omap_nand_flash_base);}module_exit(omap_nand_cleanup);MODULE_LICENSE("GPL");MODULE_AUTHOR("Jian Zhang <jzhang@ti.com>");MODULE_DESCRIPTION("Glue layer for 16-bit NAND flash on H4/H3 board");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -