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