2009-01-29 00:30:45

by Adrian McMenamin

[permalink] [raw]
Subject: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (0/3)

These patches add support for the SEGA Dreamcast VMU and clean up the
maple bus driver, allowing it to support block devices and ensuring it
supports concurrent reads and writes eg no more mixing up which thread
locks and unlocks a mutex etc.

A consequential fix for the maple keyboard driver is also attached.

The VMU is added as an MTD (mapping) device. It's flash but its is also
"smart" so could have conceivably been added as a general block device
but it transpires that as well as actually being based on flash, it
displays behaviour on plugging and unplugging and on writing that make
the MTD block layer a good fit (namely the MTD block layer's insistence
on carrying on after a perceived hotplug event).

I posted an earlier set of patches in Spring 2008 and given the
difficulties with them these are posted here for review and comment
rather than as a simple request for inclusion as is.

Signed-off-by: Adrian McMenamin <[email protected]>
---


2009-01-29 00:33:00

by Adrian McMenamin

[permalink] [raw]
Subject: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (1/3)

Rework the maple bus driver to support block reads and writes, while
also cleaning up the code and aiming for a consistent namespace for
functions.

Changes to the maple.h header file to support block reads and writes.

Signed-off-by: Adrian McMenamin <[email protected]>
---

diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 63f0de2..4d9b8b3 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -1,16 +1,10 @@
/*
* Core maple bus functionality
*
- * Copyright (C) 2007, 2008 Adrian McMenamin
+ * Copyright (C) 2007 - 2009 Adrian McMenamin
* Copyright (C) 2001 - 2008 Paul Mundt
- *
- * Based on 2.4 code by:
- *
- * Copyright (C) 2000-2001 YAEGASHI Takeshi
+ * Copyright (C) 2000 - 2001 YAEGASHI Takeshi
* Copyright (C) 2001 M. R. Brown
- * Copyright (C) 2001 Paul Mundt
- *
- * and others.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -32,7 +26,7 @@
#include <mach/dma.h>
#include <mach/sysasic.h>

-MODULE_AUTHOR("Yaegashi Takeshi, Paul Mundt, M. R. Brown, Adrian McMenamin");
+MODULE_AUTHOR("Adrian McMenamin <[email protected]>");
MODULE_DESCRIPTION("Maple bus driver for Dreamcast");
MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}");
@@ -49,7 +43,7 @@ static LIST_HEAD(maple_sentq);
/* mutex to protect queue of waiting packets */
static DEFINE_MUTEX(maple_wlist_lock);

-static struct maple_driver maple_dummy_driver;
+static struct maple_driver maple_unsupported_device;
static struct device maple_bus;
static int subdevice_map[MAPLE_PORTS];
static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr;
@@ -62,8 +56,9 @@ struct maple_device_specify {
int unit;
};

-static bool checked[4];
-static struct maple_device *baseunits[4];
+static bool checked[MAPLE_PORTS];
+static bool empty[MAPLE_PORTS];
+static struct maple_device *baseunits[MAPLE_PORTS];

/**
* maple_driver_register - register a maple driver
@@ -97,7 +92,7 @@ void maple_driver_unregister(struct maple_driver *drv)
EXPORT_SYMBOL_GPL(maple_driver_unregister);

/* set hardware registers to enable next round of dma */
-static void maplebus_dma_reset(void)
+static void maple_dma_reset(void)
{
ctrl_outl(MAPLE_MAGIC, MAPLE_RESET);
/* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */
@@ -134,21 +129,16 @@ static void maple_release_device(struct device *dev)
{
struct maple_device *mdev;
struct mapleq *mq;
- if (!dev)
- return;
+
mdev = to_maple_dev(dev);
mq = mdev->mq;
- if (mq) {
- if (mq->recvbufdcsp)
- kmem_cache_free(maple_queue_cache, mq->recvbufdcsp);
- kfree(mq);
- mq = NULL;
- }
+ kmem_cache_free(maple_queue_cache, mq->recvbuf);
+ kfree(mq);
kfree(mdev);
}

/**
- * maple_add_packet - add a single instruction to the queue
+ * maple_add_packet - add a single instruction to the maple bus queue
* @mdev: maple device
* @function: function on device being queried
* @command: maple command to add
@@ -158,68 +148,12 @@ static void maple_release_device(struct device *dev)
int maple_add_packet(struct maple_device *mdev, u32 function, u32 command,
size_t length, void *data)
{
- int locking, ret = 0;
- void *sendbuf = NULL;
-
- mutex_lock(&maple_wlist_lock);
- /* bounce if device already locked */
- locking = mutex_is_locked(&mdev->mq->mutex);
- if (locking) {
- ret = -EBUSY;
- goto out;
- }
-
- mutex_lock(&mdev->mq->mutex);
-
- if (length) {
- sendbuf = kmalloc(length * 4, GFP_KERNEL);
- if (!sendbuf) {
- mutex_unlock(&mdev->mq->mutex);
- ret = -ENOMEM;
- goto out;
- }
- ((__be32 *)sendbuf)[0] = cpu_to_be32(function);
- }
-
- mdev->mq->command = command;
- mdev->mq->length = length;
- if (length > 1)
- memcpy(sendbuf + 4, data, (length - 1) * 4);
- mdev->mq->sendbuf = sendbuf;
-
- list_add(&mdev->mq->list, &maple_waitq);
-out:
- mutex_unlock(&maple_wlist_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(maple_add_packet);
-
-/**
- * maple_add_packet_sleeps - add a single instruction to the queue
- * @mdev: maple device
- * @function: function on device being queried
- * @command: maple command to add
- * @length: length of command string (in 32 bit words)
- * @data: remainder of command string
- *
- * Same as maple_add_packet(), but waits for the lock to become free.
- */
-int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
- u32 command, size_t length, void *data)
-{
- int locking, ret = 0;
- void *sendbuf = NULL;
-
- locking = mutex_lock_interruptible(&mdev->mq->mutex);
- if (locking) {
- ret = -EIO;
- goto out;
- }
+ int ret = 0;
+ void *sendbuf;

+ sendbuf = kzalloc(length * 4, GFP_KERNEL);
if (length) {
- sendbuf = kmalloc(length * 4, GFP_KERNEL);
if (!sendbuf) {
- mutex_unlock(&mdev->mq->mutex);
ret = -ENOMEM;
goto out;
}
@@ -233,38 +167,33 @@ int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
mdev->mq->sendbuf = sendbuf;

mutex_lock(&maple_wlist_lock);
- list_add(&mdev->mq->list, &maple_waitq);
+ list_add_tail(&mdev->mq->list, &maple_waitq);
mutex_unlock(&maple_wlist_lock);
out:
return ret;
}
-EXPORT_SYMBOL_GPL(maple_add_packet_sleeps);
+EXPORT_SYMBOL_GPL(maple_add_packet);

static struct mapleq *maple_allocq(struct maple_device *mdev)
{
struct mapleq *mq;

- mq = kmalloc(sizeof(*mq), GFP_KERNEL);
+ mq = kzalloc(sizeof(*mq), GFP_KERNEL);
if (!mq)
goto failed_nomem;

mq->dev = mdev;
- mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
- mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp);
+ mq->recvbuf = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
if (!mq->recvbuf)
goto failed_p2;
- /*
- * most devices do not need the mutex - but
- * anything that injects block reads or writes
- * will rely on it
- */
- mutex_init(&mq->mutex);
+ mq->recvbuf->buf = (void *)P2SEGADDR(&((mq->recvbuf->bufx)[0]));

return mq;

failed_p2:
kfree(mq);
failed_nomem:
+ printk(KERN_INFO "maple: could not allocate memory\n");
return NULL;
}

@@ -272,12 +201,16 @@ static struct maple_device *maple_alloc_dev(int port, int unit)
{
struct maple_device *mdev;

+ /* zero this out to avoid kobj subsystem
+ * thinking it has already been registered */
+
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
return NULL;

mdev->port = port;
mdev->unit = unit;
+
mdev->mq = maple_allocq(mdev);

if (!mdev->mq) {
@@ -286,19 +219,18 @@ static struct maple_device *maple_alloc_dev(int port, int unit)
}
mdev->dev.bus = &maple_bus_type;
mdev->dev.parent = &maple_bus;
+ init_waitqueue_head(&mdev->maple_wait);
+ mdev->busy = 0;
+ mdev->callback = NULL;
+ mdev->canunload = NULL;
+ mdev->fileerrhandler = NULL;
return mdev;
}

static void maple_free_dev(struct maple_device *mdev)
{
- if (!mdev)
- return;
- if (mdev->mq) {
- if (mdev->mq->recvbufdcsp)
- kmem_cache_free(maple_queue_cache,
- mdev->mq->recvbufdcsp);
- kfree(mdev->mq);
- }
+ kmem_cache_free(maple_queue_cache, mdev->mq->recvbuf);
+ kfree(mdev->mq);
kfree(mdev);
}

@@ -320,7 +252,7 @@ static void maple_build_block(struct mapleq *mq)
maple_lastptr = maple_sendptr;

*maple_sendptr++ = (port << 16) | len | 0x80000000;
- *maple_sendptr++ = PHYSADDR(mq->recvbuf);
+ *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf);
*maple_sendptr++ =
mq->command | (to << 8) | (from << 16) | (len << 24);
while (len-- > 0)
@@ -333,20 +265,28 @@ static void maple_send(void)
int i, maple_packets = 0;
struct mapleq *mq, *nmq;

- if (!list_empty(&maple_sentq))
+ if (!maple_dma_done())
return;
+
+ /* disable DMA */
+ ctrl_outl(0, MAPLE_ENABLE);
+
+ if (!list_empty(&maple_sentq))
+ goto finish;
+
mutex_lock(&maple_wlist_lock);
- if (list_empty(&maple_waitq) || !maple_dma_done()) {
+ if (list_empty(&maple_waitq)) {
mutex_unlock(&maple_wlist_lock);
- return;
+ goto finish;
}
- mutex_unlock(&maple_wlist_lock);
+
maple_lastptr = maple_sendbuf;
maple_sendptr = maple_sendbuf;
- mutex_lock(&maple_wlist_lock);
+
list_for_each_entry_safe(mq, nmq, &maple_waitq, list) {
maple_build_block(mq);
- list_move(&mq->list, &maple_sentq);
+ list_del_init(&mq->list);
+ list_add_tail(&mq->list, &maple_sentq);
if (maple_packets++ > MAPLE_MAXPACKETS)
break;
}
@@ -356,10 +296,13 @@ static void maple_send(void)
dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE,
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
+
+finish:
+ maple_dma_reset();
}

/* check if there is a driver registered likely to match this device */
-static int check_matching_maple_driver(struct device_driver *driver,
+static int maple_check_matching_driver(struct device_driver *driver,
void *devptr)
{
struct maple_driver *maple_drv;
@@ -374,10 +317,7 @@ static int check_matching_maple_driver(struct device_driver *driver,

static void maple_detach_driver(struct maple_device *mdev)
{
- if (!mdev)
- return;
device_unregister(&mdev->dev);
- mdev = NULL;
}

/* process initial MAPLE_COMMAND_DEVINFO for each device or port */
@@ -385,9 +325,9 @@ static void maple_attach_driver(struct maple_device *mdev)
{
char *p, *recvbuf;
unsigned long function;
- int matched, retval;
+ int matched;

- recvbuf = mdev->mq->recvbuf;
+ recvbuf = mdev->mq->recvbuf->buf;
/* copy the data as individual elements in
* case of memory optimisation */
memcpy(&mdev->devinfo.function, recvbuf + 4, 4);
@@ -423,7 +363,7 @@ static void maple_attach_driver(struct maple_device *mdev)
if (function > 0x200) {
/* Do this silently - as not a real device */
function = 0;
- mdev->driver = &maple_dummy_driver;
+ mdev->driver = &maple_unsupported_device;
sprintf(mdev->dev.bus_id, "%d:0.port", mdev->port);
} else {
printk(KERN_INFO
@@ -432,24 +372,25 @@ static void maple_attach_driver(struct maple_device *mdev)

matched =
bus_for_each_drv(&maple_bus_type, NULL, mdev,
- check_matching_maple_driver);
+ maple_check_matching_driver);

if (matched == 0) {
/* Driver does not exist yet */
printk(KERN_INFO
"No maple driver found.\n");
- mdev->driver = &maple_dummy_driver;
+ mdev->driver = &maple_unsupported_device;
}
sprintf(mdev->dev.bus_id, "%d:0%d.%lX", mdev->port,
mdev->unit, function);
}
+
mdev->function = function;
mdev->dev.release = &maple_release_device;
- retval = device_register(&mdev->dev);
- if (retval) {
+
+ if (device_register(&mdev->dev)) {
printk(KERN_INFO
- "Maple bus: Attempt to register device"
- " (%x, %x) failed.\n",
+ "Maple bus: Attempt to register device"
+ " (%x, %x) failed.\n",
mdev->port, mdev->unit);
maple_free_dev(mdev);
mdev = NULL;
@@ -462,7 +403,7 @@ static void maple_attach_driver(struct maple_device *mdev)
* port and unit then return 1 - allows identification
* of which devices need to be attached or detached
*/
-static int detach_maple_device(struct device *device, void *portptr)
+static int check_maple_device(struct device *device, void *portptr)
{
struct maple_device_specify *ds;
struct maple_device *mdev;
@@ -477,21 +418,25 @@ static int detach_maple_device(struct device *device, void *portptr)
static int setup_maple_commands(struct device *device, void *ignored)
{
int add;
- struct maple_device *maple_dev = to_maple_dev(device);
-
- if ((maple_dev->interval > 0)
- && time_after(jiffies, maple_dev->when)) {
- /* bounce if we cannot lock */
- add = maple_add_packet(maple_dev,
- be32_to_cpu(maple_dev->devinfo.function),
+ struct maple_device *mdev = to_maple_dev(device);
+ if ((mdev->interval > 0)
+ && time_after(jiffies, mdev->when)) {
+ /* bounce if we cannot add */
+ add = maple_add_packet(mdev,
+ be32_to_cpu(mdev->devinfo.function),
MAPLE_COMMAND_GETCOND, 1, NULL);
if (!add)
- maple_dev->when = jiffies + maple_dev->interval;
+ mdev->when = jiffies + mdev->interval;
} else {
if (time_after(jiffies, maple_pnp_time))
- /* This will also bounce */
- maple_add_packet(maple_dev, 0,
- MAPLE_COMMAND_DEVINFO, 0, NULL);
+ /* Ensure we don't have block reads and devinfo
+ * calls interfering with one another - so flag the
+ * device as busy */
+ if (mdev->busy == 0) {
+ mdev->busy = 1;
+ maple_add_packet(mdev, 0,
+ MAPLE_COMMAND_DEVINFO, 0, NULL);
+ }
}
return 0;
}
@@ -499,29 +444,50 @@ static int setup_maple_commands(struct device *device, void *ignored)
/* VBLANK bottom half - implemented via workqueue */
static void maple_vblank_handler(struct work_struct *work)
{
- if (!list_empty(&maple_sentq) || !maple_dma_done())
+ int x, locking;
+ struct maple_device *mdev;
+
+ if (!maple_dma_done())
return;

ctrl_outl(0, MAPLE_ENABLE);

+ if (!list_empty(&maple_sentq))
+ goto finish;
+
+ /*
+ * Set up essential commands - to fetch data and
+ * check devices are still present
+ */
bus_for_each_dev(&maple_bus_type, NULL, NULL,
- setup_maple_commands);
+ setup_maple_commands);
+
+ if (time_after(jiffies, maple_pnp_time)) {
+ /*
+ * Scan the empty ports - bus is flakey and may have
+ * mis-reported emptyness
+ */
+ for (x = 0; x < MAPLE_PORTS; x++) {
+ if (checked[x] && empty[x]) {
+ mdev = baseunits[x];
+ if (!mdev)
+ break;
+ mdev->busy = 1;
+ locking = maple_add_packet(mdev, 0,
+ MAPLE_COMMAND_DEVINFO, 0, NULL);
+ if (!locking)
+ break;
+ }
+ }

- if (time_after(jiffies, maple_pnp_time))
maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL;
-
- mutex_lock(&maple_wlist_lock);
- if (!list_empty(&maple_waitq) && list_empty(&maple_sentq)) {
- mutex_unlock(&maple_wlist_lock);
- maple_send();
- } else {
- mutex_unlock(&maple_wlist_lock);
}

- maplebus_dma_reset();
+finish:
+ maple_send();
}

-/* handle devices added via hotplugs - placing them on queue for DEVINFO*/
+/* handle devices added via hotplugs - placing them on queue for DEVINFO */
static void maple_map_subunits(struct maple_device *mdev, int submask)
{
int retval, k, devcheck;
@@ -533,7 +499,7 @@ static void maple_map_subunits(struct maple_device *mdev, int submask)
ds.unit = k + 1;
retval =
bus_for_each_dev(&maple_bus_type, NULL, &ds,
- detach_maple_device);
+ check_maple_device);
if (retval) {
submask = submask >> 1;
continue;
@@ -543,6 +509,7 @@ static void maple_map_subunits(struct maple_device *mdev, int submask)
mdev_add = maple_alloc_dev(mdev->port, k + 1);
if (!mdev_add)
return;
+ mdev_add->busy = 1;
maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO,
0, NULL);
/* mark that we are checking sub devices */
@@ -564,27 +531,46 @@ static void maple_clean_submap(struct maple_device *mdev)
}

/* handle empty port or hotplug removal */
-static void maple_response_none(struct maple_device *mdev,
- struct mapleq *mq)
+static void maple_response_none(struct maple_device *mdev)
{
- if (mdev->unit != 0) {
- list_del(&mq->list);
- maple_clean_submap(mdev);
+ maple_clean_submap(mdev);
+
+ if (likely(mdev->unit != 0)) {
+ /*
+ * Block devices play up
+ * and give the impression they have
+ * been removed even when still in place or
+ * trip the mtd layer when they have
+ * really gone - this code traps that eventuality
+ * and ensures we aren't overloaded with useless
+ * error messages
+ */
+ if (mdev->canunload) {
+ if (!mdev->canunload(mdev)) {
+ mdev->busy = 2;
+ wake_up(&mdev->maple_wait);
+ return;
+ }
+ }
+
printk(KERN_INFO
- "Maple bus device detaching at (%d, %d)\n",
- mdev->port, mdev->unit);
+ "Maple bus device detaching at (%d, %d)\n",
+ mdev->port, mdev->unit);
maple_detach_driver(mdev);
return;
- }
- if (!started || !fullscan) {
- if (checked[mdev->port] == false) {
- checked[mdev->port] = true;
- printk(KERN_INFO "No maple devices attached"
- " to port %d\n", mdev->port);
+ } else {
+ if (!started || !fullscan) {
+ if (checked[mdev->port] == false) {
+ checked[mdev->port] = true;
+ empty[mdev->port] = true;
+ printk(KERN_INFO "No maple devices attached"
+ " to port %d\n", mdev->port);
+ }
+ return;
}
- return;
}
- maple_clean_submap(mdev);
+ /* Some hardware devices generate false detach messages on unit 0 */
+ mdev->busy = 0;
}

/* preprocess hotplugs or scans */
@@ -599,8 +585,11 @@ static void maple_response_devinfo(struct maple_device *mdev,
} else {
if (mdev->unit != 0)
maple_attach_driver(mdev);
+ if (mdev->unit == 0) {
+ empty[mdev->port] = false;
+ maple_attach_driver(mdev);
+ }
}
- return;
}
if (mdev->unit == 0) {
submask = recvbuf[2] & 0x1F;
@@ -611,6 +600,16 @@ static void maple_response_devinfo(struct maple_device *mdev,
}
}

+static void maple_response_fileerr(struct maple_device *mdev, void *recvbuf)
+{
+ if (mdev->fileerrhandler) {
+ mdev->fileerrhandler(mdev, recvbuf);
+ return;
+ } else
+ printk(KERN_INFO "maple: device (%d, %d) reports file error %d\n",
+ mdev->port, mdev->unit, ((int *)recvbuf)[1]);
+}
+
static void maple_port_rescan(void)
{
int i;
@@ -621,12 +620,6 @@ static void maple_port_rescan(void)
if (checked[i] == false) {
fullscan = 0;
mdev = baseunits[i];
- /*
- * test lock in case scan has failed
- * but device is still locked
- */
- if (mutex_is_locked(&mdev->mq->mutex))
- mutex_unlock(&mdev->mq->mutex);
maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO,
0, NULL);
}
@@ -637,7 +630,7 @@ static void maple_port_rescan(void)
static void maple_dma_handler(struct work_struct *work)
{
struct mapleq *mq, *nmq;
- struct maple_device *dev;
+ struct maple_device *mdev;
char *recvbuf;
enum maple_code code;

@@ -646,43 +639,53 @@ static void maple_dma_handler(struct work_struct *work)
ctrl_outl(0, MAPLE_ENABLE);
if (!list_empty(&maple_sentq)) {
list_for_each_entry_safe(mq, nmq, &maple_sentq, list) {
- recvbuf = mq->recvbuf;
+ recvbuf = mq->recvbuf->buf;
code = recvbuf[0];
- dev = mq->dev;
+ mdev = mq->dev;
kfree(mq->sendbuf);
- mutex_unlock(&mq->mutex);
list_del_init(&mq->list);
-
switch (code) {
case MAPLE_RESPONSE_NONE:
- maple_response_none(dev, mq);
+ maple_response_none(mdev);
break;

case MAPLE_RESPONSE_DEVINFO:
- maple_response_devinfo(dev, recvbuf);
+ maple_response_devinfo(mdev, recvbuf);
+ mdev->busy = 0;
break;

case MAPLE_RESPONSE_DATATRF:
- if (dev->callback)
- dev->callback(mq);
+ if (mdev->callback)
+ mdev->callback(mq);
+ mdev->busy = 0;
+ wake_up(&mdev->maple_wait);
break;

case MAPLE_RESPONSE_FILEERR:
+ maple_response_fileerr(mdev, recvbuf);
+ mdev->busy = 0;
+ wake_up(&mdev->maple_wait);
+ break;
+
case MAPLE_RESPONSE_AGAIN:
case MAPLE_RESPONSE_BADCMD:
case MAPLE_RESPONSE_BADFUNC:
- printk(KERN_DEBUG
+ printk(KERN_INFO
"Maple non-fatal error 0x%X\n",
code);
+ mdev->busy = 0;
break;

case MAPLE_RESPONSE_ALLINFO:
- printk(KERN_DEBUG
+ printk(KERN_INFO
"Maple - extended device information"
" not supported\n");
+ mdev->busy = 0;
break;

case MAPLE_RESPONSE_OK:
+ mdev->busy = 0;
+ wake_up(&mdev->maple_wait);
break;

default:
@@ -699,20 +702,19 @@ static void maple_dma_handler(struct work_struct *work)
if (!fullscan)
maple_port_rescan();
/* mark that we have been through the first scan */
- if (started == 0)
- started = 1;
+ started = 1;
}
- maplebus_dma_reset();
+ maple_send();
}

-static irqreturn_t maplebus_dma_interrupt(int irq, void *dev_id)
+static irqreturn_t maple_dma_interrupt(int irq, void *dev_id)
{
/* Load everything into the bottom half */
schedule_work(&maple_dma_process);
return IRQ_HANDLED;
}

-static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id)
+static irqreturn_t maple_vblank_interrupt(int irq, void *dev_id)
{
schedule_work(&maple_vblank_process);
return IRQ_HANDLED;
@@ -720,14 +722,14 @@ static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id)

static int maple_set_dma_interrupt_handler(void)
{
- return request_irq(HW_EVENT_MAPLE_DMA, maplebus_dma_interrupt,
- IRQF_SHARED, "maple bus DMA", &maple_dummy_driver);
+ return request_irq(HW_EVENT_MAPLE_DMA, maple_dma_interrupt,
+ IRQF_SHARED, "maple bus DMA", &maple_unsupported_device);
}

static int maple_set_vblank_interrupt_handler(void)
{
- return request_irq(HW_EVENT_VSYNC, maplebus_vblank_interrupt,
- IRQF_SHARED, "maple bus VBLANK", &maple_dummy_driver);
+ return request_irq(HW_EVENT_VSYNC, maple_vblank_interrupt,
+ IRQF_SHARED, "maple bus VBLANK", &maple_unsupported_device);
}

static int maple_get_dma_buffer(void)
@@ -740,7 +742,7 @@ static int maple_get_dma_buffer(void)
return 0;
}

-static int match_maple_bus_driver(struct device *devptr,
+static int maple_match_bus_driver(struct device *devptr,
struct device_driver *drvptr)
{
struct maple_driver *maple_drv = to_maple_driver(drvptr);
@@ -765,16 +767,18 @@ static void maple_bus_release(struct device *dev)
{
}

-static struct maple_driver maple_dummy_driver = {
+static struct maple_driver maple_unsupported_device = {
.drv = {
- .name = "maple_dummy_driver",
+ .name = "maple_unsupported_device",
.bus = &maple_bus_type,
},
};
-
+/**
+ * maple_bus_type - core maple bus structure
+ */
struct bus_type maple_bus_type = {
.name = "maple",
- .match = match_maple_bus_driver,
+ .match = maple_match_bus_driver,
.uevent = maple_bus_uevent,
};
EXPORT_SYMBOL_GPL(maple_bus_type);
@@ -788,7 +792,8 @@ static int __init maple_bus_init(void)
{
int retval, i;
struct maple_device *mdev[MAPLE_PORTS];
- ctrl_outl(0, MAPLE_STATE);
+
+ ctrl_outl(0, MAPLE_ENABLE);

retval = device_register(&maple_bus);
if (retval)
@@ -798,7 +803,7 @@ static int __init maple_bus_init(void)
if (retval)
goto cleanup_device;

- retval = driver_register(&maple_dummy_driver.drv);
+ retval = driver_register(&maple_unsupported_device.drv);
if (retval)
goto cleanup_bus;

@@ -825,9 +830,7 @@ static int __init maple_bus_init(void)
goto cleanup_irq;
}

- maple_queue_cache =
- kmem_cache_create("maple_queue_cache", 0x400, 0,
- SLAB_HWCACHE_ALIGN, NULL);
+ maple_queue_cache = KMEM_CACHE(maple_buffer, SLAB_HWCACHE_ALIGN);

if (!maple_queue_cache)
goto cleanup_bothirqs;
@@ -838,6 +841,7 @@ static int __init maple_bus_init(void)
/* setup maple ports */
for (i = 0; i < MAPLE_PORTS; i++) {
checked[i] = false;
+ empty[i] = false;
mdev[i] = maple_alloc_dev(i, 0);
baseunits[i] = mdev[i];
if (!mdev[i]) {
@@ -845,15 +849,14 @@ static int __init maple_bus_init(void)
maple_free_dev(mdev[i]);
goto cleanup_cache;
}
+ mdev[i]->busy = 1;
maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL);
subdevice_map[i] = 0;
}

- /* setup maplebus hardware */
- maplebus_dma_reset();
- /* initial detection */
+ maple_pnp_time = jiffies + HZ;
+ /* prepare initial queue */
maple_send();
- maple_pnp_time = jiffies;
printk(KERN_INFO "Maple bus core now registered.\n");

return 0;
@@ -871,7 +874,7 @@ cleanup_dma:
free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES);

cleanup_basic:
- driver_unregister(&maple_dummy_driver.drv);
+ driver_unregister(&maple_unsupported_device.drv);

cleanup_bus:
bus_unregister(&maple_bus_type);
diff --git a/include/linux/maple.h b/include/linux/maple.h
index c23d3f5..7852e8e 100644
--- a/include/linux/maple.h
+++ b/include/linux/maple.h
@@ -8,33 +8,49 @@ extern struct bus_type maple_bus_type;

/* Maple Bus command and response codes */
enum maple_code {
- MAPLE_RESPONSE_FILEERR = -5,
- MAPLE_RESPONSE_AGAIN = -4, /* request should be retransmitted */
- MAPLE_RESPONSE_BADCMD = -3,
- MAPLE_RESPONSE_BADFUNC = -2,
- MAPLE_RESPONSE_NONE = -1, /* unit didn't respond at all */
- MAPLE_COMMAND_DEVINFO = 1,
- MAPLE_COMMAND_ALLINFO = 2,
- MAPLE_COMMAND_RESET = 3,
- MAPLE_COMMAND_KILL = 4,
- MAPLE_RESPONSE_DEVINFO = 5,
- MAPLE_RESPONSE_ALLINFO = 6,
- MAPLE_RESPONSE_OK = 7,
- MAPLE_RESPONSE_DATATRF = 8,
- MAPLE_COMMAND_GETCOND = 9,
- MAPLE_COMMAND_GETMINFO = 10,
- MAPLE_COMMAND_BREAD = 11,
- MAPLE_COMMAND_BWRITE = 12,
- MAPLE_COMMAND_SETCOND = 14
+ MAPLE_RESPONSE_FILEERR = -5,
+ MAPLE_RESPONSE_AGAIN = -4, /* retransmit */
+ MAPLE_RESPONSE_BADCMD = -3,
+ MAPLE_RESPONSE_BADFUNC = -2,
+ MAPLE_RESPONSE_NONE = -1, /* unit didn't respond*/
+ MAPLE_COMMAND_DEVINFO = 1,
+ MAPLE_COMMAND_ALLINFO = 2,
+ MAPLE_COMMAND_RESET = 3,
+ MAPLE_COMMAND_KILL = 4,
+ MAPLE_RESPONSE_DEVINFO = 5,
+ MAPLE_RESPONSE_ALLINFO = 6,
+ MAPLE_RESPONSE_OK = 7,
+ MAPLE_RESPONSE_DATATRF = 8,
+ MAPLE_COMMAND_GETCOND = 9,
+ MAPLE_COMMAND_GETMINFO = 10,
+ MAPLE_COMMAND_BREAD = 11,
+ MAPLE_COMMAND_BWRITE = 12,
+ MAPLE_COMMAND_BSYNC = 13,
+ MAPLE_COMMAND_SETCOND = 14,
+ MAPLE_COMMAND_MICCONTROL = 15
+};
+
+enum maple_file_errors {
+ MAPLE_FILEERR_INVALID_PARTITION = 0x01000000,
+ MAPLE_FILEERR_PHASE_ERROR = 0x02000000,
+ MAPLE_FILEERR_INVALID_BLOCK = 0x04000000,
+ MAPLE_FILEERR_WRITE_ERROR = 0x08000000,
+ MAPLE_FILEERR_INVALID_WRITE_LENGTH = 0x10000000,
+ MAPLE_FILEERR_BAD_CRC = 0x20000000
+};
+
+struct maple_buffer {
+ char bufx[0x400];
+ void *buf;
};

struct mapleq {
struct list_head list;
struct maple_device *dev;
- void *sendbuf, *recvbuf, *recvbufdcsp;
+ struct maple_buffer *recvbuf;
+ void *sendbuf, *recvbuf_p2;
unsigned char length;
enum maple_code command;
- struct mutex mutex;
};

struct maple_devinfo {
@@ -52,11 +68,15 @@ struct maple_device {
struct maple_driver *driver;
struct mapleq *mq;
void (*callback) (struct mapleq * mq);
+ void (*fileerrhandler)(struct maple_device * mdev, void * recvbuf);
+ int (*canunload)(struct maple_device * mdev);
unsigned long when, interval, function;
struct maple_devinfo devinfo;
unsigned char port, unit;
char product_name[32];
char product_licence[64];
+ int busy;
+ wait_queue_head_t maple_wait;
struct device dev;
};

@@ -72,7 +92,7 @@ void maple_getcond_callback(struct maple_device *dev,
int maple_driver_register(struct maple_driver *);
void maple_driver_unregister(struct maple_driver *);

-int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
+int maple_add_packet(struct maple_device *mdev, u32 function,
u32 command, u32 length, void *data);
void maple_clear_dev(struct maple_device *mdev);


2009-01-29 00:33:28

by Adrian McMenamin

[permalink] [raw]
Subject: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (2/3)

Add support for the Dreamcast VMU device as a flash mapping.

This device is a "smart" flash device which operates as a (slow) block
device. The Linux MTD block layer is a good fit to the VMU because of
the way VMUs can appear on plugging in or during writes to disappear off
the bus because of the slowness of flash read and write operations.

Signed-off-by: Adrian McMenamin <[email protected]>
---

diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 0225cbb..9b3e564 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -551,5 +551,15 @@ config MTD_PLATRAM

This selection automatically selects the map_ram driver.

-endmenu
+config MTD_VMU
+ tristate "Map driver for Dreamcast VMU"
+ depends on MAPLE
+ help
+ This driver enables access to the Dreamcast Visual Memory Unit (VMU).
+
+ Most Dreamcast users will want to say Y here.

+ To build this as a module select M here, the module will be called
+ vmu-flash.
+
+endmenu
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 6d9ba35..26b28a7 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -61,3 +61,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
+obj-$(CONFIG_MTD_VMU) += vmu-flash.o
diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c
new file mode 100644
index 0000000..00d887a
--- /dev/null
+++ b/drivers/mtd/maps/vmu-flash.c
@@ -0,0 +1,786 @@
+/* vmu-flash.c
+ * Driver for SEGA Dreamcast Visual Memory Unit
+ *
+ * Copyright (c) Adrian McMenamin 2002 - 2009
+ * Copyright (c) Paul Mundt 2001
+ *
+ * Licensed under version 2 of the
+ * GNU General Public Licence
+ */
+#include <linux/init.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 {
+ unsigned char *buffer; /* Cache */
+ unsigned int block; /* Which block was cached */
+ unsigned long jiffies_atc; /* When was it cached? */
+ int valid;
+};
+
+struct mdev_part {
+ struct maple_device *mdev;
+ int partition;
+};
+
+struct vmupart {
+ u16 user_blocks;
+ u16 root_block;
+ u16 numblocks;
+ char *name;
+ struct vmu_cache *pcache;
+};
+
+struct memcard {
+ u16 tempA;
+ u16 tempB;
+ u32 partitions;
+ u32 blocklen;
+ u32 writecnt;
+ u32 readcnt;
+ u32 removeable;
+ int partition;
+ int read;
+ unsigned char *blockread;
+ struct vmupart *parts;
+ struct mtd_info *mtd;
+};
+
+struct vmu_block {
+ unsigned int num; /* block number */
+ unsigned int ofs; /* block offset */
+};
+
+static struct vmu_block *ofs_to_block(unsigned long src_ofs,
+ struct mtd_info *mtd, int partition)
+{
+ struct vmu_block *vblock;
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ int num;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ card = maple_get_drvdata(mdev);
+
+ if (src_ofs >= ((card->parts)[partition]).numblocks * card->blocklen)
+ goto failed;
+
+ num = src_ofs / card->blocklen;
+ if (num > ((card->parts)[partition]).numblocks)
+ goto failed;
+
+ vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL);
+ if (!vblock)
+ goto failed;
+
+ vblock->num = num;
+ vblock->ofs = src_ofs % card->blocklen;
+ return vblock;
+
+failed:
+ return NULL;
+}
+
+/* Maple bus callback function for reads */
+static void vmu_blockread(struct mapleq *mq)
+{
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct vmu_cache *pcache;
+ struct mdev_part *mpart;
+ int partition;
+
+ mdev = mq->dev;
+ card = maple_get_drvdata(mdev);
+ /* copy the read in data */
+ mpart = card->mtd->priv;
+ partition = mpart->partition;
+ pcache = (card->parts[partition]).pcache;
+ pcache->valid = 0;
+
+ /* waiting in queue so try to speed up */
+ if (unlikely(!card->blockread))
+ return;
+
+ memcpy(card->blockread, mq->recvbuf->buf + 12, card->blocklen);
+
+ /* fill the cache for this block */
+ if (unlikely(!pcache->buffer)) {
+ pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL);
+ if (unlikely(!pcache->buffer)) {
+ printk(KERN_WARNING "VMU read fails due to lack of memory\n");
+ return;
+ }
+ }
+ memcpy(pcache->buffer, card->blockread, card->blocklen);
+ pcache->block = ((unsigned char *)mq->recvbuf->buf)[11];
+ pcache->jiffies_atc = jiffies;
+ pcache->valid = 1;
+}
+
+/* Interface with maple bus to read blocks
+ * caching the results so that other parts
+ * of the driver can access block reads */
+static int maple_vmu_read_block(unsigned int num, unsigned char *buf,
+ struct mtd_info *mtd)
+{
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct maple_device *mdev;
+ int partition, error = 0, locking, wait;
+ unsigned char *blockread;
+ __be32 sendbuf;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ sendbuf = cpu_to_be32(partition << 24 | num);
+
+ if (mdev->busy == 1) {
+ wait_event_interruptible_timeout(mdev->maple_wait,
+ (mdev->busy == 0), HZ);
+ if (mdev->busy == 1) {
+ printk(KERN_INFO "VMU is busy\n");
+ error = -EAGAIN;
+ goto outB;
+ }
+ }
+
+ mdev->busy = 1;
+ blockread = kmalloc(card->blocklen, GFP_KERNEL);
+ if (!blockread) {
+ error = -ENOMEM;
+ mdev->busy = 0;
+ goto outB;
+ }
+ card->blockread = blockread;
+
+ maple_getcond_callback(mdev, vmu_blockread, 0, MAPLE_FUNC_MEMCARD);
+ locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_BREAD, 2, &sendbuf);
+ wait = wait_event_interruptible_timeout(mdev->maple_wait,
+ ((mdev->busy == 0) || (mdev->busy == 2)), HZ/2);
+ /*
+ * MTD layer does not handle hotplugging well
+ * so have to return errors when VMU is unplugged
+ * in the middle of a read (busy == 2)
+ */
+ if ((locking) || (mdev->busy == 2)) {
+ mdev->busy = 0;
+ error = -EIO;
+ goto outA;
+ }
+ if ((wait == 0) || (wait == -ERESTARTSYS)) {
+ kfree(mdev->mq->sendbuf);
+ list_del_init(&(mdev->mq->list));
+ mdev->busy = 0;
+ error = -EIO;
+ if (wait == -ERESTARTSYS)
+ printk(KERN_INFO "Maple: VMU read interrupted on"
+ " block 0x%X\n", num);
+ else
+ printk(KERN_INFO "Maple: VMU read timed out on block"
+ " 0x%X\n", num);
+ goto outA;
+ }
+
+ memcpy(buf, blockread, card->blocklen);
+outA:
+ kfree(blockread);
+outB:
+ return error;
+}
+
+/* communicate with maple bus for phased writing */
+static int maple_vmu_write_block(unsigned int num, const unsigned char *buf,
+ struct mtd_info *mtd)
+{
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct maple_device *mdev;
+ int partition, error, locking, x, phaselen, wait;
+ __be32 *sendbuf;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ phaselen = card->blocklen/card->writecnt;
+
+ sendbuf = kmalloc(phaselen + 4, GFP_KERNEL);
+ if (!sendbuf) {
+ error = -ENOMEM;
+ goto fail_nosendbuf;
+ }
+ for (x = 0; x < card->writecnt; x++) {
+ sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num);
+ memcpy(&sendbuf[1], buf + phaselen * x, phaselen);
+ /* wait until the device is not busy doing something else
+ * or 1 second - which ever is longer */
+ if (mdev->busy == 1) {
+ wait_event_interruptible_timeout(mdev->maple_wait,
+ (mdev->busy == 0), HZ);
+ if (mdev->busy == 1) {
+ error = -EBUSY;
+ printk(KERN_INFO "mtd: VMU write failed - "
+ "device busy\n");
+ goto fail_nolock;
+ }
+ }
+ mdev->busy = 1;
+
+ locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf);
+ wait = wait_event_interruptible_timeout(mdev->maple_wait,
+ (mdev->busy == 0), HZ/10);
+ if (locking) {
+ error = -EIO;
+ mdev->busy = 0;
+ goto fail_nolock;
+ }
+ if (mdev->busy == 2) {
+ mdev->busy = 0;
+ } else if ((wait == 0) || (wait == -ERESTARTSYS)) {
+ error = -EIO;
+ printk(KERN_INFO "mtd: VMU write failed: could not"
+ " communicate with device\n");
+ mdev->busy = 0;
+ kfree(mdev->mq->sendbuf);
+ list_del_init(&(mdev->mq->list));
+ goto fail_nolock;
+ }
+ }
+ kfree(sendbuf);
+
+ return card->blocklen;
+
+fail_nolock:
+ kfree(sendbuf);
+fail_nosendbuf:
+ printk(KERN_INFO "Maple: VMU (%d, %d): write failed\n", mdev->port,
+ mdev->unit);
+ return error;
+}
+
+/* mtd function to simulate reading byte by byte */
+static unsigned char vmu_flash_read_char(unsigned long ofs, int *retval,
+ struct mtd_info *mtd)
+{
+ struct vmu_block *vblock;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct maple_device *mdev;
+ unsigned char *buf, ret;
+ int partition, error;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+ *retval = 0;
+
+ buf = kmalloc(card->blocklen, GFP_KERNEL);
+ if (!buf) {
+ *retval = 1;
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ vblock = ofs_to_block(ofs, mtd, partition);
+ if (!vblock) {
+ *retval = 3;
+ ret = -ENOMEM;
+ goto out_buf;
+ }
+
+ error = maple_vmu_read_block(vblock->num, buf, mtd);
+ if (error) {
+ ret = error;
+ *retval = 2;
+ goto out_vblock;
+ }
+
+ ret = buf[vblock->ofs];
+
+out_vblock:
+ kfree(vblock);
+out_buf:
+ kfree(buf);
+finish:
+ return ret;
+}
+
+/* mtd higher order function to read flash */
+static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct vmu_cache *pcache;
+ struct vmu_block *vblock;
+ int index = 0, retval, partition, leftover, numblocks;
+ unsigned char cx;
+
+ if (len < 1)
+ return -EIO;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ 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;
+
+ return 0;
+}
+
+static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ int index = 0, retval, partition, error = 0, numblocks;
+ struct vmu_cache *pcache;
+ struct vmu_block *vblock;
+ unsigned char *buffer;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ /* simple sanity checks */
+ if (len < 1) {
+ error = -EIO;
+ goto failed;
+ }
+ numblocks = card->parts[partition].numblocks;
+ if (to + len > numblocks * card->blocklen)
+ len = numblocks * card->blocklen - to;
+ if (len == 0) {
+ error = -EIO;
+ goto failed;
+ }
+
+ vblock = ofs_to_block(to, mtd, partition);
+ if (!vblock) {
+ error = -ENOMEM;
+ goto failed;
+ }
+
+ buffer = kmalloc(card->blocklen, GFP_KERNEL);
+ if (!buffer) {
+ error = -ENOMEM;
+ goto fail_buffer;
+ }
+
+ do {
+ /* Read in the block we are to write to */
+ if (maple_vmu_read_block(vblock->num, buffer, mtd)) {
+ error = -EIO;
+ goto fail_io;
+ }
+
+ do {
+ buffer[vblock->ofs] = buf[index];
+ vblock->ofs++;
+ index++;
+ if (index >= len)
+ break;
+ } while (vblock->ofs < card->blocklen);
+
+ /* write out new buffer */
+ retval = maple_vmu_write_block(vblock->num, buffer, mtd);
+ /* invalidare the cache */
+ pcache = (card->parts[partition]).pcache;
+ pcache->valid = 0;
+
+ if (retval != card->blocklen) {
+ error = -EIO;
+ goto fail_io;
+ }
+
+ vblock->num++;
+ vblock->ofs = 0;
+ } while (len > index);
+
+ kfree(buffer);
+ *retlen = index;
+ kfree(vblock);
+ return 0;
+
+fail_io:
+ kfree(buffer);
+fail_buffer:
+ kfree(vblock);
+failed:
+ printk(KERN_INFO "Maple: VMU write failing with error %d\n", error);
+ return error;
+}
+
+static void vmu_flash_sync(struct mtd_info *mtd)
+{
+ /* Do nothing here */
+}
+
+/* Maple bus callback function to recursively query hardware details */
+static void vmu_queryblocks(struct mapleq *mq)
+{
+ struct maple_device *mdev;
+ unsigned short *res;
+ struct memcard *card;
+ __be32 partnum;
+ struct vmu_cache *pcache;
+ struct mdev_part *mpart;
+ struct mtd_info *mtd_cur;
+ struct vmupart *part_cur;
+ int error;
+
+ mdev = mq->dev;
+ card = maple_get_drvdata(mdev);
+ res = (unsigned short *) (mq->recvbuf->buf);
+ card->tempA = res[12];
+ card->tempB = res[6];
+
+ printk(KERN_INFO "Maple: VMU device at partition %d has %d user "
+ "blocks with a root block at %d\n", card->partition,
+ card->tempA, card->tempB);
+
+ part_cur = &((card->parts)[card->partition]);
+ part_cur->user_blocks = card->tempA;
+ part_cur->root_block = card->tempB;
+ part_cur->numblocks = card->tempB + 1;
+ part_cur->name = kmalloc(12, GFP_KERNEL);
+ if (!part_cur->name)
+ goto fail_name;
+
+ sprintf(part_cur->name, "vmu%d.%d.%d",
+ mdev->port, mdev->unit, card->partition);
+ mtd_cur = &((card->mtd)[card->partition]);
+ mtd_cur->name = part_cur->name;
+ mtd_cur->type = 8;
+ mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
+ mtd_cur->size = part_cur->numblocks * card->blocklen;
+ mtd_cur->erasesize = card->blocklen;
+ mtd_cur->write = vmu_flash_write;
+ mtd_cur->read = vmu_flash_read;
+ mtd_cur->sync = vmu_flash_sync;
+ mtd_cur->writesize = card->blocklen;
+
+ mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
+ if (!mpart)
+ goto fail_mpart;
+
+ mpart->mdev = mdev;
+ mpart->partition = card->partition;
+ mtd_cur->priv = mpart;
+ mtd_cur->owner = THIS_MODULE;
+
+ pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL);
+ if (!pcache)
+ goto fail_cache_create;
+ part_cur->pcache = pcache;
+
+ error = add_mtd_device(mtd_cur);
+ if (error)
+ goto fail_mtd_register;
+
+ maple_getcond_callback(mdev, NULL, 0,
+ MAPLE_FUNC_MEMCARD);
+
+ /*
+ * 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:
+ printk(KERN_INFO "mtd: Could not register maple device at (%d, %d)\n",
+ mdev->port, mdev->unit);
+ 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 */
+static int __devinit vmu_connect(struct maple_device *mdev)
+{
+ unsigned long test_flash_data, basic_flash_data;
+ int c, locking, 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:
+ * using Brian Kernighan's/Peter Wegner's method */
+ for (c = 0; test_flash_data; c++)
+ test_flash_data &= test_flash_data - 1;
+
+ basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
+
+ card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
+ if (!card) {
+ error = ENOMEM;
+ goto fail_nomem;
+ }
+
+ card->partitions = ((basic_flash_data >> 24) & 0xFF) + 1;
+ card->blocklen = (((basic_flash_data >> 16) & 0xFF) + 1) << 5;
+ card->writecnt = (basic_flash_data >> 12) & 0xF;
+ card->readcnt = (basic_flash_data >> 8) & 0xF;
+ card->removeable = (basic_flash_data >> 7) & 1;
+
+ card->partition = 0;
+
+ /*
+ * 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(sizeof(struct vmupart) * card->partitions,
+ GFP_KERNEL);
+ if (!card->parts) {
+ error = -ENOMEM;
+ goto fail_partitions;
+ }
+
+ card->mtd = kmalloc(sizeof(struct mtd_info) * card->partitions,
+ GFP_KERNEL);
+ if (!card->mtd) {
+ error = -ENOMEM;
+ goto fail_mtd_info;
+ }
+
+ maple_set_drvdata(mdev, card);
+
+ /*
+ * 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);
+
+ /*
+ * Set up the minfo call: vmu_queryblocks will handle
+ * the information passed back
+ */
+ locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_GETMINFO, 2, &partnum);
+ if (locking) {
+ printk(KERN_INFO "Could not lock VMU\n");
+ error = -EIO;
+ goto fail_mtd_info;
+ }
+ return 0;
+
+fail_mtd_info:
+ kfree(card->parts);
+fail_partitions:
+ kfree(card);
+fail_nomem:
+ return error;
+}
+
+static void __devexit vmu_disconnect(struct maple_device *mdev)
+{
+ struct memcard *card;
+ struct mdev_part *mpart;
+ int x;
+
+ mdev->callback = NULL;
+ card = maple_get_drvdata(mdev);
+ for (x = 0; x < card->partitions; x++) {
+ mpart = ((card->mtd)[x]).priv;
+ mpart->mdev = NULL;
+ del_mtd_device(&((card->mtd)[x]));
+ kfree(((card->parts)[x]).name);
+ }
+ kfree(card->parts);
+ kfree(card->mtd);
+ kfree(card);
+}
+
+/* Callback to handle eccentricities of both mtd subsystem
+ * and general flakyness of Dreamcast VMUs
+ */
+static int vmu_canunload(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 (mtd->usecount > 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void vmu_file_error(struct maple_device *mdev, void *recvbuf)
+{
+ enum maple_file_errors error = ((int *)recvbuf)[1];
+ char *errorstr = "maple: VMU file error -";
+
+ switch (error) {
+
+ case MAPLE_FILEERR_INVALID_PARTITION:
+ printk(KERN_INFO "%s invalid partition number\n",
+ errorstr);
+ break;
+
+ case MAPLE_FILEERR_PHASE_ERROR:
+ printk(KERN_INFO "%s phase error\n", errorstr);
+ break;
+
+ case MAPLE_FILEERR_INVALID_BLOCK:
+ printk(KERN_INFO "%s invalid block number\n",
+ errorstr);
+ break;
+
+ case MAPLE_FILEERR_WRITE_ERROR:
+ printk(KERN_INFO "%s write error\n", errorstr);
+ break;
+
+ case MAPLE_FILEERR_INVALID_WRITE_LENGTH:
+ printk(KERN_INFO "%s invalid write length\n",
+ errorstr);
+ break;
+
+ case MAPLE_FILEERR_BAD_CRC:
+ printk(KERN_INFO "%s bad CRC\n", errorstr);
+ break;
+
+ default:
+ printk(KERN_INFO "%s 0x%X\n", errorstr, error);
+ }
+}
+
+
+static int __devinit probe_maple_vmu(struct device *dev)
+{
+ int error;
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct maple_driver *mdrv = to_maple_driver(dev->driver);
+
+ mdev->canunload = vmu_canunload;
+ mdev->fileerrhandler = vmu_file_error;
+ mdev->driver = mdrv;
+
+ error = vmu_connect(mdev);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int __devexit remove_maple_vmu(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+
+ vmu_disconnect(mdev);
+ return 0;
+}
+
+static struct maple_driver vmu_flash_driver = {
+ .function = MAPLE_FUNC_MEMCARD,
+ .drv = {
+ .name = "Dreamcast_visual_memory",
+ .probe = probe_maple_vmu,
+ .remove = __devexit_p(remove_maple_vmu),
+ },
+};
+
+static int __init vmu_flash_map_init(void)
+{
+ maple_driver_register(&vmu_flash_driver);
+ return 0;
+}
+
+static void __exit vmu_flash_map_exit(void)
+{
+ maple_driver_unregister(&vmu_flash_driver);
+}
+
+module_init(vmu_flash_map_init);
+module_exit(vmu_flash_map_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adrian McMenamin");
+MODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory");

2009-01-29 00:34:14

by Adrian McMenamin

[permalink] [raw]
Subject: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (3/3)

Consequential patch to the maple keyboard driver and general fix to the keyboard driver.

Signed-off-by: Adrian McMenamin <[email protected]>
---

diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
index 22f17a5..8d3bb1d 100644
--- a/drivers/input/keyboard/maple_keyb.c
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -140,7 +140,7 @@ static void dc_kbd_callback(struct mapleq *mq)
{
struct maple_device *mapledev = mq->dev;
struct dc_kbd *kbd = maple_get_drvdata(mapledev);
- unsigned long *buf = mq->recvbuf;
+ unsigned long *buf = (unsigned long *)(mq->recvbuf->buf);

/*
* We should always get the lock because the only
@@ -159,22 +159,41 @@ static void dc_kbd_callback(struct mapleq *mq)

static int probe_maple_kbd(struct device *dev)
{
- struct maple_device *mdev = to_maple_dev(dev);
- struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ struct maple_device *mdev;
+ struct maple_driver *mdrv;
int i, error;
struct dc_kbd *kbd;
struct input_dev *idev;

- if (!(mdev->function & MAPLE_FUNC_KEYBOARD))
- return -EINVAL;
+ mdev = to_maple_dev(dev);
+ if (!mdev) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ mdrv = to_maple_driver(dev->driver);
+ if (!mdrv) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ if (!(mdev->function & MAPLE_FUNC_KEYBOARD)) {
+ error = EINVAL;
+ goto fail;
+ }

kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
- idev = input_allocate_device();
- if (!kbd || !idev) {
- error = -ENOMEM;
+ if (!kbd) {
+ error = ENOMEM;
goto fail;
}

+ idev = input_allocate_device();
+ if (!idev) {
+ error = ENOMEM;
+ goto fail_idev_alloc;
+ }
+
kbd->dev = idev;
memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));

@@ -195,7 +214,7 @@ static int probe_maple_kbd(struct device *dev)

error = input_register_device(idev);
if (error)
- goto fail;
+ goto fail_register;

/* Maple polling is locked to VBLANK - which may be just 50/s */
maple_getcond_callback(mdev, dc_kbd_callback, HZ/50,
@@ -207,11 +226,13 @@ static int probe_maple_kbd(struct device *dev)

return error;

-fail:
+fail_register:
+ maple_set_drvdata(mdev, NULL);
input_free_device(idev);
+fail_idev_alloc:
kfree(kbd);
- maple_set_drvdata(mdev, NULL);
- return error;
+fail:
+ return -error;
}

static int remove_maple_kbd(struct device *dev)

2009-01-29 00:43:03

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (1/3)

On Wednesday 28 January 2009 18:57:49 Adrian McMenamin wrote:
> --- a/drivers/sh/maple/maple.c
> +++ b/drivers/sh/maple/maple.c
> -int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
> - u32 command, size_t length, void *data)
> -{
> - int locking, ret = 0;
> - void *sendbuf = NULL;
> -
> - locking = mutex_lock_interruptible(&mdev->mq->mutex);
> - if (locking) {
> - ret = -EIO;
> - goto out;
> - }
> + int ret = 0;
> + void *sendbuf;
>
> + sendbuf = kzalloc(length * 4, GFP_KERNEL);
> if (length) {
> - sendbuf = kmalloc(length * 4, GFP_KERNEL);

that looks wonky ... you do the alloc first and then check the len ?

> failed_nomem:
> + printk(KERN_INFO "maple: could not allocate memory\n");

should be a KERN_NOTICE or worse ... KERN_INFO doesnt seem appropriate

> + if (device_register(&mdev->dev)) {
> printk(KERN_INFO
> - "Maple bus: Attempt to register device"
> - " (%x, %x) failed.\n",
> + "Maple bus: Attempt to register device"
> + " (%x, %x) failed.\n",

same here ... KERN_INFO shouldnt be used in error handlers

> +static void maple_response_fileerr(struct maple_device *mdev, void
> *recvbuf) +{
> + if (mdev->fileerrhandler) {
> + mdev->fileerrhandler(mdev, recvbuf);
> + return;
> + } else
> + printk(KERN_INFO "maple: device (%d, %d) reports file error %d\n",
> + mdev->port, mdev->unit, ((int *)recvbuf)[1]);
> +}

and here ...

> case MAPLE_RESPONSE_AGAIN:
> case MAPLE_RESPONSE_BADCMD:
> case MAPLE_RESPONSE_BADFUNC:
> - printk(KERN_DEBUG
> + printk(KERN_INFO
> "Maple non-fatal error 0x%X\n",
> code);

and here ...

> /* Maple Bus command and response codes */
> enum maple_code {
> + MAPLE_RESPONSE_FILEERR = -5,
> + MAPLE_RESPONSE_AGAIN = -4, /* retransmit */

do you really need to set every value ? i would only bother when there's a
discontinuity ...
-mike


Attachments:
(No filename) (1.83 kB)
signature.asc (835.00 B)
This is a digitally signed message part.
Download all attachments

2009-01-29 00:45:32

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (2/3)

On Wednesday 28 January 2009 19:09:10 Adrian McMenamin wrote:
> +static struct maple_driver vmu_flash_driver = {
> + .function = MAPLE_FUNC_MEMCARD,
> + .drv = {
> + .name = "Dreamcast_visual_memory",

i thought there was like a 16byte limit or something on this ?
-mike


Attachments:
(No filename) (273.00 B)
signature.asc (835.00 B)
This is a digitally signed message part.
Download all attachments

2009-01-29 00:53:00

by Adrian McMenamin

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (2/3)

On Wed, 2009-01-28 at 19:44 -0500, Mike Frysinger wrote:
> On Wednesday 28 January 2009 19:09:10 Adrian McMenamin wrote:
> > +static struct maple_driver vmu_flash_driver = {
> > + .function = MAPLE_FUNC_MEMCARD,
> > + .drv = {
> > + .name = "Dreamcast_visual_memory",
>
> i thought there was like a 16byte limit or something on this ?


Don't think so, it's just a const char* isn't it?

2009-01-29 01:13:17

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (2/3)

On Wednesday 28 January 2009 19:49:30 Adrian McMenamin wrote:
> On Wed, 2009-01-28 at 19:44 -0500, Mike Frysinger wrote:
> > On Wednesday 28 January 2009 19:09:10 Adrian McMenamin wrote:
> > > +static struct maple_driver vmu_flash_driver = {
> > > + .function = MAPLE_FUNC_MEMCARD,
> > > + .drv = {
> > > + .name = "Dreamcast_visual_memory",
> >
> > i thought there was like a 16byte limit or something on this ?
>
> Don't think so, it's just a const char* isn't it?

yeah, looks like. not sure what i remember.
-mike


Attachments:
(No filename) (521.00 B)
signature.asc (835.00 B)
This is a digitally signed message part.
Download all attachments

2009-01-29 03:01:21

by Paul Mundt

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (3/3)

On Thu, Jan 29, 2009 at 12:18:07AM +0000, Adrian McMenamin wrote:
> + if (!(mdev->function & MAPLE_FUNC_KEYBOARD)) {
> + error = EINVAL;
> + goto fail;
> + }
>
What is this all about? If the function doesn't match, you should never
enter the probe routine in the first place. The bus code should already
get this right.

2009-01-29 03:17:01

by Paul Mundt

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (1/3)

On Wed, Jan 28, 2009 at 11:57:49PM +0000, Adrian McMenamin wrote:
> Rework the maple bus driver to support block reads and writes, while
> also cleaning up the code and aiming for a consistent namespace for
> functions.
>
> Changes to the maple.h header file to support block reads and writes.
>
> Signed-off-by: Adrian McMenamin <[email protected]>
> ---
>
> diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
> index 63f0de2..4d9b8b3 100644
> --- a/drivers/sh/maple/maple.c
> +++ b/drivers/sh/maple/maple.c
> @@ -1,16 +1,10 @@
> /*
> * Core maple bus functionality
> *
> - * Copyright (C) 2007, 2008 Adrian McMenamin
> + * Copyright (C) 2007 - 2009 Adrian McMenamin
> * Copyright (C) 2001 - 2008 Paul Mundt
> - *
> - * Based on 2.4 code by:
> - *
> - * Copyright (C) 2000-2001 YAEGASHI Takeshi
> + * Copyright (C) 2000 - 2001 YAEGASHI Takeshi
> * Copyright (C) 2001 M. R. Brown
> - * Copyright (C) 2001 Paul Mundt
> - *
> - * and others.
> *

Uhm.. ok.

> static struct mapleq *maple_allocq(struct maple_device *mdev)
> {
> struct mapleq *mq;
>
> - mq = kmalloc(sizeof(*mq), GFP_KERNEL);
> + mq = kzalloc(sizeof(*mq), GFP_KERNEL);
> if (!mq)
> goto failed_nomem;
>
> mq->dev = mdev;
> - mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
> - mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp);
> + mq->recvbuf = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
> if (!mq->recvbuf)
> goto failed_p2;
> - /*
> - * most devices do not need the mutex - but
> - * anything that injects block reads or writes
> - * will rely on it
> - */
> - mutex_init(&mq->mutex);
> + mq->recvbuf->buf = (void *)P2SEGADDR(&((mq->recvbuf->bufx)[0]));
>
Please do not abuse P2SEGADDR in this fashion. No new code should be
using P2SEGADDR anyways. maple is particularly abusive in this case since
it bounces back and forth between P2SEGADDR and PHYSADDR to try and
ignore the fact cache flushing has to be handled.

You may also wish to consider ioremap/ioremap_nocache().

> return mq;
>
> failed_p2:
> kfree(mq);
> failed_nomem:
> + printk(KERN_INFO "maple: could not allocate memory\n");
> return NULL;
> }
>
printk() is pretty useless throughout all of this code, you have a struct
device, use it. dev_info()/dev_err()/etc/etc. with &mdev->dev will also
give you some meaningful indicator of which device in particular is
having issues.

> @@ -272,12 +201,16 @@ static struct maple_device *maple_alloc_dev(int port, int unit)
> {
> struct maple_device *mdev;
>
> + /* zero this out to avoid kobj subsystem
> + * thinking it has already been registered */
> +
> mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
> if (!mdev)
> return NULL;
>
> mdev->port = port;
> mdev->unit = unit;
> +
> mdev->mq = maple_allocq(mdev);
>
> if (!mdev->mq) {
> @@ -286,19 +219,18 @@ static struct maple_device *maple_alloc_dev(int port, int unit)
> }
> mdev->dev.bus = &maple_bus_type;
> mdev->dev.parent = &maple_bus;
> + init_waitqueue_head(&mdev->maple_wait);
> + mdev->busy = 0;
> + mdev->callback = NULL;
> + mdev->canunload = NULL;
> + mdev->fileerrhandler = NULL;
> return mdev;

All of these beyond the waitqueue initialization are superfluous, given
that you are kzalloc()'ing mdev in the first place.

> mdev->function = function;
> mdev->dev.release = &maple_release_device;
> - retval = device_register(&mdev->dev);
> - if (retval) {
> +
> + if (device_register(&mdev->dev)) {

This is not an improvement. There are many places throughout this code
where you have a perfectly usable errno value handed back to you that you
simply ignore in favour of a fixed error value. If you have an indicator
of what went wrong, it helps to pass that along in the error case,
especially if you are trying to debug an error.

> @@ -477,21 +418,25 @@ static int detach_maple_device(struct device *device, void *portptr)
> static int setup_maple_commands(struct device *device, void *ignored)
> {
> int add;
> - struct maple_device *maple_dev = to_maple_dev(device);
> -
> - if ((maple_dev->interval > 0)
> - && time_after(jiffies, maple_dev->when)) {
> - /* bounce if we cannot lock */
> - add = maple_add_packet(maple_dev,
> - be32_to_cpu(maple_dev->devinfo.function),
> + struct maple_device *mdev = to_maple_dev(device);
> + if ((mdev->interval > 0)
> + && time_after(jiffies, mdev->when)) {

&&'s at the end of the first line.

> @@ -52,11 +68,15 @@ struct maple_device {
> struct maple_driver *driver;
> struct mapleq *mq;
> void (*callback) (struct mapleq * mq);
> + void (*fileerrhandler)(struct maple_device * mdev, void * recvbuf);
> + int (*canunload)(struct maple_device * mdev);

I find it rather entertaining that you have no qualms about using
ridiculously long device and driver names even with fancy caps yet can't
bring yourself to insert an _ in your function pointer names.

2009-01-29 05:46:21

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (3/3)

On Wednesday 28 January 2009 19:18:07 Adrian McMenamin wrote:
> --- a/drivers/input/keyboard/maple_keyb.c
> +++ b/drivers/input/keyboard/maple_keyb.c
> - if (!kbd || !idev) {
> - error = -ENOMEM;
> + if (!kbd) {
> + error = ENOMEM;
> goto fail;
> }
> ...
> - return error;
> +fail:
> + return -error;

this driver has the weird/broken error inverting other patches of yours have
had recently ... that stuff really needs to stop showing up ;)
-mike

2009-01-29 10:36:15

by Adrian McMenamin

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (3/3)

On Thu, January 29, 2009 2:57 am, Paul Mundt wrote:
> On Thu, Jan 29, 2009 at 12:18:07AM +0000, Adrian McMenamin wrote:
>> + if (!(mdev->function & MAPLE_FUNC_KEYBOARD)) {
>> + error = EINVAL;
>> + goto fail;
>> + }
>>
> What is this all about? If the function doesn't match, you should never
> enter the probe routine in the first place. The bus code should already
> get this right.
>

It does, it is a fragment of old code from when the bus code was wrong
which for reasons which pass understanding I rewrote rather than just
excised.

2009-01-29 16:45:39

by Adrian McMenamin

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (1/3)

On Thu, January 29, 2009 3:13 am, Paul Mundt wrote:
> On Wed, Jan 28, 2009 at 11:57:49PM +0000, Adrian McMenamin wrote:
>> Rework the maple bus driver to support block reads and writes, while
>> also cleaning up the code and aiming for a consistent namespace for
>> functions.
>>
>> - mutex_init(&mq->mutex);
>> + mq->recvbuf->buf = (void *)P2SEGADDR(&((mq->recvbuf->bufx)[0]));
>>
> Please do not abuse P2SEGADDR in this fashion. No new code should be
> using P2SEGADDR anyways. maple is particularly abusive in this case since
> it bounces back and forth between P2SEGADDR and PHYSADDR to try and
> ignore the fact cache flushing has to be handled.
>
> You may also wish to consider ioremap/ioremap_nocache().
>

Can I just use dma_cache_sync before each dma run and then use the P1
address space?

2009-01-29 16:52:58

by Adrian McMenamin

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (1/3)

On Thu, January 29, 2009 4:43 pm, Adrian McMenamin wrote:
> On Thu, January 29, 2009 3:13 am, Paul Mundt wrote:
>> On Wed, Jan 28, 2009 at 11:57:49PM +0000, Adrian McMenamin wrote:
>>> Rework the maple bus driver to support block reads and writes, while
>>> also cleaning up the code and aiming for a consistent namespace for
>>> functions.
>>>
>>> - mutex_init(&mq->mutex);
>>> + mq->recvbuf->buf = (void *)P2SEGADDR(&((mq->recvbuf->bufx)[0]));
>>>
>> Please do not abuse P2SEGADDR in this fashion. No new code should be
>> using P2SEGADDR anyways. maple is particularly abusive in this case
>> since
>> it bounces back and forth between P2SEGADDR and PHYSADDR to try and
>> ignore the fact cache flushing has to be handled.
>>
>> You may also wish to consider ioremap/ioremap_nocache().
>>
>
> Can I just use dma_cache_sync before each dma run and then use the P1
> address space?
>

After the dma run I mean - the cache is flushed before the run - but not
after.

2009-01-30 09:55:50

by Adrian McMenamin

[permalink] [raw]
Subject: Re: [PATCH RFC] sh: maple: Add support for SEGA Dreamcast VMU and clean up maple bus driver (1/3)

2009/1/29 Adrian McMenamin <[email protected]>:
> On Thu, January 29, 2009 4:43 pm, Adrian McMenamin wrote:
>> On Thu, January 29, 2009 3:13 am, Paul Mundt wrote:

>>>>
>>> Please do not abuse P2SEGADDR in this fashion. No new code should be
>>> using P2SEGADDR anyways. maple is particularly abusive in this case
>>> since
>>> it bounces back and forth between P2SEGADDR and PHYSADDR to try and
>>> ignore the fact cache flushing has to be handled.
>>>
>>> You may also wish to consider ioremap/ioremap_nocache().
>>>
>>
>> Can I just use dma_cache_sync before each dma run and then use the P1
>> address space?
>>
>
> After the dma run I mean - the cache is flushed before the run - but not
> after.
>


So I have done this:

Removing P2SEGADDR call and flushing caches
---

diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 4d9b8b3..5dca308 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -186,7 +186,7 @@ static struct mapleq *maple_allocq(struct
maple_device *mdev)
mq->recvbuf = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
if (!mq->recvbuf)
goto failed_p2;
- mq->recvbuf->buf = (void *)P2SEGADDR(&((mq->recvbuf->bufx)[0]));
+ mq->recvbuf->buf = &((mq->recvbuf->bufx)[0]);

return mq;

@@ -639,9 +639,11 @@ static void maple_dma_handler(struct work_struct *work)
ctrl_outl(0, MAPLE_ENABLE);
if (!list_empty(&maple_sentq)) {
list_for_each_entry_safe(mq, nmq, &maple_sentq, list) {
+ mdev = mq->dev;
recvbuf = mq->recvbuf->buf;
+ dma_cache_sync(&mdev->dev, recvbuf, 0x400,
+ DMA_FROM_DEVICE);
code = recvbuf[0];
- mdev = mq->dev;
kfree(mq->sendbuf);
list_del_init(&mq->list);
switch (code) {


Seems to work...