// SPDX-License-Identifier: GPL-2.0-only /* vmu-flash.c * Driver for SEGA Dreamcast Visual Memory Unit * * Copyright (c) Adrian McMenamin 2002 - 2009 * Copyright (c) Paul Mundt 2001
*/ #include <linux/init.h> #include <linux/slab.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/maple.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h>
struct vmu_cache { unsignedchar *buffer; /* Cache */ unsignedint block; /* Which block was cached */ unsignedlong jiffies_atc; /* When was it cached? */ int valid;
};
struct mdev_part { struct maple_device *mdev; int partition;
};
/* Interface with maple bus to read blocks * caching the results so that other parts
* of the driver can access block reads */ staticint maple_vmu_read_block(unsignedint num, unsignedchar *buf, struct mtd_info *mtd)
{ struct memcard *card; struct mdev_part *mpart; struct maple_device *mdev; int partition, error = 0, x, wait; unsignedchar *blockread = NULL; struct vmu_cache *pcache;
__be32 sendbuf;
/* prepare the cache for this block */ if (!pcache->buffer) {
pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL); if (!pcache->buffer) {
dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due" " to lack of memory\n", mdev->port,
mdev->unit);
error = -ENOMEM; goto outB;
}
}
/* * Reads may be phased - again the hardware spec * supports this - though may not be any devices in * the wild that implement it, but we will here
*/ for (x = 0; x < card->readcnt; x++) {
sendbuf = cpu_to_be32(partition << 24 | x << 16 | num);
if (atomic_read(&mdev->busy) == 1) {
wait_event_interruptible_timeout(mdev->maple_wait,
atomic_read(&mdev->busy) == 0, HZ); if (atomic_read(&mdev->busy) == 1) {
dev_notice(&mdev->dev, "VMU at (%d, %d)" " is busy\n", mdev->port, mdev->unit);
error = -EAGAIN; goto outB;
}
}
numblocks = card->parts[partition].numblocks; if (from + len > numblocks * card->blocklen)
len = numblocks * card->blocklen - from; if (len == 0) return -EIO; /* Have we cached this bit already? */
pcache = card->parts[partition].pcache; do {
vblock = ofs_to_block(from + index, mtd, partition); if (!vblock) return -ENOMEM; /* Have we cached this and is the cache valid and timely? */ if (pcache->valid &&
time_before(jiffies, pcache->jiffies_atc + HZ) &&
(pcache->block == vblock->num)) { /* we have cached it, so do necessary copying */
leftover = card->blocklen - vblock->ofs; if (vblock->ofs + len - index < card->blocklen) { /* only a bit of this block to copy */
memcpy(buf + index,
pcache->buffer + vblock->ofs,
len - index);
index = len;
} else { /* otherwise copy remainder of whole block */
memcpy(buf + index, pcache->buffer +
vblock->ofs, leftover);
index += leftover;
}
} else { /* * Not cached so read one byte - * but cache the rest of the block
*/
cx = vmu_flash_read_char(from + index, &retval, mtd); if (retval) {
*retlen = index;
kfree(vblock); return cx;
}
memset(buf + index, cx, 1);
index++;
}
kfree(vblock);
} while (len > index);
*retlen = index;
/* * Set up a recursive call to the (probably theoretical) * second or more partition
*/ if (++card->partition < card->partitions) {
partnum = cpu_to_be32(card->partition << 24);
maple_getcond_callback(mdev, vmu_queryblocks, 0,
MAPLE_FUNC_MEMCARD);
maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
MAPLE_COMMAND_GETMINFO, 2, &partnum);
} return;
fail_mtd_register:
dev_err(&mdev->dev, "Could not register maple device at (%d, %d)" "error is 0x%X\n", mdev->port, mdev->unit, error); for (error = 0; error <= card->partition; error++) {
kfree(((card->parts)[error]).pcache);
((card->parts)[error]).pcache = NULL;
}
fail_cache_create:
fail_mpart: for (error = 0; error <= card->partition; error++) {
kfree(((card->mtd)[error]).priv);
((card->mtd)[error]).priv = NULL;
}
maple_getcond_callback(mdev, NULL, 0,
MAPLE_FUNC_MEMCARD);
kfree(part_cur->name);
fail_name: return;
}
/* Handles very basic info about the flash, queries for details */ staticint vmu_connect(struct maple_device *mdev)
{ unsignedlong test_flash_data, basic_flash_data; int c, error; struct memcard *card;
u32 partnum = 0;
test_flash_data = be32_to_cpu(mdev->devinfo.function); /* Need to count how many bits are set - to find out which * function_data element has details of the memory card
*/
c = hweight_long(test_flash_data);
/* * Not sure there are actually any multi-partition devices in the * real world, but the hardware supports them, so, so will we
*/
card->parts = kmalloc_array(card->partitions, sizeof(struct vmupart),
GFP_KERNEL); if (!card->parts) {
error = -ENOMEM; goto fail_partitions;
}
/* * We want to trap meminfo not get cond * so set interval to zero, but rely on maple bus * driver to pass back the results of the meminfo
*/
maple_getcond_callback(mdev, vmu_queryblocks, 0,
MAPLE_FUNC_MEMCARD);
/* Make sure we are clear to go */ if (atomic_read(&mdev->busy) == 1) {
wait_event_interruptible_timeout(mdev->maple_wait,
atomic_read(&mdev->busy) == 0, HZ); if (atomic_read(&mdev->busy) == 1) {
dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n",
mdev->port, mdev->unit);
error = -EAGAIN; goto fail_device_busy;
}
}
atomic_set(&mdev->busy, 1);
/* * Set up the minfo call: vmu_queryblocks will handle * the information passed back
*/
error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
MAPLE_COMMAND_GETMINFO, 2, &partnum); if (error) {
dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)" " error is 0x%X\n", mdev->port, mdev->unit, error); goto fail_mtd_info;
} return 0;
/* Callback to handle eccentricities of both mtd subsystem * and general flakyness of Dreamcast VMUs
*/ staticint vmu_can_unload(struct maple_device *mdev)
{ struct memcard *card; int x; struct mtd_info *mtd;
card = maple_get_drvdata(mdev); for (x = 0; x < card->partitions; x++) {
mtd = &((card->mtd)[x]); if (kref_read(&mtd->refcnt)) return 0;
} return 1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.