📄 maple.c
字号:
/* * Core maple bus functionality * * Copyright (C) 2007 Adrian McMenamin * * Based on 2.4 code by: * * Copyright (C) 2000-2001 YAEGASHI Takeshi * Copyright (C) 2001 M. R. Brown * Copyright (C) 2001 Paul Mundt * * and others. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */#include <linux/init.h>#include <linux/kernel.h>#include <linux/device.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/io.h>#include <linux/slab.h>#include <linux/maple.h>#include <linux/dma-mapping.h>#include <asm/cacheflush.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/mach/dma.h>#include <asm/mach/sysasic.h>#include <asm/mach/maple.h>MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin");MODULE_DESCRIPTION("Maple bus driver for Dreamcast");MODULE_LICENSE("GPL v2");MODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}");static void maple_dma_handler(struct work_struct *work);static void maple_vblank_handler(struct work_struct *work);static DECLARE_WORK(maple_dma_process, maple_dma_handler);static DECLARE_WORK(maple_vblank_process, maple_vblank_handler);static LIST_HEAD(maple_waitq);static LIST_HEAD(maple_sentq);static DEFINE_MUTEX(maple_list_lock);static struct maple_driver maple_dummy_driver;static struct device maple_bus;static int subdevice_map[MAPLE_PORTS];static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr;static unsigned long maple_pnp_time;static int started, scanning, liststatus;static struct kmem_cache *maple_queue_cache;struct maple_device_specify { int port; int unit;};/** * maple_driver_register - register a device driver * automatically makes the driver bus a maple bus * @drv: the driver to be registered */int maple_driver_register(struct device_driver *drv){ if (!drv) return -EINVAL; drv->bus = &maple_bus_type; return driver_register(drv);}EXPORT_SYMBOL_GPL(maple_driver_register);/* set hardware registers to enable next round of dma */static void maplebus_dma_reset(void){ ctrl_outl(MAPLE_MAGIC, MAPLE_RESET); /* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */ ctrl_outl(1, MAPLE_TRIGTYPE); ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(50000), MAPLE_SPEED); ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); ctrl_outl(1, MAPLE_ENABLE);}/** * maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND * @dev: device responding * @callback: handler callback * @interval: interval in jiffies between callbacks * @function: the function code for the device */void maple_getcond_callback(struct maple_device *dev, void (*callback) (struct mapleq * mq), unsigned long interval, unsigned long function){ dev->callback = callback; dev->interval = interval; dev->function = cpu_to_be32(function); dev->when = jiffies;}EXPORT_SYMBOL_GPL(maple_getcond_callback);static int maple_dma_done(void){ return (ctrl_inl(MAPLE_STATE) & 1) == 0;}static void maple_release_device(struct device *dev){ if (dev->type) { kfree(dev->type->name); kfree(dev->type); }}/** * maple_add_packet - add a single instruction to the queue * @mq: instruction to add to waiting queue */void maple_add_packet(struct mapleq *mq){ mutex_lock(&maple_list_lock); list_add(&mq->list, &maple_waitq); mutex_unlock(&maple_list_lock);}EXPORT_SYMBOL_GPL(maple_add_packet);static struct mapleq *maple_allocq(struct maple_device *dev){ struct mapleq *mq; mq = kmalloc(sizeof(*mq), GFP_KERNEL); if (!mq) return NULL; mq->dev = dev; mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL); mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp); if (!mq->recvbuf) { kfree(mq); return NULL; } return mq;}static struct maple_device *maple_alloc_dev(int port, int unit){ struct maple_device *dev; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; dev->port = port; dev->unit = unit; dev->mq = maple_allocq(dev); if (!dev->mq) { kfree(dev); return NULL; } return dev;}static void maple_free_dev(struct maple_device *mdev){ if (!mdev) return; if (mdev->mq) { kmem_cache_free(maple_queue_cache, mdev->mq->recvbufdcsp); kfree(mdev->mq); } kfree(mdev);}/* process the command queue into a maple command block * terminating command has bit 32 of first long set to 0 */static void maple_build_block(struct mapleq *mq){ int port, unit, from, to, len; unsigned long *lsendbuf = mq->sendbuf; port = mq->dev->port & 3; unit = mq->dev->unit; len = mq->length; from = port << 6; to = (port << 6) | (unit > 0 ? (1 << (unit - 1)) & 0x1f : 0x20); *maple_lastptr &= 0x7fffffff; maple_lastptr = maple_sendptr; *maple_sendptr++ = (port << 16) | len | 0x80000000; *maple_sendptr++ = PHYSADDR(mq->recvbuf); *maple_sendptr++ = mq->command | (to << 8) | (from << 16) | (len << 24); while (len-- > 0) *maple_sendptr++ = *lsendbuf++;}/* build up command queue */static void maple_send(void){ int i; int maple_packets; struct mapleq *mq, *nmq; if (!list_empty(&maple_sentq)) return; if (list_empty(&maple_waitq) || !maple_dma_done()) return; maple_packets = 0; maple_sendptr = maple_lastptr = maple_sendbuf; list_for_each_entry_safe(mq, nmq, &maple_waitq, list) { maple_build_block(mq); list_move(&mq->list, &maple_sentq); if (maple_packets++ > MAPLE_MAXPACKETS) break; } if (maple_packets > 0) { for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++) dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE, PAGE_SIZE, DMA_BIDIRECTIONAL); }}static int attach_matching_maple_driver(struct device_driver *driver, void *devptr){ struct maple_driver *maple_drv; struct maple_device *mdev; mdev = devptr; maple_drv = to_maple_driver(driver); if (mdev->devinfo.function & be32_to_cpu(maple_drv->function)) { if (maple_drv->connect(mdev) == 0) { mdev->driver = maple_drv; return 1; } } return 0;}static void maple_detach_driver(struct maple_device *mdev){ if (!mdev) return; if (mdev->driver) { if (mdev->driver->disconnect) mdev->driver->disconnect(mdev); } mdev->driver = NULL; if (mdev->registered) { maple_release_device(&mdev->dev); device_unregister(&mdev->dev); } mdev->registered = 0; maple_free_dev(mdev);}/* process initial MAPLE_COMMAND_DEVINFO for each device or port */static void maple_attach_driver(struct maple_device *dev){ char *p; char *recvbuf; unsigned long function; int matched, retval; recvbuf = dev->mq->recvbuf; memcpy(&dev->devinfo, recvbuf + 4, sizeof(dev->devinfo)); memcpy(dev->product_name, dev->devinfo.product_name, 30); memcpy(dev->product_licence, dev->devinfo.product_licence, 60); dev->product_name[30] = '\0'; dev->product_licence[60] = '\0'; for (p = dev->product_name + 29; dev->product_name <= p; p--) if (*p == ' ') *p = '\0'; else break; for (p = dev->product_licence + 59; dev->product_licence <= p; p--) if (*p == ' ') *p = '\0'; else break; function = be32_to_cpu(dev->devinfo.function); if (function > 0x200) { /* Do this silently - as not a real device */ function = 0; dev->driver = &maple_dummy_driver; sprintf(dev->dev.bus_id, "%d:0.port", dev->port); } else { printk(KERN_INFO "Maple bus at (%d, %d): Connected function 0x%lX\n", dev->port, dev->unit, function); matched = bus_for_each_drv(&maple_bus_type, NULL, dev, attach_matching_maple_driver); if (matched == 0) { /* Driver does not exist yet */ printk(KERN_INFO "No maple driver found for this device\n"); dev->driver = &maple_dummy_driver; } sprintf(dev->dev.bus_id, "%d:0%d.%lX", dev->port, dev->unit, function); } dev->function = function; dev->dev.bus = &maple_bus_type; dev->dev.parent = &maple_bus; dev->dev.release = &maple_release_device; retval = device_register(&dev->dev); if (retval) { printk(KERN_INFO "Maple bus: Attempt to register device (%x, %x) failed.\n", dev->port, dev->unit); maple_free_dev(dev); } dev->registered = 1;}/* * if device has been registered for the given * port and unit then return 1 - allows identification * of which devices need to be attached or detached */static int detach_maple_device(struct device *device, void *portptr){ struct maple_device_specify *ds; struct maple_device *mdev; ds = portptr; mdev = to_maple_dev(device); if (mdev->port == ds->port && mdev->unit == ds->unit) return 1; return 0;}static int setup_maple_commands(struct device *device, void *ignored){ struct maple_device *maple_dev = to_maple_dev(device); if ((maple_dev->interval > 0) && time_after(jiffies, maple_dev->when)) { maple_dev->when = jiffies + maple_dev->interval; maple_dev->mq->command = MAPLE_COMMAND_GETCOND; maple_dev->mq->sendbuf = &maple_dev->function; maple_dev->mq->length = 1; maple_add_packet(maple_dev->mq); liststatus++; } else { if (time_after(jiffies, maple_pnp_time)) { maple_dev->mq->command = MAPLE_COMMAND_DEVINFO; maple_dev->mq->length = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -