xref: /openbmc/linux/drivers/sh/maple/maple.c (revision b9482378)
117be2d2bSAdrian McMenamin /*
217be2d2bSAdrian McMenamin  * Core maple bus functionality
317be2d2bSAdrian McMenamin  *
417be2d2bSAdrian McMenamin  *  Copyright (C) 2007 Adrian McMenamin
517be2d2bSAdrian McMenamin  *
617be2d2bSAdrian McMenamin  * Based on 2.4 code by:
717be2d2bSAdrian McMenamin  *
817be2d2bSAdrian McMenamin  *  Copyright (C) 2000-2001 YAEGASHI Takeshi
917be2d2bSAdrian McMenamin  *  Copyright (C) 2001 M. R. Brown
1017be2d2bSAdrian McMenamin  *  Copyright (C) 2001 Paul Mundt
1117be2d2bSAdrian McMenamin  *
1217be2d2bSAdrian McMenamin  * and others.
1317be2d2bSAdrian McMenamin  *
1417be2d2bSAdrian McMenamin  * This file is subject to the terms and conditions of the GNU General Public
1517be2d2bSAdrian McMenamin  * License.  See the file "COPYING" in the main directory of this archive
1617be2d2bSAdrian McMenamin  * for more details.
1717be2d2bSAdrian McMenamin  */
1817be2d2bSAdrian McMenamin #include <linux/init.h>
1917be2d2bSAdrian McMenamin #include <linux/kernel.h>
2017be2d2bSAdrian McMenamin #include <linux/device.h>
2117be2d2bSAdrian McMenamin #include <linux/module.h>
2217be2d2bSAdrian McMenamin #include <linux/interrupt.h>
2317be2d2bSAdrian McMenamin #include <linux/list.h>
2417be2d2bSAdrian McMenamin #include <linux/io.h>
2517be2d2bSAdrian McMenamin #include <linux/slab.h>
2617be2d2bSAdrian McMenamin #include <linux/maple.h>
2717be2d2bSAdrian McMenamin #include <linux/dma-mapping.h>
2817be2d2bSAdrian McMenamin #include <asm/cacheflush.h>
2917be2d2bSAdrian McMenamin #include <asm/dma.h>
3017be2d2bSAdrian McMenamin #include <asm/io.h>
3117be2d2bSAdrian McMenamin #include <asm/mach/dma.h>
3217be2d2bSAdrian McMenamin #include <asm/mach/sysasic.h>
3317be2d2bSAdrian McMenamin #include <asm/mach/maple.h>
3417be2d2bSAdrian McMenamin 
3517be2d2bSAdrian McMenamin MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin");
3617be2d2bSAdrian McMenamin MODULE_DESCRIPTION("Maple bus driver for Dreamcast");
3717be2d2bSAdrian McMenamin MODULE_LICENSE("GPL v2");
3817be2d2bSAdrian McMenamin MODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}");
3917be2d2bSAdrian McMenamin 
4017be2d2bSAdrian McMenamin static void maple_dma_handler(struct work_struct *work);
4117be2d2bSAdrian McMenamin static void maple_vblank_handler(struct work_struct *work);
4217be2d2bSAdrian McMenamin 
4317be2d2bSAdrian McMenamin static DECLARE_WORK(maple_dma_process, maple_dma_handler);
4417be2d2bSAdrian McMenamin static DECLARE_WORK(maple_vblank_process, maple_vblank_handler);
4517be2d2bSAdrian McMenamin 
4617be2d2bSAdrian McMenamin static LIST_HEAD(maple_waitq);
4717be2d2bSAdrian McMenamin static LIST_HEAD(maple_sentq);
4817be2d2bSAdrian McMenamin 
4917be2d2bSAdrian McMenamin static DEFINE_MUTEX(maple_list_lock);
5017be2d2bSAdrian McMenamin 
5117be2d2bSAdrian McMenamin static struct maple_driver maple_dummy_driver;
5217be2d2bSAdrian McMenamin static struct device maple_bus;
5317be2d2bSAdrian McMenamin static int subdevice_map[MAPLE_PORTS];
5417be2d2bSAdrian McMenamin static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr;
5517be2d2bSAdrian McMenamin static unsigned long maple_pnp_time;
5617be2d2bSAdrian McMenamin static int started, scanning, liststatus;
5717be2d2bSAdrian McMenamin static struct kmem_cache *maple_queue_cache;
5817be2d2bSAdrian McMenamin 
5917be2d2bSAdrian McMenamin struct maple_device_specify {
6017be2d2bSAdrian McMenamin 	int port;
6117be2d2bSAdrian McMenamin 	int unit;
6217be2d2bSAdrian McMenamin };
6317be2d2bSAdrian McMenamin 
6417be2d2bSAdrian McMenamin /**
6517be2d2bSAdrian McMenamin  *  maple_driver_register - register a device driver
6617be2d2bSAdrian McMenamin  *  automatically makes the driver bus a maple bus
6717be2d2bSAdrian McMenamin  *  @drv: the driver to be registered
6817be2d2bSAdrian McMenamin  */
6917be2d2bSAdrian McMenamin int maple_driver_register(struct device_driver *drv)
7017be2d2bSAdrian McMenamin {
7117be2d2bSAdrian McMenamin 	if (!drv)
7217be2d2bSAdrian McMenamin 		return -EINVAL;
7317be2d2bSAdrian McMenamin 	drv->bus = &maple_bus_type;
7417be2d2bSAdrian McMenamin 	return driver_register(drv);
7517be2d2bSAdrian McMenamin }
76b9482378SAdrian McMenamin 
7717be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_driver_register);
7817be2d2bSAdrian McMenamin 
7917be2d2bSAdrian McMenamin /* set hardware registers to enable next round of dma */
8017be2d2bSAdrian McMenamin static void maplebus_dma_reset(void)
8117be2d2bSAdrian McMenamin {
8217be2d2bSAdrian McMenamin 	ctrl_outl(MAPLE_MAGIC, MAPLE_RESET);
8317be2d2bSAdrian McMenamin 	/* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */
8417be2d2bSAdrian McMenamin 	ctrl_outl(1, MAPLE_TRIGTYPE);
8517be2d2bSAdrian McMenamin 	ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(50000), MAPLE_SPEED);
8617be2d2bSAdrian McMenamin 	ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR);
8717be2d2bSAdrian McMenamin 	ctrl_outl(1, MAPLE_ENABLE);
8817be2d2bSAdrian McMenamin }
8917be2d2bSAdrian McMenamin 
9017be2d2bSAdrian McMenamin /**
9117be2d2bSAdrian McMenamin  * maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND
9217be2d2bSAdrian McMenamin  * @dev: device responding
9317be2d2bSAdrian McMenamin  * @callback: handler callback
9417be2d2bSAdrian McMenamin  * @interval: interval in jiffies between callbacks
9517be2d2bSAdrian McMenamin  * @function: the function code for the device
9617be2d2bSAdrian McMenamin  */
9717be2d2bSAdrian McMenamin void maple_getcond_callback(struct maple_device *dev,
9817be2d2bSAdrian McMenamin 			    void (*callback) (struct mapleq * mq),
9917be2d2bSAdrian McMenamin 			    unsigned long interval, unsigned long function)
10017be2d2bSAdrian McMenamin {
10117be2d2bSAdrian McMenamin 	dev->callback = callback;
10217be2d2bSAdrian McMenamin 	dev->interval = interval;
10317be2d2bSAdrian McMenamin 	dev->function = cpu_to_be32(function);
10417be2d2bSAdrian McMenamin 	dev->when = jiffies;
10517be2d2bSAdrian McMenamin }
106b9482378SAdrian McMenamin 
10717be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_getcond_callback);
10817be2d2bSAdrian McMenamin 
10917be2d2bSAdrian McMenamin static int maple_dma_done(void)
11017be2d2bSAdrian McMenamin {
11117be2d2bSAdrian McMenamin 	return (ctrl_inl(MAPLE_STATE) & 1) == 0;
11217be2d2bSAdrian McMenamin }
11317be2d2bSAdrian McMenamin 
11417be2d2bSAdrian McMenamin static void maple_release_device(struct device *dev)
11517be2d2bSAdrian McMenamin {
11617be2d2bSAdrian McMenamin 	if (dev->type) {
11717be2d2bSAdrian McMenamin 		kfree(dev->type->name);
11817be2d2bSAdrian McMenamin 		kfree(dev->type);
11917be2d2bSAdrian McMenamin 	}
12017be2d2bSAdrian McMenamin }
12117be2d2bSAdrian McMenamin 
12217be2d2bSAdrian McMenamin /**
12317be2d2bSAdrian McMenamin  * maple_add_packet - add a single instruction to the queue
12417be2d2bSAdrian McMenamin  * @mq: instruction to add to waiting queue
12517be2d2bSAdrian McMenamin  */
12617be2d2bSAdrian McMenamin void maple_add_packet(struct mapleq *mq)
12717be2d2bSAdrian McMenamin {
12817be2d2bSAdrian McMenamin 	mutex_lock(&maple_list_lock);
12917be2d2bSAdrian McMenamin 	list_add(&mq->list, &maple_waitq);
13017be2d2bSAdrian McMenamin 	mutex_unlock(&maple_list_lock);
13117be2d2bSAdrian McMenamin }
132b9482378SAdrian McMenamin 
13317be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_add_packet);
13417be2d2bSAdrian McMenamin 
13517be2d2bSAdrian McMenamin static struct mapleq *maple_allocq(struct maple_device *dev)
13617be2d2bSAdrian McMenamin {
13717be2d2bSAdrian McMenamin 	struct mapleq *mq;
13817be2d2bSAdrian McMenamin 
13917be2d2bSAdrian McMenamin 	mq = kmalloc(sizeof(*mq), GFP_KERNEL);
14017be2d2bSAdrian McMenamin 	if (!mq)
14117be2d2bSAdrian McMenamin 		return NULL;
14217be2d2bSAdrian McMenamin 
14317be2d2bSAdrian McMenamin 	mq->dev = dev;
14417be2d2bSAdrian McMenamin 	mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
14517be2d2bSAdrian McMenamin 	mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp);
14617be2d2bSAdrian McMenamin 	if (!mq->recvbuf) {
14717be2d2bSAdrian McMenamin 		kfree(mq);
14817be2d2bSAdrian McMenamin 		return NULL;
14917be2d2bSAdrian McMenamin 	}
15017be2d2bSAdrian McMenamin 
15117be2d2bSAdrian McMenamin 	return mq;
15217be2d2bSAdrian McMenamin }
15317be2d2bSAdrian McMenamin 
15417be2d2bSAdrian McMenamin static struct maple_device *maple_alloc_dev(int port, int unit)
15517be2d2bSAdrian McMenamin {
15617be2d2bSAdrian McMenamin 	struct maple_device *dev;
15717be2d2bSAdrian McMenamin 
15817be2d2bSAdrian McMenamin 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
15917be2d2bSAdrian McMenamin 	if (!dev)
16017be2d2bSAdrian McMenamin 		return NULL;
16117be2d2bSAdrian McMenamin 
16217be2d2bSAdrian McMenamin 	dev->port = port;
16317be2d2bSAdrian McMenamin 	dev->unit = unit;
16417be2d2bSAdrian McMenamin 	dev->mq = maple_allocq(dev);
16517be2d2bSAdrian McMenamin 
16617be2d2bSAdrian McMenamin 	if (!dev->mq) {
16717be2d2bSAdrian McMenamin 		kfree(dev);
16817be2d2bSAdrian McMenamin 		return NULL;
16917be2d2bSAdrian McMenamin 	}
17017be2d2bSAdrian McMenamin 
17117be2d2bSAdrian McMenamin 	return dev;
17217be2d2bSAdrian McMenamin }
17317be2d2bSAdrian McMenamin 
17417be2d2bSAdrian McMenamin static void maple_free_dev(struct maple_device *mdev)
17517be2d2bSAdrian McMenamin {
17617be2d2bSAdrian McMenamin 	if (!mdev)
17717be2d2bSAdrian McMenamin 		return;
17817be2d2bSAdrian McMenamin 	if (mdev->mq) {
17917be2d2bSAdrian McMenamin 		kmem_cache_free(maple_queue_cache, mdev->mq->recvbufdcsp);
18017be2d2bSAdrian McMenamin 		kfree(mdev->mq);
18117be2d2bSAdrian McMenamin 	}
18217be2d2bSAdrian McMenamin 	kfree(mdev);
18317be2d2bSAdrian McMenamin }
18417be2d2bSAdrian McMenamin 
18517be2d2bSAdrian McMenamin /* process the command queue into a maple command block
18617be2d2bSAdrian McMenamin  * terminating command has bit 32 of first long set to 0
18717be2d2bSAdrian McMenamin  */
18817be2d2bSAdrian McMenamin static void maple_build_block(struct mapleq *mq)
18917be2d2bSAdrian McMenamin {
19017be2d2bSAdrian McMenamin 	int port, unit, from, to, len;
19117be2d2bSAdrian McMenamin 	unsigned long *lsendbuf = mq->sendbuf;
19217be2d2bSAdrian McMenamin 
19317be2d2bSAdrian McMenamin 	port = mq->dev->port & 3;
19417be2d2bSAdrian McMenamin 	unit = mq->dev->unit;
19517be2d2bSAdrian McMenamin 	len = mq->length;
19617be2d2bSAdrian McMenamin 	from = port << 6;
19717be2d2bSAdrian McMenamin 	to = (port << 6) | (unit > 0 ? (1 << (unit - 1)) & 0x1f : 0x20);
19817be2d2bSAdrian McMenamin 
19917be2d2bSAdrian McMenamin 	*maple_lastptr &= 0x7fffffff;
20017be2d2bSAdrian McMenamin 	maple_lastptr = maple_sendptr;
20117be2d2bSAdrian McMenamin 
20217be2d2bSAdrian McMenamin 	*maple_sendptr++ = (port << 16) | len | 0x80000000;
20317be2d2bSAdrian McMenamin 	*maple_sendptr++ = PHYSADDR(mq->recvbuf);
20417be2d2bSAdrian McMenamin 	*maple_sendptr++ =
20517be2d2bSAdrian McMenamin 	    mq->command | (to << 8) | (from << 16) | (len << 24);
20617be2d2bSAdrian McMenamin 
20717be2d2bSAdrian McMenamin 	while (len-- > 0)
20817be2d2bSAdrian McMenamin 		*maple_sendptr++ = *lsendbuf++;
20917be2d2bSAdrian McMenamin }
21017be2d2bSAdrian McMenamin 
21117be2d2bSAdrian McMenamin /* build up command queue */
21217be2d2bSAdrian McMenamin static void maple_send(void)
21317be2d2bSAdrian McMenamin {
21417be2d2bSAdrian McMenamin 	int i;
21517be2d2bSAdrian McMenamin 	int maple_packets;
21617be2d2bSAdrian McMenamin 	struct mapleq *mq, *nmq;
21717be2d2bSAdrian McMenamin 
21817be2d2bSAdrian McMenamin 	if (!list_empty(&maple_sentq))
21917be2d2bSAdrian McMenamin 		return;
22017be2d2bSAdrian McMenamin 	if (list_empty(&maple_waitq) || !maple_dma_done())
22117be2d2bSAdrian McMenamin 		return;
22217be2d2bSAdrian McMenamin 	maple_packets = 0;
22317be2d2bSAdrian McMenamin 	maple_sendptr = maple_lastptr = maple_sendbuf;
22417be2d2bSAdrian McMenamin 	list_for_each_entry_safe(mq, nmq, &maple_waitq, list) {
22517be2d2bSAdrian McMenamin 		maple_build_block(mq);
22617be2d2bSAdrian McMenamin 		list_move(&mq->list, &maple_sentq);
22717be2d2bSAdrian McMenamin 		if (maple_packets++ > MAPLE_MAXPACKETS)
22817be2d2bSAdrian McMenamin 			break;
22917be2d2bSAdrian McMenamin 	}
23017be2d2bSAdrian McMenamin 	if (maple_packets > 0) {
23117be2d2bSAdrian McMenamin 		for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++)
23217be2d2bSAdrian McMenamin 			dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE,
23317be2d2bSAdrian McMenamin 				       PAGE_SIZE, DMA_BIDIRECTIONAL);
23417be2d2bSAdrian McMenamin 	}
23517be2d2bSAdrian McMenamin }
23617be2d2bSAdrian McMenamin 
23717be2d2bSAdrian McMenamin static int attach_matching_maple_driver(struct device_driver *driver,
23817be2d2bSAdrian McMenamin 					void *devptr)
23917be2d2bSAdrian McMenamin {
24017be2d2bSAdrian McMenamin 	struct maple_driver *maple_drv;
24117be2d2bSAdrian McMenamin 	struct maple_device *mdev;
24217be2d2bSAdrian McMenamin 
24317be2d2bSAdrian McMenamin 	mdev = devptr;
24417be2d2bSAdrian McMenamin 	maple_drv = to_maple_driver(driver);
24517be2d2bSAdrian McMenamin 	if (mdev->devinfo.function & be32_to_cpu(maple_drv->function)) {
24617be2d2bSAdrian McMenamin 		if (maple_drv->connect(mdev) == 0) {
24717be2d2bSAdrian McMenamin 			mdev->driver = maple_drv;
24817be2d2bSAdrian McMenamin 			return 1;
24917be2d2bSAdrian McMenamin 		}
25017be2d2bSAdrian McMenamin 	}
25117be2d2bSAdrian McMenamin 	return 0;
25217be2d2bSAdrian McMenamin }
25317be2d2bSAdrian McMenamin 
25417be2d2bSAdrian McMenamin static void maple_detach_driver(struct maple_device *mdev)
25517be2d2bSAdrian McMenamin {
25617be2d2bSAdrian McMenamin 	if (!mdev)
25717be2d2bSAdrian McMenamin 		return;
25817be2d2bSAdrian McMenamin 	if (mdev->driver) {
25917be2d2bSAdrian McMenamin 		if (mdev->driver->disconnect)
26017be2d2bSAdrian McMenamin 			mdev->driver->disconnect(mdev);
26117be2d2bSAdrian McMenamin 	}
26217be2d2bSAdrian McMenamin 	mdev->driver = NULL;
26317be2d2bSAdrian McMenamin 	if (mdev->registered) {
26417be2d2bSAdrian McMenamin 		maple_release_device(&mdev->dev);
26517be2d2bSAdrian McMenamin 		device_unregister(&mdev->dev);
26617be2d2bSAdrian McMenamin 	}
26717be2d2bSAdrian McMenamin 	mdev->registered = 0;
26817be2d2bSAdrian McMenamin 	maple_free_dev(mdev);
26917be2d2bSAdrian McMenamin }
27017be2d2bSAdrian McMenamin 
27117be2d2bSAdrian McMenamin /* process initial MAPLE_COMMAND_DEVINFO for each device or port */
27217be2d2bSAdrian McMenamin static void maple_attach_driver(struct maple_device *dev)
27317be2d2bSAdrian McMenamin {
27417be2d2bSAdrian McMenamin 	char *p;
27517be2d2bSAdrian McMenamin 
27617be2d2bSAdrian McMenamin 	char *recvbuf;
27717be2d2bSAdrian McMenamin 	unsigned long function;
27817be2d2bSAdrian McMenamin 	int matched, retval;
27917be2d2bSAdrian McMenamin 
28017be2d2bSAdrian McMenamin 	recvbuf = dev->mq->recvbuf;
28117be2d2bSAdrian McMenamin 	memcpy(&dev->devinfo, recvbuf + 4, sizeof(dev->devinfo));
28217be2d2bSAdrian McMenamin 	memcpy(dev->product_name, dev->devinfo.product_name, 30);
28317be2d2bSAdrian McMenamin 	memcpy(dev->product_licence, dev->devinfo.product_licence, 60);
28417be2d2bSAdrian McMenamin 	dev->product_name[30] = '\0';
28517be2d2bSAdrian McMenamin 	dev->product_licence[60] = '\0';
28617be2d2bSAdrian McMenamin 
28717be2d2bSAdrian McMenamin 	for (p = dev->product_name + 29; dev->product_name <= p; p--)
28817be2d2bSAdrian McMenamin 		if (*p == ' ')
28917be2d2bSAdrian McMenamin 			*p = '\0';
29017be2d2bSAdrian McMenamin 		else
29117be2d2bSAdrian McMenamin 			break;
29217be2d2bSAdrian McMenamin 
29317be2d2bSAdrian McMenamin 	for (p = dev->product_licence + 59; dev->product_licence <= p; p--)
29417be2d2bSAdrian McMenamin 		if (*p == ' ')
29517be2d2bSAdrian McMenamin 			*p = '\0';
29617be2d2bSAdrian McMenamin 		else
29717be2d2bSAdrian McMenamin 			break;
29817be2d2bSAdrian McMenamin 
29917be2d2bSAdrian McMenamin 	function = be32_to_cpu(dev->devinfo.function);
30017be2d2bSAdrian McMenamin 
30117be2d2bSAdrian McMenamin 	if (function > 0x200) {
30217be2d2bSAdrian McMenamin 		/* Do this silently - as not a real device */
30317be2d2bSAdrian McMenamin 		function = 0;
30417be2d2bSAdrian McMenamin 		dev->driver = &maple_dummy_driver;
30517be2d2bSAdrian McMenamin 		sprintf(dev->dev.bus_id, "%d:0.port", dev->port);
30617be2d2bSAdrian McMenamin 	} else {
30717be2d2bSAdrian McMenamin 		printk(KERN_INFO
30817be2d2bSAdrian McMenamin 		       "Maple bus at (%d, %d): Connected function 0x%lX\n",
30917be2d2bSAdrian McMenamin 		       dev->port, dev->unit, function);
31017be2d2bSAdrian McMenamin 
31117be2d2bSAdrian McMenamin 		matched =
31217be2d2bSAdrian McMenamin 		    bus_for_each_drv(&maple_bus_type, NULL, dev,
31317be2d2bSAdrian McMenamin 				     attach_matching_maple_driver);
31417be2d2bSAdrian McMenamin 
31517be2d2bSAdrian McMenamin 		if (matched == 0) {
31617be2d2bSAdrian McMenamin 			/* Driver does not exist yet */
31717be2d2bSAdrian McMenamin 			printk(KERN_INFO
31817be2d2bSAdrian McMenamin 			       "No maple driver found for this device\n");
31917be2d2bSAdrian McMenamin 			dev->driver = &maple_dummy_driver;
32017be2d2bSAdrian McMenamin 		}
32117be2d2bSAdrian McMenamin 
32217be2d2bSAdrian McMenamin 		sprintf(dev->dev.bus_id, "%d:0%d.%lX", dev->port,
32317be2d2bSAdrian McMenamin 			dev->unit, function);
32417be2d2bSAdrian McMenamin 	}
32517be2d2bSAdrian McMenamin 	dev->function = function;
32617be2d2bSAdrian McMenamin 	dev->dev.bus = &maple_bus_type;
32717be2d2bSAdrian McMenamin 	dev->dev.parent = &maple_bus;
32817be2d2bSAdrian McMenamin 	dev->dev.release = &maple_release_device;
32917be2d2bSAdrian McMenamin 	retval = device_register(&dev->dev);
33017be2d2bSAdrian McMenamin 	if (retval) {
33117be2d2bSAdrian McMenamin 		printk(KERN_INFO
33217be2d2bSAdrian McMenamin 		       "Maple bus: Attempt to register device (%x, %x) failed.\n",
33317be2d2bSAdrian McMenamin 		       dev->port, dev->unit);
33417be2d2bSAdrian McMenamin 		maple_free_dev(dev);
33517be2d2bSAdrian McMenamin 	}
33617be2d2bSAdrian McMenamin 	dev->registered = 1;
33717be2d2bSAdrian McMenamin }
33817be2d2bSAdrian McMenamin 
33917be2d2bSAdrian McMenamin /*
34017be2d2bSAdrian McMenamin  * if device has been registered for the given
34117be2d2bSAdrian McMenamin  * port and unit then return 1 - allows identification
34217be2d2bSAdrian McMenamin  * of which devices need to be attached or detached
34317be2d2bSAdrian McMenamin  */
34417be2d2bSAdrian McMenamin static int detach_maple_device(struct device *device, void *portptr)
34517be2d2bSAdrian McMenamin {
34617be2d2bSAdrian McMenamin 	struct maple_device_specify *ds;
34717be2d2bSAdrian McMenamin 	struct maple_device *mdev;
34817be2d2bSAdrian McMenamin 
34917be2d2bSAdrian McMenamin 	ds = portptr;
35017be2d2bSAdrian McMenamin 	mdev = to_maple_dev(device);
35117be2d2bSAdrian McMenamin 	if (mdev->port == ds->port && mdev->unit == ds->unit)
35217be2d2bSAdrian McMenamin 		return 1;
35317be2d2bSAdrian McMenamin 	return 0;
35417be2d2bSAdrian McMenamin }
35517be2d2bSAdrian McMenamin 
35617be2d2bSAdrian McMenamin static int setup_maple_commands(struct device *device, void *ignored)
35717be2d2bSAdrian McMenamin {
35817be2d2bSAdrian McMenamin 	struct maple_device *maple_dev = to_maple_dev(device);
35917be2d2bSAdrian McMenamin 
36017be2d2bSAdrian McMenamin 	if ((maple_dev->interval > 0)
36117be2d2bSAdrian McMenamin 	    && time_after(jiffies, maple_dev->when)) {
36217be2d2bSAdrian McMenamin 		maple_dev->when = jiffies + maple_dev->interval;
36317be2d2bSAdrian McMenamin 		maple_dev->mq->command = MAPLE_COMMAND_GETCOND;
36417be2d2bSAdrian McMenamin 		maple_dev->mq->sendbuf = &maple_dev->function;
36517be2d2bSAdrian McMenamin 		maple_dev->mq->length = 1;
36617be2d2bSAdrian McMenamin 		maple_add_packet(maple_dev->mq);
36717be2d2bSAdrian McMenamin 		liststatus++;
36817be2d2bSAdrian McMenamin 	} else {
36917be2d2bSAdrian McMenamin 		if (time_after(jiffies, maple_pnp_time)) {
37017be2d2bSAdrian McMenamin 			maple_dev->mq->command = MAPLE_COMMAND_DEVINFO;
37117be2d2bSAdrian McMenamin 			maple_dev->mq->length = 0;
37217be2d2bSAdrian McMenamin 			maple_add_packet(maple_dev->mq);
37317be2d2bSAdrian McMenamin 			liststatus++;
37417be2d2bSAdrian McMenamin 		}
37517be2d2bSAdrian McMenamin 	}
37617be2d2bSAdrian McMenamin 
37717be2d2bSAdrian McMenamin 	return 0;
37817be2d2bSAdrian McMenamin }
37917be2d2bSAdrian McMenamin 
38017be2d2bSAdrian McMenamin /* VBLANK bottom half - implemented via workqueue */
38117be2d2bSAdrian McMenamin static void maple_vblank_handler(struct work_struct *work)
38217be2d2bSAdrian McMenamin {
38317be2d2bSAdrian McMenamin 	if (!maple_dma_done())
38417be2d2bSAdrian McMenamin 		return;
38517be2d2bSAdrian McMenamin 	if (!list_empty(&maple_sentq))
38617be2d2bSAdrian McMenamin 		return;
38717be2d2bSAdrian McMenamin 	ctrl_outl(0, MAPLE_ENABLE);
38817be2d2bSAdrian McMenamin 	liststatus = 0;
38917be2d2bSAdrian McMenamin 	bus_for_each_dev(&maple_bus_type, NULL, NULL,
39017be2d2bSAdrian McMenamin 			 setup_maple_commands);
39117be2d2bSAdrian McMenamin 	if (time_after(jiffies, maple_pnp_time))
39217be2d2bSAdrian McMenamin 		maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL;
39317be2d2bSAdrian McMenamin 	if (liststatus && list_empty(&maple_sentq)) {
39417be2d2bSAdrian McMenamin 		INIT_LIST_HEAD(&maple_sentq);
39517be2d2bSAdrian McMenamin 		maple_send();
39617be2d2bSAdrian McMenamin 	}
39717be2d2bSAdrian McMenamin 	maplebus_dma_reset();
39817be2d2bSAdrian McMenamin }
39917be2d2bSAdrian McMenamin 
40017be2d2bSAdrian McMenamin /* handle devices added via hotplugs - placing them on queue for DEVINFO*/
40117be2d2bSAdrian McMenamin static void maple_map_subunits(struct maple_device *mdev, int submask)
40217be2d2bSAdrian McMenamin {
40317be2d2bSAdrian McMenamin 	int retval, k, devcheck;
40417be2d2bSAdrian McMenamin 	struct maple_device *mdev_add;
40517be2d2bSAdrian McMenamin 	struct maple_device_specify ds;
40617be2d2bSAdrian McMenamin 
40717be2d2bSAdrian McMenamin 	for (k = 0; k < 5; k++) {
40817be2d2bSAdrian McMenamin 		ds.port = mdev->port;
40917be2d2bSAdrian McMenamin 		ds.unit = k + 1;
41017be2d2bSAdrian McMenamin 		retval =
41117be2d2bSAdrian McMenamin 		    bus_for_each_dev(&maple_bus_type, NULL, &ds,
41217be2d2bSAdrian McMenamin 				     detach_maple_device);
41317be2d2bSAdrian McMenamin 		if (retval) {
41417be2d2bSAdrian McMenamin 			submask = submask >> 1;
41517be2d2bSAdrian McMenamin 			continue;
41617be2d2bSAdrian McMenamin 		}
41717be2d2bSAdrian McMenamin 		devcheck = submask & 0x01;
41817be2d2bSAdrian McMenamin 		if (devcheck) {
41917be2d2bSAdrian McMenamin 			mdev_add = maple_alloc_dev(mdev->port, k + 1);
42017be2d2bSAdrian McMenamin 			if (!mdev_add)
42117be2d2bSAdrian McMenamin 				return;
42217be2d2bSAdrian McMenamin 			mdev_add->mq->command = MAPLE_COMMAND_DEVINFO;
42317be2d2bSAdrian McMenamin 			mdev_add->mq->length = 0;
42417be2d2bSAdrian McMenamin 			maple_add_packet(mdev_add->mq);
42517be2d2bSAdrian McMenamin 			scanning = 1;
42617be2d2bSAdrian McMenamin 		}
42717be2d2bSAdrian McMenamin 		submask = submask >> 1;
42817be2d2bSAdrian McMenamin 	}
42917be2d2bSAdrian McMenamin }
43017be2d2bSAdrian McMenamin 
43117be2d2bSAdrian McMenamin /* mark a device as removed */
43217be2d2bSAdrian McMenamin static void maple_clean_submap(struct maple_device *mdev)
43317be2d2bSAdrian McMenamin {
43417be2d2bSAdrian McMenamin 	int killbit;
43517be2d2bSAdrian McMenamin 
43617be2d2bSAdrian McMenamin 	killbit = (mdev->unit > 0 ? (1 << (mdev->unit - 1)) & 0x1f : 0x20);
43717be2d2bSAdrian McMenamin 	killbit = ~killbit;
43817be2d2bSAdrian McMenamin 	killbit &= 0xFF;
43917be2d2bSAdrian McMenamin 	subdevice_map[mdev->port] = subdevice_map[mdev->port] & killbit;
44017be2d2bSAdrian McMenamin }
44117be2d2bSAdrian McMenamin 
44217be2d2bSAdrian McMenamin /* handle empty port or hotplug removal */
44317be2d2bSAdrian McMenamin static void maple_response_none(struct maple_device *mdev,
44417be2d2bSAdrian McMenamin 				struct mapleq *mq)
44517be2d2bSAdrian McMenamin {
44617be2d2bSAdrian McMenamin 	if (mdev->unit != 0) {
44717be2d2bSAdrian McMenamin 		list_del(&mq->list);
44817be2d2bSAdrian McMenamin 		maple_clean_submap(mdev);
44917be2d2bSAdrian McMenamin 		printk(KERN_INFO
45017be2d2bSAdrian McMenamin 		       "Maple bus device detaching at (%d, %d)\n",
45117be2d2bSAdrian McMenamin 		       mdev->port, mdev->unit);
45217be2d2bSAdrian McMenamin 		maple_detach_driver(mdev);
45317be2d2bSAdrian McMenamin 		return;
45417be2d2bSAdrian McMenamin 	}
45517be2d2bSAdrian McMenamin 	if (!started) {
45617be2d2bSAdrian McMenamin 		printk(KERN_INFO "No maple devices attached to port %d\n",
45717be2d2bSAdrian McMenamin 		       mdev->port);
45817be2d2bSAdrian McMenamin 		return;
45917be2d2bSAdrian McMenamin 	}
46017be2d2bSAdrian McMenamin 	maple_clean_submap(mdev);
46117be2d2bSAdrian McMenamin }
46217be2d2bSAdrian McMenamin 
46317be2d2bSAdrian McMenamin /* preprocess hotplugs or scans */
46417be2d2bSAdrian McMenamin static void maple_response_devinfo(struct maple_device *mdev,
46517be2d2bSAdrian McMenamin 				   char *recvbuf)
46617be2d2bSAdrian McMenamin {
46717be2d2bSAdrian McMenamin 	char submask;
46817be2d2bSAdrian McMenamin 	if ((!started) || (scanning == 2)) {
46917be2d2bSAdrian McMenamin 		maple_attach_driver(mdev);
47017be2d2bSAdrian McMenamin 		return;
47117be2d2bSAdrian McMenamin 	}
47217be2d2bSAdrian McMenamin 	if (mdev->unit == 0) {
47317be2d2bSAdrian McMenamin 		submask = recvbuf[2] & 0x1F;
47417be2d2bSAdrian McMenamin 		if (submask ^ subdevice_map[mdev->port]) {
47517be2d2bSAdrian McMenamin 			maple_map_subunits(mdev, submask);
47617be2d2bSAdrian McMenamin 			subdevice_map[mdev->port] = submask;
47717be2d2bSAdrian McMenamin 		}
47817be2d2bSAdrian McMenamin 	}
47917be2d2bSAdrian McMenamin }
48017be2d2bSAdrian McMenamin 
48117be2d2bSAdrian McMenamin /* maple dma end bottom half - implemented via workqueue */
48217be2d2bSAdrian McMenamin static void maple_dma_handler(struct work_struct *work)
48317be2d2bSAdrian McMenamin {
48417be2d2bSAdrian McMenamin 	struct mapleq *mq, *nmq;
48517be2d2bSAdrian McMenamin 	struct maple_device *dev;
48617be2d2bSAdrian McMenamin 	char *recvbuf;
48717be2d2bSAdrian McMenamin 	enum maple_code code;
48817be2d2bSAdrian McMenamin 
48917be2d2bSAdrian McMenamin 	if (!maple_dma_done())
49017be2d2bSAdrian McMenamin 		return;
49117be2d2bSAdrian McMenamin 	ctrl_outl(0, MAPLE_ENABLE);
49217be2d2bSAdrian McMenamin 	if (!list_empty(&maple_sentq)) {
49317be2d2bSAdrian McMenamin 		list_for_each_entry_safe(mq, nmq, &maple_sentq, list) {
49417be2d2bSAdrian McMenamin 			recvbuf = mq->recvbuf;
49517be2d2bSAdrian McMenamin 			code = recvbuf[0];
49617be2d2bSAdrian McMenamin 			dev = mq->dev;
49717be2d2bSAdrian McMenamin 			switch (code) {
49817be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_NONE:
49917be2d2bSAdrian McMenamin 				maple_response_none(dev, mq);
50017be2d2bSAdrian McMenamin 				break;
50117be2d2bSAdrian McMenamin 
50217be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_DEVINFO:
50317be2d2bSAdrian McMenamin 				maple_response_devinfo(dev, recvbuf);
50417be2d2bSAdrian McMenamin 				break;
50517be2d2bSAdrian McMenamin 
50617be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_DATATRF:
50717be2d2bSAdrian McMenamin 				if (dev->callback)
50817be2d2bSAdrian McMenamin 					dev->callback(mq);
50917be2d2bSAdrian McMenamin 				break;
51017be2d2bSAdrian McMenamin 
51117be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_FILEERR:
51217be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_AGAIN:
51317be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_BADCMD:
51417be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_BADFUNC:
51517be2d2bSAdrian McMenamin 				printk(KERN_DEBUG
51617be2d2bSAdrian McMenamin 				       "Maple non-fatal error 0x%X\n",
51717be2d2bSAdrian McMenamin 				       code);
51817be2d2bSAdrian McMenamin 				break;
51917be2d2bSAdrian McMenamin 
52017be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_ALLINFO:
52117be2d2bSAdrian McMenamin 				printk(KERN_DEBUG
52217be2d2bSAdrian McMenamin 				       "Maple - extended device information not supported\n");
52317be2d2bSAdrian McMenamin 				break;
52417be2d2bSAdrian McMenamin 
52517be2d2bSAdrian McMenamin 			case MAPLE_RESPONSE_OK:
52617be2d2bSAdrian McMenamin 				break;
52717be2d2bSAdrian McMenamin 
52817be2d2bSAdrian McMenamin 			default:
52917be2d2bSAdrian McMenamin 				break;
53017be2d2bSAdrian McMenamin 			}
53117be2d2bSAdrian McMenamin 		}
53217be2d2bSAdrian McMenamin 		INIT_LIST_HEAD(&maple_sentq);
53317be2d2bSAdrian McMenamin 		if (scanning == 1) {
53417be2d2bSAdrian McMenamin 			maple_send();
53517be2d2bSAdrian McMenamin 			scanning = 2;
53617be2d2bSAdrian McMenamin 		} else
53717be2d2bSAdrian McMenamin 			scanning = 0;
53817be2d2bSAdrian McMenamin 
53917be2d2bSAdrian McMenamin 		if (started == 0)
54017be2d2bSAdrian McMenamin 			started = 1;
54117be2d2bSAdrian McMenamin 	}
54217be2d2bSAdrian McMenamin 	maplebus_dma_reset();
54317be2d2bSAdrian McMenamin }
54417be2d2bSAdrian McMenamin 
54517be2d2bSAdrian McMenamin static irqreturn_t maplebus_dma_interrupt(int irq, void *dev_id)
54617be2d2bSAdrian McMenamin {
54717be2d2bSAdrian McMenamin 	/* Load everything into the bottom half */
54817be2d2bSAdrian McMenamin 	schedule_work(&maple_dma_process);
54917be2d2bSAdrian McMenamin 	return IRQ_HANDLED;
55017be2d2bSAdrian McMenamin }
55117be2d2bSAdrian McMenamin 
55217be2d2bSAdrian McMenamin static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id)
55317be2d2bSAdrian McMenamin {
55417be2d2bSAdrian McMenamin 	schedule_work(&maple_vblank_process);
55517be2d2bSAdrian McMenamin 	return IRQ_HANDLED;
55617be2d2bSAdrian McMenamin }
55717be2d2bSAdrian McMenamin 
55817be2d2bSAdrian McMenamin static struct irqaction maple_dma_irq = {
55917be2d2bSAdrian McMenamin 	.name = "maple bus DMA handler",
56017be2d2bSAdrian McMenamin 	.handler = maplebus_dma_interrupt,
56117be2d2bSAdrian McMenamin 	.flags = IRQF_SHARED,
56217be2d2bSAdrian McMenamin };
56317be2d2bSAdrian McMenamin 
56417be2d2bSAdrian McMenamin static struct irqaction maple_vblank_irq = {
56517be2d2bSAdrian McMenamin 	.name = "maple bus VBLANK handler",
56617be2d2bSAdrian McMenamin 	.handler = maplebus_vblank_interrupt,
56717be2d2bSAdrian McMenamin 	.flags = IRQF_SHARED,
56817be2d2bSAdrian McMenamin };
56917be2d2bSAdrian McMenamin 
57017be2d2bSAdrian McMenamin static int maple_set_dma_interrupt_handler(void)
57117be2d2bSAdrian McMenamin {
57217be2d2bSAdrian McMenamin 	return setup_irq(HW_EVENT_MAPLE_DMA, &maple_dma_irq);
57317be2d2bSAdrian McMenamin }
57417be2d2bSAdrian McMenamin 
57517be2d2bSAdrian McMenamin static int maple_set_vblank_interrupt_handler(void)
57617be2d2bSAdrian McMenamin {
57717be2d2bSAdrian McMenamin 	return setup_irq(HW_EVENT_VSYNC, &maple_vblank_irq);
57817be2d2bSAdrian McMenamin }
57917be2d2bSAdrian McMenamin 
58017be2d2bSAdrian McMenamin static int maple_get_dma_buffer(void)
58117be2d2bSAdrian McMenamin {
58217be2d2bSAdrian McMenamin 	maple_sendbuf =
58317be2d2bSAdrian McMenamin 	    (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO,
58417be2d2bSAdrian McMenamin 				      MAPLE_DMA_PAGES);
58517be2d2bSAdrian McMenamin 	if (!maple_sendbuf)
58617be2d2bSAdrian McMenamin 		return -ENOMEM;
58717be2d2bSAdrian McMenamin 	return 0;
58817be2d2bSAdrian McMenamin }
58917be2d2bSAdrian McMenamin 
59017be2d2bSAdrian McMenamin static int match_maple_bus_driver(struct device *devptr,
59117be2d2bSAdrian McMenamin 				  struct device_driver *drvptr)
59217be2d2bSAdrian McMenamin {
59317be2d2bSAdrian McMenamin 	struct maple_driver *maple_drv;
59417be2d2bSAdrian McMenamin 	struct maple_device *maple_dev;
59517be2d2bSAdrian McMenamin 
59617be2d2bSAdrian McMenamin 	maple_drv = container_of(drvptr, struct maple_driver, drv);
59717be2d2bSAdrian McMenamin 	maple_dev = container_of(devptr, struct maple_device, dev);
59817be2d2bSAdrian McMenamin 	/* Trap empty port case */
59917be2d2bSAdrian McMenamin 	if (maple_dev->devinfo.function == 0xFFFFFFFF)
60017be2d2bSAdrian McMenamin 		return 0;
60117be2d2bSAdrian McMenamin 	else if (maple_dev->devinfo.function &
60217be2d2bSAdrian McMenamin 		 be32_to_cpu(maple_drv->function))
60317be2d2bSAdrian McMenamin 		return 1;
60417be2d2bSAdrian McMenamin 	return 0;
60517be2d2bSAdrian McMenamin }
60617be2d2bSAdrian McMenamin 
607b9482378SAdrian McMenamin static int maple_bus_uevent(struct device *dev,
608b9482378SAdrian McMenamin 			    struct kobj_uevent_env *env)
60917be2d2bSAdrian McMenamin {
61017be2d2bSAdrian McMenamin 	return 0;
61117be2d2bSAdrian McMenamin }
61217be2d2bSAdrian McMenamin 
61317be2d2bSAdrian McMenamin static void maple_bus_release(struct device *dev)
61417be2d2bSAdrian McMenamin {
61517be2d2bSAdrian McMenamin }
61617be2d2bSAdrian McMenamin 
61717be2d2bSAdrian McMenamin static struct maple_driver maple_dummy_driver = {
61817be2d2bSAdrian McMenamin 	.drv = {
61917be2d2bSAdrian McMenamin 		.name = "maple_dummy_driver",
62017be2d2bSAdrian McMenamin 		.bus = &maple_bus_type,
62117be2d2bSAdrian McMenamin 		},
62217be2d2bSAdrian McMenamin };
62317be2d2bSAdrian McMenamin 
62417be2d2bSAdrian McMenamin struct bus_type maple_bus_type = {
62517be2d2bSAdrian McMenamin 	.name = "maple",
62617be2d2bSAdrian McMenamin 	.match = match_maple_bus_driver,
62717be2d2bSAdrian McMenamin 	.uevent = maple_bus_uevent,
62817be2d2bSAdrian McMenamin };
629b9482378SAdrian McMenamin 
63017be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_bus_type);
63117be2d2bSAdrian McMenamin 
63217be2d2bSAdrian McMenamin static struct device maple_bus = {
63317be2d2bSAdrian McMenamin 	.bus_id = "maple",
63417be2d2bSAdrian McMenamin 	.release = maple_bus_release,
63517be2d2bSAdrian McMenamin };
63617be2d2bSAdrian McMenamin 
63717be2d2bSAdrian McMenamin static int __init maple_bus_init(void)
63817be2d2bSAdrian McMenamin {
63917be2d2bSAdrian McMenamin 	int retval, i;
64017be2d2bSAdrian McMenamin 	struct maple_device *mdev[MAPLE_PORTS];
64117be2d2bSAdrian McMenamin 	ctrl_outl(0, MAPLE_STATE);
64217be2d2bSAdrian McMenamin 
64317be2d2bSAdrian McMenamin 	retval = device_register(&maple_bus);
64417be2d2bSAdrian McMenamin 	if (retval)
64517be2d2bSAdrian McMenamin 		goto cleanup;
64617be2d2bSAdrian McMenamin 
64717be2d2bSAdrian McMenamin 	retval = bus_register(&maple_bus_type);
64817be2d2bSAdrian McMenamin 	if (retval)
64917be2d2bSAdrian McMenamin 		goto cleanup_device;
65017be2d2bSAdrian McMenamin 
65117be2d2bSAdrian McMenamin 	retval = driver_register(&maple_dummy_driver.drv);
65217be2d2bSAdrian McMenamin 
65317be2d2bSAdrian McMenamin 	if (retval)
65417be2d2bSAdrian McMenamin 		goto cleanup_bus;
65517be2d2bSAdrian McMenamin 
65617be2d2bSAdrian McMenamin 	/* allocate memory for maple bus dma */
65717be2d2bSAdrian McMenamin 	retval = maple_get_dma_buffer();
65817be2d2bSAdrian McMenamin 	if (retval) {
65917be2d2bSAdrian McMenamin 		printk(KERN_INFO
66017be2d2bSAdrian McMenamin 		       "Maple bus: Failed to allocate Maple DMA buffers\n");
66117be2d2bSAdrian McMenamin 		goto cleanup_basic;
66217be2d2bSAdrian McMenamin 	}
66317be2d2bSAdrian McMenamin 
66417be2d2bSAdrian McMenamin 	/* set up DMA interrupt handler */
66517be2d2bSAdrian McMenamin 	retval = maple_set_dma_interrupt_handler();
66617be2d2bSAdrian McMenamin 	if (retval) {
66717be2d2bSAdrian McMenamin 		printk(KERN_INFO
66817be2d2bSAdrian McMenamin 		       "Maple bus: Failed to grab maple DMA IRQ\n");
66917be2d2bSAdrian McMenamin 		goto cleanup_dma;
67017be2d2bSAdrian McMenamin 	}
67117be2d2bSAdrian McMenamin 
67217be2d2bSAdrian McMenamin 	/* set up VBLANK interrupt handler */
67317be2d2bSAdrian McMenamin 	retval = maple_set_vblank_interrupt_handler();
67417be2d2bSAdrian McMenamin 	if (retval) {
67517be2d2bSAdrian McMenamin 		printk(KERN_INFO "Maple bus: Failed to grab VBLANK IRQ\n");
67617be2d2bSAdrian McMenamin 		goto cleanup_irq;
67717be2d2bSAdrian McMenamin 	}
67817be2d2bSAdrian McMenamin 
67917be2d2bSAdrian McMenamin 	maple_queue_cache =
68017be2d2bSAdrian McMenamin 	    kmem_cache_create("maple_queue_cache", 0x400, 0,
68117be2d2bSAdrian McMenamin 			      SLAB_HWCACHE_ALIGN, NULL);
68217be2d2bSAdrian McMenamin 
68317be2d2bSAdrian McMenamin 	if (!maple_queue_cache)
68417be2d2bSAdrian McMenamin 		goto cleanup_bothirqs;
68517be2d2bSAdrian McMenamin 
68617be2d2bSAdrian McMenamin 	/* setup maple ports */
68717be2d2bSAdrian McMenamin 	for (i = 0; i < MAPLE_PORTS; i++) {
68817be2d2bSAdrian McMenamin 		mdev[i] = maple_alloc_dev(i, 0);
68917be2d2bSAdrian McMenamin 		if (!mdev[i]) {
69017be2d2bSAdrian McMenamin 			while (i-- > 0)
69117be2d2bSAdrian McMenamin 				maple_free_dev(mdev[i]);
69217be2d2bSAdrian McMenamin 			goto cleanup_cache;
69317be2d2bSAdrian McMenamin 		}
69417be2d2bSAdrian McMenamin 		mdev[i]->registered = 0;
69517be2d2bSAdrian McMenamin 		mdev[i]->mq->command = MAPLE_COMMAND_DEVINFO;
69617be2d2bSAdrian McMenamin 		mdev[i]->mq->length = 0;
69717be2d2bSAdrian McMenamin 		maple_attach_driver(mdev[i]);
69817be2d2bSAdrian McMenamin 		maple_add_packet(mdev[i]->mq);
69917be2d2bSAdrian McMenamin 		subdevice_map[i] = 0;
70017be2d2bSAdrian McMenamin 	}
70117be2d2bSAdrian McMenamin 
70217be2d2bSAdrian McMenamin 	/* setup maplebus hardware */
70317be2d2bSAdrian McMenamin 	maplebus_dma_reset();
70417be2d2bSAdrian McMenamin 
70517be2d2bSAdrian McMenamin 	/* initial detection */
70617be2d2bSAdrian McMenamin 	maple_send();
70717be2d2bSAdrian McMenamin 
70817be2d2bSAdrian McMenamin 	maple_pnp_time = jiffies;
70917be2d2bSAdrian McMenamin 
71017be2d2bSAdrian McMenamin 	printk(KERN_INFO "Maple bus core now registered.\n");
71117be2d2bSAdrian McMenamin 
71217be2d2bSAdrian McMenamin 	return 0;
71317be2d2bSAdrian McMenamin 
71417be2d2bSAdrian McMenamin       cleanup_cache:
71517be2d2bSAdrian McMenamin 	kmem_cache_destroy(maple_queue_cache);
71617be2d2bSAdrian McMenamin 
71717be2d2bSAdrian McMenamin       cleanup_bothirqs:
71817be2d2bSAdrian McMenamin 	free_irq(HW_EVENT_VSYNC, 0);
71917be2d2bSAdrian McMenamin 
72017be2d2bSAdrian McMenamin       cleanup_irq:
72117be2d2bSAdrian McMenamin 	free_irq(HW_EVENT_MAPLE_DMA, 0);
72217be2d2bSAdrian McMenamin 
72317be2d2bSAdrian McMenamin       cleanup_dma:
72417be2d2bSAdrian McMenamin 	free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES);
72517be2d2bSAdrian McMenamin 
72617be2d2bSAdrian McMenamin       cleanup_basic:
72717be2d2bSAdrian McMenamin 	driver_unregister(&maple_dummy_driver.drv);
72817be2d2bSAdrian McMenamin 
72917be2d2bSAdrian McMenamin       cleanup_bus:
73017be2d2bSAdrian McMenamin 	bus_unregister(&maple_bus_type);
73117be2d2bSAdrian McMenamin 
73217be2d2bSAdrian McMenamin       cleanup_device:
73317be2d2bSAdrian McMenamin 	device_unregister(&maple_bus);
73417be2d2bSAdrian McMenamin 
73517be2d2bSAdrian McMenamin       cleanup:
73617be2d2bSAdrian McMenamin 	printk(KERN_INFO "Maple bus registration failed\n");
73717be2d2bSAdrian McMenamin 	return retval;
73817be2d2bSAdrian McMenamin }
739b9482378SAdrian McMenamin 
74017be2d2bSAdrian McMenamin subsys_initcall(maple_bus_init);
741