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