18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Core maple bus functionality
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2007 - 2009 Adrian McMenamin
58c2ecf20Sopenharmony_ci *  Copyright (C) 2001 - 2008 Paul Mundt
68c2ecf20Sopenharmony_ci *  Copyright (C) 2000 - 2001 YAEGASHI Takeshi
78c2ecf20Sopenharmony_ci *  Copyright (C) 2001 M. R. Brown
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
108c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
118c2ecf20Sopenharmony_ci * for more details.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/list.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/maple.h>
218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
228c2ecf20Sopenharmony_ci#include <linux/delay.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
258c2ecf20Sopenharmony_ci#include <asm/dma.h>
268c2ecf20Sopenharmony_ci#include <asm/io.h>
278c2ecf20Sopenharmony_ci#include <mach/dma.h>
288c2ecf20Sopenharmony_ci#include <mach/sysasic.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maple bus driver for Dreamcast");
328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
338c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}");
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void maple_dma_handler(struct work_struct *work);
368c2ecf20Sopenharmony_cistatic void maple_vblank_handler(struct work_struct *work);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic DECLARE_WORK(maple_dma_process, maple_dma_handler);
398c2ecf20Sopenharmony_cistatic DECLARE_WORK(maple_vblank_process, maple_vblank_handler);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic LIST_HEAD(maple_waitq);
428c2ecf20Sopenharmony_cistatic LIST_HEAD(maple_sentq);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* mutex to protect queue of waiting packets */
458c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(maple_wlist_lock);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct maple_driver maple_unsupported_device;
488c2ecf20Sopenharmony_cistatic struct device maple_bus;
498c2ecf20Sopenharmony_cistatic int subdevice_map[MAPLE_PORTS];
508c2ecf20Sopenharmony_cistatic unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr;
518c2ecf20Sopenharmony_cistatic unsigned long maple_pnp_time;
528c2ecf20Sopenharmony_cistatic int started, scanning, fullscan;
538c2ecf20Sopenharmony_cistatic struct kmem_cache *maple_queue_cache;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct maple_device_specify {
568c2ecf20Sopenharmony_ci	int port;
578c2ecf20Sopenharmony_ci	int unit;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic bool checked[MAPLE_PORTS];
618c2ecf20Sopenharmony_cistatic bool empty[MAPLE_PORTS];
628c2ecf20Sopenharmony_cistatic struct maple_device *baseunits[MAPLE_PORTS];
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/**
658c2ecf20Sopenharmony_ci * maple_driver_register - register a maple driver
668c2ecf20Sopenharmony_ci * @drv: maple driver to be registered.
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * Registers the passed in @drv, while updating the bus type.
698c2ecf20Sopenharmony_ci * Devices with matching function IDs will be automatically probed.
708c2ecf20Sopenharmony_ci */
718c2ecf20Sopenharmony_ciint maple_driver_register(struct maple_driver *drv)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (!drv)
748c2ecf20Sopenharmony_ci		return -EINVAL;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	drv->drv.bus = &maple_bus_type;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return driver_register(&drv->drv);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_driver_register);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/**
838c2ecf20Sopenharmony_ci * maple_driver_unregister - unregister a maple driver.
848c2ecf20Sopenharmony_ci * @drv: maple driver to unregister.
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * Cleans up after maple_driver_register(). To be invoked in the exit
878c2ecf20Sopenharmony_ci * path of any module drivers.
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_civoid maple_driver_unregister(struct maple_driver *drv)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	driver_unregister(&drv->drv);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_driver_unregister);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* set hardware registers to enable next round of dma */
968c2ecf20Sopenharmony_cistatic void maple_dma_reset(void)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	__raw_writel(MAPLE_MAGIC, MAPLE_RESET);
998c2ecf20Sopenharmony_ci	/* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */
1008c2ecf20Sopenharmony_ci	__raw_writel(1, MAPLE_TRIGTYPE);
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	* Maple system register
1038c2ecf20Sopenharmony_ci	* bits 31 - 16	timeout in units of 20nsec
1048c2ecf20Sopenharmony_ci	* bit 12	hard trigger - set 0 to keep responding to VBLANK
1058c2ecf20Sopenharmony_ci	* bits 9 - 8	set 00 for 2 Mbps, 01 for 1 Mbps
1068c2ecf20Sopenharmony_ci	* bits 3 - 0	delay (in 1.3ms) between VBLANK and start of DMA
1078c2ecf20Sopenharmony_ci	* max delay is 11
1088c2ecf20Sopenharmony_ci	*/
1098c2ecf20Sopenharmony_ci	__raw_writel(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED);
1108c2ecf20Sopenharmony_ci	__raw_writel(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR);
1118c2ecf20Sopenharmony_ci	__raw_writel(1, MAPLE_ENABLE);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/**
1158c2ecf20Sopenharmony_ci * maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND
1168c2ecf20Sopenharmony_ci * @dev: device responding
1178c2ecf20Sopenharmony_ci * @callback: handler callback
1188c2ecf20Sopenharmony_ci * @interval: interval in jiffies between callbacks
1198c2ecf20Sopenharmony_ci * @function: the function code for the device
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_civoid maple_getcond_callback(struct maple_device *dev,
1228c2ecf20Sopenharmony_ci			void (*callback) (struct mapleq *mq),
1238c2ecf20Sopenharmony_ci			unsigned long interval, unsigned long function)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	dev->callback = callback;
1268c2ecf20Sopenharmony_ci	dev->interval = interval;
1278c2ecf20Sopenharmony_ci	dev->function = cpu_to_be32(function);
1288c2ecf20Sopenharmony_ci	dev->when = jiffies;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_getcond_callback);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int maple_dma_done(void)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	return (__raw_readl(MAPLE_STATE) & 1) == 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void maple_release_device(struct device *dev)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct maple_device *mdev;
1408c2ecf20Sopenharmony_ci	struct mapleq *mq;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	mdev = to_maple_dev(dev);
1438c2ecf20Sopenharmony_ci	mq = mdev->mq;
1448c2ecf20Sopenharmony_ci	kmem_cache_free(maple_queue_cache, mq->recvbuf);
1458c2ecf20Sopenharmony_ci	kfree(mq);
1468c2ecf20Sopenharmony_ci	kfree(mdev);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/**
1508c2ecf20Sopenharmony_ci * maple_add_packet - add a single instruction to the maple bus queue
1518c2ecf20Sopenharmony_ci * @mdev: maple device
1528c2ecf20Sopenharmony_ci * @function: function on device being queried
1538c2ecf20Sopenharmony_ci * @command: maple command to add
1548c2ecf20Sopenharmony_ci * @length: length of command string (in 32 bit words)
1558c2ecf20Sopenharmony_ci * @data: remainder of command string
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_ciint maple_add_packet(struct maple_device *mdev, u32 function, u32 command,
1588c2ecf20Sopenharmony_ci	size_t length, void *data)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int ret = 0;
1618c2ecf20Sopenharmony_ci	void *sendbuf = NULL;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (length) {
1648c2ecf20Sopenharmony_ci		sendbuf = kcalloc(length, 4, GFP_KERNEL);
1658c2ecf20Sopenharmony_ci		if (!sendbuf) {
1668c2ecf20Sopenharmony_ci			ret = -ENOMEM;
1678c2ecf20Sopenharmony_ci			goto out;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci		((__be32 *)sendbuf)[0] = cpu_to_be32(function);
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	mdev->mq->command = command;
1738c2ecf20Sopenharmony_ci	mdev->mq->length = length;
1748c2ecf20Sopenharmony_ci	if (length > 1)
1758c2ecf20Sopenharmony_ci		memcpy(sendbuf + 4, data, (length - 1) * 4);
1768c2ecf20Sopenharmony_ci	mdev->mq->sendbuf = sendbuf;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mutex_lock(&maple_wlist_lock);
1798c2ecf20Sopenharmony_ci	list_add_tail(&mdev->mq->list, &maple_waitq);
1808c2ecf20Sopenharmony_ci	mutex_unlock(&maple_wlist_lock);
1818c2ecf20Sopenharmony_ciout:
1828c2ecf20Sopenharmony_ci	return ret;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_add_packet);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic struct mapleq *maple_allocq(struct maple_device *mdev)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct mapleq *mq;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	mq = kzalloc(sizeof(*mq), GFP_KERNEL);
1918c2ecf20Sopenharmony_ci	if (!mq)
1928c2ecf20Sopenharmony_ci		goto failed_nomem;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mq->list);
1958c2ecf20Sopenharmony_ci	mq->dev = mdev;
1968c2ecf20Sopenharmony_ci	mq->recvbuf = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
1978c2ecf20Sopenharmony_ci	if (!mq->recvbuf)
1988c2ecf20Sopenharmony_ci		goto failed_p2;
1998c2ecf20Sopenharmony_ci	mq->recvbuf->buf = &((mq->recvbuf->bufx)[0]);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return mq;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cifailed_p2:
2048c2ecf20Sopenharmony_ci	kfree(mq);
2058c2ecf20Sopenharmony_cifailed_nomem:
2068c2ecf20Sopenharmony_ci	dev_err(&mdev->dev, "could not allocate memory for device (%d, %d)\n",
2078c2ecf20Sopenharmony_ci		mdev->port, mdev->unit);
2088c2ecf20Sopenharmony_ci	return NULL;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic struct maple_device *maple_alloc_dev(int port, int unit)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct maple_device *mdev;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* zero this out to avoid kobj subsystem
2168c2ecf20Sopenharmony_ci	* thinking it has already been registered */
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
2198c2ecf20Sopenharmony_ci	if (!mdev)
2208c2ecf20Sopenharmony_ci		return NULL;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	mdev->port = port;
2238c2ecf20Sopenharmony_ci	mdev->unit = unit;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	mdev->mq = maple_allocq(mdev);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!mdev->mq) {
2288c2ecf20Sopenharmony_ci		kfree(mdev);
2298c2ecf20Sopenharmony_ci		return NULL;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	mdev->dev.bus = &maple_bus_type;
2328c2ecf20Sopenharmony_ci	mdev->dev.parent = &maple_bus;
2338c2ecf20Sopenharmony_ci	init_waitqueue_head(&mdev->maple_wait);
2348c2ecf20Sopenharmony_ci	return mdev;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void maple_free_dev(struct maple_device *mdev)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	kmem_cache_free(maple_queue_cache, mdev->mq->recvbuf);
2408c2ecf20Sopenharmony_ci	kfree(mdev->mq);
2418c2ecf20Sopenharmony_ci	kfree(mdev);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/* process the command queue into a maple command block
2458c2ecf20Sopenharmony_ci * terminating command has bit 32 of first long set to 0
2468c2ecf20Sopenharmony_ci */
2478c2ecf20Sopenharmony_cistatic void maple_build_block(struct mapleq *mq)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	int port, unit, from, to, len;
2508c2ecf20Sopenharmony_ci	unsigned long *lsendbuf = mq->sendbuf;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	port = mq->dev->port & 3;
2538c2ecf20Sopenharmony_ci	unit = mq->dev->unit;
2548c2ecf20Sopenharmony_ci	len = mq->length;
2558c2ecf20Sopenharmony_ci	from = port << 6;
2568c2ecf20Sopenharmony_ci	to = (port << 6) | (unit > 0 ? (1 << (unit - 1)) & 0x1f : 0x20);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	*maple_lastptr &= 0x7fffffff;
2598c2ecf20Sopenharmony_ci	maple_lastptr = maple_sendptr;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	*maple_sendptr++ = (port << 16) | len | 0x80000000;
2628c2ecf20Sopenharmony_ci	*maple_sendptr++ = virt_to_phys(mq->recvbuf->buf);
2638c2ecf20Sopenharmony_ci	*maple_sendptr++ =
2648c2ecf20Sopenharmony_ci	    mq->command | (to << 8) | (from << 16) | (len << 24);
2658c2ecf20Sopenharmony_ci	while (len-- > 0)
2668c2ecf20Sopenharmony_ci		*maple_sendptr++ = *lsendbuf++;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/* build up command queue */
2708c2ecf20Sopenharmony_cistatic void maple_send(void)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	int i, maple_packets = 0;
2738c2ecf20Sopenharmony_ci	struct mapleq *mq, *nmq;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (!maple_dma_done())
2768c2ecf20Sopenharmony_ci		return;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* disable DMA */
2798c2ecf20Sopenharmony_ci	__raw_writel(0, MAPLE_ENABLE);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!list_empty(&maple_sentq))
2828c2ecf20Sopenharmony_ci		goto finish;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	mutex_lock(&maple_wlist_lock);
2858c2ecf20Sopenharmony_ci	if (list_empty(&maple_waitq)) {
2868c2ecf20Sopenharmony_ci		mutex_unlock(&maple_wlist_lock);
2878c2ecf20Sopenharmony_ci		goto finish;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	maple_lastptr = maple_sendbuf;
2918c2ecf20Sopenharmony_ci	maple_sendptr = maple_sendbuf;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mq, nmq, &maple_waitq, list) {
2948c2ecf20Sopenharmony_ci		maple_build_block(mq);
2958c2ecf20Sopenharmony_ci		list_del_init(&mq->list);
2968c2ecf20Sopenharmony_ci		list_add_tail(&mq->list, &maple_sentq);
2978c2ecf20Sopenharmony_ci		if (maple_packets++ > MAPLE_MAXPACKETS)
2988c2ecf20Sopenharmony_ci			break;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	mutex_unlock(&maple_wlist_lock);
3018c2ecf20Sopenharmony_ci	if (maple_packets > 0) {
3028c2ecf20Sopenharmony_ci		for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++)
3038c2ecf20Sopenharmony_ci			__flush_purge_region(maple_sendbuf + i * PAGE_SIZE,
3048c2ecf20Sopenharmony_ci					PAGE_SIZE);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cifinish:
3088c2ecf20Sopenharmony_ci	maple_dma_reset();
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci/* check if there is a driver registered likely to match this device */
3128c2ecf20Sopenharmony_cistatic int maple_check_matching_driver(struct device_driver *driver,
3138c2ecf20Sopenharmony_ci					void *devptr)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct maple_driver *maple_drv;
3168c2ecf20Sopenharmony_ci	struct maple_device *mdev;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	mdev = devptr;
3198c2ecf20Sopenharmony_ci	maple_drv = to_maple_driver(driver);
3208c2ecf20Sopenharmony_ci	if (mdev->devinfo.function & cpu_to_be32(maple_drv->function))
3218c2ecf20Sopenharmony_ci		return 1;
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void maple_detach_driver(struct maple_device *mdev)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	device_unregister(&mdev->dev);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/* process initial MAPLE_COMMAND_DEVINFO for each device or port */
3318c2ecf20Sopenharmony_cistatic void maple_attach_driver(struct maple_device *mdev)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	char *p, *recvbuf;
3348c2ecf20Sopenharmony_ci	unsigned long function;
3358c2ecf20Sopenharmony_ci	int matched, error;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	recvbuf = mdev->mq->recvbuf->buf;
3388c2ecf20Sopenharmony_ci	/* copy the data as individual elements in
3398c2ecf20Sopenharmony_ci	* case of memory optimisation */
3408c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.function, recvbuf + 4, 4);
3418c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.function_data[0], recvbuf + 8, 12);
3428c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.area_code, recvbuf + 20, 1);
3438c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.connector_direction, recvbuf + 21, 1);
3448c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.product_name[0], recvbuf + 22, 30);
3458c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.standby_power, recvbuf + 112, 2);
3468c2ecf20Sopenharmony_ci	memcpy(&mdev->devinfo.max_power, recvbuf + 114, 2);
3478c2ecf20Sopenharmony_ci	memcpy(mdev->product_name, mdev->devinfo.product_name, 30);
3488c2ecf20Sopenharmony_ci	mdev->product_name[30] = '\0';
3498c2ecf20Sopenharmony_ci	memcpy(mdev->product_licence, mdev->devinfo.product_licence, 60);
3508c2ecf20Sopenharmony_ci	mdev->product_licence[60] = '\0';
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	for (p = mdev->product_name + 29; mdev->product_name <= p; p--)
3538c2ecf20Sopenharmony_ci		if (*p == ' ')
3548c2ecf20Sopenharmony_ci			*p = '\0';
3558c2ecf20Sopenharmony_ci		else
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci	for (p = mdev->product_licence + 59; mdev->product_licence <= p; p--)
3588c2ecf20Sopenharmony_ci		if (*p == ' ')
3598c2ecf20Sopenharmony_ci			*p = '\0';
3608c2ecf20Sopenharmony_ci		else
3618c2ecf20Sopenharmony_ci			break;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	function = be32_to_cpu(mdev->devinfo.function);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	dev_info(&mdev->dev, "detected %s: function 0x%lX: at (%d, %d)\n",
3668c2ecf20Sopenharmony_ci		mdev->product_name, function, mdev->port, mdev->unit);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (function > 0x200) {
3698c2ecf20Sopenharmony_ci		/* Do this silently - as not a real device */
3708c2ecf20Sopenharmony_ci		function = 0;
3718c2ecf20Sopenharmony_ci		mdev->driver = &maple_unsupported_device;
3728c2ecf20Sopenharmony_ci		dev_set_name(&mdev->dev, "%d:0.port", mdev->port);
3738c2ecf20Sopenharmony_ci	} else {
3748c2ecf20Sopenharmony_ci		matched =
3758c2ecf20Sopenharmony_ci			bus_for_each_drv(&maple_bus_type, NULL, mdev,
3768c2ecf20Sopenharmony_ci				maple_check_matching_driver);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		if (matched == 0) {
3798c2ecf20Sopenharmony_ci			/* Driver does not exist yet */
3808c2ecf20Sopenharmony_ci			dev_info(&mdev->dev, "no driver found\n");
3818c2ecf20Sopenharmony_ci			mdev->driver = &maple_unsupported_device;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci		dev_set_name(&mdev->dev, "%d:0%d.%lX", mdev->port,
3848c2ecf20Sopenharmony_ci			     mdev->unit, function);
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	mdev->function = function;
3888c2ecf20Sopenharmony_ci	mdev->dev.release = &maple_release_device;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	atomic_set(&mdev->busy, 0);
3918c2ecf20Sopenharmony_ci	error = device_register(&mdev->dev);
3928c2ecf20Sopenharmony_ci	if (error) {
3938c2ecf20Sopenharmony_ci		dev_warn(&mdev->dev, "could not register device at"
3948c2ecf20Sopenharmony_ci			" (%d, %d), with error 0x%X\n", mdev->unit,
3958c2ecf20Sopenharmony_ci			mdev->port, error);
3968c2ecf20Sopenharmony_ci		maple_free_dev(mdev);
3978c2ecf20Sopenharmony_ci		mdev = NULL;
3988c2ecf20Sopenharmony_ci		return;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/*
4038c2ecf20Sopenharmony_ci * if device has been registered for the given
4048c2ecf20Sopenharmony_ci * port and unit then return 1 - allows identification
4058c2ecf20Sopenharmony_ci * of which devices need to be attached or detached
4068c2ecf20Sopenharmony_ci */
4078c2ecf20Sopenharmony_cistatic int check_maple_device(struct device *device, void *portptr)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct maple_device_specify *ds;
4108c2ecf20Sopenharmony_ci	struct maple_device *mdev;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	ds = portptr;
4138c2ecf20Sopenharmony_ci	mdev = to_maple_dev(device);
4148c2ecf20Sopenharmony_ci	if (mdev->port == ds->port && mdev->unit == ds->unit)
4158c2ecf20Sopenharmony_ci		return 1;
4168c2ecf20Sopenharmony_ci	return 0;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic int setup_maple_commands(struct device *device, void *ignored)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	int add;
4228c2ecf20Sopenharmony_ci	struct maple_device *mdev = to_maple_dev(device);
4238c2ecf20Sopenharmony_ci	if (mdev->interval > 0 && atomic_read(&mdev->busy) == 0 &&
4248c2ecf20Sopenharmony_ci		time_after(jiffies, mdev->when)) {
4258c2ecf20Sopenharmony_ci		/* bounce if we cannot add */
4268c2ecf20Sopenharmony_ci		add = maple_add_packet(mdev,
4278c2ecf20Sopenharmony_ci			be32_to_cpu(mdev->devinfo.function),
4288c2ecf20Sopenharmony_ci			MAPLE_COMMAND_GETCOND, 1, NULL);
4298c2ecf20Sopenharmony_ci		if (!add)
4308c2ecf20Sopenharmony_ci			mdev->when = jiffies + mdev->interval;
4318c2ecf20Sopenharmony_ci	} else {
4328c2ecf20Sopenharmony_ci		if (time_after(jiffies, maple_pnp_time))
4338c2ecf20Sopenharmony_ci			/* Ensure we don't have block reads and devinfo
4348c2ecf20Sopenharmony_ci			* calls interfering with one another - so flag the
4358c2ecf20Sopenharmony_ci			* device as busy */
4368c2ecf20Sopenharmony_ci			if (atomic_read(&mdev->busy) == 0) {
4378c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 1);
4388c2ecf20Sopenharmony_ci				maple_add_packet(mdev, 0,
4398c2ecf20Sopenharmony_ci					MAPLE_COMMAND_DEVINFO, 0, NULL);
4408c2ecf20Sopenharmony_ci			}
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	return 0;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/* VBLANK bottom half - implemented via workqueue */
4468c2ecf20Sopenharmony_cistatic void maple_vblank_handler(struct work_struct *work)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	int x, locking;
4498c2ecf20Sopenharmony_ci	struct maple_device *mdev;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (!maple_dma_done())
4528c2ecf20Sopenharmony_ci		return;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	__raw_writel(0, MAPLE_ENABLE);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (!list_empty(&maple_sentq))
4578c2ecf20Sopenharmony_ci		goto finish;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/*
4608c2ecf20Sopenharmony_ci	* Set up essential commands - to fetch data and
4618c2ecf20Sopenharmony_ci	* check devices are still present
4628c2ecf20Sopenharmony_ci	*/
4638c2ecf20Sopenharmony_ci	bus_for_each_dev(&maple_bus_type, NULL, NULL,
4648c2ecf20Sopenharmony_ci		setup_maple_commands);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (time_after(jiffies, maple_pnp_time)) {
4678c2ecf20Sopenharmony_ci		/*
4688c2ecf20Sopenharmony_ci		* Scan the empty ports - bus is flakey and may have
4698c2ecf20Sopenharmony_ci		* mis-reported emptyness
4708c2ecf20Sopenharmony_ci		*/
4718c2ecf20Sopenharmony_ci		for (x = 0; x < MAPLE_PORTS; x++) {
4728c2ecf20Sopenharmony_ci			if (checked[x] && empty[x]) {
4738c2ecf20Sopenharmony_ci				mdev = baseunits[x];
4748c2ecf20Sopenharmony_ci				if (!mdev)
4758c2ecf20Sopenharmony_ci					break;
4768c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 1);
4778c2ecf20Sopenharmony_ci				locking = maple_add_packet(mdev, 0,
4788c2ecf20Sopenharmony_ci					MAPLE_COMMAND_DEVINFO, 0, NULL);
4798c2ecf20Sopenharmony_ci				if (!locking)
4808c2ecf20Sopenharmony_ci					break;
4818c2ecf20Sopenharmony_ci				}
4828c2ecf20Sopenharmony_ci			}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cifinish:
4888c2ecf20Sopenharmony_ci	maple_send();
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci/* handle devices added via hotplugs - placing them on queue for DEVINFO */
4928c2ecf20Sopenharmony_cistatic void maple_map_subunits(struct maple_device *mdev, int submask)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	int retval, k, devcheck;
4958c2ecf20Sopenharmony_ci	struct maple_device *mdev_add;
4968c2ecf20Sopenharmony_ci	struct maple_device_specify ds;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ds.port = mdev->port;
4998c2ecf20Sopenharmony_ci	for (k = 0; k < 5; k++) {
5008c2ecf20Sopenharmony_ci		ds.unit = k + 1;
5018c2ecf20Sopenharmony_ci		retval =
5028c2ecf20Sopenharmony_ci		    bus_for_each_dev(&maple_bus_type, NULL, &ds,
5038c2ecf20Sopenharmony_ci				     check_maple_device);
5048c2ecf20Sopenharmony_ci		if (retval) {
5058c2ecf20Sopenharmony_ci			submask = submask >> 1;
5068c2ecf20Sopenharmony_ci			continue;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci		devcheck = submask & 0x01;
5098c2ecf20Sopenharmony_ci		if (devcheck) {
5108c2ecf20Sopenharmony_ci			mdev_add = maple_alloc_dev(mdev->port, k + 1);
5118c2ecf20Sopenharmony_ci			if (!mdev_add)
5128c2ecf20Sopenharmony_ci				return;
5138c2ecf20Sopenharmony_ci			atomic_set(&mdev_add->busy, 1);
5148c2ecf20Sopenharmony_ci			maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO,
5158c2ecf20Sopenharmony_ci				0, NULL);
5168c2ecf20Sopenharmony_ci			/* mark that we are checking sub devices */
5178c2ecf20Sopenharmony_ci			scanning = 1;
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci		submask = submask >> 1;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci/* mark a device as removed */
5248c2ecf20Sopenharmony_cistatic void maple_clean_submap(struct maple_device *mdev)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	int killbit;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	killbit = (mdev->unit > 0 ? (1 << (mdev->unit - 1)) & 0x1f : 0x20);
5298c2ecf20Sopenharmony_ci	killbit = ~killbit;
5308c2ecf20Sopenharmony_ci	killbit &= 0xFF;
5318c2ecf20Sopenharmony_ci	subdevice_map[mdev->port] = subdevice_map[mdev->port] & killbit;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/* handle empty port or hotplug removal */
5358c2ecf20Sopenharmony_cistatic void maple_response_none(struct maple_device *mdev)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	maple_clean_submap(mdev);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (likely(mdev->unit != 0)) {
5408c2ecf20Sopenharmony_ci		/*
5418c2ecf20Sopenharmony_ci		* Block devices play up
5428c2ecf20Sopenharmony_ci		* and give the impression they have
5438c2ecf20Sopenharmony_ci		* been removed even when still in place or
5448c2ecf20Sopenharmony_ci		* trip the mtd layer when they have
5458c2ecf20Sopenharmony_ci		* really gone - this code traps that eventuality
5468c2ecf20Sopenharmony_ci		* and ensures we aren't overloaded with useless
5478c2ecf20Sopenharmony_ci		* error messages
5488c2ecf20Sopenharmony_ci		*/
5498c2ecf20Sopenharmony_ci		if (mdev->can_unload) {
5508c2ecf20Sopenharmony_ci			if (!mdev->can_unload(mdev)) {
5518c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 2);
5528c2ecf20Sopenharmony_ci				wake_up(&mdev->maple_wait);
5538c2ecf20Sopenharmony_ci				return;
5548c2ecf20Sopenharmony_ci			}
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		dev_info(&mdev->dev, "detaching device at (%d, %d)\n",
5588c2ecf20Sopenharmony_ci			mdev->port, mdev->unit);
5598c2ecf20Sopenharmony_ci		maple_detach_driver(mdev);
5608c2ecf20Sopenharmony_ci		return;
5618c2ecf20Sopenharmony_ci	} else {
5628c2ecf20Sopenharmony_ci		if (!started || !fullscan) {
5638c2ecf20Sopenharmony_ci			if (checked[mdev->port] == false) {
5648c2ecf20Sopenharmony_ci				checked[mdev->port] = true;
5658c2ecf20Sopenharmony_ci				empty[mdev->port] = true;
5668c2ecf20Sopenharmony_ci				dev_info(&mdev->dev, "no devices"
5678c2ecf20Sopenharmony_ci					" to port %d\n", mdev->port);
5688c2ecf20Sopenharmony_ci			}
5698c2ecf20Sopenharmony_ci			return;
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	/* Some hardware devices generate false detach messages on unit 0 */
5738c2ecf20Sopenharmony_ci	atomic_set(&mdev->busy, 0);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/* preprocess hotplugs or scans */
5778c2ecf20Sopenharmony_cistatic void maple_response_devinfo(struct maple_device *mdev,
5788c2ecf20Sopenharmony_ci				   char *recvbuf)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	char submask;
5818c2ecf20Sopenharmony_ci	if (!started || (scanning == 2) || !fullscan) {
5828c2ecf20Sopenharmony_ci		if ((mdev->unit == 0) && (checked[mdev->port] == false)) {
5838c2ecf20Sopenharmony_ci			checked[mdev->port] = true;
5848c2ecf20Sopenharmony_ci			maple_attach_driver(mdev);
5858c2ecf20Sopenharmony_ci		} else {
5868c2ecf20Sopenharmony_ci			if (mdev->unit != 0)
5878c2ecf20Sopenharmony_ci				maple_attach_driver(mdev);
5888c2ecf20Sopenharmony_ci			if (mdev->unit == 0) {
5898c2ecf20Sopenharmony_ci				empty[mdev->port] = false;
5908c2ecf20Sopenharmony_ci				maple_attach_driver(mdev);
5918c2ecf20Sopenharmony_ci			}
5928c2ecf20Sopenharmony_ci		}
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci	if (mdev->unit == 0) {
5958c2ecf20Sopenharmony_ci		submask = recvbuf[2] & 0x1F;
5968c2ecf20Sopenharmony_ci		if (submask ^ subdevice_map[mdev->port]) {
5978c2ecf20Sopenharmony_ci			maple_map_subunits(mdev, submask);
5988c2ecf20Sopenharmony_ci			subdevice_map[mdev->port] = submask;
5998c2ecf20Sopenharmony_ci		}
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic void maple_response_fileerr(struct maple_device *mdev, void *recvbuf)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	if (mdev->fileerr_handler) {
6068c2ecf20Sopenharmony_ci		mdev->fileerr_handler(mdev, recvbuf);
6078c2ecf20Sopenharmony_ci		return;
6088c2ecf20Sopenharmony_ci	} else
6098c2ecf20Sopenharmony_ci		dev_warn(&mdev->dev, "device at (%d, %d) reports"
6108c2ecf20Sopenharmony_ci			"file error 0x%X\n", mdev->port, mdev->unit,
6118c2ecf20Sopenharmony_ci			((int *)recvbuf)[1]);
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic void maple_port_rescan(void)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	int i;
6178c2ecf20Sopenharmony_ci	struct maple_device *mdev;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	fullscan = 1;
6208c2ecf20Sopenharmony_ci	for (i = 0; i < MAPLE_PORTS; i++) {
6218c2ecf20Sopenharmony_ci		if (checked[i] == false) {
6228c2ecf20Sopenharmony_ci			fullscan = 0;
6238c2ecf20Sopenharmony_ci			mdev = baseunits[i];
6248c2ecf20Sopenharmony_ci			maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO,
6258c2ecf20Sopenharmony_ci				0, NULL);
6268c2ecf20Sopenharmony_ci		}
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/* maple dma end bottom half - implemented via workqueue */
6318c2ecf20Sopenharmony_cistatic void maple_dma_handler(struct work_struct *work)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct mapleq *mq, *nmq;
6348c2ecf20Sopenharmony_ci	struct maple_device *mdev;
6358c2ecf20Sopenharmony_ci	char *recvbuf;
6368c2ecf20Sopenharmony_ci	enum maple_code code;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (!maple_dma_done())
6398c2ecf20Sopenharmony_ci		return;
6408c2ecf20Sopenharmony_ci	__raw_writel(0, MAPLE_ENABLE);
6418c2ecf20Sopenharmony_ci	if (!list_empty(&maple_sentq)) {
6428c2ecf20Sopenharmony_ci		list_for_each_entry_safe(mq, nmq, &maple_sentq, list) {
6438c2ecf20Sopenharmony_ci			mdev = mq->dev;
6448c2ecf20Sopenharmony_ci			recvbuf = mq->recvbuf->buf;
6458c2ecf20Sopenharmony_ci			__flush_invalidate_region(sh_cacheop_vaddr(recvbuf),
6468c2ecf20Sopenharmony_ci					0x400);
6478c2ecf20Sopenharmony_ci			code = recvbuf[0];
6488c2ecf20Sopenharmony_ci			kfree(mq->sendbuf);
6498c2ecf20Sopenharmony_ci			list_del_init(&mq->list);
6508c2ecf20Sopenharmony_ci			switch (code) {
6518c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_NONE:
6528c2ecf20Sopenharmony_ci				maple_response_none(mdev);
6538c2ecf20Sopenharmony_ci				break;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_DEVINFO:
6568c2ecf20Sopenharmony_ci				maple_response_devinfo(mdev, recvbuf);
6578c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 0);
6588c2ecf20Sopenharmony_ci				break;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_DATATRF:
6618c2ecf20Sopenharmony_ci				if (mdev->callback)
6628c2ecf20Sopenharmony_ci					mdev->callback(mq);
6638c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 0);
6648c2ecf20Sopenharmony_ci				wake_up(&mdev->maple_wait);
6658c2ecf20Sopenharmony_ci				break;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_FILEERR:
6688c2ecf20Sopenharmony_ci				maple_response_fileerr(mdev, recvbuf);
6698c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 0);
6708c2ecf20Sopenharmony_ci				wake_up(&mdev->maple_wait);
6718c2ecf20Sopenharmony_ci				break;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_AGAIN:
6748c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_BADCMD:
6758c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_BADFUNC:
6768c2ecf20Sopenharmony_ci				dev_warn(&mdev->dev, "non-fatal error"
6778c2ecf20Sopenharmony_ci					" 0x%X at (%d, %d)\n", code,
6788c2ecf20Sopenharmony_ci					mdev->port, mdev->unit);
6798c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 0);
6808c2ecf20Sopenharmony_ci				break;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_ALLINFO:
6838c2ecf20Sopenharmony_ci				dev_notice(&mdev->dev, "extended"
6848c2ecf20Sopenharmony_ci				" device information request for (%d, %d)"
6858c2ecf20Sopenharmony_ci				" but call is not supported\n", mdev->port,
6868c2ecf20Sopenharmony_ci				mdev->unit);
6878c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 0);
6888c2ecf20Sopenharmony_ci				break;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci			case MAPLE_RESPONSE_OK:
6918c2ecf20Sopenharmony_ci				atomic_set(&mdev->busy, 0);
6928c2ecf20Sopenharmony_ci				wake_up(&mdev->maple_wait);
6938c2ecf20Sopenharmony_ci				break;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci			default:
6968c2ecf20Sopenharmony_ci				break;
6978c2ecf20Sopenharmony_ci			}
6988c2ecf20Sopenharmony_ci		}
6998c2ecf20Sopenharmony_ci		/* if scanning is 1 then we have subdevices to check */
7008c2ecf20Sopenharmony_ci		if (scanning == 1) {
7018c2ecf20Sopenharmony_ci			maple_send();
7028c2ecf20Sopenharmony_ci			scanning = 2;
7038c2ecf20Sopenharmony_ci		} else
7048c2ecf20Sopenharmony_ci			scanning = 0;
7058c2ecf20Sopenharmony_ci		/*check if we have actually tested all ports yet */
7068c2ecf20Sopenharmony_ci		if (!fullscan)
7078c2ecf20Sopenharmony_ci			maple_port_rescan();
7088c2ecf20Sopenharmony_ci		/* mark that we have been through the first scan */
7098c2ecf20Sopenharmony_ci		started = 1;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci	maple_send();
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic irqreturn_t maple_dma_interrupt(int irq, void *dev_id)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	/* Load everything into the bottom half */
7178c2ecf20Sopenharmony_ci	schedule_work(&maple_dma_process);
7188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cistatic irqreturn_t maple_vblank_interrupt(int irq, void *dev_id)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	schedule_work(&maple_vblank_process);
7248c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic int maple_set_dma_interrupt_handler(void)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	return request_irq(HW_EVENT_MAPLE_DMA, maple_dma_interrupt,
7308c2ecf20Sopenharmony_ci		IRQF_SHARED, "maple bus DMA", &maple_unsupported_device);
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic int maple_set_vblank_interrupt_handler(void)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	return request_irq(HW_EVENT_VSYNC, maple_vblank_interrupt,
7368c2ecf20Sopenharmony_ci		IRQF_SHARED, "maple bus VBLANK", &maple_unsupported_device);
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistatic int maple_get_dma_buffer(void)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	maple_sendbuf =
7428c2ecf20Sopenharmony_ci	    (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO,
7438c2ecf20Sopenharmony_ci				      MAPLE_DMA_PAGES);
7448c2ecf20Sopenharmony_ci	if (!maple_sendbuf)
7458c2ecf20Sopenharmony_ci		return -ENOMEM;
7468c2ecf20Sopenharmony_ci	return 0;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int maple_match_bus_driver(struct device *devptr,
7508c2ecf20Sopenharmony_ci				  struct device_driver *drvptr)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	struct maple_driver *maple_drv = to_maple_driver(drvptr);
7538c2ecf20Sopenharmony_ci	struct maple_device *maple_dev = to_maple_dev(devptr);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* Trap empty port case */
7568c2ecf20Sopenharmony_ci	if (maple_dev->devinfo.function == 0xFFFFFFFF)
7578c2ecf20Sopenharmony_ci		return 0;
7588c2ecf20Sopenharmony_ci	else if (maple_dev->devinfo.function &
7598c2ecf20Sopenharmony_ci		 cpu_to_be32(maple_drv->function))
7608c2ecf20Sopenharmony_ci		return 1;
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int maple_bus_uevent(struct device *dev,
7658c2ecf20Sopenharmony_ci			    struct kobj_uevent_env *env)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	return 0;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic void maple_bus_release(struct device *dev)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic struct maple_driver maple_unsupported_device = {
7758c2ecf20Sopenharmony_ci	.drv = {
7768c2ecf20Sopenharmony_ci		.name = "maple_unsupported_device",
7778c2ecf20Sopenharmony_ci		.bus = &maple_bus_type,
7788c2ecf20Sopenharmony_ci	},
7798c2ecf20Sopenharmony_ci};
7808c2ecf20Sopenharmony_ci/*
7818c2ecf20Sopenharmony_ci * maple_bus_type - core maple bus structure
7828c2ecf20Sopenharmony_ci */
7838c2ecf20Sopenharmony_cistruct bus_type maple_bus_type = {
7848c2ecf20Sopenharmony_ci	.name = "maple",
7858c2ecf20Sopenharmony_ci	.match = maple_match_bus_driver,
7868c2ecf20Sopenharmony_ci	.uevent = maple_bus_uevent,
7878c2ecf20Sopenharmony_ci};
7888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(maple_bus_type);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic struct device maple_bus = {
7918c2ecf20Sopenharmony_ci	.init_name = "maple",
7928c2ecf20Sopenharmony_ci	.release = maple_bus_release,
7938c2ecf20Sopenharmony_ci};
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic int __init maple_bus_init(void)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	int retval, i;
7988c2ecf20Sopenharmony_ci	struct maple_device *mdev[MAPLE_PORTS];
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	__raw_writel(0, MAPLE_ENABLE);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	retval = device_register(&maple_bus);
8038c2ecf20Sopenharmony_ci	if (retval)
8048c2ecf20Sopenharmony_ci		goto cleanup;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	retval = bus_register(&maple_bus_type);
8078c2ecf20Sopenharmony_ci	if (retval)
8088c2ecf20Sopenharmony_ci		goto cleanup_device;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	retval = driver_register(&maple_unsupported_device.drv);
8118c2ecf20Sopenharmony_ci	if (retval)
8128c2ecf20Sopenharmony_ci		goto cleanup_bus;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* allocate memory for maple bus dma */
8158c2ecf20Sopenharmony_ci	retval = maple_get_dma_buffer();
8168c2ecf20Sopenharmony_ci	if (retval) {
8178c2ecf20Sopenharmony_ci		dev_err(&maple_bus, "failed to allocate DMA buffers\n");
8188c2ecf20Sopenharmony_ci		goto cleanup_basic;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/* set up DMA interrupt handler */
8228c2ecf20Sopenharmony_ci	retval = maple_set_dma_interrupt_handler();
8238c2ecf20Sopenharmony_ci	if (retval) {
8248c2ecf20Sopenharmony_ci		dev_err(&maple_bus, "bus failed to grab maple "
8258c2ecf20Sopenharmony_ci			"DMA IRQ\n");
8268c2ecf20Sopenharmony_ci		goto cleanup_dma;
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* set up VBLANK interrupt handler */
8308c2ecf20Sopenharmony_ci	retval = maple_set_vblank_interrupt_handler();
8318c2ecf20Sopenharmony_ci	if (retval) {
8328c2ecf20Sopenharmony_ci		dev_err(&maple_bus, "bus failed to grab VBLANK IRQ\n");
8338c2ecf20Sopenharmony_ci		goto cleanup_irq;
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	maple_queue_cache = KMEM_CACHE(maple_buffer, SLAB_HWCACHE_ALIGN);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (!maple_queue_cache) {
8398c2ecf20Sopenharmony_ci		retval = -ENOMEM;
8408c2ecf20Sopenharmony_ci		goto cleanup_bothirqs;
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&maple_waitq);
8448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&maple_sentq);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* setup maple ports */
8478c2ecf20Sopenharmony_ci	for (i = 0; i < MAPLE_PORTS; i++) {
8488c2ecf20Sopenharmony_ci		checked[i] = false;
8498c2ecf20Sopenharmony_ci		empty[i] = false;
8508c2ecf20Sopenharmony_ci		mdev[i] = maple_alloc_dev(i, 0);
8518c2ecf20Sopenharmony_ci		if (!mdev[i]) {
8528c2ecf20Sopenharmony_ci			while (i-- > 0)
8538c2ecf20Sopenharmony_ci				maple_free_dev(mdev[i]);
8548c2ecf20Sopenharmony_ci			retval = -ENOMEM;
8558c2ecf20Sopenharmony_ci			goto cleanup_cache;
8568c2ecf20Sopenharmony_ci		}
8578c2ecf20Sopenharmony_ci		baseunits[i] = mdev[i];
8588c2ecf20Sopenharmony_ci		atomic_set(&mdev[i]->busy, 1);
8598c2ecf20Sopenharmony_ci		maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL);
8608c2ecf20Sopenharmony_ci		subdevice_map[i] = 0;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	maple_pnp_time = jiffies + HZ;
8648c2ecf20Sopenharmony_ci	/* prepare initial queue */
8658c2ecf20Sopenharmony_ci	maple_send();
8668c2ecf20Sopenharmony_ci	dev_info(&maple_bus, "bus core now registered\n");
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	return 0;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cicleanup_cache:
8718c2ecf20Sopenharmony_ci	kmem_cache_destroy(maple_queue_cache);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cicleanup_bothirqs:
8748c2ecf20Sopenharmony_ci	free_irq(HW_EVENT_VSYNC, 0);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_cicleanup_irq:
8778c2ecf20Sopenharmony_ci	free_irq(HW_EVENT_MAPLE_DMA, 0);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_cicleanup_dma:
8808c2ecf20Sopenharmony_ci	free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cicleanup_basic:
8838c2ecf20Sopenharmony_ci	driver_unregister(&maple_unsupported_device.drv);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cicleanup_bus:
8868c2ecf20Sopenharmony_ci	bus_unregister(&maple_bus_type);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cicleanup_device:
8898c2ecf20Sopenharmony_ci	device_unregister(&maple_bus);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cicleanup:
8928c2ecf20Sopenharmony_ci	printk(KERN_ERR "Maple bus registration failed\n");
8938c2ecf20Sopenharmony_ci	return retval;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci/* Push init to later to ensure hardware gets detected */
8968c2ecf20Sopenharmony_cifs_initcall(maple_bus_init);
897