mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-28 23:08:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
74
arch/sh/drivers/dma/Kconfig
Normal file
74
arch/sh/drivers/dma/Kconfig
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
menu "DMA support"
|
||||
|
||||
|
||||
config SH_DMA
|
||||
bool "SuperH on-chip DMA controller (DMAC) support"
|
||||
depends on CPU_SH3 || CPU_SH4
|
||||
default n
|
||||
|
||||
config SH_DMA_IRQ_MULTI
|
||||
bool
|
||||
depends on SH_DMA
|
||||
default y if CPU_SUBTYPE_SH7750 || CPU_SUBTYPE_SH7751 || \
|
||||
CPU_SUBTYPE_SH7750S || CPU_SUBTYPE_SH7750R || \
|
||||
CPU_SUBTYPE_SH7751R || CPU_SUBTYPE_SH7091 || \
|
||||
CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7780 || \
|
||||
CPU_SUBTYPE_SH7785 || CPU_SUBTYPE_SH7760
|
||||
|
||||
config SH_DMA_API
|
||||
depends on SH_DMA
|
||||
bool "SuperH DMA API support"
|
||||
default n
|
||||
help
|
||||
SH_DMA_API always enabled DMA API of used SuperH.
|
||||
If you want to use DMA ENGINE, you must not enable this.
|
||||
Please enable DMA_ENGINE and SH_DMAE.
|
||||
|
||||
config NR_ONCHIP_DMA_CHANNELS
|
||||
int
|
||||
depends on SH_DMA
|
||||
default "4" if CPU_SUBTYPE_SH7750 || CPU_SUBTYPE_SH7751 || \
|
||||
CPU_SUBTYPE_SH7750S || CPU_SUBTYPE_SH7091
|
||||
default "8" if CPU_SUBTYPE_SH7750R || CPU_SUBTYPE_SH7751R || \
|
||||
CPU_SUBTYPE_SH7760
|
||||
default "12" if CPU_SUBTYPE_SH7723 || CPU_SUBTYPE_SH7780 || \
|
||||
CPU_SUBTYPE_SH7785 || CPU_SUBTYPE_SH7724
|
||||
default "6"
|
||||
help
|
||||
This allows you to specify the number of channels that the on-chip
|
||||
DMAC supports. This will be 4 for SH7750/SH7751/Sh7750S/SH7091 and 8 for the
|
||||
SH7750R/SH7751R/SH7760, 12 for the SH7723/SH7780/SH7785/SH7724, default is 6.
|
||||
|
||||
config SH_DMABRG
|
||||
bool "SH7760 DMABRG support"
|
||||
depends on CPU_SUBTYPE_SH7760
|
||||
help
|
||||
The DMABRG does data transfers from main memory to Audio/USB units
|
||||
of the SH7760.
|
||||
Say Y if you want to use Audio/USB DMA on your SH7760 board.
|
||||
|
||||
config PVR2_DMA
|
||||
tristate "PowerVR 2 DMAC support"
|
||||
depends on SH_DREAMCAST && SH_DMA
|
||||
help
|
||||
Selecting this will enable support for the PVR2 DMA controller.
|
||||
As this chains off of the on-chip DMAC, that must also be
|
||||
enabled by default.
|
||||
|
||||
This is primarily used by the pvr2fb framebuffer driver for
|
||||
certain optimizations, but is not necessary for functionality.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
config G2_DMA
|
||||
tristate "G2 Bus DMA support"
|
||||
depends on SH_DREAMCAST
|
||||
select SH_DMA_API
|
||||
help
|
||||
This enables support for the DMA controller for the Dreamcast's
|
||||
G2 bus. Drivers that want this will generally enable this on
|
||||
their own.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
endmenu
|
||||
8
arch/sh/drivers/dma/Makefile
Normal file
8
arch/sh/drivers/dma/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for the SuperH DMA specific kernel interface routines under Linux.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SH_DMA_API) += dma-sh.o dma-api.o dma-sysfs.o
|
||||
obj-$(CONFIG_PVR2_DMA) += dma-pvr2.o
|
||||
obj-$(CONFIG_G2_DMA) += dma-g2.o
|
||||
obj-$(CONFIG_SH_DMABRG) += dmabrg.o
|
||||
432
arch/sh/drivers/dma/dma-api.c
Normal file
432
arch/sh/drivers/dma/dma-api.c
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* arch/sh/drivers/dma/dma-api.c
|
||||
*
|
||||
* SuperH-specific DMA management API
|
||||
*
|
||||
* Copyright (C) 2003, 2004, 2005 Paul Mundt
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
DEFINE_SPINLOCK(dma_spin_lock);
|
||||
static LIST_HEAD(registered_dmac_list);
|
||||
|
||||
struct dma_info *get_dma_info(unsigned int chan)
|
||||
{
|
||||
struct dma_info *info;
|
||||
|
||||
/*
|
||||
* Look for each DMAC's range to determine who the owner of
|
||||
* the channel is.
|
||||
*/
|
||||
list_for_each_entry(info, ®istered_dmac_list, list) {
|
||||
if ((chan < info->first_vchannel_nr) ||
|
||||
(chan >= info->first_vchannel_nr + info->nr_channels))
|
||||
continue;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(get_dma_info);
|
||||
|
||||
struct dma_info *get_dma_info_by_name(const char *dmac_name)
|
||||
{
|
||||
struct dma_info *info;
|
||||
|
||||
list_for_each_entry(info, ®istered_dmac_list, list) {
|
||||
if (dmac_name && (strcmp(dmac_name, info->name) != 0))
|
||||
continue;
|
||||
else
|
||||
return info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(get_dma_info_by_name);
|
||||
|
||||
static unsigned int get_nr_channels(void)
|
||||
{
|
||||
struct dma_info *info;
|
||||
unsigned int nr = 0;
|
||||
|
||||
if (unlikely(list_empty(®istered_dmac_list)))
|
||||
return nr;
|
||||
|
||||
list_for_each_entry(info, ®istered_dmac_list, list)
|
||||
nr += info->nr_channels;
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
struct dma_channel *get_dma_channel(unsigned int chan)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel;
|
||||
int i;
|
||||
|
||||
if (unlikely(!info))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
for (i = 0; i < info->nr_channels; i++) {
|
||||
channel = &info->channels[i];
|
||||
if (channel->vchan == chan)
|
||||
return channel;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(get_dma_channel);
|
||||
|
||||
int get_dma_residue(unsigned int chan)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel = get_dma_channel(chan);
|
||||
|
||||
if (info->ops->get_residue)
|
||||
return info->ops->get_residue(channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(get_dma_residue);
|
||||
|
||||
static int search_cap(const char **haystack, const char *needle)
|
||||
{
|
||||
const char **p;
|
||||
|
||||
for (p = haystack; *p; p++)
|
||||
if (strcmp(*p, needle) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* request_dma_bycap - Allocate a DMA channel based on its capabilities
|
||||
* @dmac: List of DMA controllers to search
|
||||
* @caps: List of capabilities
|
||||
*
|
||||
* Search all channels of all DMA controllers to find a channel which
|
||||
* matches the requested capabilities. The result is the channel
|
||||
* number if a match is found, or %-ENODEV if no match is found.
|
||||
*
|
||||
* Note that not all DMA controllers export capabilities, in which
|
||||
* case they can never be allocated using this API, and so
|
||||
* request_dma() must be used specifying the channel number.
|
||||
*/
|
||||
int request_dma_bycap(const char **dmac, const char **caps, const char *dev_id)
|
||||
{
|
||||
unsigned int found = 0;
|
||||
struct dma_info *info;
|
||||
const char **p;
|
||||
int i;
|
||||
|
||||
BUG_ON(!dmac || !caps);
|
||||
|
||||
list_for_each_entry(info, ®istered_dmac_list, list)
|
||||
if (strcmp(*dmac, info->name) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < info->nr_channels; i++) {
|
||||
struct dma_channel *channel = &info->channels[i];
|
||||
|
||||
if (unlikely(!channel->caps))
|
||||
continue;
|
||||
|
||||
for (p = caps; *p; p++) {
|
||||
if (!search_cap(channel->caps, *p))
|
||||
break;
|
||||
if (request_dma(channel->chan, dev_id) == 0)
|
||||
return channel->chan;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(request_dma_bycap);
|
||||
|
||||
int dmac_search_free_channel(const char *dev_id)
|
||||
{
|
||||
struct dma_channel *channel = { 0 };
|
||||
struct dma_info *info = get_dma_info(0);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < info->nr_channels; i++) {
|
||||
channel = &info->channels[i];
|
||||
if (unlikely(!channel))
|
||||
return -ENODEV;
|
||||
|
||||
if (atomic_read(&channel->busy) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (info->ops->request) {
|
||||
int result = info->ops->request(channel);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
atomic_set(&channel->busy, 1);
|
||||
return channel->chan;
|
||||
}
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int request_dma(unsigned int chan, const char *dev_id)
|
||||
{
|
||||
struct dma_channel *channel = { 0 };
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
int result;
|
||||
|
||||
channel = get_dma_channel(chan);
|
||||
if (atomic_xchg(&channel->busy, 1))
|
||||
return -EBUSY;
|
||||
|
||||
strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
|
||||
|
||||
if (info->ops->request) {
|
||||
result = info->ops->request(channel);
|
||||
if (result)
|
||||
atomic_set(&channel->busy, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(request_dma);
|
||||
|
||||
void free_dma(unsigned int chan)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel = get_dma_channel(chan);
|
||||
|
||||
if (info->ops->free)
|
||||
info->ops->free(channel);
|
||||
|
||||
atomic_set(&channel->busy, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(free_dma);
|
||||
|
||||
void dma_wait_for_completion(unsigned int chan)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel = get_dma_channel(chan);
|
||||
|
||||
if (channel->flags & DMA_TEI_CAPABLE) {
|
||||
wait_event(channel->wait_queue,
|
||||
(info->ops->get_residue(channel) == 0));
|
||||
return;
|
||||
}
|
||||
|
||||
while (info->ops->get_residue(channel))
|
||||
cpu_relax();
|
||||
}
|
||||
EXPORT_SYMBOL(dma_wait_for_completion);
|
||||
|
||||
int register_chan_caps(const char *dmac, struct dma_chan_caps *caps)
|
||||
{
|
||||
struct dma_info *info;
|
||||
unsigned int found = 0;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(info, ®istered_dmac_list, list)
|
||||
if (strcmp(dmac, info->name) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (unlikely(!found))
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < info->nr_channels; i++, caps++) {
|
||||
struct dma_channel *channel;
|
||||
|
||||
if ((info->first_channel_nr + i) != caps->ch_num)
|
||||
return -EINVAL;
|
||||
|
||||
channel = &info->channels[i];
|
||||
channel->caps = caps->caplist;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(register_chan_caps);
|
||||
|
||||
void dma_configure_channel(unsigned int chan, unsigned long flags)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel = get_dma_channel(chan);
|
||||
|
||||
if (info->ops->configure)
|
||||
info->ops->configure(channel, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_configure_channel);
|
||||
|
||||
int dma_xfer(unsigned int chan, unsigned long from,
|
||||
unsigned long to, size_t size, unsigned int mode)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel = get_dma_channel(chan);
|
||||
|
||||
channel->sar = from;
|
||||
channel->dar = to;
|
||||
channel->count = size;
|
||||
channel->mode = mode;
|
||||
|
||||
return info->ops->xfer(channel);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_xfer);
|
||||
|
||||
int dma_extend(unsigned int chan, unsigned long op, void *param)
|
||||
{
|
||||
struct dma_info *info = get_dma_info(chan);
|
||||
struct dma_channel *channel = get_dma_channel(chan);
|
||||
|
||||
if (info->ops->extend)
|
||||
return info->ops->extend(channel, op, param);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_extend);
|
||||
|
||||
static int dma_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct dma_info *info = v;
|
||||
|
||||
if (list_empty(®istered_dmac_list))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Iterate over each registered DMAC
|
||||
*/
|
||||
list_for_each_entry(info, ®istered_dmac_list, list) {
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Iterate over each channel
|
||||
*/
|
||||
for (i = 0; i < info->nr_channels; i++) {
|
||||
struct dma_channel *channel = info->channels + i;
|
||||
|
||||
if (!(channel->flags & DMA_CONFIGURED))
|
||||
continue;
|
||||
|
||||
seq_printf(m, "%2d: %14s %s\n", i,
|
||||
info->name, channel->dev_id);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dma_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations dma_proc_fops = {
|
||||
.open = dma_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int register_dmac(struct dma_info *info)
|
||||
{
|
||||
unsigned int total_channels, i;
|
||||
|
||||
INIT_LIST_HEAD(&info->list);
|
||||
|
||||
printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
|
||||
info->name, info->nr_channels, info->nr_channels > 1 ? "s" : "");
|
||||
|
||||
BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
|
||||
|
||||
info->pdev = platform_device_register_simple(info->name, -1,
|
||||
NULL, 0);
|
||||
if (IS_ERR(info->pdev))
|
||||
return PTR_ERR(info->pdev);
|
||||
|
||||
/*
|
||||
* Don't touch pre-configured channels
|
||||
*/
|
||||
if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
|
||||
unsigned int size;
|
||||
|
||||
size = sizeof(struct dma_channel) * info->nr_channels;
|
||||
|
||||
info->channels = kzalloc(size, GFP_KERNEL);
|
||||
if (!info->channels)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
total_channels = get_nr_channels();
|
||||
info->first_vchannel_nr = total_channels;
|
||||
for (i = 0; i < info->nr_channels; i++) {
|
||||
struct dma_channel *chan = &info->channels[i];
|
||||
|
||||
atomic_set(&chan->busy, 0);
|
||||
|
||||
chan->chan = info->first_channel_nr + i;
|
||||
chan->vchan = info->first_channel_nr + i + total_channels;
|
||||
|
||||
memcpy(chan->dev_id, "Unused", 7);
|
||||
|
||||
if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
|
||||
chan->flags |= DMA_TEI_CAPABLE;
|
||||
|
||||
init_waitqueue_head(&chan->wait_queue);
|
||||
dma_create_sysfs_files(chan, info);
|
||||
}
|
||||
|
||||
list_add(&info->list, ®istered_dmac_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(register_dmac);
|
||||
|
||||
void unregister_dmac(struct dma_info *info)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < info->nr_channels; i++)
|
||||
dma_remove_sysfs_files(info->channels + i, info);
|
||||
|
||||
if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
|
||||
kfree(info->channels);
|
||||
|
||||
list_del(&info->list);
|
||||
platform_device_unregister(info->pdev);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_dmac);
|
||||
|
||||
static int __init dma_api_init(void)
|
||||
{
|
||||
printk(KERN_NOTICE "DMA: Registering DMA API.\n");
|
||||
return proc_create("dma", 0, NULL, &dma_proc_fops) ? 0 : -ENOMEM;
|
||||
}
|
||||
subsys_initcall(dma_api_init);
|
||||
|
||||
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
|
||||
MODULE_DESCRIPTION("DMA API for SuperH");
|
||||
MODULE_LICENSE("GPL");
|
||||
200
arch/sh/drivers/dma/dma-g2.c
Normal file
200
arch/sh/drivers/dma/dma-g2.c
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* arch/sh/drivers/dma/dma-g2.c
|
||||
*
|
||||
* G2 bus DMA support
|
||||
*
|
||||
* Copyright (C) 2003 - 2006 Paul Mundt
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <mach/sysasic.h>
|
||||
#include <mach/dma.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
struct g2_channel {
|
||||
unsigned long g2_addr; /* G2 bus address */
|
||||
unsigned long root_addr; /* Root bus (SH-4) address */
|
||||
unsigned long size; /* Size (in bytes), 32-byte aligned */
|
||||
unsigned long direction; /* Transfer direction */
|
||||
unsigned long ctrl; /* Transfer control */
|
||||
unsigned long chan_enable; /* Channel enable */
|
||||
unsigned long xfer_enable; /* Transfer enable */
|
||||
unsigned long xfer_stat; /* Transfer status */
|
||||
} __attribute__ ((aligned(32)));
|
||||
|
||||
struct g2_status {
|
||||
unsigned long g2_addr;
|
||||
unsigned long root_addr;
|
||||
unsigned long size;
|
||||
unsigned long status;
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
struct g2_dma_info {
|
||||
struct g2_channel channel[G2_NR_DMA_CHANNELS];
|
||||
unsigned long pad1[G2_NR_DMA_CHANNELS];
|
||||
unsigned long wait_state;
|
||||
unsigned long pad2[10];
|
||||
unsigned long magic;
|
||||
struct g2_status status[G2_NR_DMA_CHANNELS];
|
||||
} __attribute__ ((aligned(256)));
|
||||
|
||||
static volatile struct g2_dma_info *g2_dma = (volatile struct g2_dma_info *)0xa05f7800;
|
||||
|
||||
#define g2_bytes_remaining(i) \
|
||||
((g2_dma->channel[i].size - \
|
||||
g2_dma->status[i].size) & 0x0fffffff)
|
||||
|
||||
static irqreturn_t g2_dma_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < G2_NR_DMA_CHANNELS; i++) {
|
||||
if (g2_dma->status[i].status & 0x20000000) {
|
||||
unsigned int bytes = g2_bytes_remaining(i);
|
||||
|
||||
if (likely(bytes == 0)) {
|
||||
struct dma_info *info = dev_id;
|
||||
struct dma_channel *chan = info->channels + i;
|
||||
|
||||
wake_up(&chan->wait_queue);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int g2_enable_dma(struct dma_channel *chan)
|
||||
{
|
||||
unsigned int chan_nr = chan->chan;
|
||||
|
||||
g2_dma->channel[chan_nr].chan_enable = 1;
|
||||
g2_dma->channel[chan_nr].xfer_enable = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int g2_disable_dma(struct dma_channel *chan)
|
||||
{
|
||||
unsigned int chan_nr = chan->chan;
|
||||
|
||||
g2_dma->channel[chan_nr].chan_enable = 0;
|
||||
g2_dma->channel[chan_nr].xfer_enable = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int g2_xfer_dma(struct dma_channel *chan)
|
||||
{
|
||||
unsigned int chan_nr = chan->chan;
|
||||
|
||||
if (chan->sar & 31) {
|
||||
printk("g2dma: unaligned source 0x%lx\n", chan->sar);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->dar & 31) {
|
||||
printk("g2dma: unaligned dest 0x%lx\n", chan->dar);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Align the count */
|
||||
if (chan->count & 31)
|
||||
chan->count = (chan->count + (32 - 1)) & ~(32 - 1);
|
||||
|
||||
/* Fixup destination */
|
||||
chan->dar += 0xa0800000;
|
||||
|
||||
/* Fixup direction */
|
||||
chan->mode = !chan->mode;
|
||||
|
||||
flush_icache_range((unsigned long)chan->sar, chan->count);
|
||||
|
||||
g2_disable_dma(chan);
|
||||
|
||||
g2_dma->channel[chan_nr].g2_addr = chan->dar & 0x1fffffe0;
|
||||
g2_dma->channel[chan_nr].root_addr = chan->sar & 0x1fffffe0;
|
||||
g2_dma->channel[chan_nr].size = (chan->count & ~31) | 0x80000000;
|
||||
g2_dma->channel[chan_nr].direction = chan->mode;
|
||||
|
||||
/*
|
||||
* bit 0 - ???
|
||||
* bit 1 - if set, generate a hardware event on transfer completion
|
||||
* bit 2 - ??? something to do with suspend?
|
||||
*/
|
||||
g2_dma->channel[chan_nr].ctrl = 5; /* ?? */
|
||||
|
||||
g2_enable_dma(chan);
|
||||
|
||||
/* debug cruft */
|
||||
pr_debug("count, sar, dar, mode, ctrl, chan, xfer: %ld, 0x%08lx, "
|
||||
"0x%08lx, %ld, %ld, %ld, %ld\n",
|
||||
g2_dma->channel[chan_nr].size,
|
||||
g2_dma->channel[chan_nr].root_addr,
|
||||
g2_dma->channel[chan_nr].g2_addr,
|
||||
g2_dma->channel[chan_nr].direction,
|
||||
g2_dma->channel[chan_nr].ctrl,
|
||||
g2_dma->channel[chan_nr].chan_enable,
|
||||
g2_dma->channel[chan_nr].xfer_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int g2_get_residue(struct dma_channel *chan)
|
||||
{
|
||||
return g2_bytes_remaining(chan->chan);
|
||||
}
|
||||
|
||||
static struct dma_ops g2_dma_ops = {
|
||||
.xfer = g2_xfer_dma,
|
||||
.get_residue = g2_get_residue,
|
||||
};
|
||||
|
||||
static struct dma_info g2_dma_info = {
|
||||
.name = "g2_dmac",
|
||||
.nr_channels = 4,
|
||||
.ops = &g2_dma_ops,
|
||||
.flags = DMAC_CHANNELS_TEI_CAPABLE,
|
||||
};
|
||||
|
||||
static int __init g2_dma_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = request_irq(HW_EVENT_G2_DMA, g2_dma_interrupt, 0,
|
||||
"g2 DMA handler", &g2_dma_info);
|
||||
if (unlikely(ret))
|
||||
return -EINVAL;
|
||||
|
||||
/* Magic */
|
||||
g2_dma->wait_state = 27;
|
||||
g2_dma->magic = 0x4659404f;
|
||||
|
||||
ret = register_dmac(&g2_dma_info);
|
||||
if (unlikely(ret != 0))
|
||||
free_irq(HW_EVENT_G2_DMA, &g2_dma_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit g2_dma_exit(void)
|
||||
{
|
||||
free_irq(HW_EVENT_G2_DMA, &g2_dma_info);
|
||||
unregister_dmac(&g2_dma_info);
|
||||
}
|
||||
|
||||
subsys_initcall(g2_dma_init);
|
||||
module_exit(g2_dma_exit);
|
||||
|
||||
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
|
||||
MODULE_DESCRIPTION("G2 bus DMA driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
108
arch/sh/drivers/dma/dma-pvr2.c
Normal file
108
arch/sh/drivers/dma/dma-pvr2.c
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* arch/sh/drivers/dma/dma-pvr2.c
|
||||
*
|
||||
* NEC PowerVR 2 (Dreamcast) DMA support
|
||||
*
|
||||
* Copyright (C) 2003, 2004 Paul Mundt
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <mach/sysasic.h>
|
||||
#include <mach/dma.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned int xfer_complete;
|
||||
static int count;
|
||||
|
||||
static irqreturn_t pvr2_dma_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
if (get_dma_residue(PVR2_CASCADE_CHAN)) {
|
||||
printk(KERN_WARNING "DMA: SH DMAC did not complete transfer "
|
||||
"on channel %d, waiting..\n", PVR2_CASCADE_CHAN);
|
||||
dma_wait_for_completion(PVR2_CASCADE_CHAN);
|
||||
}
|
||||
|
||||
if (count++ < 10)
|
||||
pr_debug("Got a pvr2 dma interrupt for channel %d\n",
|
||||
irq - HW_EVENT_PVR2_DMA);
|
||||
|
||||
xfer_complete = 1;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pvr2_request_dma(struct dma_channel *chan)
|
||||
{
|
||||
if (__raw_readl(PVR2_DMA_MODE) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
__raw_writel(0, PVR2_DMA_LMMODE0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvr2_get_dma_residue(struct dma_channel *chan)
|
||||
{
|
||||
return xfer_complete == 0;
|
||||
}
|
||||
|
||||
static int pvr2_xfer_dma(struct dma_channel *chan)
|
||||
{
|
||||
if (chan->sar || !chan->dar)
|
||||
return -EINVAL;
|
||||
|
||||
xfer_complete = 0;
|
||||
|
||||
__raw_writel(chan->dar, PVR2_DMA_ADDR);
|
||||
__raw_writel(chan->count, PVR2_DMA_COUNT);
|
||||
__raw_writel(chan->mode & DMA_MODE_MASK, PVR2_DMA_MODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irqaction pvr2_dma_irq = {
|
||||
.name = "pvr2 DMA handler",
|
||||
.handler = pvr2_dma_interrupt,
|
||||
};
|
||||
|
||||
static struct dma_ops pvr2_dma_ops = {
|
||||
.request = pvr2_request_dma,
|
||||
.get_residue = pvr2_get_dma_residue,
|
||||
.xfer = pvr2_xfer_dma,
|
||||
};
|
||||
|
||||
static struct dma_info pvr2_dma_info = {
|
||||
.name = "pvr2_dmac",
|
||||
.nr_channels = 1,
|
||||
.ops = &pvr2_dma_ops,
|
||||
.flags = DMAC_CHANNELS_TEI_CAPABLE,
|
||||
};
|
||||
|
||||
static int __init pvr2_dma_init(void)
|
||||
{
|
||||
setup_irq(HW_EVENT_PVR2_DMA, &pvr2_dma_irq);
|
||||
request_dma(PVR2_CASCADE_CHAN, "pvr2 cascade");
|
||||
|
||||
return register_dmac(&pvr2_dma_info);
|
||||
}
|
||||
|
||||
static void __exit pvr2_dma_exit(void)
|
||||
{
|
||||
free_dma(PVR2_CASCADE_CHAN);
|
||||
free_irq(HW_EVENT_PVR2_DMA, 0);
|
||||
unregister_dmac(&pvr2_dma_info);
|
||||
}
|
||||
|
||||
subsys_initcall(pvr2_dma_init);
|
||||
module_exit(pvr2_dma_exit);
|
||||
|
||||
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
|
||||
MODULE_DESCRIPTION("NEC PowerVR 2 DMA driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
417
arch/sh/drivers/dma/dma-sh.c
Normal file
417
arch/sh/drivers/dma/dma-sh.c
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* arch/sh/drivers/dma/dma-sh.c
|
||||
*
|
||||
* SuperH On-chip DMAC Support
|
||||
*
|
||||
* Copyright (C) 2000 Takashi YOSHII
|
||||
* Copyright (C) 2003, 2004 Paul Mundt
|
||||
* Copyright (C) 2005 Andriy Skulysh
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <mach-dreamcast/mach/dma.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/dma-register.h>
|
||||
#include <cpu/dma-register.h>
|
||||
#include <cpu/dma.h>
|
||||
|
||||
/*
|
||||
* Define the default configuration for dual address memory-memory transfer.
|
||||
* The 0x400 value represents auto-request, external->external.
|
||||
*/
|
||||
#define RS_DUAL (DM_INC | SM_INC | RS_AUTO | TS_INDEX2VAL(XMIT_SZ_32BIT))
|
||||
|
||||
static unsigned long dma_find_base(unsigned int chan)
|
||||
{
|
||||
unsigned long base = SH_DMAC_BASE0;
|
||||
|
||||
#ifdef SH_DMAC_BASE1
|
||||
if (chan >= 6)
|
||||
base = SH_DMAC_BASE1;
|
||||
#endif
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
static unsigned long dma_base_addr(unsigned int chan)
|
||||
{
|
||||
unsigned long base = dma_find_base(chan);
|
||||
|
||||
/* Normalize offset calculation */
|
||||
if (chan >= 9)
|
||||
chan -= 6;
|
||||
if (chan >= 4)
|
||||
base += 0x10;
|
||||
|
||||
return base + (chan * 0x10);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SH_DMA_IRQ_MULTI
|
||||
static inline unsigned int get_dmte_irq(unsigned int chan)
|
||||
{
|
||||
return chan >= 6 ? DMTE6_IRQ : DMTE0_IRQ;
|
||||
}
|
||||
#else
|
||||
|
||||
static unsigned int dmte_irq_map[] = {
|
||||
DMTE0_IRQ, DMTE0_IRQ + 1, DMTE0_IRQ + 2, DMTE0_IRQ + 3,
|
||||
|
||||
#ifdef DMTE4_IRQ
|
||||
DMTE4_IRQ, DMTE4_IRQ + 1,
|
||||
#endif
|
||||
|
||||
#ifdef DMTE6_IRQ
|
||||
DMTE6_IRQ, DMTE6_IRQ + 1,
|
||||
#endif
|
||||
|
||||
#ifdef DMTE8_IRQ
|
||||
DMTE8_IRQ, DMTE9_IRQ, DMTE10_IRQ, DMTE11_IRQ,
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline unsigned int get_dmte_irq(unsigned int chan)
|
||||
{
|
||||
return dmte_irq_map[chan];
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We determine the correct shift size based off of the CHCR transmit size
|
||||
* for the given channel. Since we know that it will take:
|
||||
*
|
||||
* info->count >> ts_shift[transmit_size]
|
||||
*
|
||||
* iterations to complete the transfer.
|
||||
*/
|
||||
static unsigned int ts_shift[] = TS_SHIFT;
|
||||
|
||||
static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
|
||||
{
|
||||
u32 chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
|
||||
int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
|
||||
((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
|
||||
|
||||
return ts_shift[cnt];
|
||||
}
|
||||
|
||||
/*
|
||||
* The transfer end interrupt must read the chcr register to end the
|
||||
* hardware interrupt active condition.
|
||||
* Besides that it needs to waken any waiting process, which should handle
|
||||
* setting up the next transfer.
|
||||
*/
|
||||
static irqreturn_t dma_tei(int irq, void *dev_id)
|
||||
{
|
||||
struct dma_channel *chan = dev_id;
|
||||
u32 chcr;
|
||||
|
||||
chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
|
||||
|
||||
if (!(chcr & CHCR_TE))
|
||||
return IRQ_NONE;
|
||||
|
||||
chcr &= ~(CHCR_IE | CHCR_DE);
|
||||
__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
|
||||
|
||||
wake_up(&chan->wait_queue);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sh_dmac_request_dma(struct dma_channel *chan)
|
||||
{
|
||||
if (unlikely(!(chan->flags & DMA_TEI_CAPABLE)))
|
||||
return 0;
|
||||
|
||||
return request_irq(get_dmte_irq(chan->chan), dma_tei, IRQF_SHARED,
|
||||
chan->dev_id, chan);
|
||||
}
|
||||
|
||||
static void sh_dmac_free_dma(struct dma_channel *chan)
|
||||
{
|
||||
free_irq(get_dmte_irq(chan->chan), chan);
|
||||
}
|
||||
|
||||
static int
|
||||
sh_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr)
|
||||
{
|
||||
if (!chcr)
|
||||
chcr = RS_DUAL | CHCR_IE;
|
||||
|
||||
if (chcr & CHCR_IE) {
|
||||
chcr &= ~CHCR_IE;
|
||||
chan->flags |= DMA_TEI_CAPABLE;
|
||||
} else {
|
||||
chan->flags &= ~DMA_TEI_CAPABLE;
|
||||
}
|
||||
|
||||
__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
|
||||
|
||||
chan->flags |= DMA_CONFIGURED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_dmac_enable_dma(struct dma_channel *chan)
|
||||
{
|
||||
int irq;
|
||||
u32 chcr;
|
||||
|
||||
chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
|
||||
chcr |= CHCR_DE;
|
||||
|
||||
if (chan->flags & DMA_TEI_CAPABLE)
|
||||
chcr |= CHCR_IE;
|
||||
|
||||
__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
|
||||
|
||||
if (chan->flags & DMA_TEI_CAPABLE) {
|
||||
irq = get_dmte_irq(chan->chan);
|
||||
enable_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void sh_dmac_disable_dma(struct dma_channel *chan)
|
||||
{
|
||||
int irq;
|
||||
u32 chcr;
|
||||
|
||||
if (chan->flags & DMA_TEI_CAPABLE) {
|
||||
irq = get_dmte_irq(chan->chan);
|
||||
disable_irq(irq);
|
||||
}
|
||||
|
||||
chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
|
||||
chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
|
||||
__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
|
||||
}
|
||||
|
||||
static int sh_dmac_xfer_dma(struct dma_channel *chan)
|
||||
{
|
||||
/*
|
||||
* If we haven't pre-configured the channel with special flags, use
|
||||
* the defaults.
|
||||
*/
|
||||
if (unlikely(!(chan->flags & DMA_CONFIGURED)))
|
||||
sh_dmac_configure_channel(chan, 0);
|
||||
|
||||
sh_dmac_disable_dma(chan);
|
||||
|
||||
/*
|
||||
* Single-address mode usage note!
|
||||
*
|
||||
* It's important that we don't accidentally write any value to SAR/DAR
|
||||
* (this includes 0) that hasn't been directly specified by the user if
|
||||
* we're in single-address mode.
|
||||
*
|
||||
* In this case, only one address can be defined, anything else will
|
||||
* result in a DMA address error interrupt (at least on the SH-4),
|
||||
* which will subsequently halt the transfer.
|
||||
*
|
||||
* Channel 2 on the Dreamcast is a special case, as this is used for
|
||||
* cascading to the PVR2 DMAC. In this case, we still need to write
|
||||
* SAR and DAR, regardless of value, in order for cascading to work.
|
||||
*/
|
||||
if (chan->sar || (mach_is_dreamcast() &&
|
||||
chan->chan == PVR2_CASCADE_CHAN))
|
||||
__raw_writel(chan->sar, (dma_base_addr(chan->chan) + SAR));
|
||||
if (chan->dar || (mach_is_dreamcast() &&
|
||||
chan->chan == PVR2_CASCADE_CHAN))
|
||||
__raw_writel(chan->dar, (dma_base_addr(chan->chan) + DAR));
|
||||
|
||||
__raw_writel(chan->count >> calc_xmit_shift(chan),
|
||||
(dma_base_addr(chan->chan) + TCR));
|
||||
|
||||
sh_dmac_enable_dma(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_dmac_get_dma_residue(struct dma_channel *chan)
|
||||
{
|
||||
if (!(__raw_readl(dma_base_addr(chan->chan) + CHCR) & CHCR_DE))
|
||||
return 0;
|
||||
|
||||
return __raw_readl(dma_base_addr(chan->chan) + TCR)
|
||||
<< calc_xmit_shift(chan);
|
||||
}
|
||||
|
||||
/*
|
||||
* DMAOR handling
|
||||
*/
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7724) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7785)
|
||||
#define NR_DMAOR 2
|
||||
#else
|
||||
#define NR_DMAOR 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DMAOR bases are broken out amongst channel groups. DMAOR0 manages
|
||||
* channels 0 - 5, DMAOR1 6 - 11 (optional).
|
||||
*/
|
||||
#define dmaor_read_reg(n) __raw_readw(dma_find_base((n)*6))
|
||||
#define dmaor_write_reg(n, data) __raw_writew(data, dma_find_base(n)*6)
|
||||
|
||||
static inline int dmaor_reset(int no)
|
||||
{
|
||||
unsigned long dmaor = dmaor_read_reg(no);
|
||||
|
||||
/* Try to clear the error flags first, incase they are set */
|
||||
dmaor &= ~(DMAOR_NMIF | DMAOR_AE);
|
||||
dmaor_write_reg(no, dmaor);
|
||||
|
||||
dmaor |= DMAOR_INIT;
|
||||
dmaor_write_reg(no, dmaor);
|
||||
|
||||
/* See if we got an error again */
|
||||
if ((dmaor_read_reg(no) & (DMAOR_AE | DMAOR_NMIF))) {
|
||||
printk(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DMAE handling
|
||||
*/
|
||||
#ifdef CONFIG_CPU_SH4
|
||||
|
||||
#if defined(DMAE1_IRQ)
|
||||
#define NR_DMAE 2
|
||||
#else
|
||||
#define NR_DMAE 1
|
||||
#endif
|
||||
|
||||
static const char *dmae_name[] = {
|
||||
"DMAC Address Error0",
|
||||
"DMAC Address Error1"
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SH_DMA_IRQ_MULTI
|
||||
static inline unsigned int get_dma_error_irq(int n)
|
||||
{
|
||||
return get_dmte_irq(n * 6);
|
||||
}
|
||||
#else
|
||||
|
||||
static unsigned int dmae_irq_map[] = {
|
||||
DMAE0_IRQ,
|
||||
|
||||
#ifdef DMAE1_IRQ
|
||||
DMAE1_IRQ,
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline unsigned int get_dma_error_irq(int n)
|
||||
{
|
||||
return dmae_irq_map[n];
|
||||
}
|
||||
#endif
|
||||
|
||||
static irqreturn_t dma_err(int irq, void *dummy)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_DMAOR; i++)
|
||||
dmaor_reset(i);
|
||||
|
||||
disable_irq(irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dmae_irq_init(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < NR_DMAE; n++) {
|
||||
int i = request_irq(get_dma_error_irq(n), dma_err,
|
||||
IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]);
|
||||
if (unlikely(i < 0)) {
|
||||
printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dmae_irq_free(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < NR_DMAE; n++)
|
||||
free_irq(get_dma_error_irq(n), NULL);
|
||||
}
|
||||
#else
|
||||
static inline int dmae_irq_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dmae_irq_free(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct dma_ops sh_dmac_ops = {
|
||||
.request = sh_dmac_request_dma,
|
||||
.free = sh_dmac_free_dma,
|
||||
.get_residue = sh_dmac_get_dma_residue,
|
||||
.xfer = sh_dmac_xfer_dma,
|
||||
.configure = sh_dmac_configure_channel,
|
||||
};
|
||||
|
||||
static struct dma_info sh_dmac_info = {
|
||||
.name = "sh_dmac",
|
||||
.nr_channels = CONFIG_NR_ONCHIP_DMA_CHANNELS,
|
||||
.ops = &sh_dmac_ops,
|
||||
.flags = DMAC_CHANNELS_TEI_CAPABLE,
|
||||
};
|
||||
|
||||
static int __init sh_dmac_init(void)
|
||||
{
|
||||
struct dma_info *info = &sh_dmac_info;
|
||||
int i, rc;
|
||||
|
||||
/*
|
||||
* Initialize DMAE, for parts that support it.
|
||||
*/
|
||||
rc = dmae_irq_init();
|
||||
if (unlikely(rc != 0))
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Initialize DMAOR, and clean up any error flags that may have
|
||||
* been set.
|
||||
*/
|
||||
for (i = 0; i < NR_DMAOR; i++) {
|
||||
rc = dmaor_reset(i);
|
||||
if (unlikely(rc != 0))
|
||||
return rc;
|
||||
}
|
||||
|
||||
return register_dmac(info);
|
||||
}
|
||||
|
||||
static void __exit sh_dmac_exit(void)
|
||||
{
|
||||
dmae_irq_free();
|
||||
unregister_dmac(&sh_dmac_info);
|
||||
}
|
||||
|
||||
subsys_initcall(sh_dmac_init);
|
||||
module_exit(sh_dmac_exit);
|
||||
|
||||
MODULE_AUTHOR("Takashi YOSHII, Paul Mundt, Andriy Skulysh");
|
||||
MODULE_DESCRIPTION("SuperH On-Chip DMAC Support");
|
||||
MODULE_LICENSE("GPL");
|
||||
167
arch/sh/drivers/dma/dma-sysfs.c
Normal file
167
arch/sh/drivers/dma/dma-sysfs.c
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* arch/sh/drivers/dma/dma-sysfs.c
|
||||
*
|
||||
* sysfs interface for SH DMA API
|
||||
*
|
||||
* Copyright (C) 2004 - 2006 Paul Mundt
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
static struct bus_type dma_subsys = {
|
||||
.name = "dma",
|
||||
.dev_name = "dma",
|
||||
};
|
||||
|
||||
static ssize_t dma_show_devices(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
struct dma_info *info = get_dma_info(i);
|
||||
struct dma_channel *channel = get_dma_channel(i);
|
||||
|
||||
if (unlikely(!info) || !channel)
|
||||
continue;
|
||||
|
||||
len += sprintf(buf + len, "%2d: %14s %s\n",
|
||||
channel->chan, info->name,
|
||||
channel->dev_id);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(devices, S_IRUGO, dma_show_devices, NULL);
|
||||
|
||||
static int __init dma_subsys_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = subsys_system_register(&dma_subsys, NULL);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
return device_create_file(dma_subsys.dev_root, &dev_attr_devices);
|
||||
}
|
||||
postcore_initcall(dma_subsys_init);
|
||||
|
||||
static ssize_t dma_show_dev_id(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dma_channel *channel = to_dma_channel(dev);
|
||||
return sprintf(buf, "%s\n", channel->dev_id);
|
||||
}
|
||||
|
||||
static ssize_t dma_store_dev_id(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct dma_channel *channel = to_dma_channel(dev);
|
||||
strcpy(channel->dev_id, buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id);
|
||||
|
||||
static ssize_t dma_store_config(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct dma_channel *channel = to_dma_channel(dev);
|
||||
unsigned long config;
|
||||
|
||||
config = simple_strtoul(buf, NULL, 0);
|
||||
dma_configure_channel(channel->vchan, config);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(config, S_IWUSR, NULL, dma_store_config);
|
||||
|
||||
static ssize_t dma_show_mode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dma_channel *channel = to_dma_channel(dev);
|
||||
return sprintf(buf, "0x%08x\n", channel->mode);
|
||||
}
|
||||
|
||||
static ssize_t dma_store_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct dma_channel *channel = to_dma_channel(dev);
|
||||
channel->mode = simple_strtoul(buf, NULL, 0);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode);
|
||||
|
||||
#define dma_ro_attr(field, fmt) \
|
||||
static ssize_t dma_show_##field(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf)\
|
||||
{ \
|
||||
struct dma_channel *channel = to_dma_channel(dev); \
|
||||
return sprintf(buf, fmt, channel->field); \
|
||||
} \
|
||||
static DEVICE_ATTR(field, S_IRUGO, dma_show_##field, NULL);
|
||||
|
||||
dma_ro_attr(count, "0x%08x\n");
|
||||
dma_ro_attr(flags, "0x%08lx\n");
|
||||
|
||||
int dma_create_sysfs_files(struct dma_channel *chan, struct dma_info *info)
|
||||
{
|
||||
struct device *dev = &chan->dev;
|
||||
char name[16];
|
||||
int ret;
|
||||
|
||||
dev->id = chan->vchan;
|
||||
dev->bus = &dma_subsys;
|
||||
|
||||
ret = device_register(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret |= device_create_file(dev, &dev_attr_dev_id);
|
||||
ret |= device_create_file(dev, &dev_attr_count);
|
||||
ret |= device_create_file(dev, &dev_attr_mode);
|
||||
ret |= device_create_file(dev, &dev_attr_flags);
|
||||
ret |= device_create_file(dev, &dev_attr_config);
|
||||
|
||||
if (unlikely(ret)) {
|
||||
dev_err(&info->pdev->dev, "Failed creating attrs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "dma%d", chan->chan);
|
||||
return sysfs_create_link(&info->pdev->dev.kobj, &dev->kobj, name);
|
||||
}
|
||||
|
||||
void dma_remove_sysfs_files(struct dma_channel *chan, struct dma_info *info)
|
||||
{
|
||||
struct device *dev = &chan->dev;
|
||||
char name[16];
|
||||
|
||||
device_remove_file(dev, &dev_attr_dev_id);
|
||||
device_remove_file(dev, &dev_attr_count);
|
||||
device_remove_file(dev, &dev_attr_mode);
|
||||
device_remove_file(dev, &dev_attr_flags);
|
||||
device_remove_file(dev, &dev_attr_config);
|
||||
|
||||
snprintf(name, sizeof(name), "dma%d", chan->chan);
|
||||
sysfs_remove_link(&info->pdev->dev.kobj, name);
|
||||
|
||||
device_unregister(dev);
|
||||
}
|
||||
197
arch/sh/drivers/dma/dmabrg.c
Normal file
197
arch/sh/drivers/dma/dmabrg.c
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* SH7760 DMABRG IRQ handling
|
||||
*
|
||||
* (c) 2007 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com>
|
||||
* licensed under the GPLv2.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/dmabrg.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* The DMABRG is a special DMA unit within the SH7760. It does transfers
|
||||
* from USB-SRAM/Audio units to main memory (and also the LCDC; but that
|
||||
* part is sensibly placed in the LCDC registers and requires no irqs)
|
||||
* It has 3 IRQ lines which trigger 10 events, and works independently
|
||||
* from the traditional SH DMAC (although it blocks usage of DMAC 0)
|
||||
*
|
||||
* BRGIRQID | component | dir | meaning | source
|
||||
* -----------------------------------------------------
|
||||
* 0 | USB-DMA | ... | xfer done | DMABRGI1
|
||||
* 1 | USB-UAE | ... | USB addr err.| DMABRGI0
|
||||
* 2 | HAC0/SSI0 | play| all done | DMABRGI1
|
||||
* 3 | HAC0/SSI0 | play| half done | DMABRGI2
|
||||
* 4 | HAC0/SSI0 | rec | all done | DMABRGI1
|
||||
* 5 | HAC0/SSI0 | rec | half done | DMABRGI2
|
||||
* 6 | HAC1/SSI1 | play| all done | DMABRGI1
|
||||
* 7 | HAC1/SSI1 | play| half done | DMABRGI2
|
||||
* 8 | HAC1/SSI1 | rec | all done | DMABRGI1
|
||||
* 9 | HAC1/SSI1 | rec | half done | DMABRGI2
|
||||
*
|
||||
* all can be enabled/disabled in the DMABRGCR register,
|
||||
* as well as checked if they occurred.
|
||||
*
|
||||
* DMABRGI0 services USB DMA Address errors, but it still must be
|
||||
* enabled/acked in the DMABRGCR register. USB-DMA complete indicator
|
||||
* is grouped together with the audio buffer end indicators, too bad...
|
||||
*
|
||||
* DMABRGCR: Bits 31-24: audio-dma ENABLE flags,
|
||||
* Bits 23-16: audio-dma STATUS flags,
|
||||
* Bits 9-8: USB error/xfer ENABLE,
|
||||
* Bits 1-0: USB error/xfer STATUS.
|
||||
* Ack an IRQ by writing 0 to the STATUS flag.
|
||||
* Mask IRQ by writing 0 to ENABLE flag.
|
||||
*
|
||||
* Usage is almost like with any other IRQ:
|
||||
* dmabrg_request_irq(BRGIRQID, handler, data)
|
||||
* dmabrg_free_irq(BRGIRQID)
|
||||
*
|
||||
* handler prototype: void brgirqhandler(void *data)
|
||||
*/
|
||||
|
||||
#define DMARSRA 0xfe090000
|
||||
#define DMAOR 0xffa00040
|
||||
#define DMACHCR0 0xffa0000c
|
||||
#define DMABRGCR 0xfe3c0000
|
||||
|
||||
#define DMAOR_BRG 0x0000c000
|
||||
#define DMAOR_DMEN 0x00000001
|
||||
|
||||
#define DMABRGI0 68
|
||||
#define DMABRGI1 69
|
||||
#define DMABRGI2 70
|
||||
|
||||
struct dmabrg_handler {
|
||||
void (*handler)(void *);
|
||||
void *data;
|
||||
} *dmabrg_handlers;
|
||||
|
||||
static inline void dmabrg_call_handler(int i)
|
||||
{
|
||||
dmabrg_handlers[i].handler(dmabrg_handlers[i].data);
|
||||
}
|
||||
|
||||
/*
|
||||
* main DMABRG irq handler. It acks irqs and then
|
||||
* handles every set and unmasked bit sequentially.
|
||||
* No locking and no validity checks; it should be
|
||||
* as fast as possible (audio!)
|
||||
*/
|
||||
static irqreturn_t dmabrg_irq(int irq, void *data)
|
||||
{
|
||||
unsigned long dcr;
|
||||
unsigned int i;
|
||||
|
||||
dcr = __raw_readl(DMABRGCR);
|
||||
__raw_writel(dcr & ~0x00ff0003, DMABRGCR); /* ack all */
|
||||
dcr &= dcr >> 8; /* ignore masked */
|
||||
|
||||
/* USB stuff, get it out of the way first */
|
||||
if (dcr & 1)
|
||||
dmabrg_call_handler(DMABRGIRQ_USBDMA);
|
||||
if (dcr & 2)
|
||||
dmabrg_call_handler(DMABRGIRQ_USBDMAERR);
|
||||
|
||||
/* Audio */
|
||||
dcr >>= 16;
|
||||
while (dcr) {
|
||||
i = __ffs(dcr);
|
||||
dcr &= dcr - 1;
|
||||
dmabrg_call_handler(i + DMABRGIRQ_A0TXF);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dmabrg_disable_irq(unsigned int dmairq)
|
||||
{
|
||||
unsigned long dcr;
|
||||
dcr = __raw_readl(DMABRGCR);
|
||||
dcr &= ~(1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8));
|
||||
__raw_writel(dcr, DMABRGCR);
|
||||
}
|
||||
|
||||
static void dmabrg_enable_irq(unsigned int dmairq)
|
||||
{
|
||||
unsigned long dcr;
|
||||
dcr = __raw_readl(DMABRGCR);
|
||||
dcr |= (1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8));
|
||||
__raw_writel(dcr, DMABRGCR);
|
||||
}
|
||||
|
||||
int dmabrg_request_irq(unsigned int dmairq, void(*handler)(void*),
|
||||
void *data)
|
||||
{
|
||||
if ((dmairq > 9) || !handler)
|
||||
return -ENOENT;
|
||||
if (dmabrg_handlers[dmairq].handler)
|
||||
return -EBUSY;
|
||||
|
||||
dmabrg_handlers[dmairq].handler = handler;
|
||||
dmabrg_handlers[dmairq].data = data;
|
||||
|
||||
dmabrg_enable_irq(dmairq);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dmabrg_request_irq);
|
||||
|
||||
void dmabrg_free_irq(unsigned int dmairq)
|
||||
{
|
||||
if (likely(dmairq < 10)) {
|
||||
dmabrg_disable_irq(dmairq);
|
||||
dmabrg_handlers[dmairq].handler = NULL;
|
||||
dmabrg_handlers[dmairq].data = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dmabrg_free_irq);
|
||||
|
||||
static int __init dmabrg_init(void)
|
||||
{
|
||||
unsigned long or;
|
||||
int ret;
|
||||
|
||||
dmabrg_handlers = kzalloc(10 * sizeof(struct dmabrg_handler),
|
||||
GFP_KERNEL);
|
||||
if (!dmabrg_handlers)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_SH_DMA
|
||||
/* request DMAC channel 0 before anyone else can get it */
|
||||
ret = request_dma(0, "DMAC 0 (DMABRG)");
|
||||
if (ret < 0)
|
||||
printk(KERN_INFO "DMABRG: DMAC ch0 not reserved!\n");
|
||||
#endif
|
||||
|
||||
__raw_writel(0, DMABRGCR);
|
||||
__raw_writel(0, DMACHCR0);
|
||||
__raw_writel(0x94000000, DMARSRA); /* enable DMABRG in DMAC 0 */
|
||||
|
||||
/* enable DMABRG mode, enable the DMAC */
|
||||
or = __raw_readl(DMAOR);
|
||||
__raw_writel(or | DMAOR_BRG | DMAOR_DMEN, DMAOR);
|
||||
|
||||
ret = request_irq(DMABRGI0, dmabrg_irq, 0,
|
||||
"DMABRG USB address error", NULL);
|
||||
if (ret)
|
||||
goto out0;
|
||||
|
||||
ret = request_irq(DMABRGI1, dmabrg_irq, 0,
|
||||
"DMABRG Transfer End", NULL);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
ret = request_irq(DMABRGI2, dmabrg_irq, 0,
|
||||
"DMABRG Transfer Half", NULL);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
|
||||
free_irq(DMABRGI1, NULL);
|
||||
out1: free_irq(DMABRGI0, NULL);
|
||||
out0: kfree(dmabrg_handlers);
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(dmabrg_init);
|
||||
Loading…
Add table
Add a link
Reference in a new issue