162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Core maple bus functionality 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007 - 2009 Adrian McMenamin 562306a36Sopenharmony_ci * Copyright (C) 2001 - 2008 Paul Mundt 662306a36Sopenharmony_ci * Copyright (C) 2000 - 2001 YAEGASHI Takeshi 762306a36Sopenharmony_ci * Copyright (C) 2001 M. R. Brown 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1062306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 1162306a36Sopenharmony_ci * for more details. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/maple.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <asm/cacheflush.h> 2562306a36Sopenharmony_ci#include <asm/dma.h> 2662306a36Sopenharmony_ci#include <asm/io.h> 2762306a36Sopenharmony_ci#include <mach/dma.h> 2862306a36Sopenharmony_ci#include <mach/sysasic.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciMODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>"); 3162306a36Sopenharmony_ciMODULE_DESCRIPTION("Maple bus driver for Dreamcast"); 3262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void maple_dma_handler(struct work_struct *work); 3562306a36Sopenharmony_cistatic void maple_vblank_handler(struct work_struct *work); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic DECLARE_WORK(maple_dma_process, maple_dma_handler); 3862306a36Sopenharmony_cistatic DECLARE_WORK(maple_vblank_process, maple_vblank_handler); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic LIST_HEAD(maple_waitq); 4162306a36Sopenharmony_cistatic LIST_HEAD(maple_sentq); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* mutex to protect queue of waiting packets */ 4462306a36Sopenharmony_cistatic DEFINE_MUTEX(maple_wlist_lock); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct maple_driver maple_unsupported_device; 4762306a36Sopenharmony_cistatic struct device maple_bus; 4862306a36Sopenharmony_cistatic int subdevice_map[MAPLE_PORTS]; 4962306a36Sopenharmony_cistatic unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; 5062306a36Sopenharmony_cistatic unsigned long maple_pnp_time; 5162306a36Sopenharmony_cistatic int started, scanning, fullscan; 5262306a36Sopenharmony_cistatic struct kmem_cache *maple_queue_cache; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct maple_device_specify { 5562306a36Sopenharmony_ci int port; 5662306a36Sopenharmony_ci int unit; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic bool checked[MAPLE_PORTS]; 6062306a36Sopenharmony_cistatic bool empty[MAPLE_PORTS]; 6162306a36Sopenharmony_cistatic struct maple_device *baseunits[MAPLE_PORTS]; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * maple_driver_register - register a maple driver 6562306a36Sopenharmony_ci * @drv: maple driver to be registered. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * Registers the passed in @drv, while updating the bus type. 6862306a36Sopenharmony_ci * Devices with matching function IDs will be automatically probed. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ciint maple_driver_register(struct maple_driver *drv) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci if (!drv) 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci drv->drv.bus = &maple_bus_type; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return driver_register(&drv->drv); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_driver_register); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/** 8262306a36Sopenharmony_ci * maple_driver_unregister - unregister a maple driver. 8362306a36Sopenharmony_ci * @drv: maple driver to unregister. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Cleans up after maple_driver_register(). To be invoked in the exit 8662306a36Sopenharmony_ci * path of any module drivers. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_civoid maple_driver_unregister(struct maple_driver *drv) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci driver_unregister(&drv->drv); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_driver_unregister); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* set hardware registers to enable next round of dma */ 9562306a36Sopenharmony_cistatic void maple_dma_reset(void) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci __raw_writel(MAPLE_MAGIC, MAPLE_RESET); 9862306a36Sopenharmony_ci /* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */ 9962306a36Sopenharmony_ci __raw_writel(1, MAPLE_TRIGTYPE); 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * Maple system register 10262306a36Sopenharmony_ci * bits 31 - 16 timeout in units of 20nsec 10362306a36Sopenharmony_ci * bit 12 hard trigger - set 0 to keep responding to VBLANK 10462306a36Sopenharmony_ci * bits 9 - 8 set 00 for 2 Mbps, 01 for 1 Mbps 10562306a36Sopenharmony_ci * bits 3 - 0 delay (in 1.3ms) between VBLANK and start of DMA 10662306a36Sopenharmony_ci * max delay is 11 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci __raw_writel(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED); 10962306a36Sopenharmony_ci __raw_writel(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR); 11062306a36Sopenharmony_ci __raw_writel(1, MAPLE_ENABLE); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/** 11462306a36Sopenharmony_ci * maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND 11562306a36Sopenharmony_ci * @dev: device responding 11662306a36Sopenharmony_ci * @callback: handler callback 11762306a36Sopenharmony_ci * @interval: interval in jiffies between callbacks 11862306a36Sopenharmony_ci * @function: the function code for the device 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_civoid maple_getcond_callback(struct maple_device *dev, 12162306a36Sopenharmony_ci void (*callback) (struct mapleq *mq), 12262306a36Sopenharmony_ci unsigned long interval, unsigned long function) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci dev->callback = callback; 12562306a36Sopenharmony_ci dev->interval = interval; 12662306a36Sopenharmony_ci dev->function = cpu_to_be32(function); 12762306a36Sopenharmony_ci dev->when = jiffies; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_getcond_callback); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int maple_dma_done(void) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return (__raw_readl(MAPLE_STATE) & 1) == 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void maple_release_device(struct device *dev) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct maple_device *mdev; 13962306a36Sopenharmony_ci struct mapleq *mq; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci mdev = to_maple_dev(dev); 14262306a36Sopenharmony_ci mq = mdev->mq; 14362306a36Sopenharmony_ci kmem_cache_free(maple_queue_cache, mq->recvbuf); 14462306a36Sopenharmony_ci kfree(mq); 14562306a36Sopenharmony_ci kfree(mdev); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * maple_add_packet - add a single instruction to the maple bus queue 15062306a36Sopenharmony_ci * @mdev: maple device 15162306a36Sopenharmony_ci * @function: function on device being queried 15262306a36Sopenharmony_ci * @command: maple command to add 15362306a36Sopenharmony_ci * @length: length of command string (in 32 bit words) 15462306a36Sopenharmony_ci * @data: remainder of command string 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ciint maple_add_packet(struct maple_device *mdev, u32 function, u32 command, 15762306a36Sopenharmony_ci size_t length, void *data) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int ret = 0; 16062306a36Sopenharmony_ci void *sendbuf = NULL; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (length) { 16362306a36Sopenharmony_ci sendbuf = kcalloc(length, 4, GFP_KERNEL); 16462306a36Sopenharmony_ci if (!sendbuf) { 16562306a36Sopenharmony_ci ret = -ENOMEM; 16662306a36Sopenharmony_ci goto out; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci ((__be32 *)sendbuf)[0] = cpu_to_be32(function); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci mdev->mq->command = command; 17262306a36Sopenharmony_ci mdev->mq->length = length; 17362306a36Sopenharmony_ci if (length > 1) 17462306a36Sopenharmony_ci memcpy(sendbuf + 4, data, (length - 1) * 4); 17562306a36Sopenharmony_ci mdev->mq->sendbuf = sendbuf; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mutex_lock(&maple_wlist_lock); 17862306a36Sopenharmony_ci list_add_tail(&mdev->mq->list, &maple_waitq); 17962306a36Sopenharmony_ci mutex_unlock(&maple_wlist_lock); 18062306a36Sopenharmony_ciout: 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_add_packet); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic struct mapleq *maple_allocq(struct maple_device *mdev) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct mapleq *mq; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci mq = kzalloc(sizeof(*mq), GFP_KERNEL); 19062306a36Sopenharmony_ci if (!mq) 19162306a36Sopenharmony_ci goto failed_nomem; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci INIT_LIST_HEAD(&mq->list); 19462306a36Sopenharmony_ci mq->dev = mdev; 19562306a36Sopenharmony_ci mq->recvbuf = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL); 19662306a36Sopenharmony_ci if (!mq->recvbuf) 19762306a36Sopenharmony_ci goto failed_p2; 19862306a36Sopenharmony_ci mq->recvbuf->buf = &((mq->recvbuf->bufx)[0]); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return mq; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cifailed_p2: 20362306a36Sopenharmony_ci kfree(mq); 20462306a36Sopenharmony_cifailed_nomem: 20562306a36Sopenharmony_ci dev_err(&mdev->dev, "could not allocate memory for device (%d, %d)\n", 20662306a36Sopenharmony_ci mdev->port, mdev->unit); 20762306a36Sopenharmony_ci return NULL; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct maple_device *maple_alloc_dev(int port, int unit) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct maple_device *mdev; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* zero this out to avoid kobj subsystem 21562306a36Sopenharmony_ci * thinking it has already been registered */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 21862306a36Sopenharmony_ci if (!mdev) 21962306a36Sopenharmony_ci return NULL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci mdev->port = port; 22262306a36Sopenharmony_ci mdev->unit = unit; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci mdev->mq = maple_allocq(mdev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!mdev->mq) { 22762306a36Sopenharmony_ci kfree(mdev); 22862306a36Sopenharmony_ci return NULL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci mdev->dev.bus = &maple_bus_type; 23162306a36Sopenharmony_ci mdev->dev.parent = &maple_bus; 23262306a36Sopenharmony_ci init_waitqueue_head(&mdev->maple_wait); 23362306a36Sopenharmony_ci return mdev; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void maple_free_dev(struct maple_device *mdev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci kmem_cache_free(maple_queue_cache, mdev->mq->recvbuf); 23962306a36Sopenharmony_ci kfree(mdev->mq); 24062306a36Sopenharmony_ci kfree(mdev); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* process the command queue into a maple command block 24462306a36Sopenharmony_ci * terminating command has bit 32 of first long set to 0 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_cistatic void maple_build_block(struct mapleq *mq) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int port, unit, from, to, len; 24962306a36Sopenharmony_ci unsigned long *lsendbuf = mq->sendbuf; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci port = mq->dev->port & 3; 25262306a36Sopenharmony_ci unit = mq->dev->unit; 25362306a36Sopenharmony_ci len = mq->length; 25462306a36Sopenharmony_ci from = port << 6; 25562306a36Sopenharmony_ci to = (port << 6) | (unit > 0 ? (1 << (unit - 1)) & 0x1f : 0x20); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci *maple_lastptr &= 0x7fffffff; 25862306a36Sopenharmony_ci maple_lastptr = maple_sendptr; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci *maple_sendptr++ = (port << 16) | len | 0x80000000; 26162306a36Sopenharmony_ci *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf); 26262306a36Sopenharmony_ci *maple_sendptr++ = 26362306a36Sopenharmony_ci mq->command | (to << 8) | (from << 16) | (len << 24); 26462306a36Sopenharmony_ci while (len-- > 0) 26562306a36Sopenharmony_ci *maple_sendptr++ = *lsendbuf++; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* build up command queue */ 26962306a36Sopenharmony_cistatic void maple_send(void) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int i, maple_packets = 0; 27262306a36Sopenharmony_ci struct mapleq *mq, *nmq; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!maple_dma_done()) 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* disable DMA */ 27862306a36Sopenharmony_ci __raw_writel(0, MAPLE_ENABLE); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!list_empty(&maple_sentq)) 28162306a36Sopenharmony_ci goto finish; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci mutex_lock(&maple_wlist_lock); 28462306a36Sopenharmony_ci if (list_empty(&maple_waitq)) { 28562306a36Sopenharmony_ci mutex_unlock(&maple_wlist_lock); 28662306a36Sopenharmony_ci goto finish; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci maple_lastptr = maple_sendbuf; 29062306a36Sopenharmony_ci maple_sendptr = maple_sendbuf; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci list_for_each_entry_safe(mq, nmq, &maple_waitq, list) { 29362306a36Sopenharmony_ci maple_build_block(mq); 29462306a36Sopenharmony_ci list_del_init(&mq->list); 29562306a36Sopenharmony_ci list_add_tail(&mq->list, &maple_sentq); 29662306a36Sopenharmony_ci if (maple_packets++ > MAPLE_MAXPACKETS) 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci mutex_unlock(&maple_wlist_lock); 30062306a36Sopenharmony_ci if (maple_packets > 0) { 30162306a36Sopenharmony_ci for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++) 30262306a36Sopenharmony_ci __flush_purge_region(maple_sendbuf + i * PAGE_SIZE, 30362306a36Sopenharmony_ci PAGE_SIZE); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cifinish: 30762306a36Sopenharmony_ci maple_dma_reset(); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* check if there is a driver registered likely to match this device */ 31162306a36Sopenharmony_cistatic int maple_check_matching_driver(struct device_driver *driver, 31262306a36Sopenharmony_ci void *devptr) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct maple_driver *maple_drv; 31562306a36Sopenharmony_ci struct maple_device *mdev; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mdev = devptr; 31862306a36Sopenharmony_ci maple_drv = to_maple_driver(driver); 31962306a36Sopenharmony_ci if (mdev->devinfo.function & cpu_to_be32(maple_drv->function)) 32062306a36Sopenharmony_ci return 1; 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void maple_detach_driver(struct maple_device *mdev) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci device_unregister(&mdev->dev); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* process initial MAPLE_COMMAND_DEVINFO for each device or port */ 33062306a36Sopenharmony_cistatic void maple_attach_driver(struct maple_device *mdev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci char *p, *recvbuf; 33362306a36Sopenharmony_ci unsigned long function; 33462306a36Sopenharmony_ci int matched, error; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci recvbuf = mdev->mq->recvbuf->buf; 33762306a36Sopenharmony_ci /* copy the data as individual elements in 33862306a36Sopenharmony_ci * case of memory optimisation */ 33962306a36Sopenharmony_ci memcpy(&mdev->devinfo.function, recvbuf + 4, 4); 34062306a36Sopenharmony_ci memcpy(&mdev->devinfo.function_data[0], recvbuf + 8, 12); 34162306a36Sopenharmony_ci memcpy(&mdev->devinfo.area_code, recvbuf + 20, 1); 34262306a36Sopenharmony_ci memcpy(&mdev->devinfo.connector_direction, recvbuf + 21, 1); 34362306a36Sopenharmony_ci memcpy(&mdev->devinfo.product_name[0], recvbuf + 22, 30); 34462306a36Sopenharmony_ci memcpy(&mdev->devinfo.standby_power, recvbuf + 112, 2); 34562306a36Sopenharmony_ci memcpy(&mdev->devinfo.max_power, recvbuf + 114, 2); 34662306a36Sopenharmony_ci memcpy(mdev->product_name, mdev->devinfo.product_name, 30); 34762306a36Sopenharmony_ci mdev->product_name[30] = '\0'; 34862306a36Sopenharmony_ci memcpy(mdev->product_licence, mdev->devinfo.product_licence, 60); 34962306a36Sopenharmony_ci mdev->product_licence[60] = '\0'; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci for (p = mdev->product_name + 29; mdev->product_name <= p; p--) 35262306a36Sopenharmony_ci if (*p == ' ') 35362306a36Sopenharmony_ci *p = '\0'; 35462306a36Sopenharmony_ci else 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci for (p = mdev->product_licence + 59; mdev->product_licence <= p; p--) 35762306a36Sopenharmony_ci if (*p == ' ') 35862306a36Sopenharmony_ci *p = '\0'; 35962306a36Sopenharmony_ci else 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci function = be32_to_cpu(mdev->devinfo.function); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci dev_info(&mdev->dev, "detected %s: function 0x%lX: at (%d, %d)\n", 36562306a36Sopenharmony_ci mdev->product_name, function, mdev->port, mdev->unit); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (function > 0x200) { 36862306a36Sopenharmony_ci /* Do this silently - as not a real device */ 36962306a36Sopenharmony_ci function = 0; 37062306a36Sopenharmony_ci mdev->driver = &maple_unsupported_device; 37162306a36Sopenharmony_ci dev_set_name(&mdev->dev, "%d:0.port", mdev->port); 37262306a36Sopenharmony_ci } else { 37362306a36Sopenharmony_ci matched = 37462306a36Sopenharmony_ci bus_for_each_drv(&maple_bus_type, NULL, mdev, 37562306a36Sopenharmony_ci maple_check_matching_driver); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (matched == 0) { 37862306a36Sopenharmony_ci /* Driver does not exist yet */ 37962306a36Sopenharmony_ci dev_info(&mdev->dev, "no driver found\n"); 38062306a36Sopenharmony_ci mdev->driver = &maple_unsupported_device; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci dev_set_name(&mdev->dev, "%d:0%d.%lX", mdev->port, 38362306a36Sopenharmony_ci mdev->unit, function); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci mdev->function = function; 38762306a36Sopenharmony_ci mdev->dev.release = &maple_release_device; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 39062306a36Sopenharmony_ci error = device_register(&mdev->dev); 39162306a36Sopenharmony_ci if (error) { 39262306a36Sopenharmony_ci dev_warn(&mdev->dev, "could not register device at" 39362306a36Sopenharmony_ci " (%d, %d), with error 0x%X\n", mdev->unit, 39462306a36Sopenharmony_ci mdev->port, error); 39562306a36Sopenharmony_ci maple_free_dev(mdev); 39662306a36Sopenharmony_ci mdev = NULL; 39762306a36Sopenharmony_ci return; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* 40262306a36Sopenharmony_ci * if device has been registered for the given 40362306a36Sopenharmony_ci * port and unit then return 1 - allows identification 40462306a36Sopenharmony_ci * of which devices need to be attached or detached 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_cistatic int check_maple_device(struct device *device, void *portptr) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct maple_device_specify *ds; 40962306a36Sopenharmony_ci struct maple_device *mdev; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ds = portptr; 41262306a36Sopenharmony_ci mdev = to_maple_dev(device); 41362306a36Sopenharmony_ci if (mdev->port == ds->port && mdev->unit == ds->unit) 41462306a36Sopenharmony_ci return 1; 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int setup_maple_commands(struct device *device, void *ignored) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci int add; 42162306a36Sopenharmony_ci struct maple_device *mdev = to_maple_dev(device); 42262306a36Sopenharmony_ci if (mdev->interval > 0 && atomic_read(&mdev->busy) == 0 && 42362306a36Sopenharmony_ci time_after(jiffies, mdev->when)) { 42462306a36Sopenharmony_ci /* bounce if we cannot add */ 42562306a36Sopenharmony_ci add = maple_add_packet(mdev, 42662306a36Sopenharmony_ci be32_to_cpu(mdev->devinfo.function), 42762306a36Sopenharmony_ci MAPLE_COMMAND_GETCOND, 1, NULL); 42862306a36Sopenharmony_ci if (!add) 42962306a36Sopenharmony_ci mdev->when = jiffies + mdev->interval; 43062306a36Sopenharmony_ci } else { 43162306a36Sopenharmony_ci if (time_after(jiffies, maple_pnp_time)) 43262306a36Sopenharmony_ci /* Ensure we don't have block reads and devinfo 43362306a36Sopenharmony_ci * calls interfering with one another - so flag the 43462306a36Sopenharmony_ci * device as busy */ 43562306a36Sopenharmony_ci if (atomic_read(&mdev->busy) == 0) { 43662306a36Sopenharmony_ci atomic_set(&mdev->busy, 1); 43762306a36Sopenharmony_ci maple_add_packet(mdev, 0, 43862306a36Sopenharmony_ci MAPLE_COMMAND_DEVINFO, 0, NULL); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* VBLANK bottom half - implemented via workqueue */ 44562306a36Sopenharmony_cistatic void maple_vblank_handler(struct work_struct *work) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci int x, locking; 44862306a36Sopenharmony_ci struct maple_device *mdev; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!maple_dma_done()) 45162306a36Sopenharmony_ci return; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci __raw_writel(0, MAPLE_ENABLE); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!list_empty(&maple_sentq)) 45662306a36Sopenharmony_ci goto finish; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * Set up essential commands - to fetch data and 46062306a36Sopenharmony_ci * check devices are still present 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci bus_for_each_dev(&maple_bus_type, NULL, NULL, 46362306a36Sopenharmony_ci setup_maple_commands); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (time_after(jiffies, maple_pnp_time)) { 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * Scan the empty ports - bus is flakey and may have 46862306a36Sopenharmony_ci * mis-reported emptyness 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci for (x = 0; x < MAPLE_PORTS; x++) { 47162306a36Sopenharmony_ci if (checked[x] && empty[x]) { 47262306a36Sopenharmony_ci mdev = baseunits[x]; 47362306a36Sopenharmony_ci if (!mdev) 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci atomic_set(&mdev->busy, 1); 47662306a36Sopenharmony_ci locking = maple_add_packet(mdev, 0, 47762306a36Sopenharmony_ci MAPLE_COMMAND_DEVINFO, 0, NULL); 47862306a36Sopenharmony_ci if (!locking) 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cifinish: 48762306a36Sopenharmony_ci maple_send(); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* handle devices added via hotplugs - placing them on queue for DEVINFO */ 49162306a36Sopenharmony_cistatic void maple_map_subunits(struct maple_device *mdev, int submask) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int retval, k, devcheck; 49462306a36Sopenharmony_ci struct maple_device *mdev_add; 49562306a36Sopenharmony_ci struct maple_device_specify ds; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ds.port = mdev->port; 49862306a36Sopenharmony_ci for (k = 0; k < 5; k++) { 49962306a36Sopenharmony_ci ds.unit = k + 1; 50062306a36Sopenharmony_ci retval = 50162306a36Sopenharmony_ci bus_for_each_dev(&maple_bus_type, NULL, &ds, 50262306a36Sopenharmony_ci check_maple_device); 50362306a36Sopenharmony_ci if (retval) { 50462306a36Sopenharmony_ci submask = submask >> 1; 50562306a36Sopenharmony_ci continue; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci devcheck = submask & 0x01; 50862306a36Sopenharmony_ci if (devcheck) { 50962306a36Sopenharmony_ci mdev_add = maple_alloc_dev(mdev->port, k + 1); 51062306a36Sopenharmony_ci if (!mdev_add) 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci atomic_set(&mdev_add->busy, 1); 51362306a36Sopenharmony_ci maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO, 51462306a36Sopenharmony_ci 0, NULL); 51562306a36Sopenharmony_ci /* mark that we are checking sub devices */ 51662306a36Sopenharmony_ci scanning = 1; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci submask = submask >> 1; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* mark a device as removed */ 52362306a36Sopenharmony_cistatic void maple_clean_submap(struct maple_device *mdev) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci int killbit; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci killbit = (mdev->unit > 0 ? (1 << (mdev->unit - 1)) & 0x1f : 0x20); 52862306a36Sopenharmony_ci killbit = ~killbit; 52962306a36Sopenharmony_ci killbit &= 0xFF; 53062306a36Sopenharmony_ci subdevice_map[mdev->port] = subdevice_map[mdev->port] & killbit; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/* handle empty port or hotplug removal */ 53462306a36Sopenharmony_cistatic void maple_response_none(struct maple_device *mdev) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci maple_clean_submap(mdev); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (likely(mdev->unit != 0)) { 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * Block devices play up 54162306a36Sopenharmony_ci * and give the impression they have 54262306a36Sopenharmony_ci * been removed even when still in place or 54362306a36Sopenharmony_ci * trip the mtd layer when they have 54462306a36Sopenharmony_ci * really gone - this code traps that eventuality 54562306a36Sopenharmony_ci * and ensures we aren't overloaded with useless 54662306a36Sopenharmony_ci * error messages 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (mdev->can_unload) { 54962306a36Sopenharmony_ci if (!mdev->can_unload(mdev)) { 55062306a36Sopenharmony_ci atomic_set(&mdev->busy, 2); 55162306a36Sopenharmony_ci wake_up(&mdev->maple_wait); 55262306a36Sopenharmony_ci return; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci dev_info(&mdev->dev, "detaching device at (%d, %d)\n", 55762306a36Sopenharmony_ci mdev->port, mdev->unit); 55862306a36Sopenharmony_ci maple_detach_driver(mdev); 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci } else { 56162306a36Sopenharmony_ci if (!started || !fullscan) { 56262306a36Sopenharmony_ci if (checked[mdev->port] == false) { 56362306a36Sopenharmony_ci checked[mdev->port] = true; 56462306a36Sopenharmony_ci empty[mdev->port] = true; 56562306a36Sopenharmony_ci dev_info(&mdev->dev, "no devices" 56662306a36Sopenharmony_ci " to port %d\n", mdev->port); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci return; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci /* Some hardware devices generate false detach messages on unit 0 */ 57262306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/* preprocess hotplugs or scans */ 57662306a36Sopenharmony_cistatic void maple_response_devinfo(struct maple_device *mdev, 57762306a36Sopenharmony_ci char *recvbuf) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci char submask; 58062306a36Sopenharmony_ci if (!started || (scanning == 2) || !fullscan) { 58162306a36Sopenharmony_ci if ((mdev->unit == 0) && (checked[mdev->port] == false)) { 58262306a36Sopenharmony_ci checked[mdev->port] = true; 58362306a36Sopenharmony_ci maple_attach_driver(mdev); 58462306a36Sopenharmony_ci } else { 58562306a36Sopenharmony_ci if (mdev->unit != 0) 58662306a36Sopenharmony_ci maple_attach_driver(mdev); 58762306a36Sopenharmony_ci if (mdev->unit == 0) { 58862306a36Sopenharmony_ci empty[mdev->port] = false; 58962306a36Sopenharmony_ci maple_attach_driver(mdev); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci if (mdev->unit == 0) { 59462306a36Sopenharmony_ci submask = recvbuf[2] & 0x1F; 59562306a36Sopenharmony_ci if (submask ^ subdevice_map[mdev->port]) { 59662306a36Sopenharmony_ci maple_map_subunits(mdev, submask); 59762306a36Sopenharmony_ci subdevice_map[mdev->port] = submask; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void maple_response_fileerr(struct maple_device *mdev, void *recvbuf) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (mdev->fileerr_handler) { 60562306a36Sopenharmony_ci mdev->fileerr_handler(mdev, recvbuf); 60662306a36Sopenharmony_ci return; 60762306a36Sopenharmony_ci } else 60862306a36Sopenharmony_ci dev_warn(&mdev->dev, "device at (%d, %d) reports" 60962306a36Sopenharmony_ci "file error 0x%X\n", mdev->port, mdev->unit, 61062306a36Sopenharmony_ci ((int *)recvbuf)[1]); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void maple_port_rescan(void) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci int i; 61662306a36Sopenharmony_ci struct maple_device *mdev; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci fullscan = 1; 61962306a36Sopenharmony_ci for (i = 0; i < MAPLE_PORTS; i++) { 62062306a36Sopenharmony_ci if (checked[i] == false) { 62162306a36Sopenharmony_ci fullscan = 0; 62262306a36Sopenharmony_ci mdev = baseunits[i]; 62362306a36Sopenharmony_ci maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO, 62462306a36Sopenharmony_ci 0, NULL); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* maple dma end bottom half - implemented via workqueue */ 63062306a36Sopenharmony_cistatic void maple_dma_handler(struct work_struct *work) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct mapleq *mq, *nmq; 63362306a36Sopenharmony_ci struct maple_device *mdev; 63462306a36Sopenharmony_ci char *recvbuf; 63562306a36Sopenharmony_ci enum maple_code code; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!maple_dma_done()) 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci __raw_writel(0, MAPLE_ENABLE); 64062306a36Sopenharmony_ci if (!list_empty(&maple_sentq)) { 64162306a36Sopenharmony_ci list_for_each_entry_safe(mq, nmq, &maple_sentq, list) { 64262306a36Sopenharmony_ci mdev = mq->dev; 64362306a36Sopenharmony_ci recvbuf = mq->recvbuf->buf; 64462306a36Sopenharmony_ci __flush_invalidate_region(sh_cacheop_vaddr(recvbuf), 64562306a36Sopenharmony_ci 0x400); 64662306a36Sopenharmony_ci code = recvbuf[0]; 64762306a36Sopenharmony_ci kfree(mq->sendbuf); 64862306a36Sopenharmony_ci list_del_init(&mq->list); 64962306a36Sopenharmony_ci switch (code) { 65062306a36Sopenharmony_ci case MAPLE_RESPONSE_NONE: 65162306a36Sopenharmony_ci maple_response_none(mdev); 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci case MAPLE_RESPONSE_DEVINFO: 65562306a36Sopenharmony_ci maple_response_devinfo(mdev, recvbuf); 65662306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci case MAPLE_RESPONSE_DATATRF: 66062306a36Sopenharmony_ci if (mdev->callback) 66162306a36Sopenharmony_ci mdev->callback(mq); 66262306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 66362306a36Sopenharmony_ci wake_up(&mdev->maple_wait); 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci case MAPLE_RESPONSE_FILEERR: 66762306a36Sopenharmony_ci maple_response_fileerr(mdev, recvbuf); 66862306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 66962306a36Sopenharmony_ci wake_up(&mdev->maple_wait); 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci case MAPLE_RESPONSE_AGAIN: 67362306a36Sopenharmony_ci case MAPLE_RESPONSE_BADCMD: 67462306a36Sopenharmony_ci case MAPLE_RESPONSE_BADFUNC: 67562306a36Sopenharmony_ci dev_warn(&mdev->dev, "non-fatal error" 67662306a36Sopenharmony_ci " 0x%X at (%d, %d)\n", code, 67762306a36Sopenharmony_ci mdev->port, mdev->unit); 67862306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci case MAPLE_RESPONSE_ALLINFO: 68262306a36Sopenharmony_ci dev_notice(&mdev->dev, "extended" 68362306a36Sopenharmony_ci " device information request for (%d, %d)" 68462306a36Sopenharmony_ci " but call is not supported\n", mdev->port, 68562306a36Sopenharmony_ci mdev->unit); 68662306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci case MAPLE_RESPONSE_OK: 69062306a36Sopenharmony_ci atomic_set(&mdev->busy, 0); 69162306a36Sopenharmony_ci wake_up(&mdev->maple_wait); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci default: 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci /* if scanning is 1 then we have subdevices to check */ 69962306a36Sopenharmony_ci if (scanning == 1) { 70062306a36Sopenharmony_ci maple_send(); 70162306a36Sopenharmony_ci scanning = 2; 70262306a36Sopenharmony_ci } else 70362306a36Sopenharmony_ci scanning = 0; 70462306a36Sopenharmony_ci /*check if we have actually tested all ports yet */ 70562306a36Sopenharmony_ci if (!fullscan) 70662306a36Sopenharmony_ci maple_port_rescan(); 70762306a36Sopenharmony_ci /* mark that we have been through the first scan */ 70862306a36Sopenharmony_ci started = 1; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci maple_send(); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic irqreturn_t maple_dma_interrupt(int irq, void *dev_id) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci /* Load everything into the bottom half */ 71662306a36Sopenharmony_ci schedule_work(&maple_dma_process); 71762306a36Sopenharmony_ci return IRQ_HANDLED; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic irqreturn_t maple_vblank_interrupt(int irq, void *dev_id) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci schedule_work(&maple_vblank_process); 72362306a36Sopenharmony_ci return IRQ_HANDLED; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int maple_set_dma_interrupt_handler(void) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci return request_irq(HW_EVENT_MAPLE_DMA, maple_dma_interrupt, 72962306a36Sopenharmony_ci IRQF_SHARED, "maple bus DMA", &maple_unsupported_device); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int maple_set_vblank_interrupt_handler(void) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci return request_irq(HW_EVENT_VSYNC, maple_vblank_interrupt, 73562306a36Sopenharmony_ci IRQF_SHARED, "maple bus VBLANK", &maple_unsupported_device); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int maple_get_dma_buffer(void) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci maple_sendbuf = 74162306a36Sopenharmony_ci (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, 74262306a36Sopenharmony_ci MAPLE_DMA_PAGES); 74362306a36Sopenharmony_ci if (!maple_sendbuf) 74462306a36Sopenharmony_ci return -ENOMEM; 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int maple_match_bus_driver(struct device *devptr, 74962306a36Sopenharmony_ci struct device_driver *drvptr) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct maple_driver *maple_drv = to_maple_driver(drvptr); 75262306a36Sopenharmony_ci struct maple_device *maple_dev = to_maple_dev(devptr); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Trap empty port case */ 75562306a36Sopenharmony_ci if (maple_dev->devinfo.function == 0xFFFFFFFF) 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci else if (maple_dev->devinfo.function & 75862306a36Sopenharmony_ci cpu_to_be32(maple_drv->function)) 75962306a36Sopenharmony_ci return 1; 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic void maple_bus_release(struct device *dev) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic struct maple_driver maple_unsupported_device = { 76862306a36Sopenharmony_ci .drv = { 76962306a36Sopenharmony_ci .name = "maple_unsupported_device", 77062306a36Sopenharmony_ci .bus = &maple_bus_type, 77162306a36Sopenharmony_ci }, 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci/* 77462306a36Sopenharmony_ci * maple_bus_type - core maple bus structure 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_cistruct bus_type maple_bus_type = { 77762306a36Sopenharmony_ci .name = "maple", 77862306a36Sopenharmony_ci .match = maple_match_bus_driver, 77962306a36Sopenharmony_ci}; 78062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_bus_type); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic struct device maple_bus = { 78362306a36Sopenharmony_ci .init_name = "maple", 78462306a36Sopenharmony_ci .release = maple_bus_release, 78562306a36Sopenharmony_ci}; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int __init maple_bus_init(void) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci int retval, i; 79062306a36Sopenharmony_ci struct maple_device *mdev[MAPLE_PORTS]; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci __raw_writel(0, MAPLE_ENABLE); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci retval = device_register(&maple_bus); 79562306a36Sopenharmony_ci if (retval) 79662306a36Sopenharmony_ci goto cleanup; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci retval = bus_register(&maple_bus_type); 79962306a36Sopenharmony_ci if (retval) 80062306a36Sopenharmony_ci goto cleanup_device; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci retval = driver_register(&maple_unsupported_device.drv); 80362306a36Sopenharmony_ci if (retval) 80462306a36Sopenharmony_ci goto cleanup_bus; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* allocate memory for maple bus dma */ 80762306a36Sopenharmony_ci retval = maple_get_dma_buffer(); 80862306a36Sopenharmony_ci if (retval) { 80962306a36Sopenharmony_ci dev_err(&maple_bus, "failed to allocate DMA buffers\n"); 81062306a36Sopenharmony_ci goto cleanup_basic; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* set up DMA interrupt handler */ 81462306a36Sopenharmony_ci retval = maple_set_dma_interrupt_handler(); 81562306a36Sopenharmony_ci if (retval) { 81662306a36Sopenharmony_ci dev_err(&maple_bus, "bus failed to grab maple " 81762306a36Sopenharmony_ci "DMA IRQ\n"); 81862306a36Sopenharmony_ci goto cleanup_dma; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* set up VBLANK interrupt handler */ 82262306a36Sopenharmony_ci retval = maple_set_vblank_interrupt_handler(); 82362306a36Sopenharmony_ci if (retval) { 82462306a36Sopenharmony_ci dev_err(&maple_bus, "bus failed to grab VBLANK IRQ\n"); 82562306a36Sopenharmony_ci goto cleanup_irq; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci maple_queue_cache = KMEM_CACHE(maple_buffer, SLAB_HWCACHE_ALIGN); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!maple_queue_cache) { 83162306a36Sopenharmony_ci retval = -ENOMEM; 83262306a36Sopenharmony_ci goto cleanup_bothirqs; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci INIT_LIST_HEAD(&maple_waitq); 83662306a36Sopenharmony_ci INIT_LIST_HEAD(&maple_sentq); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* setup maple ports */ 83962306a36Sopenharmony_ci for (i = 0; i < MAPLE_PORTS; i++) { 84062306a36Sopenharmony_ci checked[i] = false; 84162306a36Sopenharmony_ci empty[i] = false; 84262306a36Sopenharmony_ci mdev[i] = maple_alloc_dev(i, 0); 84362306a36Sopenharmony_ci if (!mdev[i]) { 84462306a36Sopenharmony_ci while (i-- > 0) 84562306a36Sopenharmony_ci maple_free_dev(mdev[i]); 84662306a36Sopenharmony_ci retval = -ENOMEM; 84762306a36Sopenharmony_ci goto cleanup_cache; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci baseunits[i] = mdev[i]; 85062306a36Sopenharmony_ci atomic_set(&mdev[i]->busy, 1); 85162306a36Sopenharmony_ci maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL); 85262306a36Sopenharmony_ci subdevice_map[i] = 0; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci maple_pnp_time = jiffies + HZ; 85662306a36Sopenharmony_ci /* prepare initial queue */ 85762306a36Sopenharmony_ci maple_send(); 85862306a36Sopenharmony_ci dev_info(&maple_bus, "bus core now registered\n"); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return 0; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cicleanup_cache: 86362306a36Sopenharmony_ci kmem_cache_destroy(maple_queue_cache); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cicleanup_bothirqs: 86662306a36Sopenharmony_ci free_irq(HW_EVENT_VSYNC, 0); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cicleanup_irq: 86962306a36Sopenharmony_ci free_irq(HW_EVENT_MAPLE_DMA, 0); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cicleanup_dma: 87262306a36Sopenharmony_ci free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cicleanup_basic: 87562306a36Sopenharmony_ci driver_unregister(&maple_unsupported_device.drv); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cicleanup_bus: 87862306a36Sopenharmony_ci bus_unregister(&maple_bus_type); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cicleanup_device: 88162306a36Sopenharmony_ci device_unregister(&maple_bus); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cicleanup: 88462306a36Sopenharmony_ci printk(KERN_ERR "Maple bus registration failed\n"); 88562306a36Sopenharmony_ci return retval; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci/* Push init to later to ensure hardware gets detected */ 88862306a36Sopenharmony_cifs_initcall(maple_bus_init); 889