mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-24 02:18:54 +00:00
0d831770b1
This extends the current SH DMA API somewhat to support a proper virtual channel abstraction, and also works to represent this through the driver model by giving each DMAC its own platform device. There's also a few other minor changes to support a few new CPU subtypes, and make TEI generation for the SH DMAC configurable. Signed-off-by: Paul Mundt <lethal@linux-sh.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
198 lines
4.4 KiB
C
198 lines
4.4 KiB
C
/*
|
|
* arch/sh/kernel/cpu/bus.c
|
|
*
|
|
* Virtual bus for SuperH.
|
|
*
|
|
* Copyright (C) 2004 Paul Mundt
|
|
*
|
|
* Shamelessly cloned from arch/arm/mach-omap/bus.c, which was written
|
|
* by:
|
|
*
|
|
* Copyright (C) 2003 - 2004 Nokia Corporation
|
|
* Written by Tony Lindgren <tony@atomide.com>
|
|
* Portions of code based on sa1111.c.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/device.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <asm/bus-sh.h>
|
|
|
|
static int sh_bus_match(struct device *dev, struct device_driver *drv)
|
|
{
|
|
struct sh_driver *shdrv = to_sh_driver(drv);
|
|
struct sh_dev *shdev = to_sh_dev(dev);
|
|
|
|
return shdev->dev_id == shdrv->dev_id;
|
|
}
|
|
|
|
static int sh_bus_suspend(struct device *dev, pm_message_t state)
|
|
{
|
|
struct sh_dev *shdev = to_sh_dev(dev);
|
|
struct sh_driver *shdrv = to_sh_driver(dev->driver);
|
|
|
|
if (shdrv && shdrv->suspend)
|
|
return shdrv->suspend(shdev, state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sh_bus_resume(struct device *dev)
|
|
{
|
|
struct sh_dev *shdev = to_sh_dev(dev);
|
|
struct sh_driver *shdrv = to_sh_driver(dev->driver);
|
|
|
|
if (shdrv && shdrv->resume)
|
|
return shdrv->resume(shdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sh_device_probe(struct device *dev)
|
|
{
|
|
struct sh_dev *shdev = to_sh_dev(dev);
|
|
struct sh_driver *shdrv = to_sh_driver(dev->driver);
|
|
|
|
if (shdrv && shdrv->probe)
|
|
return shdrv->probe(shdev);
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int sh_device_remove(struct device *dev)
|
|
{
|
|
struct sh_dev *shdev = to_sh_dev(dev);
|
|
struct sh_driver *shdrv = to_sh_driver(dev->driver);
|
|
|
|
if (shdrv && shdrv->remove)
|
|
return shdrv->remove(shdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct device sh_bus_devices[SH_NR_BUSES] = {
|
|
{
|
|
.bus_id = SH_BUS_NAME_VIRT,
|
|
},
|
|
};
|
|
|
|
struct bus_type sh_bus_types[SH_NR_BUSES] = {
|
|
{
|
|
.name = SH_BUS_NAME_VIRT,
|
|
.match = sh_bus_match,
|
|
.probe = sh_bus_probe,
|
|
.remove = sh_bus_remove,
|
|
.suspend = sh_bus_suspend,
|
|
.resume = sh_bus_resume,
|
|
},
|
|
};
|
|
|
|
int sh_device_register(struct sh_dev *dev)
|
|
{
|
|
if (!dev)
|
|
return -EINVAL;
|
|
|
|
if (dev->bus_id < 0 || dev->bus_id >= SH_NR_BUSES) {
|
|
printk(KERN_ERR "%s: bus_id invalid: %s bus: %d\n",
|
|
__FUNCTION__, dev->name, dev->bus_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev->dev.parent = &sh_bus_devices[dev->bus_id];
|
|
dev->dev.bus = &sh_bus_types[dev->bus_id];
|
|
|
|
/* This is needed for USB OHCI to work */
|
|
if (dev->dma_mask)
|
|
dev->dev.dma_mask = dev->dma_mask;
|
|
if (dev->coherent_dma_mask)
|
|
dev->dev.coherent_dma_mask = dev->coherent_dma_mask;
|
|
|
|
snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u",
|
|
dev->name, dev->dev_id);
|
|
|
|
printk(KERN_INFO "Registering SH device '%s'. Parent at %s\n",
|
|
dev->dev.bus_id, dev->dev.parent->bus_id);
|
|
|
|
return device_register(&dev->dev);
|
|
}
|
|
|
|
void sh_device_unregister(struct sh_dev *dev)
|
|
{
|
|
device_unregister(&dev->dev);
|
|
}
|
|
|
|
int sh_driver_register(struct sh_driver *drv)
|
|
{
|
|
if (!drv)
|
|
return -EINVAL;
|
|
|
|
if (drv->bus_id < 0 || drv->bus_id >= SH_NR_BUSES) {
|
|
printk(KERN_ERR "%s: bus_id invalid: bus: %d device %d\n",
|
|
__FUNCTION__, drv->bus_id, drv->dev_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
drv->drv.bus = &sh_bus_types[drv->bus_id];
|
|
|
|
return driver_register(&drv->drv);
|
|
}
|
|
|
|
void sh_driver_unregister(struct sh_driver *drv)
|
|
{
|
|
driver_unregister(&drv->drv);
|
|
}
|
|
|
|
static int __init sh_bus_init(void)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
for (i = 0; i < SH_NR_BUSES; i++) {
|
|
ret = device_register(&sh_bus_devices[i]);
|
|
if (ret != 0) {
|
|
printk(KERN_ERR "Unable to register bus device %s\n",
|
|
sh_bus_devices[i].bus_id);
|
|
continue;
|
|
}
|
|
|
|
ret = bus_register(&sh_bus_types[i]);
|
|
if (ret != 0) {
|
|
printk(KERN_ERR "Unable to register bus %s\n",
|
|
sh_bus_types[i].name);
|
|
device_unregister(&sh_bus_devices[i]);
|
|
}
|
|
}
|
|
|
|
printk(KERN_INFO "SH Virtual Bus initialized\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit sh_bus_exit(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SH_NR_BUSES; i++) {
|
|
bus_unregister(&sh_bus_types[i]);
|
|
device_unregister(&sh_bus_devices[i]);
|
|
}
|
|
}
|
|
|
|
module_init(sh_bus_init);
|
|
module_exit(sh_bus_exit);
|
|
|
|
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
|
|
MODULE_DESCRIPTION("SH Virtual Bus");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
EXPORT_SYMBOL(sh_bus_types);
|
|
EXPORT_SYMBOL(sh_device_register);
|
|
EXPORT_SYMBOL(sh_device_unregister);
|
|
EXPORT_SYMBOL(sh_driver_register);
|
|
EXPORT_SYMBOL(sh_driver_unregister);
|
|
|