[go: nahoru, domu]

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