fsl_upm.c 8.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Freescale UPM NAND driver.
 *
 * Copyright © 2007-2008  MontaVista Software, Inc.
 *
 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * 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.
 */

#include <linux/kernel.h>
#include <linux/module.h>
16
#include <linux/delay.h>
17 18 19 20 21 22 23 24 25
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/io.h>
#include <asm/fsl_lbc.h>

26 27 28 29
#define FSL_UPM_WAIT_RUN_PATTERN  0x1
#define FSL_UPM_WAIT_WRITE_BYTE   0x2
#define FSL_UPM_WAIT_WRITE_BUFFER 0x4

30 31 32 33 34 35 36 37 38 39 40 41 42
struct fsl_upm_nand {
	struct device *dev;
	struct mtd_info mtd;
	struct nand_chip chip;
	int last_ctrl;
#ifdef CONFIG_MTD_PARTITIONS
	struct mtd_partition *parts;
#endif

	struct fsl_upm upm;
	uint8_t upm_addr_offset;
	uint8_t upm_cmd_offset;
	void __iomem *io_base;
43 44 45 46
	int rnb_gpio[NAND_MAX_CHIPS];
	uint32_t mchip_offsets[NAND_MAX_CHIPS];
	uint32_t mchip_count;
	uint32_t mchip_number;
47
	int chip_delay;
48
	uint32_t wait_flags;
49 50 51 52 53 54 55 56
};

#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)

static int fun_chip_ready(struct mtd_info *mtd)
{
	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);

57
	if (gpio_get_value(fun->rnb_gpio[fun->mchip_number]))
58 59 60 61 62 63 64 65
		return 1;

	dev_vdbg(fun->dev, "busy\n");
	return 0;
}

static void fun_wait_rnb(struct fsl_upm_nand *fun)
{
66 67
	if (fun->rnb_gpio[fun->mchip_number] >= 0) {
		int cnt = 1000000;
68 69 70

		while (--cnt && !fun_chip_ready(&fun->mtd))
			cpu_relax();
71 72 73 74
		if (!cnt)
			dev_err(fun->dev, "tired waiting for RNB\n");
	} else {
		ndelay(100);
75 76 77 78 79
	}
}

static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
80
	struct nand_chip *chip = mtd->priv;
81
	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
82
	u32 mar;
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

	if (!(ctrl & fun->last_ctrl)) {
		fsl_upm_end_pattern(&fun->upm);

		if (cmd == NAND_CMD_NONE)
			return;

		fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
	}

	if (ctrl & NAND_CTRL_CHANGE) {
		if (ctrl & NAND_ALE)
			fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
		else if (ctrl & NAND_CLE)
			fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
	}

100 101 102
	mar = (cmd << (32 - fun->upm.width)) |
		fun->mchip_offsets[fun->mchip_number];
	fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar);
103

104 105
	if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
		fun_wait_rnb(fun);
106 107
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
static void fun_select_chip(struct mtd_info *mtd, int mchip_nr)
{
	struct nand_chip *chip = mtd->priv;
	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);

	if (mchip_nr == -1) {
		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
	} else if (mchip_nr >= 0) {
		fun->mchip_number = mchip_nr;
		chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
		chip->IO_ADDR_W = chip->IO_ADDR_R;
	} else {
		BUG();
	}
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
static uint8_t fun_read_byte(struct mtd_info *mtd)
{
	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);

	return in_8(fun->chip.IO_ADDR_R);
}

static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
	int i;

	for (i = 0; i < len; i++)
		buf[i] = in_8(fun->chip.IO_ADDR_R);
}

static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
	int i;

	for (i = 0; i < len; i++) {
		out_8(fun->chip.IO_ADDR_W, buf[i]);
147 148
		if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
			fun_wait_rnb(fun);
149
	}
150 151
	if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
		fun_wait_rnb(fun);
152 153
}

154 155 156
static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
				   const struct device_node *upm_np,
				   const struct resource *io_res)
157 158
{
	int ret;
159
	struct device_node *flash_np;
160 161 162 163 164 165 166
#ifdef CONFIG_MTD_PARTITIONS
	static const char *part_types[] = { "cmdlinepart", NULL, };
#endif

	fun->chip.IO_ADDR_R = fun->io_base;
	fun->chip.IO_ADDR_W = fun->io_base;
	fun->chip.cmd_ctrl = fun_cmd_ctrl;
167
	fun->chip.chip_delay = fun->chip_delay;
168 169 170 171
	fun->chip.read_byte = fun_read_byte;
	fun->chip.read_buf = fun_read_buf;
	fun->chip.write_buf = fun_write_buf;
	fun->chip.ecc.mode = NAND_ECC_SOFT;
172 173
	if (fun->mchip_count > 1)
		fun->chip.select_chip = fun_select_chip;
174

175
	if (fun->rnb_gpio[0] >= 0)
176 177 178 179 180
		fun->chip.dev_ready = fun_chip_ready;

	fun->mtd.priv = &fun->chip;
	fun->mtd.owner = THIS_MODULE;

181 182 183 184 185 186 187 188 189 190 191
	flash_np = of_get_next_child(upm_np, NULL);
	if (!flash_np)
		return -ENODEV;

	fun->mtd.name = kasprintf(GFP_KERNEL, "%x.%s", io_res->start,
				  flash_np->name);
	if (!fun->mtd.name) {
		ret = -ENOMEM;
		goto err;
	}

192
	ret = nand_scan(&fun->mtd, fun->mchip_count);
193
	if (ret)
194
		goto err;
195 196 197

#ifdef CONFIG_MTD_PARTITIONS
	ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0);
198 199

#ifdef CONFIG_MTD_OF_PARTS
200 201 202 203 204
	if (ret == 0) {
		ret = of_mtd_parse_partitions(fun->dev, flash_np, &fun->parts);
		if (ret < 0)
			goto err;
	}
205
#endif
206
	if (ret > 0)
207 208
		ret = add_mtd_partitions(&fun->mtd, fun->parts, ret);
	else
209
#endif
210 211 212 213
		ret = add_mtd_device(&fun->mtd);
err:
	of_node_put(flash_np);
	return ret;
214 215 216 217 218 219 220 221
}

static int __devinit fun_probe(struct of_device *ofdev,
			       const struct of_device_id *ofid)
{
	struct fsl_upm_nand *fun;
	struct resource io_res;
	const uint32_t *prop;
222
	int rnb_gpio;
223 224
	int ret;
	int size;
225
	int i;
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

	fun = kzalloc(sizeof(*fun), GFP_KERNEL);
	if (!fun)
		return -ENOMEM;

	ret = of_address_to_resource(ofdev->node, 0, &io_res);
	if (ret) {
		dev_err(&ofdev->dev, "can't get IO base\n");
		goto err1;
	}

	ret = fsl_upm_find(io_res.start, &fun->upm);
	if (ret) {
		dev_err(&ofdev->dev, "can't find UPM\n");
		goto err1;
	}

	prop = of_get_property(ofdev->node, "fsl,upm-addr-offset", &size);
	if (!prop || size != sizeof(uint32_t)) {
		dev_err(&ofdev->dev, "can't get UPM address offset\n");
		ret = -EINVAL;
247
		goto err1;
248 249 250 251 252 253 254
	}
	fun->upm_addr_offset = *prop;

	prop = of_get_property(ofdev->node, "fsl,upm-cmd-offset", &size);
	if (!prop || size != sizeof(uint32_t)) {
		dev_err(&ofdev->dev, "can't get UPM command offset\n");
		ret = -EINVAL;
255
		goto err1;
256 257 258
	}
	fun->upm_cmd_offset = *prop;

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
	prop = of_get_property(ofdev->node,
			       "fsl,upm-addr-line-cs-offsets", &size);
	if (prop && (size / sizeof(uint32_t)) > 0) {
		fun->mchip_count = size / sizeof(uint32_t);
		if (fun->mchip_count >= NAND_MAX_CHIPS) {
			dev_err(&ofdev->dev, "too much multiple chips\n");
			goto err1;
		}
		for (i = 0; i < fun->mchip_count; i++)
			fun->mchip_offsets[i] = prop[i];
	} else {
		fun->mchip_count = 1;
	}

	for (i = 0; i < fun->mchip_count; i++) {
		fun->rnb_gpio[i] = -1;
		rnb_gpio = of_get_gpio(ofdev->node, i);
		if (rnb_gpio >= 0) {
			ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev));
			if (ret) {
				dev_err(&ofdev->dev,
					"can't request RNB gpio #%d\n", i);
				goto err2;
			}
			gpio_direction_input(rnb_gpio);
			fun->rnb_gpio[i] = rnb_gpio;
		} else if (rnb_gpio == -EINVAL) {
			dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
287 288 289 290
			goto err2;
		}
	}

291 292 293 294 295 296
	prop = of_get_property(ofdev->node, "chip-delay", NULL);
	if (prop)
		fun->chip_delay = *prop;
	else
		fun->chip_delay = 50;

297 298 299 300 301 302 303
	prop = of_get_property(ofdev->node, "fsl,upm-wait-flags", &size);
	if (prop && size == sizeof(uint32_t))
		fun->wait_flags = *prop;
	else
		fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
				  FSL_UPM_WAIT_WRITE_BYTE;

304
	fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
305
					    io_res.end - io_res.start + 1);
306 307 308 309 310 311 312 313
	if (!fun->io_base) {
		ret = -ENOMEM;
		goto err2;
	}

	fun->dev = &ofdev->dev;
	fun->last_ctrl = NAND_CLE;

314
	ret = fun_chip_init(fun, ofdev->node, &io_res);
315 316 317 318 319 320 321
	if (ret)
		goto err2;

	dev_set_drvdata(&ofdev->dev, fun);

	return 0;
err2:
322 323 324 325 326
	for (i = 0; i < fun->mchip_count; i++) {
		if (fun->rnb_gpio[i] < 0)
			break;
		gpio_free(fun->rnb_gpio[i]);
	}
327 328 329 330 331 332 333 334 335
err1:
	kfree(fun);

	return ret;
}

static int __devexit fun_remove(struct of_device *ofdev)
{
	struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
336
	int i;
337 338

	nand_release(&fun->mtd);
339
	kfree(fun->mtd.name);
340

341 342 343 344 345
	for (i = 0; i < fun->mchip_count; i++) {
		if (fun->rnb_gpio[i] < 0)
			break;
		gpio_free(fun->rnb_gpio[i]);
	}
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

	kfree(fun);

	return 0;
}

static struct of_device_id of_fun_match[] = {
	{ .compatible = "fsl,upm-nand" },
	{},
};
MODULE_DEVICE_TABLE(of, of_fun_match);

static struct of_platform_driver of_fun_driver = {
	.name		= "fsl,upm-nand",
	.match_table	= of_fun_match,
	.probe		= fun_probe,
	.remove		= __devexit_p(fun_remove),
};

static int __init fun_module_init(void)
{
	return of_register_platform_driver(&of_fun_driver);
}
module_init(fun_module_init);

static void __exit fun_module_exit(void)
{
	of_unregister_platform_driver(&of_fun_driver);
}
module_exit(fun_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
		   "LocalBus User-Programmable Machine");