Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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

View 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

View 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, &registered_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, &registered_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(&registered_dmac_list)))
return nr;
list_for_each_entry(info, &registered_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, &registered_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, &registered_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(&registered_dmac_list))
return 0;
/*
* Iterate over each registered DMAC
*/
list_for_each_entry(info, &registered_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, &registered_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");

View 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");

View 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");

View 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");

View 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);
}

View 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);