| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/drivers/mtd/nand/cmx270-nand.c | 
 | 3 |  * | 
 | 4 |  *  Copyright (C) 2006 Compulab, Ltd. | 
 | 5 |  *  Mike Rapoport <mike@compulab.co.il> | 
 | 6 |  * | 
 | 7 |  *  Derived from drivers/mtd/nand/h1910.c | 
 | 8 |  *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de) | 
 | 9 |  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) | 
 | 10 |  * | 
 | 11 |  * | 
 | 12 |  * This program is free software; you can redistribute it and/or modify | 
 | 13 |  * it under the terms of the GNU General Public License version 2 as | 
 | 14 |  * published by the Free Software Foundation. | 
 | 15 |  * | 
 | 16 |  *  Overview: | 
 | 17 |  *   This is a device driver for the NAND flash device found on the | 
 | 18 |  *   CM-X270 board. | 
 | 19 |  */ | 
 | 20 |  | 
 | 21 | #include <linux/mtd/nand.h> | 
 | 22 | #include <linux/mtd/partitions.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 23 | #include <linux/slab.h> | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 24 | #include <linux/gpio.h> | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 25 |  | 
 | 26 | #include <asm/io.h> | 
 | 27 | #include <asm/irq.h> | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 28 | #include <asm/mach-types.h> | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 29 |  | 
| Eric Miao | b74d196 | 2009-01-20 10:31:55 +0800 | [diff] [blame] | 30 | #include <mach/pxa2xx-regs.h> | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 31 |  | 
 | 32 | #define GPIO_NAND_CS	(11) | 
 | 33 | #define GPIO_NAND_RB	(89) | 
 | 34 |  | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 35 | /* MTD structure for CM-X270 board */ | 
 | 36 | static struct mtd_info *cmx270_nand_mtd; | 
 | 37 |  | 
 | 38 | /* remaped IO address of the device */ | 
 | 39 | static void __iomem *cmx270_nand_io; | 
 | 40 |  | 
 | 41 | /* | 
 | 42 |  * Define static partitions for flash device | 
 | 43 |  */ | 
 | 44 | static struct mtd_partition partition_info[] = { | 
 | 45 | 	[0] = { | 
 | 46 | 		.name	= "cmx270-0", | 
 | 47 | 		.offset	= 0, | 
 | 48 | 		.size	= MTDPART_SIZ_FULL | 
 | 49 | 	} | 
 | 50 | }; | 
 | 51 | #define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) | 
 | 52 |  | 
 | 53 | const char *part_probes[] = { "cmdlinepart", NULL }; | 
 | 54 |  | 
 | 55 | static u_char cmx270_read_byte(struct mtd_info *mtd) | 
 | 56 | { | 
 | 57 | 	struct nand_chip *this = mtd->priv; | 
 | 58 |  | 
 | 59 | 	return (readl(this->IO_ADDR_R) >> 16); | 
 | 60 | } | 
 | 61 |  | 
 | 62 | static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) | 
 | 63 | { | 
 | 64 | 	int i; | 
 | 65 | 	struct nand_chip *this = mtd->priv; | 
 | 66 |  | 
 | 67 | 	for (i=0; i<len; i++) | 
 | 68 | 		writel((*buf++ << 16), this->IO_ADDR_W); | 
 | 69 | } | 
 | 70 |  | 
 | 71 | static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) | 
 | 72 | { | 
 | 73 | 	int i; | 
 | 74 | 	struct nand_chip *this = mtd->priv; | 
 | 75 |  | 
 | 76 | 	for (i=0; i<len; i++) | 
 | 77 | 		*buf++ = readl(this->IO_ADDR_R) >> 16; | 
 | 78 | } | 
 | 79 |  | 
 | 80 | static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) | 
 | 81 | { | 
 | 82 | 	int i; | 
 | 83 | 	struct nand_chip *this = mtd->priv; | 
 | 84 |  | 
 | 85 | 	for (i=0; i<len; i++) | 
 | 86 | 		if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16)) | 
 | 87 | 			return -EFAULT; | 
 | 88 |  | 
 | 89 | 	return 0; | 
 | 90 | } | 
 | 91 |  | 
 | 92 | static inline void nand_cs_on(void) | 
 | 93 | { | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 94 | 	gpio_set_value(GPIO_NAND_CS, 0); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 95 | } | 
 | 96 |  | 
 | 97 | static void nand_cs_off(void) | 
 | 98 | { | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 99 | 	dsb(); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 100 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 101 | 	gpio_set_value(GPIO_NAND_CS, 1); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 102 | } | 
 | 103 |  | 
 | 104 | /* | 
 | 105 |  *	hardware specific access to control-lines | 
 | 106 |  */ | 
 | 107 | static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, | 
 | 108 | 			     unsigned int ctrl) | 
 | 109 | { | 
 | 110 | 	struct nand_chip* this = mtd->priv; | 
 | 111 | 	unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; | 
 | 112 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 113 | 	dsb(); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 114 |  | 
 | 115 | 	if (ctrl & NAND_CTRL_CHANGE) { | 
 | 116 | 		if ( ctrl & NAND_ALE ) | 
 | 117 | 			nandaddr |=  (1 << 3); | 
 | 118 | 		else | 
 | 119 | 			nandaddr &= ~(1 << 3); | 
 | 120 | 		if ( ctrl & NAND_CLE ) | 
 | 121 | 			nandaddr |=  (1 << 2); | 
 | 122 | 		else | 
 | 123 | 			nandaddr &= ~(1 << 2); | 
 | 124 | 		if ( ctrl & NAND_NCE ) | 
 | 125 | 			nand_cs_on(); | 
 | 126 | 		else | 
 | 127 | 			nand_cs_off(); | 
 | 128 | 	} | 
 | 129 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 130 | 	dsb(); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 131 | 	this->IO_ADDR_W = (void __iomem*)nandaddr; | 
 | 132 | 	if (dat != NAND_CMD_NONE) | 
 | 133 | 		writel((dat << 16), this->IO_ADDR_W); | 
 | 134 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 135 | 	dsb(); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 136 | } | 
 | 137 |  | 
 | 138 | /* | 
 | 139 |  *	read device ready pin | 
 | 140 |  */ | 
 | 141 | static int cmx270_device_ready(struct mtd_info *mtd) | 
 | 142 | { | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 143 | 	dsb(); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 144 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 145 | 	return (gpio_get_value(GPIO_NAND_RB)); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 146 | } | 
 | 147 |  | 
 | 148 | /* | 
 | 149 |  * Main initialization routine | 
 | 150 |  */ | 
| Peter Huewe | 627df23 | 2009-06-11 02:23:33 +0200 | [diff] [blame] | 151 | static int __init cmx270_init(void) | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 152 | { | 
 | 153 | 	struct nand_chip *this; | 
 | 154 | 	const char *part_type; | 
 | 155 | 	struct mtd_partition *mtd_parts; | 
 | 156 | 	int mtd_parts_nb = 0; | 
 | 157 | 	int ret; | 
 | 158 |  | 
| Mike Rapoport | a7f3f03 | 2008-10-05 10:26:55 +0100 | [diff] [blame] | 159 | 	if (!(machine_is_armcore() && cpu_is_pxa27x())) | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 160 | 		return -ENODEV; | 
 | 161 |  | 
 | 162 | 	ret = gpio_request(GPIO_NAND_CS, "NAND CS"); | 
 | 163 | 	if (ret) { | 
 | 164 | 		pr_warning("CM-X270: failed to request NAND CS gpio\n"); | 
 | 165 | 		return ret; | 
 | 166 | 	} | 
 | 167 |  | 
 | 168 | 	gpio_direction_output(GPIO_NAND_CS, 1); | 
 | 169 |  | 
 | 170 | 	ret = gpio_request(GPIO_NAND_RB, "NAND R/B"); | 
 | 171 | 	if (ret) { | 
 | 172 | 		pr_warning("CM-X270: failed to request NAND R/B gpio\n"); | 
 | 173 | 		goto err_gpio_request; | 
 | 174 | 	} | 
 | 175 |  | 
 | 176 | 	gpio_direction_input(GPIO_NAND_RB); | 
 | 177 |  | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 178 | 	/* Allocate memory for MTD device structure and private data */ | 
 | 179 | 	cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) + | 
 | 180 | 				  sizeof(struct nand_chip), | 
 | 181 | 				  GFP_KERNEL); | 
 | 182 | 	if (!cmx270_nand_mtd) { | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 183 | 		pr_debug("Unable to allocate CM-X270 NAND MTD device structure.\n"); | 
 | 184 | 		ret = -ENOMEM; | 
 | 185 | 		goto err_kzalloc; | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 186 | 	} | 
 | 187 |  | 
 | 188 | 	cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); | 
 | 189 | 	if (!cmx270_nand_io) { | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 190 | 		pr_debug("Unable to ioremap NAND device\n"); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 191 | 		ret = -EINVAL; | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 192 | 		goto err_ioremap; | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 193 | 	} | 
 | 194 |  | 
 | 195 | 	/* Get pointer to private data */ | 
 | 196 | 	this = (struct nand_chip *)(&cmx270_nand_mtd[1]); | 
 | 197 |  | 
 | 198 | 	/* Link the private data with the MTD structure */ | 
 | 199 | 	cmx270_nand_mtd->owner = THIS_MODULE; | 
 | 200 | 	cmx270_nand_mtd->priv = this; | 
 | 201 |  | 
 | 202 | 	/* insert callbacks */ | 
 | 203 | 	this->IO_ADDR_R = cmx270_nand_io; | 
 | 204 | 	this->IO_ADDR_W = cmx270_nand_io; | 
 | 205 | 	this->cmd_ctrl = cmx270_hwcontrol; | 
 | 206 | 	this->dev_ready = cmx270_device_ready; | 
 | 207 |  | 
 | 208 | 	/* 15 us command delay time */ | 
 | 209 | 	this->chip_delay = 20; | 
 | 210 | 	this->ecc.mode = NAND_ECC_SOFT; | 
 | 211 |  | 
 | 212 | 	/* read/write functions */ | 
 | 213 | 	this->read_byte = cmx270_read_byte; | 
 | 214 | 	this->read_buf = cmx270_read_buf; | 
 | 215 | 	this->write_buf = cmx270_write_buf; | 
 | 216 | 	this->verify_buf = cmx270_verify_buf; | 
 | 217 |  | 
 | 218 | 	/* Scan to find existence of the device */ | 
 | 219 | 	if (nand_scan (cmx270_nand_mtd, 1)) { | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 220 | 		pr_notice("No NAND device\n"); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 221 | 		ret = -ENXIO; | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 222 | 		goto err_scan; | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 223 | 	} | 
 | 224 |  | 
 | 225 | #ifdef CONFIG_MTD_CMDLINE_PARTS | 
 | 226 | 	mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes, | 
 | 227 | 					    &mtd_parts, 0); | 
 | 228 | 	if (mtd_parts_nb > 0) | 
 | 229 | 		part_type = "command line"; | 
 | 230 | 	else | 
 | 231 | 		mtd_parts_nb = 0; | 
 | 232 | #endif | 
 | 233 | 	if (!mtd_parts_nb) { | 
 | 234 | 		mtd_parts = partition_info; | 
 | 235 | 		mtd_parts_nb = NUM_PARTITIONS; | 
 | 236 | 		part_type = "static"; | 
 | 237 | 	} | 
 | 238 |  | 
 | 239 | 	/* Register the partitions */ | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 240 | 	pr_notice("Using %s partition definition\n", part_type); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 241 | 	ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); | 
 | 242 | 	if (ret) | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 243 | 		goto err_scan; | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 244 |  | 
 | 245 | 	/* Return happy */ | 
 | 246 | 	return 0; | 
 | 247 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 248 | err_scan: | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 249 | 	iounmap(cmx270_nand_io); | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 250 | err_ioremap: | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 251 | 	kfree(cmx270_nand_mtd); | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 252 | err_kzalloc: | 
 | 253 | 	gpio_free(GPIO_NAND_RB); | 
 | 254 | err_gpio_request: | 
 | 255 | 	gpio_free(GPIO_NAND_CS); | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 256 |  | 
 | 257 | 	return ret; | 
 | 258 |  | 
 | 259 | } | 
 | 260 | module_init(cmx270_init); | 
 | 261 |  | 
 | 262 | /* | 
 | 263 |  * Clean up routine | 
 | 264 |  */ | 
| Peter Huewe | 627df23 | 2009-06-11 02:23:33 +0200 | [diff] [blame] | 265 | static void __exit cmx270_cleanup(void) | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 266 | { | 
 | 267 | 	/* Release resources, unregister device */ | 
 | 268 | 	nand_release(cmx270_nand_mtd); | 
 | 269 |  | 
| Mike Rapoport | 70eb33d | 2008-06-17 09:48:46 +0100 | [diff] [blame] | 270 | 	gpio_free(GPIO_NAND_RB); | 
 | 271 | 	gpio_free(GPIO_NAND_CS); | 
 | 272 |  | 
| Mike Rapoport | 54d33c4 | 2007-04-22 08:53:21 +0300 | [diff] [blame] | 273 | 	iounmap(cmx270_nand_io); | 
 | 274 |  | 
 | 275 | 	/* Free the MTD device structure */ | 
 | 276 | 	kfree (cmx270_nand_mtd); | 
 | 277 | } | 
 | 278 | module_exit(cmx270_cleanup); | 
 | 279 |  | 
 | 280 | MODULE_LICENSE("GPL"); | 
 | 281 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); | 
 | 282 | MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module"); |