xref: /linux/drivers/mtd/maps/vmu-flash.c (revision 06f25510692385ed4dadd23f7d3d064d1ab11c2d)
147a72688SAdrian McMenamin /* vmu-flash.c
247a72688SAdrian McMenamin  * Driver for SEGA Dreamcast Visual Memory Unit
347a72688SAdrian McMenamin  *
447a72688SAdrian McMenamin  * Copyright (c) Adrian McMenamin 2002 - 2009
547a72688SAdrian McMenamin  * Copyright (c) Paul Mundt 2001
647a72688SAdrian McMenamin  *
747a72688SAdrian McMenamin  * Licensed under version 2 of the
847a72688SAdrian McMenamin  * GNU General Public Licence
947a72688SAdrian McMenamin  */
1047a72688SAdrian McMenamin #include <linux/init.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
1247a72688SAdrian McMenamin #include <linux/sched.h>
1347a72688SAdrian McMenamin #include <linux/delay.h>
1447a72688SAdrian McMenamin #include <linux/maple.h>
1547a72688SAdrian McMenamin #include <linux/mtd/mtd.h>
1647a72688SAdrian McMenamin #include <linux/mtd/map.h>
1747a72688SAdrian McMenamin 
1847a72688SAdrian McMenamin struct vmu_cache {
1947a72688SAdrian McMenamin 	unsigned char *buffer;		/* Cache */
2047a72688SAdrian McMenamin 	unsigned int block;		/* Which block was cached */
2147a72688SAdrian McMenamin 	unsigned long jiffies_atc;	/* When was it cached? */
2247a72688SAdrian McMenamin 	int valid;
2347a72688SAdrian McMenamin };
2447a72688SAdrian McMenamin 
2547a72688SAdrian McMenamin struct mdev_part {
2647a72688SAdrian McMenamin 	struct maple_device *mdev;
2747a72688SAdrian McMenamin 	int partition;
2847a72688SAdrian McMenamin };
2947a72688SAdrian McMenamin 
3047a72688SAdrian McMenamin struct vmupart {
3147a72688SAdrian McMenamin 	u16 user_blocks;
3247a72688SAdrian McMenamin 	u16 root_block;
3347a72688SAdrian McMenamin 	u16 numblocks;
3447a72688SAdrian McMenamin 	char *name;
3547a72688SAdrian McMenamin 	struct vmu_cache *pcache;
3647a72688SAdrian McMenamin };
3747a72688SAdrian McMenamin 
3847a72688SAdrian McMenamin struct memcard {
3947a72688SAdrian McMenamin 	u16 tempA;
4047a72688SAdrian McMenamin 	u16 tempB;
4147a72688SAdrian McMenamin 	u32 partitions;
4247a72688SAdrian McMenamin 	u32 blocklen;
4347a72688SAdrian McMenamin 	u32 writecnt;
4447a72688SAdrian McMenamin 	u32 readcnt;
4547a72688SAdrian McMenamin 	u32 removeable;
4647a72688SAdrian McMenamin 	int partition;
4747a72688SAdrian McMenamin 	int read;
4847a72688SAdrian McMenamin 	unsigned char *blockread;
4947a72688SAdrian McMenamin 	struct vmupart *parts;
5047a72688SAdrian McMenamin 	struct mtd_info *mtd;
5147a72688SAdrian McMenamin };
5247a72688SAdrian McMenamin 
5347a72688SAdrian McMenamin struct vmu_block {
5447a72688SAdrian McMenamin 	unsigned int num; /* block number */
5547a72688SAdrian McMenamin 	unsigned int ofs; /* block offset */
5647a72688SAdrian McMenamin };
5747a72688SAdrian McMenamin 
5847a72688SAdrian McMenamin static struct vmu_block *ofs_to_block(unsigned long src_ofs,
5947a72688SAdrian McMenamin 	struct mtd_info *mtd, int partition)
6047a72688SAdrian McMenamin {
6147a72688SAdrian McMenamin 	struct vmu_block *vblock;
6247a72688SAdrian McMenamin 	struct maple_device *mdev;
6347a72688SAdrian McMenamin 	struct memcard *card;
6447a72688SAdrian McMenamin 	struct mdev_part *mpart;
6547a72688SAdrian McMenamin 	int num;
6647a72688SAdrian McMenamin 
6747a72688SAdrian McMenamin 	mpart = mtd->priv;
6847a72688SAdrian McMenamin 	mdev = mpart->mdev;
6947a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
7047a72688SAdrian McMenamin 
7147a72688SAdrian McMenamin 	if (src_ofs >= card->parts[partition].numblocks * card->blocklen)
7247a72688SAdrian McMenamin 		goto failed;
7347a72688SAdrian McMenamin 
7447a72688SAdrian McMenamin 	num = src_ofs / card->blocklen;
7547a72688SAdrian McMenamin 	if (num > card->parts[partition].numblocks)
7647a72688SAdrian McMenamin 		goto failed;
7747a72688SAdrian McMenamin 
7847a72688SAdrian McMenamin 	vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL);
7947a72688SAdrian McMenamin 	if (!vblock)
8047a72688SAdrian McMenamin 		goto failed;
8147a72688SAdrian McMenamin 
8247a72688SAdrian McMenamin 	vblock->num = num;
8347a72688SAdrian McMenamin 	vblock->ofs = src_ofs % card->blocklen;
8447a72688SAdrian McMenamin 	return vblock;
8547a72688SAdrian McMenamin 
8647a72688SAdrian McMenamin failed:
8747a72688SAdrian McMenamin 	return NULL;
8847a72688SAdrian McMenamin }
8947a72688SAdrian McMenamin 
9047a72688SAdrian McMenamin /* Maple bus callback function for reads */
9147a72688SAdrian McMenamin static void vmu_blockread(struct mapleq *mq)
9247a72688SAdrian McMenamin {
9347a72688SAdrian McMenamin 	struct maple_device *mdev;
9447a72688SAdrian McMenamin 	struct memcard *card;
9547a72688SAdrian McMenamin 
9647a72688SAdrian McMenamin 	mdev = mq->dev;
9747a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
9847a72688SAdrian McMenamin 	/* copy the read in data */
9947a72688SAdrian McMenamin 
10047a72688SAdrian McMenamin 	if (unlikely(!card->blockread))
10147a72688SAdrian McMenamin 		return;
10247a72688SAdrian McMenamin 
10347a72688SAdrian McMenamin 	memcpy(card->blockread, mq->recvbuf->buf + 12,
10447a72688SAdrian McMenamin 		card->blocklen/card->readcnt);
10547a72688SAdrian McMenamin 
10647a72688SAdrian McMenamin }
10747a72688SAdrian McMenamin 
10847a72688SAdrian McMenamin /* Interface with maple bus to read blocks
10947a72688SAdrian McMenamin  * caching the results so that other parts
11047a72688SAdrian McMenamin  * of the driver can access block reads */
11147a72688SAdrian McMenamin static int maple_vmu_read_block(unsigned int num, unsigned char *buf,
11247a72688SAdrian McMenamin 	struct mtd_info *mtd)
11347a72688SAdrian McMenamin {
11447a72688SAdrian McMenamin 	struct memcard *card;
11547a72688SAdrian McMenamin 	struct mdev_part *mpart;
11647a72688SAdrian McMenamin 	struct maple_device *mdev;
11747a72688SAdrian McMenamin 	int partition, error = 0, x, wait;
11847a72688SAdrian McMenamin 	unsigned char *blockread = NULL;
11947a72688SAdrian McMenamin 	struct vmu_cache *pcache;
12047a72688SAdrian McMenamin 	__be32 sendbuf;
12147a72688SAdrian McMenamin 
12247a72688SAdrian McMenamin 	mpart = mtd->priv;
12347a72688SAdrian McMenamin 	mdev = mpart->mdev;
12447a72688SAdrian McMenamin 	partition = mpart->partition;
12547a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
12647a72688SAdrian McMenamin 	pcache = card->parts[partition].pcache;
12747a72688SAdrian McMenamin 	pcache->valid = 0;
12847a72688SAdrian McMenamin 
12947a72688SAdrian McMenamin 	/* prepare the cache for this block */
13047a72688SAdrian McMenamin 	if (!pcache->buffer) {
13147a72688SAdrian McMenamin 		pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL);
13247a72688SAdrian McMenamin 		if (!pcache->buffer) {
13347a72688SAdrian McMenamin 			dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due"
13447a72688SAdrian McMenamin 				" to lack of memory\n", mdev->port,
13547a72688SAdrian McMenamin 				mdev->unit);
13647a72688SAdrian McMenamin 			error = -ENOMEM;
13747a72688SAdrian McMenamin 			goto outB;
13847a72688SAdrian McMenamin 		}
13947a72688SAdrian McMenamin 	}
14047a72688SAdrian McMenamin 
14147a72688SAdrian McMenamin 	/*
14247a72688SAdrian McMenamin 	* Reads may be phased - again the hardware spec
14347a72688SAdrian McMenamin 	* supports this - though may not be any devices in
14447a72688SAdrian McMenamin 	* the wild that implement it, but we will here
14547a72688SAdrian McMenamin 	*/
14647a72688SAdrian McMenamin 	for (x = 0; x < card->readcnt; x++) {
14747a72688SAdrian McMenamin 		sendbuf = cpu_to_be32(partition << 24 | x << 16 | num);
14847a72688SAdrian McMenamin 
14947a72688SAdrian McMenamin 		if (atomic_read(&mdev->busy) == 1) {
15047a72688SAdrian McMenamin 			wait_event_interruptible_timeout(mdev->maple_wait,
15147a72688SAdrian McMenamin 				atomic_read(&mdev->busy) == 0, HZ);
15247a72688SAdrian McMenamin 			if (atomic_read(&mdev->busy) == 1) {
15347a72688SAdrian McMenamin 				dev_notice(&mdev->dev, "VMU at (%d, %d)"
15447a72688SAdrian McMenamin 					" is busy\n", mdev->port, mdev->unit);
15547a72688SAdrian McMenamin 				error = -EAGAIN;
15647a72688SAdrian McMenamin 				goto outB;
15747a72688SAdrian McMenamin 			}
15847a72688SAdrian McMenamin 		}
15947a72688SAdrian McMenamin 
16047a72688SAdrian McMenamin 		atomic_set(&mdev->busy, 1);
16147a72688SAdrian McMenamin 		blockread = kmalloc(card->blocklen/card->readcnt, GFP_KERNEL);
16247a72688SAdrian McMenamin 		if (!blockread) {
16347a72688SAdrian McMenamin 			error = -ENOMEM;
16447a72688SAdrian McMenamin 			atomic_set(&mdev->busy, 0);
16547a72688SAdrian McMenamin 			goto outB;
16647a72688SAdrian McMenamin 		}
16747a72688SAdrian McMenamin 		card->blockread = blockread;
16847a72688SAdrian McMenamin 
16947a72688SAdrian McMenamin 		maple_getcond_callback(mdev, vmu_blockread, 0,
17047a72688SAdrian McMenamin 			MAPLE_FUNC_MEMCARD);
17147a72688SAdrian McMenamin 		error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
17247a72688SAdrian McMenamin 				MAPLE_COMMAND_BREAD, 2, &sendbuf);
17347a72688SAdrian McMenamin 		/* Very long timeouts seem to be needed when box is stressed */
17447a72688SAdrian McMenamin 		wait = wait_event_interruptible_timeout(mdev->maple_wait,
17547a72688SAdrian McMenamin 			(atomic_read(&mdev->busy) == 0 ||
17647a72688SAdrian McMenamin 			atomic_read(&mdev->busy) == 2), HZ * 3);
17747a72688SAdrian McMenamin 		/*
17847a72688SAdrian McMenamin 		* MTD layer does not handle hotplugging well
17947a72688SAdrian McMenamin 		* so have to return errors when VMU is unplugged
18047a72688SAdrian McMenamin 		* in the middle of a read (busy == 2)
18147a72688SAdrian McMenamin 		*/
18247a72688SAdrian McMenamin 		if (error || atomic_read(&mdev->busy) == 2) {
18347a72688SAdrian McMenamin 			if (atomic_read(&mdev->busy) == 2)
18447a72688SAdrian McMenamin 				error = -ENXIO;
18547a72688SAdrian McMenamin 			atomic_set(&mdev->busy, 0);
18647a72688SAdrian McMenamin 			card->blockread = NULL;
18747a72688SAdrian McMenamin 			goto outA;
18847a72688SAdrian McMenamin 		}
18947a72688SAdrian McMenamin 		if (wait == 0 || wait == -ERESTARTSYS) {
19047a72688SAdrian McMenamin 			card->blockread = NULL;
19147a72688SAdrian McMenamin 			atomic_set(&mdev->busy, 0);
19247a72688SAdrian McMenamin 			error = -EIO;
19347a72688SAdrian McMenamin 			list_del_init(&(mdev->mq->list));
19447a72688SAdrian McMenamin 			kfree(mdev->mq->sendbuf);
19547a72688SAdrian McMenamin 			mdev->mq->sendbuf = NULL;
19647a72688SAdrian McMenamin 			if (wait == -ERESTARTSYS) {
19747a72688SAdrian McMenamin 				dev_warn(&mdev->dev, "VMU read on (%d, %d)"
19847a72688SAdrian McMenamin 					" interrupted on block 0x%X\n",
19947a72688SAdrian McMenamin 					mdev->port, mdev->unit, num);
20047a72688SAdrian McMenamin 			} else
20147a72688SAdrian McMenamin 				dev_notice(&mdev->dev, "VMU read on (%d, %d)"
20247a72688SAdrian McMenamin 					" timed out on block 0x%X\n",
20347a72688SAdrian McMenamin 					mdev->port, mdev->unit, num);
20447a72688SAdrian McMenamin 			goto outA;
20547a72688SAdrian McMenamin 		}
20647a72688SAdrian McMenamin 
20747a72688SAdrian McMenamin 		memcpy(buf + (card->blocklen/card->readcnt) * x, blockread,
20847a72688SAdrian McMenamin 			card->blocklen/card->readcnt);
20947a72688SAdrian McMenamin 
21047a72688SAdrian McMenamin 		memcpy(pcache->buffer + (card->blocklen/card->readcnt) * x,
21147a72688SAdrian McMenamin 			card->blockread, card->blocklen/card->readcnt);
21247a72688SAdrian McMenamin 		card->blockread = NULL;
21347a72688SAdrian McMenamin 		pcache->block = num;
21447a72688SAdrian McMenamin 		pcache->jiffies_atc = jiffies;
21547a72688SAdrian McMenamin 		pcache->valid = 1;
21647a72688SAdrian McMenamin 		kfree(blockread);
21747a72688SAdrian McMenamin 	}
21847a72688SAdrian McMenamin 
21947a72688SAdrian McMenamin 	return error;
22047a72688SAdrian McMenamin 
22147a72688SAdrian McMenamin outA:
22247a72688SAdrian McMenamin 	kfree(blockread);
22347a72688SAdrian McMenamin outB:
22447a72688SAdrian McMenamin 	return error;
22547a72688SAdrian McMenamin }
22647a72688SAdrian McMenamin 
22747a72688SAdrian McMenamin /* communicate with maple bus for phased writing */
22847a72688SAdrian McMenamin static int maple_vmu_write_block(unsigned int num, const unsigned char *buf,
22947a72688SAdrian McMenamin 	struct mtd_info *mtd)
23047a72688SAdrian McMenamin {
23147a72688SAdrian McMenamin 	struct memcard *card;
23247a72688SAdrian McMenamin 	struct mdev_part *mpart;
23347a72688SAdrian McMenamin 	struct maple_device *mdev;
23447a72688SAdrian McMenamin 	int partition, error, locking, x, phaselen, wait;
23547a72688SAdrian McMenamin 	__be32 *sendbuf;
23647a72688SAdrian McMenamin 
23747a72688SAdrian McMenamin 	mpart = mtd->priv;
23847a72688SAdrian McMenamin 	mdev = mpart->mdev;
23947a72688SAdrian McMenamin 	partition = mpart->partition;
24047a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
24147a72688SAdrian McMenamin 
24247a72688SAdrian McMenamin 	phaselen = card->blocklen/card->writecnt;
24347a72688SAdrian McMenamin 
24447a72688SAdrian McMenamin 	sendbuf = kmalloc(phaselen + 4, GFP_KERNEL);
24547a72688SAdrian McMenamin 	if (!sendbuf) {
24647a72688SAdrian McMenamin 		error = -ENOMEM;
24747a72688SAdrian McMenamin 		goto fail_nosendbuf;
24847a72688SAdrian McMenamin 	}
24947a72688SAdrian McMenamin 	for (x = 0; x < card->writecnt; x++) {
25047a72688SAdrian McMenamin 		sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num);
25147a72688SAdrian McMenamin 		memcpy(&sendbuf[1], buf + phaselen * x, phaselen);
25247a72688SAdrian McMenamin 		/* wait until the device is not busy doing something else
25347a72688SAdrian McMenamin 		* or 1 second - which ever is longer */
25447a72688SAdrian McMenamin 		if (atomic_read(&mdev->busy) == 1) {
25547a72688SAdrian McMenamin 			wait_event_interruptible_timeout(mdev->maple_wait,
25647a72688SAdrian McMenamin 				atomic_read(&mdev->busy) == 0, HZ);
25747a72688SAdrian McMenamin 			if (atomic_read(&mdev->busy) == 1) {
25847a72688SAdrian McMenamin 				error = -EBUSY;
25947a72688SAdrian McMenamin 				dev_notice(&mdev->dev, "VMU write at (%d, %d)"
26047a72688SAdrian McMenamin 					"failed - device is busy\n",
26147a72688SAdrian McMenamin 					mdev->port, mdev->unit);
26247a72688SAdrian McMenamin 				goto fail_nolock;
26347a72688SAdrian McMenamin 			}
26447a72688SAdrian McMenamin 		}
26547a72688SAdrian McMenamin 		atomic_set(&mdev->busy, 1);
26647a72688SAdrian McMenamin 
26747a72688SAdrian McMenamin 		locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
26847a72688SAdrian McMenamin 			MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf);
26947a72688SAdrian McMenamin 		wait = wait_event_interruptible_timeout(mdev->maple_wait,
27047a72688SAdrian McMenamin 			atomic_read(&mdev->busy) == 0, HZ/10);
27147a72688SAdrian McMenamin 		if (locking) {
27247a72688SAdrian McMenamin 			error = -EIO;
27347a72688SAdrian McMenamin 			atomic_set(&mdev->busy, 0);
27447a72688SAdrian McMenamin 			goto fail_nolock;
27547a72688SAdrian McMenamin 		}
27647a72688SAdrian McMenamin 		if (atomic_read(&mdev->busy) == 2) {
27747a72688SAdrian McMenamin 			atomic_set(&mdev->busy, 0);
27847a72688SAdrian McMenamin 		} else if (wait == 0 || wait == -ERESTARTSYS) {
27947a72688SAdrian McMenamin 			error = -EIO;
28047a72688SAdrian McMenamin 			dev_warn(&mdev->dev, "Write at (%d, %d) of block"
28147a72688SAdrian McMenamin 				" 0x%X at phase %d failed: could not"
28247a72688SAdrian McMenamin 				" communicate with VMU", mdev->port,
28347a72688SAdrian McMenamin 				mdev->unit, num, x);
28447a72688SAdrian McMenamin 			atomic_set(&mdev->busy, 0);
28547a72688SAdrian McMenamin 			kfree(mdev->mq->sendbuf);
28647a72688SAdrian McMenamin 			mdev->mq->sendbuf = NULL;
28747a72688SAdrian McMenamin 			list_del_init(&(mdev->mq->list));
28847a72688SAdrian McMenamin 			goto fail_nolock;
28947a72688SAdrian McMenamin 		}
29047a72688SAdrian McMenamin 	}
29147a72688SAdrian McMenamin 	kfree(sendbuf);
29247a72688SAdrian McMenamin 
29347a72688SAdrian McMenamin 	return card->blocklen;
29447a72688SAdrian McMenamin 
29547a72688SAdrian McMenamin fail_nolock:
29647a72688SAdrian McMenamin 	kfree(sendbuf);
29747a72688SAdrian McMenamin fail_nosendbuf:
29847a72688SAdrian McMenamin 	dev_err(&mdev->dev, "VMU (%d, %d): write failed\n", mdev->port,
29947a72688SAdrian McMenamin 		mdev->unit);
30047a72688SAdrian McMenamin 	return error;
30147a72688SAdrian McMenamin }
30247a72688SAdrian McMenamin 
30347a72688SAdrian McMenamin /* mtd function to simulate reading byte by byte */
30447a72688SAdrian McMenamin static unsigned char vmu_flash_read_char(unsigned long ofs, int *retval,
30547a72688SAdrian McMenamin 	struct mtd_info *mtd)
30647a72688SAdrian McMenamin {
30747a72688SAdrian McMenamin 	struct vmu_block *vblock;
30847a72688SAdrian McMenamin 	struct memcard *card;
30947a72688SAdrian McMenamin 	struct mdev_part *mpart;
31047a72688SAdrian McMenamin 	struct maple_device *mdev;
31147a72688SAdrian McMenamin 	unsigned char *buf, ret;
31247a72688SAdrian McMenamin 	int partition, error;
31347a72688SAdrian McMenamin 
31447a72688SAdrian McMenamin 	mpart = mtd->priv;
31547a72688SAdrian McMenamin 	mdev = mpart->mdev;
31647a72688SAdrian McMenamin 	partition = mpart->partition;
31747a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
31847a72688SAdrian McMenamin 	*retval =  0;
31947a72688SAdrian McMenamin 
32047a72688SAdrian McMenamin 	buf = kmalloc(card->blocklen, GFP_KERNEL);
32147a72688SAdrian McMenamin 	if (!buf) {
32247a72688SAdrian McMenamin 		*retval = 1;
32347a72688SAdrian McMenamin 		ret = -ENOMEM;
32447a72688SAdrian McMenamin 		goto finish;
32547a72688SAdrian McMenamin 	}
32647a72688SAdrian McMenamin 
32747a72688SAdrian McMenamin 	vblock = ofs_to_block(ofs, mtd, partition);
32847a72688SAdrian McMenamin 	if (!vblock) {
32947a72688SAdrian McMenamin 		*retval = 3;
33047a72688SAdrian McMenamin 		ret = -ENOMEM;
33147a72688SAdrian McMenamin 		goto out_buf;
33247a72688SAdrian McMenamin 	}
33347a72688SAdrian McMenamin 
33447a72688SAdrian McMenamin 	error = maple_vmu_read_block(vblock->num, buf, mtd);
33547a72688SAdrian McMenamin 	if (error) {
33647a72688SAdrian McMenamin 		ret = error;
33747a72688SAdrian McMenamin 		*retval = 2;
33847a72688SAdrian McMenamin 		goto out_vblock;
33947a72688SAdrian McMenamin 	}
34047a72688SAdrian McMenamin 
34147a72688SAdrian McMenamin 	ret = buf[vblock->ofs];
34247a72688SAdrian McMenamin 
34347a72688SAdrian McMenamin out_vblock:
34447a72688SAdrian McMenamin 	kfree(vblock);
34547a72688SAdrian McMenamin out_buf:
34647a72688SAdrian McMenamin 	kfree(buf);
34747a72688SAdrian McMenamin finish:
34847a72688SAdrian McMenamin 	return ret;
34947a72688SAdrian McMenamin }
35047a72688SAdrian McMenamin 
35147a72688SAdrian McMenamin /* mtd higher order function to read flash */
35247a72688SAdrian McMenamin static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
35347a72688SAdrian McMenamin 	size_t *retlen,  u_char *buf)
35447a72688SAdrian McMenamin {
35547a72688SAdrian McMenamin 	struct maple_device *mdev;
35647a72688SAdrian McMenamin 	struct memcard *card;
35747a72688SAdrian McMenamin 	struct mdev_part *mpart;
35847a72688SAdrian McMenamin 	struct vmu_cache *pcache;
35947a72688SAdrian McMenamin 	struct vmu_block *vblock;
36047a72688SAdrian McMenamin 	int index = 0, retval, partition, leftover, numblocks;
36147a72688SAdrian McMenamin 	unsigned char cx;
36247a72688SAdrian McMenamin 
36347a72688SAdrian McMenamin 	mpart = mtd->priv;
36447a72688SAdrian McMenamin 	mdev = mpart->mdev;
36547a72688SAdrian McMenamin 	partition = mpart->partition;
36647a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
36747a72688SAdrian McMenamin 
36847a72688SAdrian McMenamin 	numblocks = card->parts[partition].numblocks;
36947a72688SAdrian McMenamin 	if (from + len > numblocks * card->blocklen)
37047a72688SAdrian McMenamin 		len = numblocks * card->blocklen - from;
37147a72688SAdrian McMenamin 	if (len == 0)
37247a72688SAdrian McMenamin 		return -EIO;
37347a72688SAdrian McMenamin 	/* Have we cached this bit already? */
37447a72688SAdrian McMenamin 	pcache = card->parts[partition].pcache;
37547a72688SAdrian McMenamin 	do {
37647a72688SAdrian McMenamin 		vblock =  ofs_to_block(from + index, mtd, partition);
37747a72688SAdrian McMenamin 		if (!vblock)
37847a72688SAdrian McMenamin 			return -ENOMEM;
37947a72688SAdrian McMenamin 		/* Have we cached this and is the cache valid and timely? */
38047a72688SAdrian McMenamin 		if (pcache->valid &&
38147a72688SAdrian McMenamin 			time_before(jiffies, pcache->jiffies_atc + HZ) &&
38247a72688SAdrian McMenamin 			(pcache->block == vblock->num)) {
38347a72688SAdrian McMenamin 			/* we have cached it, so do necessary copying */
38447a72688SAdrian McMenamin 			leftover = card->blocklen - vblock->ofs;
38547a72688SAdrian McMenamin 			if (vblock->ofs + len - index < card->blocklen) {
38647a72688SAdrian McMenamin 				/* only a bit of this block to copy */
38747a72688SAdrian McMenamin 				memcpy(buf + index,
38847a72688SAdrian McMenamin 					pcache->buffer + vblock->ofs,
38947a72688SAdrian McMenamin 					len - index);
39047a72688SAdrian McMenamin 				index = len;
39147a72688SAdrian McMenamin 			} else {
39247a72688SAdrian McMenamin 				/* otherwise copy remainder of whole block */
39347a72688SAdrian McMenamin 				memcpy(buf + index, pcache->buffer +
39447a72688SAdrian McMenamin 					vblock->ofs, leftover);
39547a72688SAdrian McMenamin 				index += leftover;
39647a72688SAdrian McMenamin 			}
39747a72688SAdrian McMenamin 		} else {
39847a72688SAdrian McMenamin 			/*
39947a72688SAdrian McMenamin 			* Not cached so read one byte -
40047a72688SAdrian McMenamin 			* but cache the rest of the block
40147a72688SAdrian McMenamin 			*/
40247a72688SAdrian McMenamin 			cx = vmu_flash_read_char(from + index, &retval, mtd);
40347a72688SAdrian McMenamin 			if (retval) {
40447a72688SAdrian McMenamin 				*retlen = index;
40547a72688SAdrian McMenamin 				kfree(vblock);
40647a72688SAdrian McMenamin 				return cx;
40747a72688SAdrian McMenamin 			}
40847a72688SAdrian McMenamin 			memset(buf + index, cx, 1);
40947a72688SAdrian McMenamin 			index++;
41047a72688SAdrian McMenamin 		}
41147a72688SAdrian McMenamin 		kfree(vblock);
41247a72688SAdrian McMenamin 	} while (len > index);
41347a72688SAdrian McMenamin 	*retlen = index;
41447a72688SAdrian McMenamin 
41547a72688SAdrian McMenamin 	return 0;
41647a72688SAdrian McMenamin }
41747a72688SAdrian McMenamin 
41847a72688SAdrian McMenamin static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
41947a72688SAdrian McMenamin 	size_t *retlen, const u_char *buf)
42047a72688SAdrian McMenamin {
42147a72688SAdrian McMenamin 	struct maple_device *mdev;
42247a72688SAdrian McMenamin 	struct memcard *card;
42347a72688SAdrian McMenamin 	struct mdev_part *mpart;
42447a72688SAdrian McMenamin 	int index = 0, partition, error = 0, numblocks;
42547a72688SAdrian McMenamin 	struct vmu_cache *pcache;
42647a72688SAdrian McMenamin 	struct vmu_block *vblock;
42747a72688SAdrian McMenamin 	unsigned char *buffer;
42847a72688SAdrian McMenamin 
42947a72688SAdrian McMenamin 	mpart = mtd->priv;
43047a72688SAdrian McMenamin 	mdev = mpart->mdev;
43147a72688SAdrian McMenamin 	partition = mpart->partition;
43247a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
43347a72688SAdrian McMenamin 
43447a72688SAdrian McMenamin 	numblocks = card->parts[partition].numblocks;
43547a72688SAdrian McMenamin 	if (to + len > numblocks * card->blocklen)
43647a72688SAdrian McMenamin 		len = numblocks * card->blocklen - to;
43747a72688SAdrian McMenamin 	if (len == 0) {
43847a72688SAdrian McMenamin 		error = -EIO;
43947a72688SAdrian McMenamin 		goto failed;
44047a72688SAdrian McMenamin 	}
44147a72688SAdrian McMenamin 
44247a72688SAdrian McMenamin 	vblock = ofs_to_block(to, mtd, partition);
44347a72688SAdrian McMenamin 	if (!vblock) {
44447a72688SAdrian McMenamin 		error = -ENOMEM;
44547a72688SAdrian McMenamin 		goto failed;
44647a72688SAdrian McMenamin 	}
44747a72688SAdrian McMenamin 
44847a72688SAdrian McMenamin 	buffer = kmalloc(card->blocklen, GFP_KERNEL);
44947a72688SAdrian McMenamin 	if (!buffer) {
45047a72688SAdrian McMenamin 		error = -ENOMEM;
45147a72688SAdrian McMenamin 		goto fail_buffer;
45247a72688SAdrian McMenamin 	}
45347a72688SAdrian McMenamin 
45447a72688SAdrian McMenamin 	do {
45547a72688SAdrian McMenamin 		/* Read in the block we are to write to */
45647a72688SAdrian McMenamin 		error = maple_vmu_read_block(vblock->num, buffer, mtd);
45747a72688SAdrian McMenamin 		if (error)
45847a72688SAdrian McMenamin 			goto fail_io;
45947a72688SAdrian McMenamin 
46047a72688SAdrian McMenamin 		do {
46147a72688SAdrian McMenamin 			buffer[vblock->ofs] = buf[index];
46247a72688SAdrian McMenamin 			vblock->ofs++;
46347a72688SAdrian McMenamin 			index++;
46447a72688SAdrian McMenamin 			if (index >= len)
46547a72688SAdrian McMenamin 				break;
46647a72688SAdrian McMenamin 		} while (vblock->ofs < card->blocklen);
46747a72688SAdrian McMenamin 
46847a72688SAdrian McMenamin 		/* write out new buffer */
46947a72688SAdrian McMenamin 		error = maple_vmu_write_block(vblock->num, buffer, mtd);
47047a72688SAdrian McMenamin 		/* invalidate the cache */
47147a72688SAdrian McMenamin 		pcache = card->parts[partition].pcache;
47247a72688SAdrian McMenamin 		pcache->valid = 0;
47347a72688SAdrian McMenamin 
47447a72688SAdrian McMenamin 		if (error != card->blocklen)
47547a72688SAdrian McMenamin 			goto fail_io;
47647a72688SAdrian McMenamin 
47747a72688SAdrian McMenamin 		vblock->num++;
47847a72688SAdrian McMenamin 		vblock->ofs = 0;
47947a72688SAdrian McMenamin 	} while (len > index);
48047a72688SAdrian McMenamin 
48147a72688SAdrian McMenamin 	kfree(buffer);
48247a72688SAdrian McMenamin 	*retlen = index;
48347a72688SAdrian McMenamin 	kfree(vblock);
48447a72688SAdrian McMenamin 	return 0;
48547a72688SAdrian McMenamin 
48647a72688SAdrian McMenamin fail_io:
48747a72688SAdrian McMenamin 	kfree(buffer);
48847a72688SAdrian McMenamin fail_buffer:
48947a72688SAdrian McMenamin 	kfree(vblock);
49047a72688SAdrian McMenamin failed:
49147a72688SAdrian McMenamin 	dev_err(&mdev->dev, "VMU write failing with error %d\n", error);
49247a72688SAdrian McMenamin 	return error;
49347a72688SAdrian McMenamin }
49447a72688SAdrian McMenamin 
49547a72688SAdrian McMenamin static void vmu_flash_sync(struct mtd_info *mtd)
49647a72688SAdrian McMenamin {
49747a72688SAdrian McMenamin 	/* Do nothing here */
49847a72688SAdrian McMenamin }
49947a72688SAdrian McMenamin 
50047a72688SAdrian McMenamin /* Maple bus callback function to recursively query hardware details */
50147a72688SAdrian McMenamin static void vmu_queryblocks(struct mapleq *mq)
50247a72688SAdrian McMenamin {
50347a72688SAdrian McMenamin 	struct maple_device *mdev;
50447a72688SAdrian McMenamin 	unsigned short *res;
50547a72688SAdrian McMenamin 	struct memcard *card;
50647a72688SAdrian McMenamin 	__be32 partnum;
50747a72688SAdrian McMenamin 	struct vmu_cache *pcache;
50847a72688SAdrian McMenamin 	struct mdev_part *mpart;
50947a72688SAdrian McMenamin 	struct mtd_info *mtd_cur;
51047a72688SAdrian McMenamin 	struct vmupart *part_cur;
51147a72688SAdrian McMenamin 	int error;
51247a72688SAdrian McMenamin 
51347a72688SAdrian McMenamin 	mdev = mq->dev;
51447a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
51547a72688SAdrian McMenamin 	res = (unsigned short *) (mq->recvbuf->buf);
51647a72688SAdrian McMenamin 	card->tempA = res[12];
51747a72688SAdrian McMenamin 	card->tempB = res[6];
51847a72688SAdrian McMenamin 
51947a72688SAdrian McMenamin 	dev_info(&mdev->dev, "VMU device at partition %d has %d user "
52047a72688SAdrian McMenamin 		"blocks with a root block at %d\n", card->partition,
52147a72688SAdrian McMenamin 		card->tempA, card->tempB);
52247a72688SAdrian McMenamin 
52347a72688SAdrian McMenamin 	part_cur = &card->parts[card->partition];
52447a72688SAdrian McMenamin 	part_cur->user_blocks = card->tempA;
52547a72688SAdrian McMenamin 	part_cur->root_block = card->tempB;
52647a72688SAdrian McMenamin 	part_cur->numblocks = card->tempB + 1;
52747a72688SAdrian McMenamin 	part_cur->name = kmalloc(12, GFP_KERNEL);
52847a72688SAdrian McMenamin 	if (!part_cur->name)
52947a72688SAdrian McMenamin 		goto fail_name;
53047a72688SAdrian McMenamin 
53147a72688SAdrian McMenamin 	sprintf(part_cur->name, "vmu%d.%d.%d",
53247a72688SAdrian McMenamin 		mdev->port, mdev->unit, card->partition);
53347a72688SAdrian McMenamin 	mtd_cur = &card->mtd[card->partition];
53447a72688SAdrian McMenamin 	mtd_cur->name = part_cur->name;
53547a72688SAdrian McMenamin 	mtd_cur->type = 8;
53647a72688SAdrian McMenamin 	mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
53747a72688SAdrian McMenamin 	mtd_cur->size = part_cur->numblocks * card->blocklen;
53847a72688SAdrian McMenamin 	mtd_cur->erasesize = card->blocklen;
5393c3c10bbSArtem Bityutskiy 	mtd_cur->_write = vmu_flash_write;
5403c3c10bbSArtem Bityutskiy 	mtd_cur->_read = vmu_flash_read;
5413c3c10bbSArtem Bityutskiy 	mtd_cur->_sync = vmu_flash_sync;
54247a72688SAdrian McMenamin 	mtd_cur->writesize = card->blocklen;
54347a72688SAdrian McMenamin 
54447a72688SAdrian McMenamin 	mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
54547a72688SAdrian McMenamin 	if (!mpart)
54647a72688SAdrian McMenamin 		goto fail_mpart;
54747a72688SAdrian McMenamin 
54847a72688SAdrian McMenamin 	mpart->mdev = mdev;
54947a72688SAdrian McMenamin 	mpart->partition = card->partition;
55047a72688SAdrian McMenamin 	mtd_cur->priv = mpart;
55147a72688SAdrian McMenamin 	mtd_cur->owner = THIS_MODULE;
55247a72688SAdrian McMenamin 
55347a72688SAdrian McMenamin 	pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL);
55447a72688SAdrian McMenamin 	if (!pcache)
55547a72688SAdrian McMenamin 		goto fail_cache_create;
55647a72688SAdrian McMenamin 	part_cur->pcache = pcache;
55747a72688SAdrian McMenamin 
558ee0e87b1SJamie Iles 	error = mtd_device_register(mtd_cur, NULL, 0);
55947a72688SAdrian McMenamin 	if (error)
56047a72688SAdrian McMenamin 		goto fail_mtd_register;
56147a72688SAdrian McMenamin 
56247a72688SAdrian McMenamin 	maple_getcond_callback(mdev, NULL, 0,
56347a72688SAdrian McMenamin 		MAPLE_FUNC_MEMCARD);
56447a72688SAdrian McMenamin 
56547a72688SAdrian McMenamin 	/*
56647a72688SAdrian McMenamin 	* Set up a recursive call to the (probably theoretical)
56747a72688SAdrian McMenamin 	* second or more partition
56847a72688SAdrian McMenamin 	*/
56947a72688SAdrian McMenamin 	if (++card->partition < card->partitions) {
57047a72688SAdrian McMenamin 		partnum = cpu_to_be32(card->partition << 24);
57147a72688SAdrian McMenamin 		maple_getcond_callback(mdev, vmu_queryblocks, 0,
57247a72688SAdrian McMenamin 			MAPLE_FUNC_MEMCARD);
57347a72688SAdrian McMenamin 		maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
57447a72688SAdrian McMenamin 			MAPLE_COMMAND_GETMINFO, 2, &partnum);
57547a72688SAdrian McMenamin 	}
57647a72688SAdrian McMenamin 	return;
57747a72688SAdrian McMenamin 
57847a72688SAdrian McMenamin fail_mtd_register:
57947a72688SAdrian McMenamin 	dev_err(&mdev->dev, "Could not register maple device at (%d, %d)"
58047a72688SAdrian McMenamin 		"error is 0x%X\n", mdev->port, mdev->unit, error);
58147a72688SAdrian McMenamin 	for (error = 0; error <= card->partition; error++) {
58247a72688SAdrian McMenamin 		kfree(((card->parts)[error]).pcache);
58347a72688SAdrian McMenamin 		((card->parts)[error]).pcache = NULL;
58447a72688SAdrian McMenamin 	}
58547a72688SAdrian McMenamin fail_cache_create:
58647a72688SAdrian McMenamin fail_mpart:
58747a72688SAdrian McMenamin 	for (error = 0; error <= card->partition; error++) {
58847a72688SAdrian McMenamin 		kfree(((card->mtd)[error]).priv);
58947a72688SAdrian McMenamin 		((card->mtd)[error]).priv = NULL;
59047a72688SAdrian McMenamin 	}
59147a72688SAdrian McMenamin 	maple_getcond_callback(mdev, NULL, 0,
59247a72688SAdrian McMenamin 		MAPLE_FUNC_MEMCARD);
59347a72688SAdrian McMenamin 	kfree(part_cur->name);
59447a72688SAdrian McMenamin fail_name:
59547a72688SAdrian McMenamin 	return;
59647a72688SAdrian McMenamin }
59747a72688SAdrian McMenamin 
59847a72688SAdrian McMenamin /* Handles very basic info about the flash, queries for details */
599*06f25510SBill Pemberton static int vmu_connect(struct maple_device *mdev)
60047a72688SAdrian McMenamin {
60147a72688SAdrian McMenamin 	unsigned long test_flash_data, basic_flash_data;
60247a72688SAdrian McMenamin 	int c, error;
60347a72688SAdrian McMenamin 	struct memcard *card;
60447a72688SAdrian McMenamin 	u32 partnum = 0;
60547a72688SAdrian McMenamin 
60647a72688SAdrian McMenamin 	test_flash_data = be32_to_cpu(mdev->devinfo.function);
60747a72688SAdrian McMenamin 	/* Need to count how many bits are set - to find out which
608782e5711SAkinobu Mita 	 * function_data element has details of the memory card
609782e5711SAkinobu Mita 	 */
610782e5711SAkinobu Mita 	c = hweight_long(test_flash_data);
61147a72688SAdrian McMenamin 
61247a72688SAdrian McMenamin 	basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
61347a72688SAdrian McMenamin 
61447a72688SAdrian McMenamin 	card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
61547a72688SAdrian McMenamin 	if (!card) {
616895fb494SRoel Kluin 		error = -ENOMEM;
61747a72688SAdrian McMenamin 		goto fail_nomem;
61847a72688SAdrian McMenamin 	}
61947a72688SAdrian McMenamin 
62047a72688SAdrian McMenamin 	card->partitions = (basic_flash_data >> 24 & 0xFF) + 1;
62147a72688SAdrian McMenamin 	card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5;
62247a72688SAdrian McMenamin 	card->writecnt = basic_flash_data >> 12 & 0xF;
62347a72688SAdrian McMenamin 	card->readcnt = basic_flash_data >> 8 & 0xF;
62447a72688SAdrian McMenamin 	card->removeable = basic_flash_data >> 7 & 1;
62547a72688SAdrian McMenamin 
62647a72688SAdrian McMenamin 	card->partition = 0;
62747a72688SAdrian McMenamin 
62847a72688SAdrian McMenamin 	/*
62947a72688SAdrian McMenamin 	* Not sure there are actually any multi-partition devices in the
63047a72688SAdrian McMenamin 	* real world, but the hardware supports them, so, so will we
63147a72688SAdrian McMenamin 	*/
63247a72688SAdrian McMenamin 	card->parts = kmalloc(sizeof(struct vmupart) * card->partitions,
63347a72688SAdrian McMenamin 		GFP_KERNEL);
63447a72688SAdrian McMenamin 	if (!card->parts) {
63547a72688SAdrian McMenamin 		error = -ENOMEM;
63647a72688SAdrian McMenamin 		goto fail_partitions;
63747a72688SAdrian McMenamin 	}
63847a72688SAdrian McMenamin 
63947a72688SAdrian McMenamin 	card->mtd = kmalloc(sizeof(struct mtd_info) * card->partitions,
64047a72688SAdrian McMenamin 		GFP_KERNEL);
64147a72688SAdrian McMenamin 	if (!card->mtd) {
64247a72688SAdrian McMenamin 		error = -ENOMEM;
64347a72688SAdrian McMenamin 		goto fail_mtd_info;
64447a72688SAdrian McMenamin 	}
64547a72688SAdrian McMenamin 
64647a72688SAdrian McMenamin 	maple_set_drvdata(mdev, card);
64747a72688SAdrian McMenamin 
64847a72688SAdrian McMenamin 	/*
64947a72688SAdrian McMenamin 	* We want to trap meminfo not get cond
65047a72688SAdrian McMenamin 	* so set interval to zero, but rely on maple bus
65147a72688SAdrian McMenamin 	* driver to pass back the results of the meminfo
65247a72688SAdrian McMenamin 	*/
65347a72688SAdrian McMenamin 	maple_getcond_callback(mdev, vmu_queryblocks, 0,
65447a72688SAdrian McMenamin 		MAPLE_FUNC_MEMCARD);
65547a72688SAdrian McMenamin 
65647a72688SAdrian McMenamin 	/* Make sure we are clear to go */
65747a72688SAdrian McMenamin 	if (atomic_read(&mdev->busy) == 1) {
65847a72688SAdrian McMenamin 		wait_event_interruptible_timeout(mdev->maple_wait,
65947a72688SAdrian McMenamin 			atomic_read(&mdev->busy) == 0, HZ);
66047a72688SAdrian McMenamin 		if (atomic_read(&mdev->busy) == 1) {
66147a72688SAdrian McMenamin 			dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n",
66247a72688SAdrian McMenamin 				mdev->port, mdev->unit);
66347a72688SAdrian McMenamin 			error = -EAGAIN;
66447a72688SAdrian McMenamin 			goto fail_device_busy;
66547a72688SAdrian McMenamin 		}
66647a72688SAdrian McMenamin 	}
66747a72688SAdrian McMenamin 
66847a72688SAdrian McMenamin 	atomic_set(&mdev->busy, 1);
66947a72688SAdrian McMenamin 
67047a72688SAdrian McMenamin 	/*
67147a72688SAdrian McMenamin 	* Set up the minfo call: vmu_queryblocks will handle
67247a72688SAdrian McMenamin 	* the information passed back
67347a72688SAdrian McMenamin 	*/
67447a72688SAdrian McMenamin 	error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
67547a72688SAdrian McMenamin 		MAPLE_COMMAND_GETMINFO, 2, &partnum);
67647a72688SAdrian McMenamin 	if (error) {
67747a72688SAdrian McMenamin 		dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)"
67847a72688SAdrian McMenamin 			" error is 0x%X\n", mdev->port, mdev->unit, error);
67947a72688SAdrian McMenamin 		goto fail_mtd_info;
68047a72688SAdrian McMenamin 	}
68147a72688SAdrian McMenamin 	return 0;
68247a72688SAdrian McMenamin 
68347a72688SAdrian McMenamin fail_device_busy:
68447a72688SAdrian McMenamin 	kfree(card->mtd);
68547a72688SAdrian McMenamin fail_mtd_info:
68647a72688SAdrian McMenamin 	kfree(card->parts);
68747a72688SAdrian McMenamin fail_partitions:
68847a72688SAdrian McMenamin 	kfree(card);
68947a72688SAdrian McMenamin fail_nomem:
69047a72688SAdrian McMenamin 	return error;
69147a72688SAdrian McMenamin }
69247a72688SAdrian McMenamin 
69347a72688SAdrian McMenamin static void __devexit vmu_disconnect(struct maple_device *mdev)
69447a72688SAdrian McMenamin {
69547a72688SAdrian McMenamin 	struct memcard *card;
69647a72688SAdrian McMenamin 	struct mdev_part *mpart;
69747a72688SAdrian McMenamin 	int x;
69847a72688SAdrian McMenamin 
69947a72688SAdrian McMenamin 	mdev->callback = NULL;
70047a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
70147a72688SAdrian McMenamin 	for (x = 0; x < card->partitions; x++) {
70247a72688SAdrian McMenamin 		mpart = ((card->mtd)[x]).priv;
70347a72688SAdrian McMenamin 		mpart->mdev = NULL;
704ee0e87b1SJamie Iles 		mtd_device_unregister(&((card->mtd)[x]));
70547a72688SAdrian McMenamin 		kfree(((card->parts)[x]).name);
70647a72688SAdrian McMenamin 	}
70747a72688SAdrian McMenamin 	kfree(card->parts);
70847a72688SAdrian McMenamin 	kfree(card->mtd);
70947a72688SAdrian McMenamin 	kfree(card);
71047a72688SAdrian McMenamin }
71147a72688SAdrian McMenamin 
71247a72688SAdrian McMenamin /* Callback to handle eccentricities of both mtd subsystem
71347a72688SAdrian McMenamin  * and general flakyness of Dreamcast VMUs
71447a72688SAdrian McMenamin  */
71547a72688SAdrian McMenamin static int vmu_can_unload(struct maple_device *mdev)
71647a72688SAdrian McMenamin {
71747a72688SAdrian McMenamin 	struct memcard *card;
71847a72688SAdrian McMenamin 	int x;
71947a72688SAdrian McMenamin 	struct mtd_info *mtd;
72047a72688SAdrian McMenamin 
72147a72688SAdrian McMenamin 	card = maple_get_drvdata(mdev);
72247a72688SAdrian McMenamin 	for (x = 0; x < card->partitions; x++) {
72347a72688SAdrian McMenamin 		mtd = &((card->mtd)[x]);
72447a72688SAdrian McMenamin 		if (mtd->usecount > 0)
72547a72688SAdrian McMenamin 			return 0;
72647a72688SAdrian McMenamin 	}
72747a72688SAdrian McMenamin 	return 1;
72847a72688SAdrian McMenamin }
72947a72688SAdrian McMenamin 
73047a72688SAdrian McMenamin #define ERRSTR "VMU at (%d, %d) file error -"
73147a72688SAdrian McMenamin 
73247a72688SAdrian McMenamin static void vmu_file_error(struct maple_device *mdev, void *recvbuf)
73347a72688SAdrian McMenamin {
73447a72688SAdrian McMenamin 	enum maple_file_errors error = ((int *)recvbuf)[1];
73547a72688SAdrian McMenamin 
73647a72688SAdrian McMenamin 	switch (error) {
73747a72688SAdrian McMenamin 
73847a72688SAdrian McMenamin 	case MAPLE_FILEERR_INVALID_PARTITION:
73947a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " invalid partition number\n",
74047a72688SAdrian McMenamin 			mdev->port, mdev->unit);
74147a72688SAdrian McMenamin 		break;
74247a72688SAdrian McMenamin 
74347a72688SAdrian McMenamin 	case MAPLE_FILEERR_PHASE_ERROR:
74447a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " phase error\n",
74547a72688SAdrian McMenamin 			mdev->port, mdev->unit);
74647a72688SAdrian McMenamin 		break;
74747a72688SAdrian McMenamin 
74847a72688SAdrian McMenamin 	case MAPLE_FILEERR_INVALID_BLOCK:
74947a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " invalid block number\n",
75047a72688SAdrian McMenamin 			mdev->port, mdev->unit);
75147a72688SAdrian McMenamin 		break;
75247a72688SAdrian McMenamin 
75347a72688SAdrian McMenamin 	case MAPLE_FILEERR_WRITE_ERROR:
75447a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " write error\n",
75547a72688SAdrian McMenamin 			mdev->port, mdev->unit);
75647a72688SAdrian McMenamin 		break;
75747a72688SAdrian McMenamin 
75847a72688SAdrian McMenamin 	case MAPLE_FILEERR_INVALID_WRITE_LENGTH:
75947a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " invalid write length\n",
76047a72688SAdrian McMenamin 			mdev->port, mdev->unit);
76147a72688SAdrian McMenamin 		break;
76247a72688SAdrian McMenamin 
76347a72688SAdrian McMenamin 	case MAPLE_FILEERR_BAD_CRC:
76447a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " bad CRC\n",
76547a72688SAdrian McMenamin 			mdev->port, mdev->unit);
76647a72688SAdrian McMenamin 		break;
76747a72688SAdrian McMenamin 
76847a72688SAdrian McMenamin 	default:
76947a72688SAdrian McMenamin 		dev_notice(&mdev->dev, ERRSTR " 0x%X\n",
77047a72688SAdrian McMenamin 			mdev->port, mdev->unit, error);
77147a72688SAdrian McMenamin 	}
77247a72688SAdrian McMenamin }
77347a72688SAdrian McMenamin 
77447a72688SAdrian McMenamin 
775*06f25510SBill Pemberton static int probe_maple_vmu(struct device *dev)
77647a72688SAdrian McMenamin {
77747a72688SAdrian McMenamin 	int error;
77847a72688SAdrian McMenamin 	struct maple_device *mdev = to_maple_dev(dev);
77947a72688SAdrian McMenamin 	struct maple_driver *mdrv = to_maple_driver(dev->driver);
78047a72688SAdrian McMenamin 
78147a72688SAdrian McMenamin 	mdev->can_unload = vmu_can_unload;
78247a72688SAdrian McMenamin 	mdev->fileerr_handler = vmu_file_error;
78347a72688SAdrian McMenamin 	mdev->driver = mdrv;
78447a72688SAdrian McMenamin 
78547a72688SAdrian McMenamin 	error = vmu_connect(mdev);
78647a72688SAdrian McMenamin 	if (error)
78747a72688SAdrian McMenamin 		return error;
78847a72688SAdrian McMenamin 
78947a72688SAdrian McMenamin 	return 0;
79047a72688SAdrian McMenamin }
79147a72688SAdrian McMenamin 
79247a72688SAdrian McMenamin static int __devexit remove_maple_vmu(struct device *dev)
79347a72688SAdrian McMenamin {
79447a72688SAdrian McMenamin 	struct maple_device *mdev = to_maple_dev(dev);
79547a72688SAdrian McMenamin 
79647a72688SAdrian McMenamin 	vmu_disconnect(mdev);
79747a72688SAdrian McMenamin 	return 0;
79847a72688SAdrian McMenamin }
79947a72688SAdrian McMenamin 
80047a72688SAdrian McMenamin static struct maple_driver vmu_flash_driver = {
80147a72688SAdrian McMenamin 	.function =	MAPLE_FUNC_MEMCARD,
80247a72688SAdrian McMenamin 	.drv = {
80347a72688SAdrian McMenamin 		.name =		"Dreamcast_visual_memory",
80447a72688SAdrian McMenamin 		.probe =	probe_maple_vmu,
8055153b88cSBill Pemberton 		.remove =	remove_maple_vmu,
80647a72688SAdrian McMenamin 	},
80747a72688SAdrian McMenamin };
80847a72688SAdrian McMenamin 
80947a72688SAdrian McMenamin static int __init vmu_flash_map_init(void)
81047a72688SAdrian McMenamin {
81147a72688SAdrian McMenamin 	return maple_driver_register(&vmu_flash_driver);
81247a72688SAdrian McMenamin }
81347a72688SAdrian McMenamin 
81447a72688SAdrian McMenamin static void __exit vmu_flash_map_exit(void)
81547a72688SAdrian McMenamin {
81647a72688SAdrian McMenamin 	maple_driver_unregister(&vmu_flash_driver);
81747a72688SAdrian McMenamin }
81847a72688SAdrian McMenamin 
81947a72688SAdrian McMenamin module_init(vmu_flash_map_init);
82047a72688SAdrian McMenamin module_exit(vmu_flash_map_exit);
82147a72688SAdrian McMenamin 
82247a72688SAdrian McMenamin MODULE_LICENSE("GPL");
82347a72688SAdrian McMenamin MODULE_AUTHOR("Adrian McMenamin");
82447a72688SAdrian McMenamin MODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory");
825