18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* vmu-flash.c
38c2ecf20Sopenharmony_ci * Driver for SEGA Dreamcast Visual Memory Unit
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) Adrian McMenamin 2002 - 2009
68c2ecf20Sopenharmony_ci * Copyright (c) Paul Mundt 2001
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/maple.h>
138c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
148c2ecf20Sopenharmony_ci#include <linux/mtd/map.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct vmu_cache {
178c2ecf20Sopenharmony_ci	unsigned char *buffer;		/* Cache */
188c2ecf20Sopenharmony_ci	unsigned int block;		/* Which block was cached */
198c2ecf20Sopenharmony_ci	unsigned long jiffies_atc;	/* When was it cached? */
208c2ecf20Sopenharmony_ci	int valid;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct mdev_part {
248c2ecf20Sopenharmony_ci	struct maple_device *mdev;
258c2ecf20Sopenharmony_ci	int partition;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct vmupart {
298c2ecf20Sopenharmony_ci	u16 user_blocks;
308c2ecf20Sopenharmony_ci	u16 root_block;
318c2ecf20Sopenharmony_ci	u16 numblocks;
328c2ecf20Sopenharmony_ci	char *name;
338c2ecf20Sopenharmony_ci	struct vmu_cache *pcache;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct memcard {
378c2ecf20Sopenharmony_ci	u16 tempA;
388c2ecf20Sopenharmony_ci	u16 tempB;
398c2ecf20Sopenharmony_ci	u32 partitions;
408c2ecf20Sopenharmony_ci	u32 blocklen;
418c2ecf20Sopenharmony_ci	u32 writecnt;
428c2ecf20Sopenharmony_ci	u32 readcnt;
438c2ecf20Sopenharmony_ci	u32 removable;
448c2ecf20Sopenharmony_ci	int partition;
458c2ecf20Sopenharmony_ci	int read;
468c2ecf20Sopenharmony_ci	unsigned char *blockread;
478c2ecf20Sopenharmony_ci	struct vmupart *parts;
488c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct vmu_block {
528c2ecf20Sopenharmony_ci	unsigned int num; /* block number */
538c2ecf20Sopenharmony_ci	unsigned int ofs; /* block offset */
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct vmu_block *ofs_to_block(unsigned long src_ofs,
578c2ecf20Sopenharmony_ci	struct mtd_info *mtd, int partition)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct vmu_block *vblock;
608c2ecf20Sopenharmony_ci	struct maple_device *mdev;
618c2ecf20Sopenharmony_ci	struct memcard *card;
628c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
638c2ecf20Sopenharmony_ci	int num;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	mpart = mtd->priv;
668c2ecf20Sopenharmony_ci	mdev = mpart->mdev;
678c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (src_ofs >= card->parts[partition].numblocks * card->blocklen)
708c2ecf20Sopenharmony_ci		goto failed;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	num = src_ofs / card->blocklen;
738c2ecf20Sopenharmony_ci	if (num > card->parts[partition].numblocks)
748c2ecf20Sopenharmony_ci		goto failed;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL);
778c2ecf20Sopenharmony_ci	if (!vblock)
788c2ecf20Sopenharmony_ci		goto failed;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	vblock->num = num;
818c2ecf20Sopenharmony_ci	vblock->ofs = src_ofs % card->blocklen;
828c2ecf20Sopenharmony_ci	return vblock;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cifailed:
858c2ecf20Sopenharmony_ci	return NULL;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Maple bus callback function for reads */
898c2ecf20Sopenharmony_cistatic void vmu_blockread(struct mapleq *mq)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct maple_device *mdev;
928c2ecf20Sopenharmony_ci	struct memcard *card;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	mdev = mq->dev;
958c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
968c2ecf20Sopenharmony_ci	/* copy the read in data */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (unlikely(!card->blockread))
998c2ecf20Sopenharmony_ci		return;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	memcpy(card->blockread, mq->recvbuf->buf + 12,
1028c2ecf20Sopenharmony_ci		card->blocklen/card->readcnt);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Interface with maple bus to read blocks
1078c2ecf20Sopenharmony_ci * caching the results so that other parts
1088c2ecf20Sopenharmony_ci * of the driver can access block reads */
1098c2ecf20Sopenharmony_cistatic int maple_vmu_read_block(unsigned int num, unsigned char *buf,
1108c2ecf20Sopenharmony_ci	struct mtd_info *mtd)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct memcard *card;
1138c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
1148c2ecf20Sopenharmony_ci	struct maple_device *mdev;
1158c2ecf20Sopenharmony_ci	int partition, error = 0, x, wait;
1168c2ecf20Sopenharmony_ci	unsigned char *blockread = NULL;
1178c2ecf20Sopenharmony_ci	struct vmu_cache *pcache;
1188c2ecf20Sopenharmony_ci	__be32 sendbuf;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	mpart = mtd->priv;
1218c2ecf20Sopenharmony_ci	mdev = mpart->mdev;
1228c2ecf20Sopenharmony_ci	partition = mpart->partition;
1238c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
1248c2ecf20Sopenharmony_ci	pcache = card->parts[partition].pcache;
1258c2ecf20Sopenharmony_ci	pcache->valid = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* prepare the cache for this block */
1288c2ecf20Sopenharmony_ci	if (!pcache->buffer) {
1298c2ecf20Sopenharmony_ci		pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL);
1308c2ecf20Sopenharmony_ci		if (!pcache->buffer) {
1318c2ecf20Sopenharmony_ci			dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due"
1328c2ecf20Sopenharmony_ci				" to lack of memory\n", mdev->port,
1338c2ecf20Sopenharmony_ci				mdev->unit);
1348c2ecf20Sopenharmony_ci			error = -ENOMEM;
1358c2ecf20Sopenharmony_ci			goto outB;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/*
1408c2ecf20Sopenharmony_ci	* Reads may be phased - again the hardware spec
1418c2ecf20Sopenharmony_ci	* supports this - though may not be any devices in
1428c2ecf20Sopenharmony_ci	* the wild that implement it, but we will here
1438c2ecf20Sopenharmony_ci	*/
1448c2ecf20Sopenharmony_ci	for (x = 0; x < card->readcnt; x++) {
1458c2ecf20Sopenharmony_ci		sendbuf = cpu_to_be32(partition << 24 | x << 16 | num);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		if (atomic_read(&mdev->busy) == 1) {
1488c2ecf20Sopenharmony_ci			wait_event_interruptible_timeout(mdev->maple_wait,
1498c2ecf20Sopenharmony_ci				atomic_read(&mdev->busy) == 0, HZ);
1508c2ecf20Sopenharmony_ci			if (atomic_read(&mdev->busy) == 1) {
1518c2ecf20Sopenharmony_ci				dev_notice(&mdev->dev, "VMU at (%d, %d)"
1528c2ecf20Sopenharmony_ci					" is busy\n", mdev->port, mdev->unit);
1538c2ecf20Sopenharmony_ci				error = -EAGAIN;
1548c2ecf20Sopenharmony_ci				goto outB;
1558c2ecf20Sopenharmony_ci			}
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		atomic_set(&mdev->busy, 1);
1598c2ecf20Sopenharmony_ci		blockread = kmalloc(card->blocklen/card->readcnt, GFP_KERNEL);
1608c2ecf20Sopenharmony_ci		if (!blockread) {
1618c2ecf20Sopenharmony_ci			error = -ENOMEM;
1628c2ecf20Sopenharmony_ci			atomic_set(&mdev->busy, 0);
1638c2ecf20Sopenharmony_ci			goto outB;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci		card->blockread = blockread;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		maple_getcond_callback(mdev, vmu_blockread, 0,
1688c2ecf20Sopenharmony_ci			MAPLE_FUNC_MEMCARD);
1698c2ecf20Sopenharmony_ci		error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
1708c2ecf20Sopenharmony_ci				MAPLE_COMMAND_BREAD, 2, &sendbuf);
1718c2ecf20Sopenharmony_ci		/* Very long timeouts seem to be needed when box is stressed */
1728c2ecf20Sopenharmony_ci		wait = wait_event_interruptible_timeout(mdev->maple_wait,
1738c2ecf20Sopenharmony_ci			(atomic_read(&mdev->busy) == 0 ||
1748c2ecf20Sopenharmony_ci			atomic_read(&mdev->busy) == 2), HZ * 3);
1758c2ecf20Sopenharmony_ci		/*
1768c2ecf20Sopenharmony_ci		* MTD layer does not handle hotplugging well
1778c2ecf20Sopenharmony_ci		* so have to return errors when VMU is unplugged
1788c2ecf20Sopenharmony_ci		* in the middle of a read (busy == 2)
1798c2ecf20Sopenharmony_ci		*/
1808c2ecf20Sopenharmony_ci		if (error || atomic_read(&mdev->busy) == 2) {
1818c2ecf20Sopenharmony_ci			if (atomic_read(&mdev->busy) == 2)
1828c2ecf20Sopenharmony_ci				error = -ENXIO;
1838c2ecf20Sopenharmony_ci			atomic_set(&mdev->busy, 0);
1848c2ecf20Sopenharmony_ci			card->blockread = NULL;
1858c2ecf20Sopenharmony_ci			goto outA;
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci		if (wait == 0 || wait == -ERESTARTSYS) {
1888c2ecf20Sopenharmony_ci			card->blockread = NULL;
1898c2ecf20Sopenharmony_ci			atomic_set(&mdev->busy, 0);
1908c2ecf20Sopenharmony_ci			error = -EIO;
1918c2ecf20Sopenharmony_ci			list_del_init(&(mdev->mq->list));
1928c2ecf20Sopenharmony_ci			kfree(mdev->mq->sendbuf);
1938c2ecf20Sopenharmony_ci			mdev->mq->sendbuf = NULL;
1948c2ecf20Sopenharmony_ci			if (wait == -ERESTARTSYS) {
1958c2ecf20Sopenharmony_ci				dev_warn(&mdev->dev, "VMU read on (%d, %d)"
1968c2ecf20Sopenharmony_ci					" interrupted on block 0x%X\n",
1978c2ecf20Sopenharmony_ci					mdev->port, mdev->unit, num);
1988c2ecf20Sopenharmony_ci			} else
1998c2ecf20Sopenharmony_ci				dev_notice(&mdev->dev, "VMU read on (%d, %d)"
2008c2ecf20Sopenharmony_ci					" timed out on block 0x%X\n",
2018c2ecf20Sopenharmony_ci					mdev->port, mdev->unit, num);
2028c2ecf20Sopenharmony_ci			goto outA;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		memcpy(buf + (card->blocklen/card->readcnt) * x, blockread,
2068c2ecf20Sopenharmony_ci			card->blocklen/card->readcnt);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		memcpy(pcache->buffer + (card->blocklen/card->readcnt) * x,
2098c2ecf20Sopenharmony_ci			card->blockread, card->blocklen/card->readcnt);
2108c2ecf20Sopenharmony_ci		card->blockread = NULL;
2118c2ecf20Sopenharmony_ci		pcache->block = num;
2128c2ecf20Sopenharmony_ci		pcache->jiffies_atc = jiffies;
2138c2ecf20Sopenharmony_ci		pcache->valid = 1;
2148c2ecf20Sopenharmony_ci		kfree(blockread);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return error;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cioutA:
2208c2ecf20Sopenharmony_ci	kfree(blockread);
2218c2ecf20Sopenharmony_cioutB:
2228c2ecf20Sopenharmony_ci	return error;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/* communicate with maple bus for phased writing */
2268c2ecf20Sopenharmony_cistatic int maple_vmu_write_block(unsigned int num, const unsigned char *buf,
2278c2ecf20Sopenharmony_ci	struct mtd_info *mtd)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct memcard *card;
2308c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
2318c2ecf20Sopenharmony_ci	struct maple_device *mdev;
2328c2ecf20Sopenharmony_ci	int partition, error, locking, x, phaselen, wait;
2338c2ecf20Sopenharmony_ci	__be32 *sendbuf;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	mpart = mtd->priv;
2368c2ecf20Sopenharmony_ci	mdev = mpart->mdev;
2378c2ecf20Sopenharmony_ci	partition = mpart->partition;
2388c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	phaselen = card->blocklen/card->writecnt;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	sendbuf = kmalloc(phaselen + 4, GFP_KERNEL);
2438c2ecf20Sopenharmony_ci	if (!sendbuf) {
2448c2ecf20Sopenharmony_ci		error = -ENOMEM;
2458c2ecf20Sopenharmony_ci		goto fail_nosendbuf;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	for (x = 0; x < card->writecnt; x++) {
2488c2ecf20Sopenharmony_ci		sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num);
2498c2ecf20Sopenharmony_ci		memcpy(&sendbuf[1], buf + phaselen * x, phaselen);
2508c2ecf20Sopenharmony_ci		/* wait until the device is not busy doing something else
2518c2ecf20Sopenharmony_ci		* or 1 second - which ever is longer */
2528c2ecf20Sopenharmony_ci		if (atomic_read(&mdev->busy) == 1) {
2538c2ecf20Sopenharmony_ci			wait_event_interruptible_timeout(mdev->maple_wait,
2548c2ecf20Sopenharmony_ci				atomic_read(&mdev->busy) == 0, HZ);
2558c2ecf20Sopenharmony_ci			if (atomic_read(&mdev->busy) == 1) {
2568c2ecf20Sopenharmony_ci				error = -EBUSY;
2578c2ecf20Sopenharmony_ci				dev_notice(&mdev->dev, "VMU write at (%d, %d)"
2588c2ecf20Sopenharmony_ci					"failed - device is busy\n",
2598c2ecf20Sopenharmony_ci					mdev->port, mdev->unit);
2608c2ecf20Sopenharmony_ci				goto fail_nolock;
2618c2ecf20Sopenharmony_ci			}
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci		atomic_set(&mdev->busy, 1);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
2668c2ecf20Sopenharmony_ci			MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf);
2678c2ecf20Sopenharmony_ci		wait = wait_event_interruptible_timeout(mdev->maple_wait,
2688c2ecf20Sopenharmony_ci			atomic_read(&mdev->busy) == 0, HZ/10);
2698c2ecf20Sopenharmony_ci		if (locking) {
2708c2ecf20Sopenharmony_ci			error = -EIO;
2718c2ecf20Sopenharmony_ci			atomic_set(&mdev->busy, 0);
2728c2ecf20Sopenharmony_ci			goto fail_nolock;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci		if (atomic_read(&mdev->busy) == 2) {
2758c2ecf20Sopenharmony_ci			atomic_set(&mdev->busy, 0);
2768c2ecf20Sopenharmony_ci		} else if (wait == 0 || wait == -ERESTARTSYS) {
2778c2ecf20Sopenharmony_ci			error = -EIO;
2788c2ecf20Sopenharmony_ci			dev_warn(&mdev->dev, "Write at (%d, %d) of block"
2798c2ecf20Sopenharmony_ci				" 0x%X at phase %d failed: could not"
2808c2ecf20Sopenharmony_ci				" communicate with VMU", mdev->port,
2818c2ecf20Sopenharmony_ci				mdev->unit, num, x);
2828c2ecf20Sopenharmony_ci			atomic_set(&mdev->busy, 0);
2838c2ecf20Sopenharmony_ci			kfree(mdev->mq->sendbuf);
2848c2ecf20Sopenharmony_ci			mdev->mq->sendbuf = NULL;
2858c2ecf20Sopenharmony_ci			list_del_init(&(mdev->mq->list));
2868c2ecf20Sopenharmony_ci			goto fail_nolock;
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci	kfree(sendbuf);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return card->blocklen;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cifail_nolock:
2948c2ecf20Sopenharmony_ci	kfree(sendbuf);
2958c2ecf20Sopenharmony_cifail_nosendbuf:
2968c2ecf20Sopenharmony_ci	dev_err(&mdev->dev, "VMU (%d, %d): write failed\n", mdev->port,
2978c2ecf20Sopenharmony_ci		mdev->unit);
2988c2ecf20Sopenharmony_ci	return error;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/* mtd function to simulate reading byte by byte */
3028c2ecf20Sopenharmony_cistatic unsigned char vmu_flash_read_char(unsigned long ofs, int *retval,
3038c2ecf20Sopenharmony_ci	struct mtd_info *mtd)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct vmu_block *vblock;
3068c2ecf20Sopenharmony_ci	struct memcard *card;
3078c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
3088c2ecf20Sopenharmony_ci	struct maple_device *mdev;
3098c2ecf20Sopenharmony_ci	unsigned char *buf, ret;
3108c2ecf20Sopenharmony_ci	int partition, error;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	mpart = mtd->priv;
3138c2ecf20Sopenharmony_ci	mdev = mpart->mdev;
3148c2ecf20Sopenharmony_ci	partition = mpart->partition;
3158c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
3168c2ecf20Sopenharmony_ci	*retval =  0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	buf = kmalloc(card->blocklen, GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!buf) {
3208c2ecf20Sopenharmony_ci		*retval = 1;
3218c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3228c2ecf20Sopenharmony_ci		goto finish;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	vblock = ofs_to_block(ofs, mtd, partition);
3268c2ecf20Sopenharmony_ci	if (!vblock) {
3278c2ecf20Sopenharmony_ci		*retval = 3;
3288c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3298c2ecf20Sopenharmony_ci		goto out_buf;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	error = maple_vmu_read_block(vblock->num, buf, mtd);
3338c2ecf20Sopenharmony_ci	if (error) {
3348c2ecf20Sopenharmony_ci		ret = error;
3358c2ecf20Sopenharmony_ci		*retval = 2;
3368c2ecf20Sopenharmony_ci		goto out_vblock;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ret = buf[vblock->ofs];
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ciout_vblock:
3428c2ecf20Sopenharmony_ci	kfree(vblock);
3438c2ecf20Sopenharmony_ciout_buf:
3448c2ecf20Sopenharmony_ci	kfree(buf);
3458c2ecf20Sopenharmony_cifinish:
3468c2ecf20Sopenharmony_ci	return ret;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/* mtd higher order function to read flash */
3508c2ecf20Sopenharmony_cistatic int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
3518c2ecf20Sopenharmony_ci	size_t *retlen,  u_char *buf)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct maple_device *mdev;
3548c2ecf20Sopenharmony_ci	struct memcard *card;
3558c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
3568c2ecf20Sopenharmony_ci	struct vmu_cache *pcache;
3578c2ecf20Sopenharmony_ci	struct vmu_block *vblock;
3588c2ecf20Sopenharmony_ci	int index = 0, retval, partition, leftover, numblocks;
3598c2ecf20Sopenharmony_ci	unsigned char cx;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	mpart = mtd->priv;
3628c2ecf20Sopenharmony_ci	mdev = mpart->mdev;
3638c2ecf20Sopenharmony_ci	partition = mpart->partition;
3648c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	numblocks = card->parts[partition].numblocks;
3678c2ecf20Sopenharmony_ci	if (from + len > numblocks * card->blocklen)
3688c2ecf20Sopenharmony_ci		len = numblocks * card->blocklen - from;
3698c2ecf20Sopenharmony_ci	if (len == 0)
3708c2ecf20Sopenharmony_ci		return -EIO;
3718c2ecf20Sopenharmony_ci	/* Have we cached this bit already? */
3728c2ecf20Sopenharmony_ci	pcache = card->parts[partition].pcache;
3738c2ecf20Sopenharmony_ci	do {
3748c2ecf20Sopenharmony_ci		vblock =  ofs_to_block(from + index, mtd, partition);
3758c2ecf20Sopenharmony_ci		if (!vblock)
3768c2ecf20Sopenharmony_ci			return -ENOMEM;
3778c2ecf20Sopenharmony_ci		/* Have we cached this and is the cache valid and timely? */
3788c2ecf20Sopenharmony_ci		if (pcache->valid &&
3798c2ecf20Sopenharmony_ci			time_before(jiffies, pcache->jiffies_atc + HZ) &&
3808c2ecf20Sopenharmony_ci			(pcache->block == vblock->num)) {
3818c2ecf20Sopenharmony_ci			/* we have cached it, so do necessary copying */
3828c2ecf20Sopenharmony_ci			leftover = card->blocklen - vblock->ofs;
3838c2ecf20Sopenharmony_ci			if (vblock->ofs + len - index < card->blocklen) {
3848c2ecf20Sopenharmony_ci				/* only a bit of this block to copy */
3858c2ecf20Sopenharmony_ci				memcpy(buf + index,
3868c2ecf20Sopenharmony_ci					pcache->buffer + vblock->ofs,
3878c2ecf20Sopenharmony_ci					len - index);
3888c2ecf20Sopenharmony_ci				index = len;
3898c2ecf20Sopenharmony_ci			} else {
3908c2ecf20Sopenharmony_ci				/* otherwise copy remainder of whole block */
3918c2ecf20Sopenharmony_ci				memcpy(buf + index, pcache->buffer +
3928c2ecf20Sopenharmony_ci					vblock->ofs, leftover);
3938c2ecf20Sopenharmony_ci				index += leftover;
3948c2ecf20Sopenharmony_ci			}
3958c2ecf20Sopenharmony_ci		} else {
3968c2ecf20Sopenharmony_ci			/*
3978c2ecf20Sopenharmony_ci			* Not cached so read one byte -
3988c2ecf20Sopenharmony_ci			* but cache the rest of the block
3998c2ecf20Sopenharmony_ci			*/
4008c2ecf20Sopenharmony_ci			cx = vmu_flash_read_char(from + index, &retval, mtd);
4018c2ecf20Sopenharmony_ci			if (retval) {
4028c2ecf20Sopenharmony_ci				*retlen = index;
4038c2ecf20Sopenharmony_ci				kfree(vblock);
4048c2ecf20Sopenharmony_ci				return cx;
4058c2ecf20Sopenharmony_ci			}
4068c2ecf20Sopenharmony_ci			memset(buf + index, cx, 1);
4078c2ecf20Sopenharmony_ci			index++;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci		kfree(vblock);
4108c2ecf20Sopenharmony_ci	} while (len > index);
4118c2ecf20Sopenharmony_ci	*retlen = index;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
4178c2ecf20Sopenharmony_ci	size_t *retlen, const u_char *buf)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct maple_device *mdev;
4208c2ecf20Sopenharmony_ci	struct memcard *card;
4218c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
4228c2ecf20Sopenharmony_ci	int index = 0, partition, error = 0, numblocks;
4238c2ecf20Sopenharmony_ci	struct vmu_cache *pcache;
4248c2ecf20Sopenharmony_ci	struct vmu_block *vblock;
4258c2ecf20Sopenharmony_ci	unsigned char *buffer;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	mpart = mtd->priv;
4288c2ecf20Sopenharmony_ci	mdev = mpart->mdev;
4298c2ecf20Sopenharmony_ci	partition = mpart->partition;
4308c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	numblocks = card->parts[partition].numblocks;
4338c2ecf20Sopenharmony_ci	if (to + len > numblocks * card->blocklen)
4348c2ecf20Sopenharmony_ci		len = numblocks * card->blocklen - to;
4358c2ecf20Sopenharmony_ci	if (len == 0) {
4368c2ecf20Sopenharmony_ci		error = -EIO;
4378c2ecf20Sopenharmony_ci		goto failed;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	vblock = ofs_to_block(to, mtd, partition);
4418c2ecf20Sopenharmony_ci	if (!vblock) {
4428c2ecf20Sopenharmony_ci		error = -ENOMEM;
4438c2ecf20Sopenharmony_ci		goto failed;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	buffer = kmalloc(card->blocklen, GFP_KERNEL);
4478c2ecf20Sopenharmony_ci	if (!buffer) {
4488c2ecf20Sopenharmony_ci		error = -ENOMEM;
4498c2ecf20Sopenharmony_ci		goto fail_buffer;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	do {
4538c2ecf20Sopenharmony_ci		/* Read in the block we are to write to */
4548c2ecf20Sopenharmony_ci		error = maple_vmu_read_block(vblock->num, buffer, mtd);
4558c2ecf20Sopenharmony_ci		if (error)
4568c2ecf20Sopenharmony_ci			goto fail_io;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		do {
4598c2ecf20Sopenharmony_ci			buffer[vblock->ofs] = buf[index];
4608c2ecf20Sopenharmony_ci			vblock->ofs++;
4618c2ecf20Sopenharmony_ci			index++;
4628c2ecf20Sopenharmony_ci			if (index >= len)
4638c2ecf20Sopenharmony_ci				break;
4648c2ecf20Sopenharmony_ci		} while (vblock->ofs < card->blocklen);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		/* write out new buffer */
4678c2ecf20Sopenharmony_ci		error = maple_vmu_write_block(vblock->num, buffer, mtd);
4688c2ecf20Sopenharmony_ci		/* invalidate the cache */
4698c2ecf20Sopenharmony_ci		pcache = card->parts[partition].pcache;
4708c2ecf20Sopenharmony_ci		pcache->valid = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		if (error != card->blocklen)
4738c2ecf20Sopenharmony_ci			goto fail_io;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		vblock->num++;
4768c2ecf20Sopenharmony_ci		vblock->ofs = 0;
4778c2ecf20Sopenharmony_ci	} while (len > index);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	kfree(buffer);
4808c2ecf20Sopenharmony_ci	*retlen = index;
4818c2ecf20Sopenharmony_ci	kfree(vblock);
4828c2ecf20Sopenharmony_ci	return 0;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cifail_io:
4858c2ecf20Sopenharmony_ci	kfree(buffer);
4868c2ecf20Sopenharmony_cifail_buffer:
4878c2ecf20Sopenharmony_ci	kfree(vblock);
4888c2ecf20Sopenharmony_cifailed:
4898c2ecf20Sopenharmony_ci	dev_err(&mdev->dev, "VMU write failing with error %d\n", error);
4908c2ecf20Sopenharmony_ci	return error;
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic void vmu_flash_sync(struct mtd_info *mtd)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	/* Do nothing here */
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci/* Maple bus callback function to recursively query hardware details */
4998c2ecf20Sopenharmony_cistatic void vmu_queryblocks(struct mapleq *mq)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct maple_device *mdev;
5028c2ecf20Sopenharmony_ci	unsigned short *res;
5038c2ecf20Sopenharmony_ci	struct memcard *card;
5048c2ecf20Sopenharmony_ci	__be32 partnum;
5058c2ecf20Sopenharmony_ci	struct vmu_cache *pcache;
5068c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
5078c2ecf20Sopenharmony_ci	struct mtd_info *mtd_cur;
5088c2ecf20Sopenharmony_ci	struct vmupart *part_cur;
5098c2ecf20Sopenharmony_ci	int error;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	mdev = mq->dev;
5128c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
5138c2ecf20Sopenharmony_ci	res = (unsigned short *) (mq->recvbuf->buf);
5148c2ecf20Sopenharmony_ci	card->tempA = res[12];
5158c2ecf20Sopenharmony_ci	card->tempB = res[6];
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	dev_info(&mdev->dev, "VMU device at partition %d has %d user "
5188c2ecf20Sopenharmony_ci		"blocks with a root block at %d\n", card->partition,
5198c2ecf20Sopenharmony_ci		card->tempA, card->tempB);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	part_cur = &card->parts[card->partition];
5228c2ecf20Sopenharmony_ci	part_cur->user_blocks = card->tempA;
5238c2ecf20Sopenharmony_ci	part_cur->root_block = card->tempB;
5248c2ecf20Sopenharmony_ci	part_cur->numblocks = card->tempB + 1;
5258c2ecf20Sopenharmony_ci	part_cur->name = kmalloc(12, GFP_KERNEL);
5268c2ecf20Sopenharmony_ci	if (!part_cur->name)
5278c2ecf20Sopenharmony_ci		goto fail_name;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	sprintf(part_cur->name, "vmu%d.%d.%d",
5308c2ecf20Sopenharmony_ci		mdev->port, mdev->unit, card->partition);
5318c2ecf20Sopenharmony_ci	mtd_cur = &card->mtd[card->partition];
5328c2ecf20Sopenharmony_ci	mtd_cur->name = part_cur->name;
5338c2ecf20Sopenharmony_ci	mtd_cur->type = 8;
5348c2ecf20Sopenharmony_ci	mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
5358c2ecf20Sopenharmony_ci	mtd_cur->size = part_cur->numblocks * card->blocklen;
5368c2ecf20Sopenharmony_ci	mtd_cur->erasesize = card->blocklen;
5378c2ecf20Sopenharmony_ci	mtd_cur->_write = vmu_flash_write;
5388c2ecf20Sopenharmony_ci	mtd_cur->_read = vmu_flash_read;
5398c2ecf20Sopenharmony_ci	mtd_cur->_sync = vmu_flash_sync;
5408c2ecf20Sopenharmony_ci	mtd_cur->writesize = card->blocklen;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
5438c2ecf20Sopenharmony_ci	if (!mpart)
5448c2ecf20Sopenharmony_ci		goto fail_mpart;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	mpart->mdev = mdev;
5478c2ecf20Sopenharmony_ci	mpart->partition = card->partition;
5488c2ecf20Sopenharmony_ci	mtd_cur->priv = mpart;
5498c2ecf20Sopenharmony_ci	mtd_cur->owner = THIS_MODULE;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL);
5528c2ecf20Sopenharmony_ci	if (!pcache)
5538c2ecf20Sopenharmony_ci		goto fail_cache_create;
5548c2ecf20Sopenharmony_ci	part_cur->pcache = pcache;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	error = mtd_device_register(mtd_cur, NULL, 0);
5578c2ecf20Sopenharmony_ci	if (error)
5588c2ecf20Sopenharmony_ci		goto fail_mtd_register;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	maple_getcond_callback(mdev, NULL, 0,
5618c2ecf20Sopenharmony_ci		MAPLE_FUNC_MEMCARD);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/*
5648c2ecf20Sopenharmony_ci	* Set up a recursive call to the (probably theoretical)
5658c2ecf20Sopenharmony_ci	* second or more partition
5668c2ecf20Sopenharmony_ci	*/
5678c2ecf20Sopenharmony_ci	if (++card->partition < card->partitions) {
5688c2ecf20Sopenharmony_ci		partnum = cpu_to_be32(card->partition << 24);
5698c2ecf20Sopenharmony_ci		maple_getcond_callback(mdev, vmu_queryblocks, 0,
5708c2ecf20Sopenharmony_ci			MAPLE_FUNC_MEMCARD);
5718c2ecf20Sopenharmony_ci		maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
5728c2ecf20Sopenharmony_ci			MAPLE_COMMAND_GETMINFO, 2, &partnum);
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	return;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cifail_mtd_register:
5778c2ecf20Sopenharmony_ci	dev_err(&mdev->dev, "Could not register maple device at (%d, %d)"
5788c2ecf20Sopenharmony_ci		"error is 0x%X\n", mdev->port, mdev->unit, error);
5798c2ecf20Sopenharmony_ci	for (error = 0; error <= card->partition; error++) {
5808c2ecf20Sopenharmony_ci		kfree(((card->parts)[error]).pcache);
5818c2ecf20Sopenharmony_ci		((card->parts)[error]).pcache = NULL;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_cifail_cache_create:
5848c2ecf20Sopenharmony_cifail_mpart:
5858c2ecf20Sopenharmony_ci	for (error = 0; error <= card->partition; error++) {
5868c2ecf20Sopenharmony_ci		kfree(((card->mtd)[error]).priv);
5878c2ecf20Sopenharmony_ci		((card->mtd)[error]).priv = NULL;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci	maple_getcond_callback(mdev, NULL, 0,
5908c2ecf20Sopenharmony_ci		MAPLE_FUNC_MEMCARD);
5918c2ecf20Sopenharmony_ci	kfree(part_cur->name);
5928c2ecf20Sopenharmony_cifail_name:
5938c2ecf20Sopenharmony_ci	return;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci/* Handles very basic info about the flash, queries for details */
5978c2ecf20Sopenharmony_cistatic int vmu_connect(struct maple_device *mdev)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	unsigned long test_flash_data, basic_flash_data;
6008c2ecf20Sopenharmony_ci	int c, error;
6018c2ecf20Sopenharmony_ci	struct memcard *card;
6028c2ecf20Sopenharmony_ci	u32 partnum = 0;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	test_flash_data = be32_to_cpu(mdev->devinfo.function);
6058c2ecf20Sopenharmony_ci	/* Need to count how many bits are set - to find out which
6068c2ecf20Sopenharmony_ci	 * function_data element has details of the memory card
6078c2ecf20Sopenharmony_ci	 */
6088c2ecf20Sopenharmony_ci	c = hweight_long(test_flash_data);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
6138c2ecf20Sopenharmony_ci	if (!card) {
6148c2ecf20Sopenharmony_ci		error = -ENOMEM;
6158c2ecf20Sopenharmony_ci		goto fail_nomem;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	card->partitions = (basic_flash_data >> 24 & 0xFF) + 1;
6198c2ecf20Sopenharmony_ci	card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5;
6208c2ecf20Sopenharmony_ci	card->writecnt = basic_flash_data >> 12 & 0xF;
6218c2ecf20Sopenharmony_ci	card->readcnt = basic_flash_data >> 8 & 0xF;
6228c2ecf20Sopenharmony_ci	card->removable = basic_flash_data >> 7 & 1;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	card->partition = 0;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	/*
6278c2ecf20Sopenharmony_ci	* Not sure there are actually any multi-partition devices in the
6288c2ecf20Sopenharmony_ci	* real world, but the hardware supports them, so, so will we
6298c2ecf20Sopenharmony_ci	*/
6308c2ecf20Sopenharmony_ci	card->parts = kmalloc_array(card->partitions, sizeof(struct vmupart),
6318c2ecf20Sopenharmony_ci				    GFP_KERNEL);
6328c2ecf20Sopenharmony_ci	if (!card->parts) {
6338c2ecf20Sopenharmony_ci		error = -ENOMEM;
6348c2ecf20Sopenharmony_ci		goto fail_partitions;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	card->mtd = kmalloc_array(card->partitions, sizeof(struct mtd_info),
6388c2ecf20Sopenharmony_ci				  GFP_KERNEL);
6398c2ecf20Sopenharmony_ci	if (!card->mtd) {
6408c2ecf20Sopenharmony_ci		error = -ENOMEM;
6418c2ecf20Sopenharmony_ci		goto fail_mtd_info;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	maple_set_drvdata(mdev, card);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/*
6478c2ecf20Sopenharmony_ci	* We want to trap meminfo not get cond
6488c2ecf20Sopenharmony_ci	* so set interval to zero, but rely on maple bus
6498c2ecf20Sopenharmony_ci	* driver to pass back the results of the meminfo
6508c2ecf20Sopenharmony_ci	*/
6518c2ecf20Sopenharmony_ci	maple_getcond_callback(mdev, vmu_queryblocks, 0,
6528c2ecf20Sopenharmony_ci		MAPLE_FUNC_MEMCARD);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* Make sure we are clear to go */
6558c2ecf20Sopenharmony_ci	if (atomic_read(&mdev->busy) == 1) {
6568c2ecf20Sopenharmony_ci		wait_event_interruptible_timeout(mdev->maple_wait,
6578c2ecf20Sopenharmony_ci			atomic_read(&mdev->busy) == 0, HZ);
6588c2ecf20Sopenharmony_ci		if (atomic_read(&mdev->busy) == 1) {
6598c2ecf20Sopenharmony_ci			dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n",
6608c2ecf20Sopenharmony_ci				mdev->port, mdev->unit);
6618c2ecf20Sopenharmony_ci			error = -EAGAIN;
6628c2ecf20Sopenharmony_ci			goto fail_device_busy;
6638c2ecf20Sopenharmony_ci		}
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	atomic_set(&mdev->busy, 1);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/*
6698c2ecf20Sopenharmony_ci	* Set up the minfo call: vmu_queryblocks will handle
6708c2ecf20Sopenharmony_ci	* the information passed back
6718c2ecf20Sopenharmony_ci	*/
6728c2ecf20Sopenharmony_ci	error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
6738c2ecf20Sopenharmony_ci		MAPLE_COMMAND_GETMINFO, 2, &partnum);
6748c2ecf20Sopenharmony_ci	if (error) {
6758c2ecf20Sopenharmony_ci		dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)"
6768c2ecf20Sopenharmony_ci			" error is 0x%X\n", mdev->port, mdev->unit, error);
6778c2ecf20Sopenharmony_ci		goto fail_mtd_info;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	return 0;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cifail_device_busy:
6828c2ecf20Sopenharmony_ci	kfree(card->mtd);
6838c2ecf20Sopenharmony_cifail_mtd_info:
6848c2ecf20Sopenharmony_ci	kfree(card->parts);
6858c2ecf20Sopenharmony_cifail_partitions:
6868c2ecf20Sopenharmony_ci	kfree(card);
6878c2ecf20Sopenharmony_cifail_nomem:
6888c2ecf20Sopenharmony_ci	return error;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic void vmu_disconnect(struct maple_device *mdev)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct memcard *card;
6948c2ecf20Sopenharmony_ci	struct mdev_part *mpart;
6958c2ecf20Sopenharmony_ci	int x;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	mdev->callback = NULL;
6988c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
6998c2ecf20Sopenharmony_ci	for (x = 0; x < card->partitions; x++) {
7008c2ecf20Sopenharmony_ci		mpart = ((card->mtd)[x]).priv;
7018c2ecf20Sopenharmony_ci		mpart->mdev = NULL;
7028c2ecf20Sopenharmony_ci		mtd_device_unregister(&((card->mtd)[x]));
7038c2ecf20Sopenharmony_ci		kfree(((card->parts)[x]).name);
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci	kfree(card->parts);
7068c2ecf20Sopenharmony_ci	kfree(card->mtd);
7078c2ecf20Sopenharmony_ci	kfree(card);
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci/* Callback to handle eccentricities of both mtd subsystem
7118c2ecf20Sopenharmony_ci * and general flakyness of Dreamcast VMUs
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_cistatic int vmu_can_unload(struct maple_device *mdev)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct memcard *card;
7168c2ecf20Sopenharmony_ci	int x;
7178c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	card = maple_get_drvdata(mdev);
7208c2ecf20Sopenharmony_ci	for (x = 0; x < card->partitions; x++) {
7218c2ecf20Sopenharmony_ci		mtd = &((card->mtd)[x]);
7228c2ecf20Sopenharmony_ci		if (mtd->usecount > 0)
7238c2ecf20Sopenharmony_ci			return 0;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci	return 1;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci#define ERRSTR "VMU at (%d, %d) file error -"
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic void vmu_file_error(struct maple_device *mdev, void *recvbuf)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	enum maple_file_errors error = ((int *)recvbuf)[1];
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	switch (error) {
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	case MAPLE_FILEERR_INVALID_PARTITION:
7378c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " invalid partition number\n",
7388c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
7398c2ecf20Sopenharmony_ci		break;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	case MAPLE_FILEERR_PHASE_ERROR:
7428c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " phase error\n",
7438c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
7448c2ecf20Sopenharmony_ci		break;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	case MAPLE_FILEERR_INVALID_BLOCK:
7478c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " invalid block number\n",
7488c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
7498c2ecf20Sopenharmony_ci		break;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	case MAPLE_FILEERR_WRITE_ERROR:
7528c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " write error\n",
7538c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
7548c2ecf20Sopenharmony_ci		break;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	case MAPLE_FILEERR_INVALID_WRITE_LENGTH:
7578c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " invalid write length\n",
7588c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
7598c2ecf20Sopenharmony_ci		break;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	case MAPLE_FILEERR_BAD_CRC:
7628c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " bad CRC\n",
7638c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
7648c2ecf20Sopenharmony_ci		break;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	default:
7678c2ecf20Sopenharmony_ci		dev_notice(&mdev->dev, ERRSTR " 0x%X\n",
7688c2ecf20Sopenharmony_ci			mdev->port, mdev->unit, error);
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic int probe_maple_vmu(struct device *dev)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	struct maple_device *mdev = to_maple_dev(dev);
7768c2ecf20Sopenharmony_ci	struct maple_driver *mdrv = to_maple_driver(dev->driver);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	mdev->can_unload = vmu_can_unload;
7798c2ecf20Sopenharmony_ci	mdev->fileerr_handler = vmu_file_error;
7808c2ecf20Sopenharmony_ci	mdev->driver = mdrv;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	return vmu_connect(mdev);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic int remove_maple_vmu(struct device *dev)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct maple_device *mdev = to_maple_dev(dev);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	vmu_disconnect(mdev);
7908c2ecf20Sopenharmony_ci	return 0;
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic struct maple_driver vmu_flash_driver = {
7948c2ecf20Sopenharmony_ci	.function =	MAPLE_FUNC_MEMCARD,
7958c2ecf20Sopenharmony_ci	.drv = {
7968c2ecf20Sopenharmony_ci		.name =		"Dreamcast_visual_memory",
7978c2ecf20Sopenharmony_ci		.probe =	probe_maple_vmu,
7988c2ecf20Sopenharmony_ci		.remove =	remove_maple_vmu,
7998c2ecf20Sopenharmony_ci	},
8008c2ecf20Sopenharmony_ci};
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int __init vmu_flash_map_init(void)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	return maple_driver_register(&vmu_flash_driver);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic void __exit vmu_flash_map_exit(void)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	maple_driver_unregister(&vmu_flash_driver);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cimodule_init(vmu_flash_map_init);
8138c2ecf20Sopenharmony_cimodule_exit(vmu_flash_map_exit);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Adrian McMenamin");
8178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory");
818