#include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <asm/arch/gpio.h>
#include <asm/io.h> #include <asm/irq.h>
#include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> #include <asm/mach-types.h>
#define GPIO_NAND_CS (82) #define GPIO_NAND_RB (83)
/* MTD structure for CM-X270 board */ static struct mtd_info *up270_nand_mtd;
/* remaped IO address of the device */ static void __iomem *up270_nand_io;
/* * Define static partitions for flash device */ static struct mtd_partition partition_info[] = { [0] = { .name = "UP270-nand", .offset = 0, .size = 0x4000000, } }; #define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
const char *part_probes[] = { "cmdlinepart", NULL };
static u_char up270_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv;
return readl(this->IO_ADDR_R); }
static void up270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++) writel(*buf++, this->IO_ADDR_W); }
static void up270_read_buf(struct mtd_info *mtd, u_char *buf, int len) { int i; struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++) *buf++ = readl(this->IO_ADDR_R); }
static int up270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++) if (buf[i] != (u_char)(readl(this->IO_ADDR_R))) return -EFAULT;
return 0; }
static inline void nand_cs_on(void) { gpio_set_value(GPIO_NAND_CS, 0); }
static void nand_cs_off(void) { dsb(); gpio_set_value(GPIO_NAND_CS, 1); }
/* * hardware specific access to control-lines */ static void up270_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct nand_chip* this = mtd->priv; unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
dsb();
if (ctrl & NAND_CTRL_CHANGE) { if ( ctrl & NAND_ALE ) nandaddr |= (1 << 2); else nandaddr &= ~(1 << 2); if ( ctrl & NAND_CLE ) nandaddr |= (1 << 3); else nandaddr &= ~(1 << 3); if ( ctrl & NAND_NCE ) nand_cs_on(); else nand_cs_off(); }
dsb();
this->IO_ADDR_W = (void __iomem*)nandaddr; if (dat != NAND_CMD_NONE) writel(dat, this->IO_ADDR_W);
dsb(); }
/* * read device ready pin */ static int up270_device_ready(struct mtd_info *mtd) { dsb(); return (gpio_get_value(GPIO_NAND_RB)); }
/* * Main initialization routine */ static int up270_init(void) { struct nand_chip *this; const char *part_type; struct mtd_partition *mtd_parts; int mtd_parts_nb = 0; int ret;
ret = gpio_request(GPIO_NAND_CS, "NAND CS"); if (ret) { printk("UP270: failed to request NAND CS gpio\n"); return ret; }
gpio_direction_output(GPIO_NAND_CS, 1);
ret = gpio_request(GPIO_NAND_RB, "NAND R/B"); if (ret) { printk("UP270: failed to request NAND R/B gpio\n"); goto err_gpio_request; }
gpio_direction_input(GPIO_NAND_RB);
/* Allocate memory for MTD device structure and private data */ up270_nand_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!up270_nand_mtd) { printk("Unable to allocate UP270 NAND MTD device structure.\n"); ret = -ENOMEM; goto err_kzalloc; }
up270_nand_io = ioremap(0x04000000, 12); if (!up270_nand_io) { printk("Unable to ioremap NAND device\n"); ret = -EINVAL; goto err_ioremap; }
/* Get pointer to private data */ this = (struct nand_chip *)(&up270_nand_mtd[1]);
/* Link the private data with the MTD structure */ up270_nand_mtd->owner = THIS_MODULE; up270_nand_mtd->priv = this;
/* insert callbacks */ this->IO_ADDR_R = up270_nand_io; this->IO_ADDR_W = up270_nand_io; this->cmd_ctrl = up270_hwcontrol; this->dev_ready = up270_device_ready;
/* 15 us command delay time */ this->chip_delay = 20; this->ecc.mode = NAND_ECC_SOFT;
/* read/write functions */ this->read_byte = up270_read_byte; this->read_buf = up270_read_buf; this->write_buf = up270_write_buf; this->verify_buf = up270_verify_buf;
/* Scan to find existence of the device */ if (nand_scan (up270_nand_mtd, 1)) { printk(KERN_NOTICE "No NAND device\n"); ret = -ENXIO; goto err_scan; }
#ifdef CONFIG_MTD_CMDLINE_PARTS mtd_parts_nb = parse_mtd_partitions(up270_nand_mtd, part_probes, &mtd_parts, 0); if (mtd_parts_nb > 0) part_type = "command line"; else mtd_parts_nb = 0; #endif if (!mtd_parts_nb) { mtd_parts = partition_info; mtd_parts_nb = NUM_PARTITIONS; part_type = "static"; }
/* Register the partitions */ printk(KERN_NOTICE "Using %s partition definition\n", part_type); ret = add_mtd_partitions(up270_nand_mtd, mtd_parts, mtd_parts_nb); if (ret) goto err_scan;
/* Return happy */ return 0;
err_scan: iounmap(up270_nand_io); err_ioremap: kfree(up270_nand_mtd); err_kzalloc: gpio_free(GPIO_NAND_RB); err_gpio_request: gpio_free(GPIO_NAND_CS);
return ret;
} module_init(up270_init);
/* * Clean up routine */ static void up270_cleanup(void) { /* Release resources, unregister device */ nand_release(up270_nand_mtd);
gpio_free(GPIO_NAND_RB); gpio_free(GPIO_NAND_CS);
iounmap(up270_nand_io);
/* Free the MTD device structure */ kfree (up270_nand_mtd); } module_exit(up270_cleanup);
MODULE_LICENSE("GPL");
|