277 lines
6.4 KiB
C
277 lines
6.4 KiB
C
/* linux/arch/arm/mach-bast/dma-sysfs.c
|
|
*
|
|
* (c) 2005 Simtec Electronics
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
*
|
|
* S3C2440 DMA core sysfs interface
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/sysdev.h>
|
|
#include <linux/config.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/hardware.h>
|
|
#include <asm/io.h>
|
|
#include <asm/dma.h>
|
|
|
|
#include <asm/mach/dma.h>
|
|
#include <asm/arch/map.h>
|
|
|
|
|
|
extern struct sysdev_class dma_sysclass;
|
|
extern s3c24xx_dma_chan_t s3c24xx_chans[S3C24XX_DMA_CHANNELS];
|
|
|
|
#define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
|
|
|
|
static const char *s3c24xx_dma_decode_state(s3c24xx_dma_state_t state)
|
|
{
|
|
switch (state) {
|
|
case S3C24XX_DMA_IDLE:
|
|
return "Idle";
|
|
case S3C24XX_DMA_RUNNING:
|
|
return "Running";
|
|
case S3C24XX_DMA_PAUSED:
|
|
return "Paused";
|
|
default:
|
|
return "Invalid";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const char *s3c24xx_dma_decode_loadstate(s3c24xx_dma_loadst_t state)
|
|
{
|
|
switch (state) {
|
|
case S3C24XX_DMALOAD_NONE:
|
|
return "Nothing Loaded";
|
|
case S3C24XX_DMALOAD_1LOADED:
|
|
return "1 Loaded";
|
|
case S3C24XX_DMALOAD_1RUNNING:
|
|
return "1 Running";
|
|
case S3C24XX_DMALOAD_1LOADED_1RUNNING:
|
|
return "1 Loaded, 1 Running";
|
|
default:
|
|
return "Invalid Load State";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const char *s3c24xx_dma_decode_source(s3c24xx_dmasrc_t source)
|
|
{
|
|
switch (source) {
|
|
case S3C24XX_DMASRC_HW:
|
|
return "Hardware";
|
|
case S3C24XX_DMASRC_MEM:
|
|
return "Memory";
|
|
default:
|
|
return "Unknown Source";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct outbuff {
|
|
char *buff;
|
|
int offset;
|
|
int size;
|
|
};
|
|
|
|
static inline void outbuf_init(struct outbuff *ob, char *buf, int size)
|
|
{
|
|
ob->buff = buf;
|
|
ob->offset = 0;
|
|
ob->size = size;
|
|
}
|
|
|
|
static void outbuf_pf(struct outbuff *ob, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
|
|
ob->offset += vsnprintf(ob->buff + ob->offset,
|
|
ob->size - ob->offset,
|
|
fmt, va);
|
|
va_end(va);
|
|
}
|
|
|
|
static void dma_sysfs_showregs(s3c24xx_dma_chan_t *chan, struct outbuff *buf)
|
|
{
|
|
outbuf_pf(buf, "Hardware Registers:\n\n");
|
|
|
|
outbuf_pf(buf, " DISRC \t\t\t%08lx\n", readl(chan->regs + 0x00));
|
|
outbuf_pf(buf, " DISRCC\t\t\t%08lx\n", readl(chan->regs + 0x04));
|
|
outbuf_pf(buf, " DIDST \t\t\t%08lx\n", readl(chan->regs + 0x08));
|
|
outbuf_pf(buf, " DIDSTC\t\t\t%08lx\n", readl(chan->regs + 0x0C));
|
|
outbuf_pf(buf, " DCON \t\t\t%08lx\n", readl(chan->regs + 0x10));
|
|
outbuf_pf(buf, " DSTAT \t\t\t%08lx\n", readl(chan->regs + 0x14));
|
|
outbuf_pf(buf, " DCSRC \t\t\t%08lx\n", readl(chan->regs + 0x18));
|
|
outbuf_pf(buf, " DCDST \t\t\t%08lx\n", readl(chan->regs + 0x1c));
|
|
outbuf_pf(buf, " DMTRG \t\t\t%08lx\n", readl(chan->regs + 0x20));
|
|
}
|
|
|
|
static void dma_sysfs_showbuf(s3c24xx_dma_chan_t *chan,
|
|
struct outbuff *buf,
|
|
s3c24xx_dma_buf_t *ptr)
|
|
{
|
|
if (ptr == NULL)
|
|
return;
|
|
|
|
outbuf_pf(buf, " buff %p: data=%08x, size=%08x, id=%08x\n",
|
|
ptr, ptr->data, ptr->size, ptr->id);
|
|
}
|
|
|
|
static void dma_sysfs_showbuffers(s3c24xx_dma_chan_t *chan, struct outbuff *buf)
|
|
{
|
|
s3c24xx_dma_buf_t *ptr = chan->next;
|
|
unsigned long flags;
|
|
|
|
local_irq_save(flags);
|
|
|
|
outbuf_pf(buf, "Current Buffer:\n");
|
|
dma_sysfs_showbuf(chan, buf, chan->curr);
|
|
|
|
outbuf_pf(buf, "\nBuffer Queue: %p -> %p\n", chan->next, chan->end);
|
|
|
|
for (; ptr != NULL; ptr = ptr->next)
|
|
dma_sysfs_showbuf(chan, buf, ptr);
|
|
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
static void dma_sysfs_showstate(s3c24xx_dma_chan_t *chan, struct outbuff *buf)
|
|
{
|
|
outbuf_pf(buf, "IRQ:\t\t\t\t%d\n", chan->irq);
|
|
outbuf_pf(buf, "Register Base:\t\t\t0x%08x\n", chan->regs);
|
|
outbuf_pf(buf, "Address Register:\t\t0x%08lx\n", chan->addr_reg);
|
|
outbuf_pf(buf, "Hardware Register:\t\t0x%08lx\n", chan->dev_addr);
|
|
outbuf_pf(buf, "Load Timeout:\t\t\t0x%08x\n", chan->load_timeout);
|
|
outbuf_pf(buf, "Data Source:\t\t\t%s\n",
|
|
s3c24xx_dma_decode_source(chan->source));
|
|
|
|
outbuf_pf(buf, "Channel State:\t\t\t%s (%d)\n",
|
|
s3c24xx_dma_decode_state(chan->state), chan->state);
|
|
outbuf_pf(buf, "Load State:\t\t\t%s (%d)\n",
|
|
s3c24xx_dma_decode_loadstate(chan->load_state),
|
|
chan->load_state);
|
|
|
|
if (chan->client != NULL) {
|
|
outbuf_pf(buf, "Claimed by:\t\t\t%s\n",
|
|
chan->client->name);
|
|
} else {
|
|
outbuf_pf(buf, "Claimed by:\t\t\tNo-one\n");
|
|
}
|
|
}
|
|
|
|
static inline s3c24xx_dma_chan_t *dev_to_chan(struct sys_device *dev)
|
|
{
|
|
return &s3c24xx_chans[dev->id];
|
|
}
|
|
|
|
static ssize_t dma_sysfs_show_regs(struct sys_device *dev, char *buf)
|
|
{
|
|
s3c24xx_dma_chan_t *cp = dev_to_chan(dev);
|
|
struct outbuff buff;
|
|
|
|
outbuf_init(&buff, buf, PAGE_SIZE);
|
|
dma_sysfs_showregs(cp, &buff);
|
|
|
|
return buff.offset;
|
|
}
|
|
|
|
static ssize_t dma_sysfs_show_state(struct sys_device *dev, char *buf)
|
|
{
|
|
s3c24xx_dma_chan_t *cp = dev_to_chan(dev);
|
|
struct outbuff buff;
|
|
|
|
outbuf_init(&buff, buf, PAGE_SIZE);
|
|
dma_sysfs_showstate(cp, &buff);
|
|
|
|
return buff.offset;
|
|
}
|
|
|
|
static ssize_t dma_sysfs_show_buffers(struct sys_device *dev, char *buf)
|
|
{
|
|
s3c24xx_dma_chan_t *cp = dev_to_chan(dev);
|
|
struct outbuff buff;
|
|
|
|
outbuf_init(&buff, buf, PAGE_SIZE);
|
|
dma_sysfs_showbuffers(cp, &buff);
|
|
|
|
return buff.offset;
|
|
}
|
|
|
|
static ssize_t dma_sysfs_show_all(struct sys_device *dev, char *buf)
|
|
{
|
|
s3c24xx_dma_chan_t *cp = dev_to_chan(dev);
|
|
struct outbuff buff;
|
|
|
|
outbuf_init(&buff, buf, PAGE_SIZE);
|
|
|
|
dma_sysfs_showstate(cp, &buff);
|
|
dma_sysfs_showregs(cp, &buff);
|
|
dma_sysfs_showbuffers(cp, &buff);
|
|
|
|
return buff.offset;
|
|
}
|
|
|
|
static SYSDEV_ATTR(all, S_IRUGO, dma_sysfs_show_all, NULL);
|
|
static SYSDEV_ATTR(regs, S_IRUGO, dma_sysfs_show_regs, NULL);
|
|
static SYSDEV_ATTR(state, S_IRUGO, dma_sysfs_show_state, NULL);
|
|
static SYSDEV_ATTR(buffers, S_IRUGO, dma_sysfs_show_buffers, NULL);
|
|
|
|
static struct sysdev_attribute *attrs[] = {
|
|
&attr_all,
|
|
&attr_regs,
|
|
&attr_state,
|
|
&attr_buffers,
|
|
};
|
|
|
|
static int dma_sysfs_attach(struct sys_device *dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(attrs); i++)
|
|
sysdev_create_file(dev, attrs[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dma_sysfs_remove(struct sys_device *dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(attrs); i++)
|
|
sysdev_create_file(dev, attrs[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct sysdev_driver dma_sysfs_driver = {
|
|
.add = dma_sysfs_attach,
|
|
.remove = dma_sysfs_remove,
|
|
};
|
|
|
|
static int __init s3c24xx_init_dma_sysfs(void)
|
|
{
|
|
printk("S3C24XX DMA sysfs support, (c) 2005 Simtec Electronics\n");
|
|
|
|
return sysdev_driver_register(&dma_sysclass, &dma_sysfs_driver);
|
|
}
|
|
|
|
__initcall(s3c24xx_init_dma_sysfs);
|
|
|