117be2d2bSAdrian McMenamin /* 217be2d2bSAdrian McMenamin * Core maple bus functionality 317be2d2bSAdrian McMenamin * 4bd496669SAdrian McMenamin * Copyright (C) 2007, 2008 Adrian McMenamin 563870295SPaul Mundt * Copyright (C) 2001 - 2008 Paul Mundt 617be2d2bSAdrian McMenamin * 717be2d2bSAdrian McMenamin * Based on 2.4 code by: 817be2d2bSAdrian McMenamin * 917be2d2bSAdrian McMenamin * Copyright (C) 2000-2001 YAEGASHI Takeshi 1017be2d2bSAdrian McMenamin * Copyright (C) 2001 M. R. Brown 1117be2d2bSAdrian McMenamin * Copyright (C) 2001 Paul Mundt 1217be2d2bSAdrian McMenamin * 1317be2d2bSAdrian McMenamin * and others. 1417be2d2bSAdrian McMenamin * 1517be2d2bSAdrian McMenamin * This file is subject to the terms and conditions of the GNU General Public 1617be2d2bSAdrian McMenamin * License. See the file "COPYING" in the main directory of this archive 1717be2d2bSAdrian McMenamin * for more details. 1817be2d2bSAdrian McMenamin */ 1917be2d2bSAdrian McMenamin #include <linux/init.h> 2017be2d2bSAdrian McMenamin #include <linux/kernel.h> 2117be2d2bSAdrian McMenamin #include <linux/device.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> 281795cf48SAdrian McMenamin #include <linux/delay.h> 2917be2d2bSAdrian McMenamin #include <asm/cacheflush.h> 3017be2d2bSAdrian McMenamin #include <asm/dma.h> 3117be2d2bSAdrian McMenamin #include <asm/io.h> 321795cf48SAdrian McMenamin #include <mach/dma.h> 331795cf48SAdrian McMenamin #include <mach/sysasic.h> 3417be2d2bSAdrian McMenamin 3563870295SPaul Mundt MODULE_AUTHOR("Yaegashi 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 491795cf48SAdrian McMenamin /* mutex to protect queue of waiting packets */ 501795cf48SAdrian McMenamin static DEFINE_MUTEX(maple_wlist_lock); 5117be2d2bSAdrian McMenamin 5217be2d2bSAdrian McMenamin static struct maple_driver maple_dummy_driver; 5317be2d2bSAdrian McMenamin static struct device maple_bus; 5417be2d2bSAdrian McMenamin static int subdevice_map[MAPLE_PORTS]; 5517be2d2bSAdrian McMenamin static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; 5617be2d2bSAdrian McMenamin static unsigned long maple_pnp_time; 571795cf48SAdrian McMenamin static int started, scanning, fullscan; 5817be2d2bSAdrian McMenamin static struct kmem_cache *maple_queue_cache; 5917be2d2bSAdrian McMenamin 6017be2d2bSAdrian McMenamin struct maple_device_specify { 6117be2d2bSAdrian McMenamin int port; 6217be2d2bSAdrian McMenamin int unit; 6317be2d2bSAdrian McMenamin }; 6417be2d2bSAdrian McMenamin 65bd496669SAdrian McMenamin static bool checked[4]; 66bd496669SAdrian McMenamin static struct maple_device *baseunits[4]; 67bd496669SAdrian McMenamin 6817be2d2bSAdrian McMenamin /** 6963870295SPaul Mundt * maple_driver_register - register a maple driver 7063870295SPaul Mundt * @drv: maple driver to be registered. 7163870295SPaul Mundt * 7263870295SPaul Mundt * Registers the passed in @drv, while updating the bus type. 7363870295SPaul Mundt * Devices with matching function IDs will be automatically probed. 7417be2d2bSAdrian McMenamin */ 7563870295SPaul Mundt int maple_driver_register(struct maple_driver *drv) 7617be2d2bSAdrian McMenamin { 7717be2d2bSAdrian McMenamin if (!drv) 7817be2d2bSAdrian McMenamin return -EINVAL; 7963870295SPaul Mundt 8063870295SPaul Mundt drv->drv.bus = &maple_bus_type; 8163870295SPaul Mundt 8263870295SPaul Mundt return driver_register(&drv->drv); 8317be2d2bSAdrian McMenamin } 8417be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_driver_register); 8517be2d2bSAdrian McMenamin 8663870295SPaul Mundt /** 8763870295SPaul Mundt * maple_driver_unregister - unregister a maple driver. 8863870295SPaul Mundt * @drv: maple driver to unregister. 8963870295SPaul Mundt * 9063870295SPaul Mundt * Cleans up after maple_driver_register(). To be invoked in the exit 9163870295SPaul Mundt * path of any module drivers. 9263870295SPaul Mundt */ 9363870295SPaul Mundt void maple_driver_unregister(struct maple_driver *drv) 9463870295SPaul Mundt { 9563870295SPaul Mundt driver_unregister(&drv->drv); 9663870295SPaul Mundt } 9761787063SPaul Mundt EXPORT_SYMBOL_GPL(maple_driver_unregister); 9863870295SPaul Mundt 9917be2d2bSAdrian McMenamin /* set hardware registers to enable next round of dma */ 10017be2d2bSAdrian McMenamin static void maplebus_dma_reset(void) 10117be2d2bSAdrian McMenamin { 10217be2d2bSAdrian McMenamin ctrl_outl(MAPLE_MAGIC, MAPLE_RESET); 10317be2d2bSAdrian McMenamin /* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */ 10417be2d2bSAdrian McMenamin ctrl_outl(1, MAPLE_TRIGTYPE); 10517be2d2bSAdrian McMenamin ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(50000), MAPLE_SPEED); 10617be2d2bSAdrian McMenamin ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); 10717be2d2bSAdrian McMenamin ctrl_outl(1, MAPLE_ENABLE); 10817be2d2bSAdrian McMenamin } 10917be2d2bSAdrian McMenamin 11017be2d2bSAdrian McMenamin /** 11117be2d2bSAdrian McMenamin * maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND 11217be2d2bSAdrian McMenamin * @dev: device responding 11317be2d2bSAdrian McMenamin * @callback: handler callback 11417be2d2bSAdrian McMenamin * @interval: interval in jiffies between callbacks 11517be2d2bSAdrian McMenamin * @function: the function code for the device 11617be2d2bSAdrian McMenamin */ 11717be2d2bSAdrian McMenamin void maple_getcond_callback(struct maple_device *dev, 11817be2d2bSAdrian McMenamin void (*callback) (struct mapleq *mq), 11917be2d2bSAdrian McMenamin unsigned long interval, unsigned long function) 12017be2d2bSAdrian McMenamin { 12117be2d2bSAdrian McMenamin dev->callback = callback; 12217be2d2bSAdrian McMenamin dev->interval = interval; 12317be2d2bSAdrian McMenamin dev->function = cpu_to_be32(function); 12417be2d2bSAdrian McMenamin dev->when = jiffies; 12517be2d2bSAdrian McMenamin } 12617be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_getcond_callback); 12717be2d2bSAdrian McMenamin 12817be2d2bSAdrian McMenamin static int maple_dma_done(void) 12917be2d2bSAdrian McMenamin { 13017be2d2bSAdrian McMenamin return (ctrl_inl(MAPLE_STATE) & 1) == 0; 13117be2d2bSAdrian McMenamin } 13217be2d2bSAdrian McMenamin 13317be2d2bSAdrian McMenamin static void maple_release_device(struct device *dev) 13417be2d2bSAdrian McMenamin { 135b3c69e24SAdrian McMenamin struct maple_device *mdev; 136b3c69e24SAdrian McMenamin struct mapleq *mq; 137b3c69e24SAdrian McMenamin if (!dev) 138b3c69e24SAdrian McMenamin return; 139b3c69e24SAdrian McMenamin mdev = to_maple_dev(dev); 140b3c69e24SAdrian McMenamin mq = mdev->mq; 141b3c69e24SAdrian McMenamin if (mq) { 142b3c69e24SAdrian McMenamin if (mq->recvbufdcsp) 143b3c69e24SAdrian McMenamin kmem_cache_free(maple_queue_cache, mq->recvbufdcsp); 144b3c69e24SAdrian McMenamin kfree(mq); 145b3c69e24SAdrian McMenamin mq = NULL; 14617be2d2bSAdrian McMenamin } 147b3c69e24SAdrian McMenamin kfree(mdev); 14817be2d2bSAdrian McMenamin } 14917be2d2bSAdrian McMenamin 1501795cf48SAdrian McMenamin /* 15117be2d2bSAdrian McMenamin * maple_add_packet - add a single instruction to the queue 1521795cf48SAdrian McMenamin * @mdev - maple device 1531795cf48SAdrian McMenamin * @function - function on device being queried 1541795cf48SAdrian McMenamin * @command - maple command to add 1551795cf48SAdrian McMenamin * @length - length of command string (in 32 bit words) 1561795cf48SAdrian McMenamin * @data - remainder of command string 15717be2d2bSAdrian McMenamin */ 1581795cf48SAdrian McMenamin int maple_add_packet(struct maple_device *mdev, u32 function, u32 command, 1591795cf48SAdrian McMenamin size_t length, void *data) 16017be2d2bSAdrian McMenamin { 1611795cf48SAdrian McMenamin int locking, ret = 0; 1621795cf48SAdrian McMenamin void *sendbuf = NULL; 1631795cf48SAdrian McMenamin 1641795cf48SAdrian McMenamin mutex_lock(&maple_wlist_lock); 1651795cf48SAdrian McMenamin /* bounce if device already locked */ 1661795cf48SAdrian McMenamin locking = mutex_is_locked(&mdev->mq->mutex); 1671795cf48SAdrian McMenamin if (locking) { 1681795cf48SAdrian McMenamin ret = -EBUSY; 1691795cf48SAdrian McMenamin goto out; 1701795cf48SAdrian McMenamin } 1711795cf48SAdrian McMenamin 1721795cf48SAdrian McMenamin mutex_lock(&mdev->mq->mutex); 1731795cf48SAdrian McMenamin 1741795cf48SAdrian McMenamin if (length) { 1751795cf48SAdrian McMenamin sendbuf = kmalloc(length * 4, GFP_KERNEL); 1761795cf48SAdrian McMenamin if (!sendbuf) { 1771795cf48SAdrian McMenamin mutex_unlock(&mdev->mq->mutex); 1781795cf48SAdrian McMenamin ret = -ENOMEM; 1791795cf48SAdrian McMenamin goto out; 1801795cf48SAdrian McMenamin } 1811795cf48SAdrian McMenamin ((__be32 *)sendbuf)[0] = cpu_to_be32(function); 1821795cf48SAdrian McMenamin } 1831795cf48SAdrian McMenamin 1841795cf48SAdrian McMenamin mdev->mq->command = command; 1851795cf48SAdrian McMenamin mdev->mq->length = length; 1861795cf48SAdrian McMenamin if (length > 1) 1871795cf48SAdrian McMenamin memcpy(sendbuf + 4, data, (length - 1) * 4); 1881795cf48SAdrian McMenamin mdev->mq->sendbuf = sendbuf; 1891795cf48SAdrian McMenamin 1901795cf48SAdrian McMenamin list_add(&mdev->mq->list, &maple_waitq); 1911795cf48SAdrian McMenamin out: 1921795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 1931795cf48SAdrian McMenamin return ret; 19417be2d2bSAdrian McMenamin } 19517be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_add_packet); 19617be2d2bSAdrian McMenamin 1971795cf48SAdrian McMenamin /* 1981795cf48SAdrian McMenamin * maple_add_packet_sleeps - add a single instruction to the queue 1991795cf48SAdrian McMenamin * - waits for lock to be free 2001795cf48SAdrian McMenamin * @mdev - maple device 2011795cf48SAdrian McMenamin * @function - function on device being queried 2021795cf48SAdrian McMenamin * @command - maple command to add 2031795cf48SAdrian McMenamin * @length - length of command string (in 32 bit words) 2041795cf48SAdrian McMenamin * @data - remainder of command string 2051795cf48SAdrian McMenamin */ 2061795cf48SAdrian McMenamin int maple_add_packet_sleeps(struct maple_device *mdev, u32 function, 2071795cf48SAdrian McMenamin u32 command, size_t length, void *data) 2081795cf48SAdrian McMenamin { 2091795cf48SAdrian McMenamin int locking, ret = 0; 2101795cf48SAdrian McMenamin void *sendbuf = NULL; 2111795cf48SAdrian McMenamin 2121795cf48SAdrian McMenamin locking = mutex_lock_interruptible(&mdev->mq->mutex); 2131795cf48SAdrian McMenamin if (locking) { 2141795cf48SAdrian McMenamin ret = -EIO; 2151795cf48SAdrian McMenamin goto out; 2161795cf48SAdrian McMenamin } 2171795cf48SAdrian McMenamin 2181795cf48SAdrian McMenamin if (length) { 2191795cf48SAdrian McMenamin sendbuf = kmalloc(length * 4, GFP_KERNEL); 2201795cf48SAdrian McMenamin if (!sendbuf) { 2211795cf48SAdrian McMenamin mutex_unlock(&mdev->mq->mutex); 2221795cf48SAdrian McMenamin ret = -ENOMEM; 2231795cf48SAdrian McMenamin goto out; 2241795cf48SAdrian McMenamin } 2251795cf48SAdrian McMenamin ((__be32 *)sendbuf)[0] = cpu_to_be32(function); 2261795cf48SAdrian McMenamin } 2271795cf48SAdrian McMenamin 2281795cf48SAdrian McMenamin mdev->mq->command = command; 2291795cf48SAdrian McMenamin mdev->mq->length = length; 2301795cf48SAdrian McMenamin if (length > 1) 2311795cf48SAdrian McMenamin memcpy(sendbuf + 4, data, (length - 1) * 4); 2321795cf48SAdrian McMenamin mdev->mq->sendbuf = sendbuf; 2331795cf48SAdrian McMenamin 2341795cf48SAdrian McMenamin mutex_lock(&maple_wlist_lock); 2351795cf48SAdrian McMenamin list_add(&mdev->mq->list, &maple_waitq); 2361795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 2371795cf48SAdrian McMenamin out: 2381795cf48SAdrian McMenamin return ret; 2391795cf48SAdrian McMenamin } 2401795cf48SAdrian McMenamin EXPORT_SYMBOL_GPL(maple_add_packet_sleeps); 2411795cf48SAdrian McMenamin 242b3c69e24SAdrian McMenamin static struct mapleq *maple_allocq(struct maple_device *mdev) 24317be2d2bSAdrian McMenamin { 24417be2d2bSAdrian McMenamin struct mapleq *mq; 24517be2d2bSAdrian McMenamin 24617be2d2bSAdrian McMenamin mq = kmalloc(sizeof(*mq), GFP_KERNEL); 24717be2d2bSAdrian McMenamin if (!mq) 2481795cf48SAdrian McMenamin goto failed_nomem; 24917be2d2bSAdrian McMenamin 250b3c69e24SAdrian McMenamin mq->dev = mdev; 25117be2d2bSAdrian McMenamin mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL); 25217be2d2bSAdrian McMenamin mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp); 2531795cf48SAdrian McMenamin if (!mq->recvbuf) 2541795cf48SAdrian McMenamin goto failed_p2; 2551795cf48SAdrian McMenamin /* 2561795cf48SAdrian McMenamin * most devices do not need the mutex - but 2571795cf48SAdrian McMenamin * anything that injects block reads or writes 2581795cf48SAdrian McMenamin * will rely on it 2591795cf48SAdrian McMenamin */ 2601795cf48SAdrian McMenamin mutex_init(&mq->mutex); 26117be2d2bSAdrian McMenamin 26217be2d2bSAdrian McMenamin return mq; 2631795cf48SAdrian McMenamin 2641795cf48SAdrian McMenamin failed_p2: 2651795cf48SAdrian McMenamin kfree(mq); 2661795cf48SAdrian McMenamin failed_nomem: 2671795cf48SAdrian McMenamin return NULL; 26817be2d2bSAdrian McMenamin } 26917be2d2bSAdrian McMenamin 27017be2d2bSAdrian McMenamin static struct maple_device *maple_alloc_dev(int port, int unit) 27117be2d2bSAdrian McMenamin { 272b3c69e24SAdrian McMenamin struct maple_device *mdev; 27317be2d2bSAdrian McMenamin 274b3c69e24SAdrian McMenamin mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 275b3c69e24SAdrian McMenamin if (!mdev) 27617be2d2bSAdrian McMenamin return NULL; 27717be2d2bSAdrian McMenamin 278b3c69e24SAdrian McMenamin mdev->port = port; 279b3c69e24SAdrian McMenamin mdev->unit = unit; 280b3c69e24SAdrian McMenamin mdev->mq = maple_allocq(mdev); 28117be2d2bSAdrian McMenamin 282b3c69e24SAdrian McMenamin if (!mdev->mq) { 283b3c69e24SAdrian McMenamin kfree(mdev); 28417be2d2bSAdrian McMenamin return NULL; 28517be2d2bSAdrian McMenamin } 286b3c69e24SAdrian McMenamin mdev->dev.bus = &maple_bus_type; 287b3c69e24SAdrian McMenamin mdev->dev.parent = &maple_bus; 288b3c69e24SAdrian McMenamin return mdev; 28917be2d2bSAdrian McMenamin } 29017be2d2bSAdrian McMenamin 29117be2d2bSAdrian McMenamin static void maple_free_dev(struct maple_device *mdev) 29217be2d2bSAdrian McMenamin { 29317be2d2bSAdrian McMenamin if (!mdev) 29417be2d2bSAdrian McMenamin return; 29517be2d2bSAdrian McMenamin if (mdev->mq) { 296b3c69e24SAdrian McMenamin if (mdev->mq->recvbufdcsp) 297b3c69e24SAdrian McMenamin kmem_cache_free(maple_queue_cache, 298b3c69e24SAdrian McMenamin mdev->mq->recvbufdcsp); 29917be2d2bSAdrian McMenamin kfree(mdev->mq); 30017be2d2bSAdrian McMenamin } 30117be2d2bSAdrian McMenamin kfree(mdev); 30217be2d2bSAdrian McMenamin } 30317be2d2bSAdrian McMenamin 30417be2d2bSAdrian McMenamin /* process the command queue into a maple command block 30517be2d2bSAdrian McMenamin * terminating command has bit 32 of first long set to 0 30617be2d2bSAdrian McMenamin */ 30717be2d2bSAdrian McMenamin static void maple_build_block(struct mapleq *mq) 30817be2d2bSAdrian McMenamin { 30917be2d2bSAdrian McMenamin int port, unit, from, to, len; 31017be2d2bSAdrian McMenamin unsigned long *lsendbuf = mq->sendbuf; 31117be2d2bSAdrian McMenamin 31217be2d2bSAdrian McMenamin port = mq->dev->port & 3; 31317be2d2bSAdrian McMenamin unit = mq->dev->unit; 31417be2d2bSAdrian McMenamin len = mq->length; 31517be2d2bSAdrian McMenamin from = port << 6; 31617be2d2bSAdrian McMenamin to = (port << 6) | (unit > 0 ? (1 << (unit - 1)) & 0x1f : 0x20); 31717be2d2bSAdrian McMenamin 31817be2d2bSAdrian McMenamin *maple_lastptr &= 0x7fffffff; 31917be2d2bSAdrian McMenamin maple_lastptr = maple_sendptr; 32017be2d2bSAdrian McMenamin 32117be2d2bSAdrian McMenamin *maple_sendptr++ = (port << 16) | len | 0x80000000; 32217be2d2bSAdrian McMenamin *maple_sendptr++ = PHYSADDR(mq->recvbuf); 32317be2d2bSAdrian McMenamin *maple_sendptr++ = 32417be2d2bSAdrian McMenamin mq->command | (to << 8) | (from << 16) | (len << 24); 32517be2d2bSAdrian McMenamin while (len-- > 0) 32617be2d2bSAdrian McMenamin *maple_sendptr++ = *lsendbuf++; 32717be2d2bSAdrian McMenamin } 32817be2d2bSAdrian McMenamin 32917be2d2bSAdrian McMenamin /* build up command queue */ 33017be2d2bSAdrian McMenamin static void maple_send(void) 33117be2d2bSAdrian McMenamin { 3321795cf48SAdrian McMenamin int i, maple_packets = 0; 33317be2d2bSAdrian McMenamin struct mapleq *mq, *nmq; 33417be2d2bSAdrian McMenamin 33517be2d2bSAdrian McMenamin if (!list_empty(&maple_sentq)) 33617be2d2bSAdrian McMenamin return; 3371795cf48SAdrian McMenamin mutex_lock(&maple_wlist_lock); 3381795cf48SAdrian McMenamin if (list_empty(&maple_waitq) || !maple_dma_done()) { 3391795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 34017be2d2bSAdrian McMenamin return; 3411795cf48SAdrian McMenamin } 3421795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 3431795cf48SAdrian McMenamin maple_lastptr = maple_sendbuf; 3441795cf48SAdrian McMenamin maple_sendptr = maple_sendbuf; 3451795cf48SAdrian McMenamin mutex_lock(&maple_wlist_lock); 34617be2d2bSAdrian McMenamin list_for_each_entry_safe(mq, nmq, &maple_waitq, list) { 34717be2d2bSAdrian McMenamin maple_build_block(mq); 34817be2d2bSAdrian McMenamin list_move(&mq->list, &maple_sentq); 34917be2d2bSAdrian McMenamin if (maple_packets++ > MAPLE_MAXPACKETS) 35017be2d2bSAdrian McMenamin break; 35117be2d2bSAdrian McMenamin } 3521795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 35317be2d2bSAdrian McMenamin if (maple_packets > 0) { 35417be2d2bSAdrian McMenamin for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++) 35517be2d2bSAdrian McMenamin dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE, 35617be2d2bSAdrian McMenamin PAGE_SIZE, DMA_BIDIRECTIONAL); 35717be2d2bSAdrian McMenamin } 35817be2d2bSAdrian McMenamin } 35917be2d2bSAdrian McMenamin 3601795cf48SAdrian McMenamin /* check if there is a driver registered likely to match this device */ 3611795cf48SAdrian McMenamin static int check_matching_maple_driver(struct device_driver *driver, 36217be2d2bSAdrian McMenamin void *devptr) 36317be2d2bSAdrian McMenamin { 36417be2d2bSAdrian McMenamin struct maple_driver *maple_drv; 36517be2d2bSAdrian McMenamin struct maple_device *mdev; 36617be2d2bSAdrian McMenamin 36717be2d2bSAdrian McMenamin mdev = devptr; 36817be2d2bSAdrian McMenamin maple_drv = to_maple_driver(driver); 3691795cf48SAdrian McMenamin if (mdev->devinfo.function & cpu_to_be32(maple_drv->function)) 37017be2d2bSAdrian McMenamin return 1; 37117be2d2bSAdrian McMenamin return 0; 37217be2d2bSAdrian McMenamin } 37317be2d2bSAdrian McMenamin 37417be2d2bSAdrian McMenamin static void maple_detach_driver(struct maple_device *mdev) 37517be2d2bSAdrian McMenamin { 37617be2d2bSAdrian McMenamin if (!mdev) 37717be2d2bSAdrian McMenamin return; 37817be2d2bSAdrian McMenamin device_unregister(&mdev->dev); 379b3c69e24SAdrian McMenamin mdev = NULL; 38017be2d2bSAdrian McMenamin } 38117be2d2bSAdrian McMenamin 38217be2d2bSAdrian McMenamin /* process initial MAPLE_COMMAND_DEVINFO for each device or port */ 383b3c69e24SAdrian McMenamin static void maple_attach_driver(struct maple_device *mdev) 38417be2d2bSAdrian McMenamin { 385b3c69e24SAdrian McMenamin char *p, *recvbuf; 38617be2d2bSAdrian McMenamin unsigned long function; 38717be2d2bSAdrian McMenamin int matched, retval; 38817be2d2bSAdrian McMenamin 389b3c69e24SAdrian McMenamin recvbuf = mdev->mq->recvbuf; 390b3c69e24SAdrian McMenamin /* copy the data as individual elements in 391b3c69e24SAdrian McMenamin * case of memory optimisation */ 392b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.function, recvbuf + 4, 4); 393b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.function_data[0], recvbuf + 8, 12); 394b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.area_code, recvbuf + 20, 1); 395b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.connector_direction, recvbuf + 21, 1); 396b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.product_name[0], recvbuf + 22, 30); 397b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.product_licence[0], recvbuf + 52, 60); 398b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.standby_power, recvbuf + 112, 2); 399b3c69e24SAdrian McMenamin memcpy(&mdev->devinfo.max_power, recvbuf + 114, 2); 400b3c69e24SAdrian McMenamin memcpy(mdev->product_name, mdev->devinfo.product_name, 30); 401b3c69e24SAdrian McMenamin mdev->product_name[30] = '\0'; 402b3c69e24SAdrian McMenamin memcpy(mdev->product_licence, mdev->devinfo.product_licence, 60); 403b3c69e24SAdrian McMenamin mdev->product_licence[60] = '\0'; 40417be2d2bSAdrian McMenamin 405b3c69e24SAdrian McMenamin for (p = mdev->product_name + 29; mdev->product_name <= p; p--) 406b3c69e24SAdrian McMenamin if (*p == ' ') 407b3c69e24SAdrian McMenamin *p = '\0'; 408b3c69e24SAdrian McMenamin else 409b3c69e24SAdrian McMenamin break; 410b3c69e24SAdrian McMenamin for (p = mdev->product_licence + 59; mdev->product_licence <= p; p--) 41117be2d2bSAdrian McMenamin if (*p == ' ') 41217be2d2bSAdrian McMenamin *p = '\0'; 41317be2d2bSAdrian McMenamin else 41417be2d2bSAdrian McMenamin break; 41517be2d2bSAdrian McMenamin 416b3c69e24SAdrian McMenamin printk(KERN_INFO "Maple device detected: %s\n", 417b3c69e24SAdrian McMenamin mdev->product_name); 418b3c69e24SAdrian McMenamin printk(KERN_INFO "Maple device: %s\n", mdev->product_licence); 41917be2d2bSAdrian McMenamin 420b3c69e24SAdrian McMenamin function = be32_to_cpu(mdev->devinfo.function); 42117be2d2bSAdrian McMenamin 42217be2d2bSAdrian McMenamin if (function > 0x200) { 42317be2d2bSAdrian McMenamin /* Do this silently - as not a real device */ 42417be2d2bSAdrian McMenamin function = 0; 425b3c69e24SAdrian McMenamin mdev->driver = &maple_dummy_driver; 426b3c69e24SAdrian McMenamin sprintf(mdev->dev.bus_id, "%d:0.port", mdev->port); 42717be2d2bSAdrian McMenamin } else { 42817be2d2bSAdrian McMenamin printk(KERN_INFO 429b3c69e24SAdrian McMenamin "Maple bus at (%d, %d): Function 0x%lX\n", 430b3c69e24SAdrian McMenamin mdev->port, mdev->unit, function); 43117be2d2bSAdrian McMenamin 43217be2d2bSAdrian McMenamin matched = 433b3c69e24SAdrian McMenamin bus_for_each_drv(&maple_bus_type, NULL, mdev, 4341795cf48SAdrian McMenamin check_matching_maple_driver); 43517be2d2bSAdrian McMenamin 43617be2d2bSAdrian McMenamin if (matched == 0) { 43717be2d2bSAdrian McMenamin /* Driver does not exist yet */ 43817be2d2bSAdrian McMenamin printk(KERN_INFO 439b3c69e24SAdrian McMenamin "No maple driver found.\n"); 440b3c69e24SAdrian McMenamin mdev->driver = &maple_dummy_driver; 44117be2d2bSAdrian McMenamin } 442b3c69e24SAdrian McMenamin sprintf(mdev->dev.bus_id, "%d:0%d.%lX", mdev->port, 443b3c69e24SAdrian McMenamin mdev->unit, function); 44417be2d2bSAdrian McMenamin } 445b3c69e24SAdrian McMenamin mdev->function = function; 446b3c69e24SAdrian McMenamin mdev->dev.release = &maple_release_device; 447b3c69e24SAdrian McMenamin retval = device_register(&mdev->dev); 44817be2d2bSAdrian McMenamin if (retval) { 44917be2d2bSAdrian McMenamin printk(KERN_INFO 450b3c69e24SAdrian McMenamin "Maple bus: Attempt to register device" 451b3c69e24SAdrian McMenamin " (%x, %x) failed.\n", 452b3c69e24SAdrian McMenamin mdev->port, mdev->unit); 453b3c69e24SAdrian McMenamin maple_free_dev(mdev); 454b3c69e24SAdrian McMenamin mdev = NULL; 455b3c69e24SAdrian McMenamin return; 45617be2d2bSAdrian McMenamin } 45717be2d2bSAdrian McMenamin } 45817be2d2bSAdrian McMenamin 45917be2d2bSAdrian McMenamin /* 46017be2d2bSAdrian McMenamin * if device has been registered for the given 46117be2d2bSAdrian McMenamin * port and unit then return 1 - allows identification 46217be2d2bSAdrian McMenamin * of which devices need to be attached or detached 46317be2d2bSAdrian McMenamin */ 46417be2d2bSAdrian McMenamin static int detach_maple_device(struct device *device, void *portptr) 46517be2d2bSAdrian McMenamin { 46617be2d2bSAdrian McMenamin struct maple_device_specify *ds; 46717be2d2bSAdrian McMenamin struct maple_device *mdev; 46817be2d2bSAdrian McMenamin 46917be2d2bSAdrian McMenamin ds = portptr; 47017be2d2bSAdrian McMenamin mdev = to_maple_dev(device); 47117be2d2bSAdrian McMenamin if (mdev->port == ds->port && mdev->unit == ds->unit) 47217be2d2bSAdrian McMenamin return 1; 47317be2d2bSAdrian McMenamin return 0; 47417be2d2bSAdrian McMenamin } 47517be2d2bSAdrian McMenamin 47617be2d2bSAdrian McMenamin static int setup_maple_commands(struct device *device, void *ignored) 47717be2d2bSAdrian McMenamin { 4781795cf48SAdrian McMenamin int add; 47917be2d2bSAdrian McMenamin struct maple_device *maple_dev = to_maple_dev(device); 48017be2d2bSAdrian McMenamin 48117be2d2bSAdrian McMenamin if ((maple_dev->interval > 0) 48217be2d2bSAdrian McMenamin && time_after(jiffies, maple_dev->when)) { 4831795cf48SAdrian McMenamin /* bounce if we cannot lock */ 4841795cf48SAdrian McMenamin add = maple_add_packet(maple_dev, 4851795cf48SAdrian McMenamin be32_to_cpu(maple_dev->devinfo.function), 4861795cf48SAdrian McMenamin MAPLE_COMMAND_GETCOND, 1, NULL); 4871795cf48SAdrian McMenamin if (!add) 48817be2d2bSAdrian McMenamin maple_dev->when = jiffies + maple_dev->interval; 48917be2d2bSAdrian McMenamin } else { 4901795cf48SAdrian McMenamin if (time_after(jiffies, maple_pnp_time)) 4911795cf48SAdrian McMenamin /* This will also bounce */ 4921795cf48SAdrian McMenamin maple_add_packet(maple_dev, 0, 4931795cf48SAdrian McMenamin MAPLE_COMMAND_DEVINFO, 0, NULL); 49417be2d2bSAdrian McMenamin } 49517be2d2bSAdrian McMenamin return 0; 49617be2d2bSAdrian McMenamin } 49717be2d2bSAdrian McMenamin 49817be2d2bSAdrian McMenamin /* VBLANK bottom half - implemented via workqueue */ 49917be2d2bSAdrian McMenamin static void maple_vblank_handler(struct work_struct *work) 50017be2d2bSAdrian McMenamin { 5011795cf48SAdrian McMenamin if (!list_empty(&maple_sentq) || !maple_dma_done()) 50217be2d2bSAdrian McMenamin return; 5031795cf48SAdrian McMenamin 50417be2d2bSAdrian McMenamin ctrl_outl(0, MAPLE_ENABLE); 5051795cf48SAdrian McMenamin 50617be2d2bSAdrian McMenamin bus_for_each_dev(&maple_bus_type, NULL, NULL, 50717be2d2bSAdrian McMenamin setup_maple_commands); 5081795cf48SAdrian McMenamin 50917be2d2bSAdrian McMenamin if (time_after(jiffies, maple_pnp_time)) 51017be2d2bSAdrian McMenamin maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL; 5111795cf48SAdrian McMenamin 5121795cf48SAdrian McMenamin mutex_lock(&maple_wlist_lock); 5131795cf48SAdrian McMenamin if (!list_empty(&maple_waitq) && list_empty(&maple_sentq)) { 5141795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 51517be2d2bSAdrian McMenamin maple_send(); 5161795cf48SAdrian McMenamin } else { 5171795cf48SAdrian McMenamin mutex_unlock(&maple_wlist_lock); 51817be2d2bSAdrian McMenamin } 5191795cf48SAdrian McMenamin 52017be2d2bSAdrian McMenamin maplebus_dma_reset(); 52117be2d2bSAdrian McMenamin } 52217be2d2bSAdrian McMenamin 52317be2d2bSAdrian McMenamin /* handle devices added via hotplugs - placing them on queue for DEVINFO*/ 52417be2d2bSAdrian McMenamin static void maple_map_subunits(struct maple_device *mdev, int submask) 52517be2d2bSAdrian McMenamin { 52617be2d2bSAdrian McMenamin int retval, k, devcheck; 52717be2d2bSAdrian McMenamin struct maple_device *mdev_add; 52817be2d2bSAdrian McMenamin struct maple_device_specify ds; 52917be2d2bSAdrian McMenamin 53017be2d2bSAdrian McMenamin ds.port = mdev->port; 5311795cf48SAdrian McMenamin for (k = 0; k < 5; k++) { 53217be2d2bSAdrian McMenamin ds.unit = k + 1; 53317be2d2bSAdrian McMenamin retval = 53417be2d2bSAdrian McMenamin bus_for_each_dev(&maple_bus_type, NULL, &ds, 53517be2d2bSAdrian McMenamin detach_maple_device); 53617be2d2bSAdrian McMenamin if (retval) { 53717be2d2bSAdrian McMenamin submask = submask >> 1; 53817be2d2bSAdrian McMenamin continue; 53917be2d2bSAdrian McMenamin } 54017be2d2bSAdrian McMenamin devcheck = submask & 0x01; 54117be2d2bSAdrian McMenamin if (devcheck) { 54217be2d2bSAdrian McMenamin mdev_add = maple_alloc_dev(mdev->port, k + 1); 54317be2d2bSAdrian McMenamin if (!mdev_add) 54417be2d2bSAdrian McMenamin return; 5451795cf48SAdrian McMenamin maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO, 5461795cf48SAdrian McMenamin 0, NULL); 5471795cf48SAdrian McMenamin /* mark that we are checking sub devices */ 54817be2d2bSAdrian McMenamin scanning = 1; 54917be2d2bSAdrian McMenamin } 55017be2d2bSAdrian McMenamin submask = submask >> 1; 55117be2d2bSAdrian McMenamin } 55217be2d2bSAdrian McMenamin } 55317be2d2bSAdrian McMenamin 55417be2d2bSAdrian McMenamin /* mark a device as removed */ 55517be2d2bSAdrian McMenamin static void maple_clean_submap(struct maple_device *mdev) 55617be2d2bSAdrian McMenamin { 55717be2d2bSAdrian McMenamin int killbit; 55817be2d2bSAdrian McMenamin 55917be2d2bSAdrian McMenamin killbit = (mdev->unit > 0 ? (1 << (mdev->unit - 1)) & 0x1f : 0x20); 56017be2d2bSAdrian McMenamin killbit = ~killbit; 56117be2d2bSAdrian McMenamin killbit &= 0xFF; 56217be2d2bSAdrian McMenamin subdevice_map[mdev->port] = subdevice_map[mdev->port] & killbit; 56317be2d2bSAdrian McMenamin } 56417be2d2bSAdrian McMenamin 56517be2d2bSAdrian McMenamin /* handle empty port or hotplug removal */ 56617be2d2bSAdrian McMenamin static void maple_response_none(struct maple_device *mdev, 56717be2d2bSAdrian McMenamin struct mapleq *mq) 56817be2d2bSAdrian McMenamin { 56917be2d2bSAdrian McMenamin if (mdev->unit != 0) { 57017be2d2bSAdrian McMenamin list_del(&mq->list); 57117be2d2bSAdrian McMenamin maple_clean_submap(mdev); 57217be2d2bSAdrian McMenamin printk(KERN_INFO 57317be2d2bSAdrian McMenamin "Maple bus device detaching at (%d, %d)\n", 57417be2d2bSAdrian McMenamin mdev->port, mdev->unit); 57517be2d2bSAdrian McMenamin maple_detach_driver(mdev); 57617be2d2bSAdrian McMenamin return; 57717be2d2bSAdrian McMenamin } 578bd496669SAdrian McMenamin if (!started || !fullscan) { 579bd496669SAdrian McMenamin if (checked[mdev->port] == false) { 580bd496669SAdrian McMenamin checked[mdev->port] = true; 581bd496669SAdrian McMenamin printk(KERN_INFO "No maple devices attached" 582bd496669SAdrian McMenamin " to port %d\n", mdev->port); 583bd496669SAdrian McMenamin } 58417be2d2bSAdrian McMenamin return; 58517be2d2bSAdrian McMenamin } 58617be2d2bSAdrian McMenamin maple_clean_submap(mdev); 58717be2d2bSAdrian McMenamin } 58817be2d2bSAdrian McMenamin 58917be2d2bSAdrian McMenamin /* preprocess hotplugs or scans */ 59017be2d2bSAdrian McMenamin static void maple_response_devinfo(struct maple_device *mdev, 59117be2d2bSAdrian McMenamin char *recvbuf) 59217be2d2bSAdrian McMenamin { 59317be2d2bSAdrian McMenamin char submask; 594bd496669SAdrian McMenamin if (!started || (scanning == 2) || !fullscan) { 595bd496669SAdrian McMenamin if ((mdev->unit == 0) && (checked[mdev->port] == false)) { 596bd496669SAdrian McMenamin checked[mdev->port] = true; 59717be2d2bSAdrian McMenamin maple_attach_driver(mdev); 598bd496669SAdrian McMenamin } else { 599bd496669SAdrian McMenamin if (mdev->unit != 0) 600bd496669SAdrian McMenamin maple_attach_driver(mdev); 601bd496669SAdrian McMenamin } 60217be2d2bSAdrian McMenamin return; 60317be2d2bSAdrian McMenamin } 60417be2d2bSAdrian McMenamin if (mdev->unit == 0) { 60517be2d2bSAdrian McMenamin submask = recvbuf[2] & 0x1F; 60617be2d2bSAdrian McMenamin if (submask ^ subdevice_map[mdev->port]) { 60717be2d2bSAdrian McMenamin maple_map_subunits(mdev, submask); 60817be2d2bSAdrian McMenamin subdevice_map[mdev->port] = submask; 60917be2d2bSAdrian McMenamin } 61017be2d2bSAdrian McMenamin } 61117be2d2bSAdrian McMenamin } 61217be2d2bSAdrian McMenamin 6131795cf48SAdrian McMenamin static void maple_port_rescan(void) 6141795cf48SAdrian McMenamin { 6151795cf48SAdrian McMenamin int i; 6161795cf48SAdrian McMenamin struct maple_device *mdev; 6171795cf48SAdrian McMenamin 6181795cf48SAdrian McMenamin fullscan = 1; 6191795cf48SAdrian McMenamin for (i = 0; i < MAPLE_PORTS; i++) { 6201795cf48SAdrian McMenamin if (checked[i] == false) { 6211795cf48SAdrian McMenamin fullscan = 0; 6221795cf48SAdrian McMenamin mdev = baseunits[i]; 6231795cf48SAdrian McMenamin /* 6241795cf48SAdrian McMenamin * test lock in case scan has failed 6251795cf48SAdrian McMenamin * but device is still locked 6261795cf48SAdrian McMenamin */ 6271795cf48SAdrian McMenamin if (mutex_is_locked(&mdev->mq->mutex)) 6281795cf48SAdrian McMenamin mutex_unlock(&mdev->mq->mutex); 6291795cf48SAdrian McMenamin maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO, 6301795cf48SAdrian McMenamin 0, NULL); 6311795cf48SAdrian McMenamin } 6321795cf48SAdrian McMenamin } 6331795cf48SAdrian McMenamin } 6341795cf48SAdrian McMenamin 63517be2d2bSAdrian McMenamin /* maple dma end bottom half - implemented via workqueue */ 63617be2d2bSAdrian McMenamin static void maple_dma_handler(struct work_struct *work) 63717be2d2bSAdrian McMenamin { 63817be2d2bSAdrian McMenamin struct mapleq *mq, *nmq; 63917be2d2bSAdrian McMenamin struct maple_device *dev; 64017be2d2bSAdrian McMenamin char *recvbuf; 64117be2d2bSAdrian McMenamin enum maple_code code; 64217be2d2bSAdrian McMenamin 64317be2d2bSAdrian McMenamin if (!maple_dma_done()) 64417be2d2bSAdrian McMenamin return; 64517be2d2bSAdrian McMenamin ctrl_outl(0, MAPLE_ENABLE); 64617be2d2bSAdrian McMenamin if (!list_empty(&maple_sentq)) { 64717be2d2bSAdrian McMenamin list_for_each_entry_safe(mq, nmq, &maple_sentq, list) { 64817be2d2bSAdrian McMenamin recvbuf = mq->recvbuf; 64917be2d2bSAdrian McMenamin code = recvbuf[0]; 65017be2d2bSAdrian McMenamin dev = mq->dev; 6511795cf48SAdrian McMenamin kfree(mq->sendbuf); 6521795cf48SAdrian McMenamin mutex_unlock(&mq->mutex); 6531795cf48SAdrian McMenamin list_del_init(&mq->list); 6541795cf48SAdrian McMenamin 65517be2d2bSAdrian McMenamin switch (code) { 65617be2d2bSAdrian McMenamin case MAPLE_RESPONSE_NONE: 65717be2d2bSAdrian McMenamin maple_response_none(dev, mq); 65817be2d2bSAdrian McMenamin break; 65917be2d2bSAdrian McMenamin 66017be2d2bSAdrian McMenamin case MAPLE_RESPONSE_DEVINFO: 66117be2d2bSAdrian McMenamin maple_response_devinfo(dev, recvbuf); 66217be2d2bSAdrian McMenamin break; 66317be2d2bSAdrian McMenamin 66417be2d2bSAdrian McMenamin case MAPLE_RESPONSE_DATATRF: 66517be2d2bSAdrian McMenamin if (dev->callback) 66617be2d2bSAdrian McMenamin dev->callback(mq); 66717be2d2bSAdrian McMenamin break; 66817be2d2bSAdrian McMenamin 66917be2d2bSAdrian McMenamin case MAPLE_RESPONSE_FILEERR: 67017be2d2bSAdrian McMenamin case MAPLE_RESPONSE_AGAIN: 67117be2d2bSAdrian McMenamin case MAPLE_RESPONSE_BADCMD: 67217be2d2bSAdrian McMenamin case MAPLE_RESPONSE_BADFUNC: 67317be2d2bSAdrian McMenamin printk(KERN_DEBUG 67417be2d2bSAdrian McMenamin "Maple non-fatal error 0x%X\n", 67517be2d2bSAdrian McMenamin code); 67617be2d2bSAdrian McMenamin break; 67717be2d2bSAdrian McMenamin 67817be2d2bSAdrian McMenamin case MAPLE_RESPONSE_ALLINFO: 67917be2d2bSAdrian McMenamin printk(KERN_DEBUG 680b3c69e24SAdrian McMenamin "Maple - extended device information" 681b3c69e24SAdrian McMenamin " not supported\n"); 68217be2d2bSAdrian McMenamin break; 68317be2d2bSAdrian McMenamin 68417be2d2bSAdrian McMenamin case MAPLE_RESPONSE_OK: 68517be2d2bSAdrian McMenamin break; 68617be2d2bSAdrian McMenamin 68717be2d2bSAdrian McMenamin default: 68817be2d2bSAdrian McMenamin break; 68917be2d2bSAdrian McMenamin } 69017be2d2bSAdrian McMenamin } 6911795cf48SAdrian McMenamin /* if scanning is 1 then we have subdevices to check */ 69217be2d2bSAdrian McMenamin if (scanning == 1) { 69317be2d2bSAdrian McMenamin maple_send(); 69417be2d2bSAdrian McMenamin scanning = 2; 69517be2d2bSAdrian McMenamin } else 69617be2d2bSAdrian McMenamin scanning = 0; 6971795cf48SAdrian McMenamin /*check if we have actually tested all ports yet */ 6981795cf48SAdrian McMenamin if (!fullscan) 6991795cf48SAdrian McMenamin maple_port_rescan(); 7001795cf48SAdrian McMenamin /* mark that we have been through the first scan */ 70117be2d2bSAdrian McMenamin if (started == 0) 70217be2d2bSAdrian McMenamin started = 1; 70317be2d2bSAdrian McMenamin } 70417be2d2bSAdrian McMenamin maplebus_dma_reset(); 70517be2d2bSAdrian McMenamin } 70617be2d2bSAdrian McMenamin 70717be2d2bSAdrian McMenamin static irqreturn_t maplebus_dma_interrupt(int irq, void *dev_id) 70817be2d2bSAdrian McMenamin { 70917be2d2bSAdrian McMenamin /* Load everything into the bottom half */ 71017be2d2bSAdrian McMenamin schedule_work(&maple_dma_process); 71117be2d2bSAdrian McMenamin return IRQ_HANDLED; 71217be2d2bSAdrian McMenamin } 71317be2d2bSAdrian McMenamin 71417be2d2bSAdrian McMenamin static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id) 71517be2d2bSAdrian McMenamin { 71617be2d2bSAdrian McMenamin schedule_work(&maple_vblank_process); 71717be2d2bSAdrian McMenamin return IRQ_HANDLED; 71817be2d2bSAdrian McMenamin } 71917be2d2bSAdrian McMenamin 72017be2d2bSAdrian McMenamin static int maple_set_dma_interrupt_handler(void) 72117be2d2bSAdrian McMenamin { 722b3c69e24SAdrian McMenamin return request_irq(HW_EVENT_MAPLE_DMA, maplebus_dma_interrupt, 723b3c69e24SAdrian McMenamin IRQF_SHARED, "maple bus DMA", &maple_dummy_driver); 72417be2d2bSAdrian McMenamin } 72517be2d2bSAdrian McMenamin 72617be2d2bSAdrian McMenamin static int maple_set_vblank_interrupt_handler(void) 72717be2d2bSAdrian McMenamin { 728b3c69e24SAdrian McMenamin return request_irq(HW_EVENT_VSYNC, maplebus_vblank_interrupt, 729b3c69e24SAdrian McMenamin IRQF_SHARED, "maple bus VBLANK", &maple_dummy_driver); 73017be2d2bSAdrian McMenamin } 73117be2d2bSAdrian McMenamin 73217be2d2bSAdrian McMenamin static int maple_get_dma_buffer(void) 73317be2d2bSAdrian McMenamin { 73417be2d2bSAdrian McMenamin maple_sendbuf = 73517be2d2bSAdrian McMenamin (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, 73617be2d2bSAdrian McMenamin MAPLE_DMA_PAGES); 73717be2d2bSAdrian McMenamin if (!maple_sendbuf) 73817be2d2bSAdrian McMenamin return -ENOMEM; 73917be2d2bSAdrian McMenamin return 0; 74017be2d2bSAdrian McMenamin } 74117be2d2bSAdrian McMenamin 74217be2d2bSAdrian McMenamin static int match_maple_bus_driver(struct device *devptr, 74317be2d2bSAdrian McMenamin struct device_driver *drvptr) 74417be2d2bSAdrian McMenamin { 74563870295SPaul Mundt struct maple_driver *maple_drv = to_maple_driver(drvptr); 74663870295SPaul Mundt struct maple_device *maple_dev = to_maple_dev(devptr); 74717be2d2bSAdrian McMenamin 74817be2d2bSAdrian McMenamin /* Trap empty port case */ 74917be2d2bSAdrian McMenamin if (maple_dev->devinfo.function == 0xFFFFFFFF) 75017be2d2bSAdrian McMenamin return 0; 75117be2d2bSAdrian McMenamin else if (maple_dev->devinfo.function & 7521795cf48SAdrian McMenamin cpu_to_be32(maple_drv->function)) 75317be2d2bSAdrian McMenamin return 1; 75417be2d2bSAdrian McMenamin return 0; 75517be2d2bSAdrian McMenamin } 75617be2d2bSAdrian McMenamin 757b9482378SAdrian McMenamin static int maple_bus_uevent(struct device *dev, 758b9482378SAdrian McMenamin struct kobj_uevent_env *env) 75917be2d2bSAdrian McMenamin { 76017be2d2bSAdrian McMenamin return 0; 76117be2d2bSAdrian McMenamin } 76217be2d2bSAdrian McMenamin 76317be2d2bSAdrian McMenamin static void maple_bus_release(struct device *dev) 76417be2d2bSAdrian McMenamin { 76517be2d2bSAdrian McMenamin } 76617be2d2bSAdrian McMenamin 76717be2d2bSAdrian McMenamin static struct maple_driver maple_dummy_driver = { 76817be2d2bSAdrian McMenamin .drv = { 76917be2d2bSAdrian McMenamin .name = "maple_dummy_driver", 77017be2d2bSAdrian McMenamin .bus = &maple_bus_type, 77117be2d2bSAdrian McMenamin }, 77217be2d2bSAdrian McMenamin }; 77317be2d2bSAdrian McMenamin 77417be2d2bSAdrian McMenamin struct bus_type maple_bus_type = { 77517be2d2bSAdrian McMenamin .name = "maple", 77617be2d2bSAdrian McMenamin .match = match_maple_bus_driver, 77717be2d2bSAdrian McMenamin .uevent = maple_bus_uevent, 77817be2d2bSAdrian McMenamin }; 77917be2d2bSAdrian McMenamin EXPORT_SYMBOL_GPL(maple_bus_type); 78017be2d2bSAdrian McMenamin 78117be2d2bSAdrian McMenamin static struct device maple_bus = { 78217be2d2bSAdrian McMenamin .bus_id = "maple", 78317be2d2bSAdrian McMenamin .release = maple_bus_release, 78417be2d2bSAdrian McMenamin }; 78517be2d2bSAdrian McMenamin 78617be2d2bSAdrian McMenamin static int __init maple_bus_init(void) 78717be2d2bSAdrian McMenamin { 78817be2d2bSAdrian McMenamin int retval, i; 78917be2d2bSAdrian McMenamin struct maple_device *mdev[MAPLE_PORTS]; 79017be2d2bSAdrian McMenamin ctrl_outl(0, MAPLE_STATE); 79117be2d2bSAdrian McMenamin 79217be2d2bSAdrian McMenamin retval = device_register(&maple_bus); 79317be2d2bSAdrian McMenamin if (retval) 79417be2d2bSAdrian McMenamin goto cleanup; 79517be2d2bSAdrian McMenamin 79617be2d2bSAdrian McMenamin retval = bus_register(&maple_bus_type); 79717be2d2bSAdrian McMenamin if (retval) 79817be2d2bSAdrian McMenamin goto cleanup_device; 79917be2d2bSAdrian McMenamin 80017be2d2bSAdrian McMenamin retval = driver_register(&maple_dummy_driver.drv); 80117be2d2bSAdrian McMenamin if (retval) 80217be2d2bSAdrian McMenamin goto cleanup_bus; 80317be2d2bSAdrian McMenamin 80417be2d2bSAdrian McMenamin /* allocate memory for maple bus dma */ 80517be2d2bSAdrian McMenamin retval = maple_get_dma_buffer(); 80617be2d2bSAdrian McMenamin if (retval) { 80717be2d2bSAdrian McMenamin printk(KERN_INFO 80817be2d2bSAdrian McMenamin "Maple bus: Failed to allocate Maple DMA buffers\n"); 80917be2d2bSAdrian McMenamin goto cleanup_basic; 81017be2d2bSAdrian McMenamin } 81117be2d2bSAdrian McMenamin 81217be2d2bSAdrian McMenamin /* set up DMA interrupt handler */ 81317be2d2bSAdrian McMenamin retval = maple_set_dma_interrupt_handler(); 81417be2d2bSAdrian McMenamin if (retval) { 81517be2d2bSAdrian McMenamin printk(KERN_INFO 81617be2d2bSAdrian McMenamin "Maple bus: Failed to grab maple DMA IRQ\n"); 81717be2d2bSAdrian McMenamin goto cleanup_dma; 81817be2d2bSAdrian McMenamin } 81917be2d2bSAdrian McMenamin 82017be2d2bSAdrian McMenamin /* set up VBLANK interrupt handler */ 82117be2d2bSAdrian McMenamin retval = maple_set_vblank_interrupt_handler(); 82217be2d2bSAdrian McMenamin if (retval) { 82317be2d2bSAdrian McMenamin printk(KERN_INFO "Maple bus: Failed to grab VBLANK IRQ\n"); 82417be2d2bSAdrian McMenamin goto cleanup_irq; 82517be2d2bSAdrian McMenamin } 82617be2d2bSAdrian McMenamin 82717be2d2bSAdrian McMenamin maple_queue_cache = 82817be2d2bSAdrian McMenamin kmem_cache_create("maple_queue_cache", 0x400, 0, 829b3c69e24SAdrian McMenamin SLAB_POISON|SLAB_HWCACHE_ALIGN, NULL); 83017be2d2bSAdrian McMenamin 83117be2d2bSAdrian McMenamin if (!maple_queue_cache) 83217be2d2bSAdrian McMenamin goto cleanup_bothirqs; 83317be2d2bSAdrian McMenamin 8341795cf48SAdrian McMenamin INIT_LIST_HEAD(&maple_waitq); 8351795cf48SAdrian McMenamin INIT_LIST_HEAD(&maple_sentq); 8361795cf48SAdrian McMenamin 83717be2d2bSAdrian McMenamin /* setup maple ports */ 83817be2d2bSAdrian McMenamin for (i = 0; i < MAPLE_PORTS; i++) { 839bd496669SAdrian McMenamin checked[i] = false; 84017be2d2bSAdrian McMenamin mdev[i] = maple_alloc_dev(i, 0); 841bd496669SAdrian McMenamin baseunits[i] = mdev[i]; 84217be2d2bSAdrian McMenamin if (!mdev[i]) { 84317be2d2bSAdrian McMenamin while (i-- > 0) 84417be2d2bSAdrian McMenamin maple_free_dev(mdev[i]); 84517be2d2bSAdrian McMenamin goto cleanup_cache; 84617be2d2bSAdrian McMenamin } 8471795cf48SAdrian McMenamin maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL); 84817be2d2bSAdrian McMenamin subdevice_map[i] = 0; 84917be2d2bSAdrian McMenamin } 85017be2d2bSAdrian McMenamin 85117be2d2bSAdrian McMenamin /* setup maplebus hardware */ 85217be2d2bSAdrian McMenamin maplebus_dma_reset(); 85317be2d2bSAdrian McMenamin /* initial detection */ 85417be2d2bSAdrian McMenamin maple_send(); 85517be2d2bSAdrian McMenamin maple_pnp_time = jiffies; 85617be2d2bSAdrian McMenamin printk(KERN_INFO "Maple bus core now registered.\n"); 85717be2d2bSAdrian McMenamin 85817be2d2bSAdrian McMenamin return 0; 85917be2d2bSAdrian McMenamin 86017be2d2bSAdrian McMenamin cleanup_cache: 86117be2d2bSAdrian McMenamin kmem_cache_destroy(maple_queue_cache); 86217be2d2bSAdrian McMenamin 86317be2d2bSAdrian McMenamin cleanup_bothirqs: 86417be2d2bSAdrian McMenamin free_irq(HW_EVENT_VSYNC, 0); 86517be2d2bSAdrian McMenamin 86617be2d2bSAdrian McMenamin cleanup_irq: 86717be2d2bSAdrian McMenamin free_irq(HW_EVENT_MAPLE_DMA, 0); 86817be2d2bSAdrian McMenamin 86917be2d2bSAdrian McMenamin cleanup_dma: 87017be2d2bSAdrian McMenamin free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES); 87117be2d2bSAdrian McMenamin 87217be2d2bSAdrian McMenamin cleanup_basic: 87317be2d2bSAdrian McMenamin driver_unregister(&maple_dummy_driver.drv); 87417be2d2bSAdrian McMenamin 87517be2d2bSAdrian McMenamin cleanup_bus: 87617be2d2bSAdrian McMenamin bus_unregister(&maple_bus_type); 87717be2d2bSAdrian McMenamin 87817be2d2bSAdrian McMenamin cleanup_device: 87917be2d2bSAdrian McMenamin device_unregister(&maple_bus); 88017be2d2bSAdrian McMenamin 88117be2d2bSAdrian McMenamin cleanup: 88217be2d2bSAdrian McMenamin printk(KERN_INFO "Maple bus registration failed\n"); 88317be2d2bSAdrian McMenamin return retval; 88417be2d2bSAdrian McMenamin } 885b3c69e24SAdrian McMenamin /* Push init to later to ensure hardware gets detected */ 886b3c69e24SAdrian McMenamin fs_initcall(maple_bus_init); 887