sharp.c 13.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6
/*
 * MTD chip driver for pre-CFI Sharp flash chips
 *
 * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
 *           2000,2001 Lineo, Inc.
 *
7
 * $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $
L
Linus Torvalds 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 * Devices supported:
 *   LH28F016SCT Symmetrical block flash memory, 2Mx8
 *   LH28F008SCT Symmetrical block flash memory, 1Mx8
 *
 * Documentation:
 *   http://www.sharpmeg.com/datasheets/memic/flashcmp/
 *   http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf
 *   016sctl9.pdf
 *
 * Limitations:
 *   This driver only supports 4x1 arrangement of chips.
 *   Not tested on anything but PowerPC.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
#include <linux/delay.h>
#include <linux/init.h>
34
#include <linux/slab.h>
L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

#define CMD_RESET		0xffffffff
#define CMD_READ_ID		0x90909090
#define CMD_READ_STATUS		0x70707070
#define CMD_CLEAR_STATUS	0x50505050
#define CMD_BLOCK_ERASE_1	0x20202020
#define CMD_BLOCK_ERASE_2	0xd0d0d0d0
#define CMD_BYTE_WRITE		0x40404040
#define CMD_SUSPEND		0xb0b0b0b0
#define CMD_RESUME		0xd0d0d0d0
#define CMD_SET_BLOCK_LOCK_1	0x60606060
#define CMD_SET_BLOCK_LOCK_2	0x01010101
#define CMD_SET_MASTER_LOCK_1	0x60606060
#define CMD_SET_MASTER_LOCK_2	0xf1f1f1f1
#define CMD_CLEAR_BLOCK_LOCKS_1	0x60606060
#define CMD_CLEAR_BLOCK_LOCKS_2	0xd0d0d0d0

#define SR_READY		0x80808080 // 1 = ready
#define SR_ERASE_SUSPEND	0x40404040 // 1 = block erase suspended
#define SR_ERROR_ERASE		0x20202020 // 1 = error in block erase or clear lock bits
#define SR_ERROR_WRITE		0x10101010 // 1 = error in byte write or set lock bit
#define	SR_VPP			0x08080808 // 1 = Vpp is low
#define SR_WRITE_SUSPEND	0x04040404 // 1 = byte write suspended
#define SR_PROTECT		0x02020202 // 1 = lock bit set
#define SR_RESERVED		0x01010101

#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT)

/* Configuration options */

#undef AUTOUNLOCK  /* automatically unlocks blocks before erasing */

struct mtd_info *sharp_probe(struct map_info *);

static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd);

static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
	size_t *retlen, u_char *buf);
static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len,
	size_t *retlen, const u_char *buf);
static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr);
static void sharp_sync(struct mtd_info *mtd);
static int sharp_suspend(struct mtd_info *mtd);
static void sharp_resume(struct mtd_info *mtd);
static void sharp_destroy(struct mtd_info *mtd);

static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
	unsigned long adr, __u32 datum);
static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
	unsigned long adr);
#ifdef AUTOUNLOCK
static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
	unsigned long adr);
#endif


struct sharp_info{
	struct flchip *chip;
	int bogus;
	int chipshift;
	int numchips;
	struct flchip chips[1];
};

struct mtd_info *sharp_probe(struct map_info *map);
static void sharp_destroy(struct mtd_info *mtd);

static struct mtd_chip_driver sharp_chipdrv = {
	.probe		= sharp_probe,
	.destroy	= sharp_destroy,
	.name		= "sharp",
	.module		= THIS_MODULE
};


struct mtd_info *sharp_probe(struct map_info *map)
{
	struct mtd_info *mtd = NULL;
	struct sharp_info *sharp = NULL;
	int width;

	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
	if(!mtd)
		return NULL;

	sharp = kmalloc(sizeof(*sharp), GFP_KERNEL);
	if(!sharp) {
		kfree(mtd);
		return NULL;
	}

	memset(mtd, 0, sizeof(*mtd));

	width = sharp_probe_map(map,mtd);
	if(!width){
		kfree(mtd);
		kfree(sharp);
		return NULL;
	}

	mtd->priv = map;
	mtd->type = MTD_NORFLASH;
	mtd->erase = sharp_erase;
	mtd->read = sharp_read;
	mtd->write = sharp_write;
	mtd->sync = sharp_sync;
	mtd->suspend = sharp_suspend;
	mtd->resume = sharp_resume;
	mtd->flags = MTD_CAP_NORFLASH;
	mtd->name = map->name;

	memset(sharp, 0, sizeof(*sharp));
	sharp->chipshift = 23;
	sharp->numchips = 1;
	sharp->chips[0].start = 0;
	sharp->chips[0].state = FL_READY;
	sharp->chips[0].mutex = &sharp->chips[0]._spinlock;
	sharp->chips[0].word_write_time = 0;
	init_waitqueue_head(&sharp->chips[0].wq);
	spin_lock_init(&sharp->chips[0]._spinlock);

	map->fldrv = &sharp_chipdrv;
	map->fldrv_priv = sharp;

	__module_get(THIS_MODULE);
	return mtd;
}

163 164 165 166 167 168 169
static inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr)
{
	map_word map_cmd;
	map_cmd.x[0] = cmd;
	map_write(map, map_cmd, adr);
}

L
Linus Torvalds 已提交
170 171
static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd)
{
172
	map_word tmp, read0, read4;
L
Linus Torvalds 已提交
173 174 175
	unsigned long base = 0;
	int width = 4;

176
	tmp = map_read(map, base+0);
L
Linus Torvalds 已提交
177

178
	sharp_send_cmd(map, CMD_READ_ID, base+0);
L
Linus Torvalds 已提交
179

180 181 182
	read0 = map_read(map, base+0);
	read4 = map_read(map, base+4);
	if(read0.x[0] == 0x89898989){
L
Linus Torvalds 已提交
183
		printk("Looks like sharp flash\n");
184
		switch(read4.x[0]){
L
Linus Torvalds 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
		case 0xaaaaaaaa:
		case 0xa0a0a0a0:
			/* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/
			/* a0 - LH28F016SCT-Z4  2Mx8, 32 64k blocks*/
			mtd->erasesize = 0x10000 * width;
			mtd->size = 0x200000 * width;
			return width;
		case 0xa6a6a6a6:
			/* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/
			/* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/
			mtd->erasesize = 0x10000 * width;
			mtd->size = 0x100000 * width;
			return width;
#if 0
		case 0x00000000: /* unknown */
			/* XX - LH28F004SCT 512kx8, 8 64k blocks*/
			mtd->erasesize = 0x10000 * width;
			mtd->size = 0x80000 * width;
			return width;
#endif
		default:
206 207
			printk("Sort-of looks like sharp flash, 0x%08lx 0x%08lx\n",
				read0.x[0], read4.x[0]);
L
Linus Torvalds 已提交
208
		}
209
	}else if((map_read(map, base+0).x[0] == CMD_READ_ID)){
L
Linus Torvalds 已提交
210 211
		/* RAM, probably */
		printk("Looks like RAM\n");
212
		map_write(map, tmp, base+0);
L
Linus Torvalds 已提交
213
	}else{
214 215
		printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n",
			read0.x[0], read4.x[0]);
L
Linus Torvalds 已提交
216 217 218 219 220 221 222 223
	}

	return 0;
}

/* This function returns with the chip->mutex lock held. */
static int sharp_wait(struct map_info *map, struct flchip *chip)
{
224 225
	int i;
	map_word status;
L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233 234
	unsigned long timeo = jiffies + HZ;
	DECLARE_WAITQUEUE(wait, current);
	int adr = 0;

retry:
	spin_lock_bh(chip->mutex);

	switch(chip->state){
	case FL_READY:
235
		sharp_send_cmd(map, CMD_READ_STATUS, adr);
L
Linus Torvalds 已提交
236 237
		chip->state = FL_STATUS;
	case FL_STATUS:
238
		for(i=0;i<100;i++){
239 240
			status = map_read(map, adr);
			if((status.x[0] & SR_READY)==SR_READY)
241 242
				break;
			udelay(1);
L
Linus Torvalds 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
		}
		break;
	default:
		printk("Waiting for chip\n");

		set_current_state(TASK_INTERRUPTIBLE);
		add_wait_queue(&chip->wq, &wait);

		spin_unlock_bh(chip->mutex);

		schedule();
		remove_wait_queue(&chip->wq, &wait);

		if(signal_pending(current))
			return -EINTR;

		timeo = jiffies + HZ;

		goto retry;
	}

264
	sharp_send_cmd(map, CMD_RESET, adr);
L
Linus Torvalds 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

	chip->state = FL_READY;

	return 0;
}

static void sharp_release(struct flchip *chip)
{
	wake_up(&chip->wq);
	spin_unlock_bh(chip->mutex);
}

static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
	size_t *retlen, u_char *buf)
{
	struct map_info *map = mtd->priv;
	struct sharp_info *sharp = map->fldrv_priv;
	int chipnum;
	int ret = 0;
	int ofs = 0;

	chipnum = (from >> sharp->chipshift);
	ofs = from & ((1 << sharp->chipshift)-1);

	*retlen = 0;

	while(len){
		unsigned long thislen;

		if(chipnum>=sharp->numchips)
			break;

		thislen = len;
		if(ofs+thislen >= (1<<sharp->chipshift))
			thislen = (1<<sharp->chipshift) - ofs;

		ret = sharp_wait(map,&sharp->chips[chipnum]);
		if(ret<0)
			break;

		map_copy_from(map,buf,ofs,thislen);

		sharp_release(&sharp->chips[chipnum]);

		*retlen += thislen;
		len -= thislen;
		buf += thislen;

		ofs = 0;
		chipnum++;
	}
	return ret;
}

static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len,
	size_t *retlen, const u_char *buf)
{
	struct map_info *map = mtd->priv;
	struct sharp_info *sharp = map->fldrv_priv;
	int ret = 0;
	int i,j;
	int chipnum;
	unsigned long ofs;
	union { u32 l; unsigned char uc[4]; } tbuf;

	*retlen = 0;

	while(len){
		tbuf.l = 0xffffffff;
		chipnum = to >> sharp->chipshift;
		ofs = to & ((1<<sharp->chipshift)-1);

		j=0;
		for(i=ofs&3;i<4 && len;i++){
			tbuf.uc[i] = *buf;
			buf++;
			to++;
			len--;
			j++;
		}
		sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l);
		if(ret<0)
			return ret;
		(*retlen)+=j;
	}

	return 0;
}

static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
	unsigned long adr, __u32 datum)
{
	int ret;
	int timeo;
	int try;
	int i;
361
	map_word data, status;
L
Linus Torvalds 已提交
362

363
	status.x[0] = 0;
L
Linus Torvalds 已提交
364 365 366
	ret = sharp_wait(map,chip);

	for(try=0;try<10;try++){
367
		sharp_send_cmd(map, CMD_BYTE_WRITE, adr);
L
Linus Torvalds 已提交
368
		/* cpu_to_le32 -> hack to fix the writel be->le conversion */
369 370
		data.x[0] = cpu_to_le32(datum);
		map_write(map, data, adr);
L
Linus Torvalds 已提交
371 372 373 374 375

		chip->state = FL_WRITING;

		timeo = jiffies + (HZ/2);

376
		sharp_send_cmd(map, CMD_READ_STATUS, adr);
L
Linus Torvalds 已提交
377
		for(i=0;i<100;i++){
378 379
			status = map_read(map, adr);
			if((status.x[0] & SR_READY) == SR_READY)
L
Linus Torvalds 已提交
380 381 382 383 384 385
				break;
		}
		if(i==100){
			printk("sharp: timed out writing\n");
		}

386
		if(!(status.x[0] & SR_ERRORS))
L
Linus Torvalds 已提交
387 388
			break;

389
		printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]);
L
Linus Torvalds 已提交
390

391
		sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
L
Linus Torvalds 已提交
392
	}
393
	sharp_send_cmd(map, CMD_RESET, adr);
L
Linus Torvalds 已提交
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
	chip->state = FL_READY;

	wake_up(&chip->wq);
	spin_unlock_bh(chip->mutex);

	return 0;
}

static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct map_info *map = mtd->priv;
	struct sharp_info *sharp = map->fldrv_priv;
	unsigned long adr,len;
	int chipnum, ret=0;

//printk("sharp_erase()\n");
	if(instr->addr & (mtd->erasesize - 1))
		return -EINVAL;
	if(instr->len & (mtd->erasesize - 1))
		return -EINVAL;
	if(instr->len + instr->addr > mtd->size)
		return -EINVAL;

	chipnum = instr->addr >> sharp->chipshift;
	adr = instr->addr & ((1<<sharp->chipshift)-1);
	len = instr->len;

	while(len){
		ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr);
		if(ret)return ret;

		adr += mtd->erasesize;
		len -= mtd->erasesize;
		if(adr >> sharp->chipshift){
			adr = 0;
			chipnum++;
			if(chipnum>=sharp->numchips)
				break;
		}
	}

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}

static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip,
	unsigned long adr)
{
	int ret;
	unsigned long timeo;
446
	map_word status;
L
Linus Torvalds 已提交
447 448
	DECLARE_WAITQUEUE(wait, current);

449 450
	sharp_send_cmd(map, CMD_READ_STATUS, adr);
	status = map_read(map, adr);
L
Linus Torvalds 已提交
451 452 453 454

	timeo = jiffies + HZ;

	while(time_before(jiffies, timeo)){
455 456 457
		sharp_send_cmd(map, CMD_READ_STATUS, adr);
		status = map_read(map, adr);
		if((status.x[0] & SR_READY)==SR_READY){
L
Linus Torvalds 已提交
458 459 460 461 462 463 464 465 466 467 468 469 470
			ret = 0;
			goto out;
		}
		set_current_state(TASK_INTERRUPTIBLE);
		add_wait_queue(&chip->wq, &wait);

		//spin_unlock_bh(chip->mutex);

		schedule_timeout(1);
		schedule();
		remove_wait_queue(&chip->wq, &wait);

		//spin_lock_bh(chip->mutex);
471

L
Linus Torvalds 已提交
472 473 474 475
		if (signal_pending(current)){
			ret = -EINTR;
			goto out;
		}
476

L
Linus Torvalds 已提交
477 478 479 480 481 482 483 484 485 486 487
	}
	ret = -ETIME;
out:
	return ret;
}

static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
	unsigned long adr)
{
	int ret;
	//int timeo;
488
	map_word status;
L
Linus Torvalds 已提交
489 490 491 492 493 494 495 496 497
	//int i;

//printk("sharp_erase_oneblock()\n");

#ifdef AUTOUNLOCK
	/* This seems like a good place to do an unlock */
	sharp_unlock_oneblock(map,chip,adr);
#endif

498 499
	sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr);
	sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr);
L
Linus Torvalds 已提交
500 501 502 503 504 505

	chip->state = FL_ERASING;

	ret = sharp_do_wait_for_ready(map,chip,adr);
	if(ret<0)return ret;

506 507
	sharp_send_cmd(map, CMD_READ_STATUS, adr);
	status = map_read(map, adr);
L
Linus Torvalds 已提交
508

509 510
	if(!(status.x[0] & SR_ERRORS)){
		sharp_send_cmd(map, CMD_RESET, adr);
L
Linus Torvalds 已提交
511 512 513 514 515
		chip->state = FL_READY;
		//spin_unlock_bh(chip->mutex);
		return 0;
	}

516 517
	printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]);
	sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
L
Linus Torvalds 已提交
518 519 520 521 522 523 524 525 526 527 528

	//spin_unlock_bh(chip->mutex);

	return -EIO;
}

#ifdef AUTOUNLOCK
static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
	unsigned long adr)
{
	int i;
529
	map_word status;
L
Linus Torvalds 已提交
530

531 532
	sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr);
	sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr);
L
Linus Torvalds 已提交
533 534 535

	udelay(100);

536 537
	status = map_read(map, adr);
	printk("status=%08lx\n", status.x[0]);
L
Linus Torvalds 已提交
538 539

	for(i=0;i<1000;i++){
540 541 542
		//sharp_send_cmd(map, CMD_READ_STATUS, adr);
		status = map_read(map, adr);
		if((status.x[0] & SR_READY) == SR_READY)
L
Linus Torvalds 已提交
543 544 545 546 547 548 549
			break;
		udelay(100);
	}
	if(i==1000){
		printk("sharp: timed out unlocking block\n");
	}

550 551
	if(!(status.x[0] & SR_ERRORS)){
		sharp_send_cmd(map, CMD_RESET, adr);
L
Linus Torvalds 已提交
552 553 554 555
		chip->state = FL_READY;
		return;
	}

556 557
	printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]);
	sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
L
Linus Torvalds 已提交
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
}
#endif

static void sharp_sync(struct mtd_info *mtd)
{
	//printk("sharp_sync()\n");
}

static int sharp_suspend(struct mtd_info *mtd)
{
	printk("sharp_suspend()\n");
	return -EINVAL;
}

static void sharp_resume(struct mtd_info *mtd)
{
	printk("sharp_resume()\n");
575

L
Linus Torvalds 已提交
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
}

static void sharp_destroy(struct mtd_info *mtd)
{
	printk("sharp_destroy()\n");

}

int __init sharp_probe_init(void)
{
	printk("MTD Sharp chip driver <ds@lineo.com>\n");

	register_mtd_chip_driver(&sharp_chipdrv);

	return 0;
}

static void __exit sharp_probe_exit(void)
{
	unregister_mtd_chip_driver(&sharp_chipdrv);
}

module_init(sharp_probe_init);
module_exit(sharp_probe_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Schleef <ds@schleef.org>");
MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips");