From: Paul Mundt This updates the SuperH DMA driver, as well as cleaning up the registration interface. We also drop the use of bitfields in dma-sh since things like the SH4-202, 7751R, 7760, etc. all have a completely different set of register definitions. Signed-off-by: Paul Mundt Signed-off-by: Andrew Morton --- 25-akpm/arch/sh/drivers/dma/Makefile | 1 25-akpm/arch/sh/drivers/dma/dma-api.c | 175 ++++++++++++++++++++++++-------- 25-akpm/arch/sh/drivers/dma/dma-isa.c | 29 +++-- 25-akpm/arch/sh/drivers/dma/dma-sh.c | 164 +++++++++++++++-------------- 25-akpm/arch/sh/drivers/dma/dma-sysfs.c | 133 ++++++++++++++++++++++++ 25-akpm/include/asm-sh/cpu-sh4/dma.h | 10 + 25-akpm/include/asm-sh/dma.h | 71 +++++++++--- 7 files changed, 438 insertions(+), 145 deletions(-) diff -puN arch/sh/drivers/dma/dma-api.c~sh-dma-driver-updates arch/sh/drivers/dma/dma-api.c --- 25/arch/sh/drivers/dma/dma-api.c~sh-dma-driver-updates 2004-06-23 20:00:14.618467680 -0700 +++ 25-akpm/arch/sh/drivers/dma/dma-api.c 2004-06-23 20:00:14.631465704 -0700 @@ -3,23 +3,24 @@ * * SuperH-specific DMA management API * - * Copyright (C) 2003 Paul Mundt + * 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 #include #include #include #include +#include #include -struct dma_info dma_info[MAX_DMA_CHANNELS] = { { 0, } }; spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(registered_dmac_list); -/* +/* * A brief note about the reasons for this API as it stands. * * For starters, the old ISA DMA API didn't work for us for a number of @@ -54,124 +55,213 @@ spinlock_t dma_spin_lock = SPIN_LOCK_UNL struct dma_info *get_dma_info(unsigned int chan) { - return dma_info + chan; + struct list_head *pos, *tmp; + unsigned int total = 0; + + /* + * Look for each DMAC's range to determine who the owner of + * the channel is. + */ + list_for_each_safe(pos, tmp, ®istered_dmac_list) { + struct dma_info *info = list_entry(pos, struct dma_info, list); + + total += info->nr_channels; + if (chan > total) + continue; + + return info; + } + + return NULL; +} + +struct dma_channel *get_dma_channel(unsigned int chan) +{ + struct dma_info *info = get_dma_info(chan); + + if (!info) + return ERR_PTR(-EINVAL); + + return info->channels + chan; } int get_dma_residue(unsigned int chan) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; if (info->ops->get_residue) - return info->ops->get_residue(info); - + return info->ops->get_residue(channel); + return 0; } int request_dma(unsigned int chan, const char *dev_id) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; - down(&info->sem); + down(&channel->sem); if (!info->ops || chan >= MAX_DMA_CHANNELS) { - up(&info->sem); + up(&channel->sem); return -EINVAL; } - - atomic_set(&info->busy, 1); - info->dev_id = dev_id; + atomic_set(&channel->busy, 1); - up(&info->sem); + strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id)); + + up(&channel->sem); if (info->ops->request) - return info->ops->request(info); - + return info->ops->request(channel); + return 0; } void free_dma(unsigned int chan) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; if (info->ops->free) - info->ops->free(info); - - atomic_set(&info->busy, 0); + info->ops->free(channel); + + atomic_set(&channel->busy, 0); } void dma_wait_for_completion(unsigned int chan) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; - if (info->tei_capable) { - wait_event(info->wait_queue, (info->ops->get_residue(info) == 0)); + if (channel->flags & DMA_TEI_CAPABLE) { + wait_event(channel->wait_queue, + (info->ops->get_residue(channel) == 0)); return; } - while (info->ops->get_residue(info)) + while (info->ops->get_residue(channel)) cpu_relax(); } void dma_configure_channel(unsigned int chan, unsigned long flags) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; if (info->ops->configure) - info->ops->configure(info, flags); + info->ops->configure(channel, flags); } 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 = &info->channels[chan]; - info->sar = from; - info->dar = to; - info->count = size; - info->mode = mode; + channel->sar = from; + channel->dar = to; + channel->count = size; + channel->mode = mode; - return info->ops->xfer(info); + return info->ops->xfer(channel); } #ifdef CONFIG_PROC_FS static int dma_read_proc(char *buf, char **start, off_t off, int len, int *eof, void *data) { - struct dma_info *info; + struct list_head *pos, *tmp; char *p = buf; - int i; - for (i = 0, info = dma_info; i < MAX_DMA_CHANNELS; i++, info++) { - if (!atomic_read(&info->busy)) - continue; + if (list_empty(®istered_dmac_list)) + return 0; - p += sprintf(p, "%2d: %14s %s\n", i, - info->ops->name, info->dev_id); + /* + * Iterate over each registered DMAC + */ + list_for_each_safe(pos, tmp, ®istered_dmac_list) { + struct dma_info *info = list_entry(pos, struct dma_info, 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; + + p += sprintf(p, "%2d: %14s %s\n", i, + info->name, channel->dev_id); + } } return p - buf; } #endif -int __init register_dmac(struct dma_ops *ops) + +int __init register_dmac(struct dma_info *info) { int i; - printk("DMA: Registering %s handler.\n", ops->name); + INIT_LIST_HEAD(&info->list); + + printk(KERN_INFO "DMA: Registering %s handler (%d channels).\n", + info->name, info->nr_channels); + + BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels); + + /* + * 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 = kmalloc(size, GFP_KERNEL); + if (!info->channels) + return -ENOMEM; + + memset(info->channels, 0, size); + } + + for (i = 0; i < info->nr_channels; i++) { + struct dma_channel *chan = info->channels + i; + + chan->chan = i; + + memcpy(chan->dev_id, "Unused", 7); - for (i = 0; i < MAX_DMA_CHANNELS; i++) { - struct dma_info *info = get_dma_info(i); + if (info->flags & DMAC_CHANNELS_TEI_CAPABLE) + chan->flags |= DMA_TEI_CAPABLE; - info->chan = i; + init_MUTEX(&chan->sem); + init_waitqueue_head(&chan->wait_queue); - init_MUTEX(&info->sem); - init_waitqueue_head(&info->wait_queue); +#ifdef CONFIG_SYSFS + dma_create_sysfs_files(chan); +#endif } + list_add(&info->list, ®istered_dmac_list); + return 0; } +void __exit unregister_dmac(struct dma_info *info) +{ + if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) + kfree(info->channels); + + list_del(&info->list); +} + static int __init dma_api_init(void) { printk("DMA: Registering DMA API.\n"); @@ -191,8 +281,11 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(request_dma); EXPORT_SYMBOL(free_dma); +EXPORT_SYMBOL(register_dmac); +EXPORT_SYMBOL(unregister_dmac); EXPORT_SYMBOL(get_dma_residue); EXPORT_SYMBOL(get_dma_info); +EXPORT_SYMBOL(get_dma_channel); EXPORT_SYMBOL(dma_xfer); EXPORT_SYMBOL(dma_wait_for_completion); EXPORT_SYMBOL(dma_configure_channel); diff -puN arch/sh/drivers/dma/dma-isa.c~sh-dma-driver-updates arch/sh/drivers/dma/dma-isa.c --- 25/arch/sh/drivers/dma/dma-isa.c~sh-dma-driver-updates 2004-06-23 20:00:14.619467528 -0700 +++ 25-akpm/arch/sh/drivers/dma/dma-isa.c 2004-06-23 20:00:14.632465552 -0700 @@ -3,13 +3,14 @@ * * Generic ISA DMA wrapper for SH DMA API * - * Copyright (C) 2003 Paul Mundt + * 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 +#include #include /* @@ -39,55 +40,67 @@ unsigned long __deprecated claim_dma_loc return flags; } +EXPORT_SYMBOL(claim_dma_lock); void __deprecated release_dma_lock(unsigned long flags) { spin_unlock_irqrestore(&dma_spin_lock, flags); } +EXPORT_SYMBOL(release_dma_lock); void __deprecated disable_dma(unsigned int chan) { /* Nothing */ } +EXPORT_SYMBOL(disable_dma); void __deprecated enable_dma(unsigned int chan) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; - info->ops->xfer(info); + info->ops->xfer(channel); } +EXPORT_SYMBOL(enable_dma); void clear_dma_ff(unsigned int chan) { /* Nothing */ } +EXPORT_SYMBOL(clear_dma_ff); void set_dma_mode(unsigned int chan, char mode) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; - info->mode = mode; + channel->mode = mode; } +EXPORT_SYMBOL(set_dma_mode); void set_dma_addr(unsigned int chan, unsigned int addr) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; /* * Single address mode is the only thing supported through * this interface. */ - if ((info->mode & DMA_MODE_MASK) == DMA_MODE_READ) { - info->sar = addr; + if ((channel->mode & DMA_MODE_MASK) == DMA_MODE_READ) { + channel->sar = addr; } else { - info->dar = addr; + channel->dar = addr; } } +EXPORT_SYMBOL(set_dma_addr); void set_dma_count(unsigned int chan, unsigned int count) { struct dma_info *info = get_dma_info(chan); + struct dma_channel *channel = &info->channels[chan]; - info->count = count; + channel->count = count; } +EXPORT_SYMBOL(set_dma_count); diff -puN arch/sh/drivers/dma/dma-sh.c~sh-dma-driver-updates arch/sh/drivers/dma/dma-sh.c --- 25/arch/sh/drivers/dma/dma-sh.c~sh-dma-driver-updates 2004-06-23 20:00:14.621467224 -0700 +++ 25-akpm/arch/sh/drivers/dma/dma-sh.c 2004-06-23 20:00:14.633465400 -0700 @@ -1,10 +1,10 @@ /* - * arch/sh/kernel/cpu/dma.c + * arch/sh/drivers/dma/dma-sh.c * - * Copyright (C) 2000 Takashi YOSHII - * Copyright (C) 2003 Paul Mundt + * SuperH On-chip DMAC Support * - * PC like DMA API for SuperH's DMAC. + * Copyright (C) 2000 Takashi YOSHII + * 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 @@ -29,43 +29,29 @@ * Defaults to a 64-bit transfer size. */ enum { - XMIT_SZ_64BIT = 0, - XMIT_SZ_8BIT = 1, - XMIT_SZ_16BIT = 2, - XMIT_SZ_32BIT = 3, - XMIT_SZ_256BIT = 4, + XMIT_SZ_64BIT, + XMIT_SZ_8BIT, + XMIT_SZ_16BIT, + XMIT_SZ_32BIT, + XMIT_SZ_256BIT, }; /* * The DMA count is defined as the number of bytes to transfer. */ static unsigned int ts_shift[] = { - [XMIT_SZ_64BIT] 3, - [XMIT_SZ_8BIT] 0, - [XMIT_SZ_16BIT] 1, - [XMIT_SZ_32BIT] 2, - [XMIT_SZ_256BIT] 5, + [XMIT_SZ_64BIT] = 3, + [XMIT_SZ_8BIT] = 0, + [XMIT_SZ_16BIT] = 1, + [XMIT_SZ_32BIT] = 2, + [XMIT_SZ_256BIT] = 5, }; -struct sh_dmac_channel { - unsigned long sar; - unsigned long dar; - unsigned long dmatcr; - unsigned long chcr; -} __attribute__ ((aligned(16))); - -struct sh_dmac_info { - struct sh_dmac_channel channel[4]; - unsigned long dmaor; -}; - -static volatile struct sh_dmac_info *sh_dmac = (volatile struct sh_dmac_info *)SH_DMAC_BASE; - static inline unsigned int get_dmte_irq(unsigned int chan) { unsigned int irq; - /* + /* * Normally we could just do DMTE0_IRQ + chan outright, though in the * case of the 7751R, the DMTE IRQs for channels > 4 start right above * the SCIF @@ -84,13 +70,17 @@ static inline unsigned int get_dmte_irq( * 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] + * info->count >> ts_shift[transmit_size] * * iterations to complete the transfer. */ -static inline unsigned int calc_xmit_shift(struct dma_info *info) +static inline unsigned int calc_xmit_shift(struct dma_channel *chan) { - return ts_shift[(sh_dmac->channel[info->chan].chcr >> 4) & 0x0007]; + u32 chcr = ctrl_inl(CHCR[chan->chan]); + + chcr >>= 4; + + return ts_shift[chcr & 0x0007]; } /* @@ -101,68 +91,79 @@ static inline unsigned int calc_xmit_shi */ static irqreturn_t dma_tei(int irq, void *dev_id, struct pt_regs *regs) { - struct dma_info * info = (struct dma_info *)dev_id; - u32 chcr = sh_dmac->channel[info->chan].chcr; + struct dma_channel *chan = (struct dma_channel *)dev_id; + u32 chcr; + + chcr = ctrl_inl(CHCR[chan->chan]); if (!(chcr & CHCR_TE)) return IRQ_NONE; - sh_dmac->channel[info->chan].chcr = chcr & ~(CHCR_IE | CHCR_DE); + chcr &= ~(CHCR_IE | CHCR_DE); + ctrl_outl(chcr, CHCR[chan->chan]); - wake_up(&info->wait_queue); + wake_up(&chan->wait_queue); return IRQ_HANDLED; } -static int sh_dmac_request_dma(struct dma_info *info) +static int sh_dmac_request_dma(struct dma_channel *chan) { - return request_irq(get_dmte_irq(info->chan), dma_tei, - SA_INTERRUPT, "DMAC Transfer End", info); + return request_irq(get_dmte_irq(chan->chan), dma_tei, + SA_INTERRUPT, "DMAC Transfer End", chan); } -static void sh_dmac_free_dma(struct dma_info *info) +static void sh_dmac_free_dma(struct dma_channel *chan) { - free_irq(get_dmte_irq(info->chan), info); + free_irq(get_dmte_irq(chan->chan), chan); } -static void sh_dmac_configure_channel(struct dma_info *info, unsigned long chcr) +static void sh_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr) { if (!chcr) chcr = RS_DUAL; - sh_dmac->channel[info->chan].chcr = chcr; + ctrl_outl(chcr, CHCR[chan->chan]); - info->configured = 1; + chan->flags |= DMA_CONFIGURED; } -static void sh_dmac_enable_dma(struct dma_info *info) +static void sh_dmac_enable_dma(struct dma_channel *chan) { - int irq = get_dmte_irq(info->chan); + int irq = get_dmte_irq(chan->chan); + u32 chcr; + + chcr = ctrl_inl(CHCR[chan->chan]); + chcr |= CHCR_DE | CHCR_IE; + ctrl_outl(chcr, CHCR[chan->chan]); - sh_dmac->channel[info->chan].chcr |= (CHCR_DE | CHCR_IE); enable_irq(irq); } -static void sh_dmac_disable_dma(struct dma_info *info) +static void sh_dmac_disable_dma(struct dma_channel *chan) { - int irq = get_dmte_irq(info->chan); + int irq = get_dmte_irq(chan->chan); + u32 chcr; disable_irq(irq); - sh_dmac->channel[info->chan].chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); + + chcr = ctrl_inl(CHCR[chan->chan]); + chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); + ctrl_outl(chcr, CHCR[chan->chan]); } -static int sh_dmac_xfer_dma(struct dma_info *info) +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 (!info->configured) - sh_dmac_configure_channel(info, 0); + if (!(chan->flags & DMA_CONFIGURED)) + sh_dmac_configure_channel(chan, 0); - sh_dmac_disable_dma(info); - - /* + sh_dmac_disable_dma(chan); + + /* * Single-address mode usage note! * * It's important that we don't accidentally write any value to SAR/DAR @@ -177,33 +178,36 @@ static int sh_dmac_xfer_dma(struct dma_i * 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 (info->sar || (mach_is_dreamcast() && info->chan == 2)) - sh_dmac->channel[info->chan].sar = info->sar; - if (info->dar || (mach_is_dreamcast() && info->chan == 2)) - sh_dmac->channel[info->chan].dar = info->dar; - - sh_dmac->channel[info->chan].dmatcr = info->count >> calc_xmit_shift(info); + if (chan->sar || (mach_is_dreamcast() && chan->chan == 2)) + ctrl_outl(chan->sar, SAR[chan->chan]); + if (chan->dar || (mach_is_dreamcast() && chan->chan == 2)) + ctrl_outl(chan->dar, DAR[chan->chan]); + + ctrl_outl(chan->count >> calc_xmit_shift(chan), DMATCR[chan->chan]); - sh_dmac_enable_dma(info); + sh_dmac_enable_dma(chan); return 0; } -static int sh_dmac_get_dma_residue(struct dma_info *info) +static int sh_dmac_get_dma_residue(struct dma_channel *chan) { - if (!(sh_dmac->channel[info->chan].chcr & CHCR_DE)) + if (!(ctrl_inl(CHCR[chan->chan]) & CHCR_DE)) return 0; - return sh_dmac->channel[info->chan].dmatcr << calc_xmit_shift(info); + return ctrl_inl(DMATCR[chan->chan]) << calc_xmit_shift(chan); } #if defined(CONFIG_CPU_SH4) static irqreturn_t dma_err(int irq, void *dev_id, struct pt_regs *regs) { - printk("DMAE: DMAOR=%lx\n", sh_dmac->dmaor); + unsigned long dmaor = ctrl_inl(DMAOR); + + printk("DMAE: DMAOR=%lx\n", dmaor); - sh_dmac->dmaor &= ~(DMAOR_NMIF | DMAOR_AE); - sh_dmac->dmaor |= DMAOR_DME; + ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_NMIF, DMAOR); + ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_AE, DMAOR); + ctrl_outl(ctrl_inl(DMAOR)|DMAOR_DME, DMAOR); disable_irq(irq); @@ -212,16 +216,23 @@ static irqreturn_t dma_err(int irq, void #endif static struct dma_ops sh_dmac_ops = { - .name = "SuperH DMAC", .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 = "SuperH DMAC", + .nr_channels = 4, + .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; #ifdef CONFIG_CPU_SH4 @@ -231,18 +242,15 @@ static int __init sh_dmac_init(void) return i; #endif - for (i = 0; i < MAX_DMAC_CHANNELS; i++) { + for (i = 0; i < info->nr_channels; i++) { int irq = get_dmte_irq(i); make_ipr_irq(irq, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY); - - dma_info[i].ops = &sh_dmac_ops; - dma_info[i].tei_capable = 1; } - sh_dmac->dmaor |= 0x8000 | DMAOR_DME; + ctrl_outl(0x8000 | DMAOR_DME, DMAOR); - return register_dmac(&sh_dmac_ops); + return register_dmac(info); } static void __exit sh_dmac_exit(void) diff -puN /dev/null arch/sh/drivers/dma/dma-sysfs.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/arch/sh/drivers/dma/dma-sysfs.c 2004-06-23 20:00:14.634465248 -0700 @@ -0,0 +1,133 @@ +/* + * arch/sh/drivers/dma/dma-sysfs.c + * + * sysfs interface for SH DMA API + * + * Copyright (C) 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 +#include +#include +#include +#include + +static struct sysdev_class dma_sysclass = { + set_kset_name("dma"), +}; + +EXPORT_SYMBOL(dma_sysclass); + +static ssize_t dma_show_devices(struct sys_device *dev, char *buf) +{ + ssize_t len = 0; + int i; + + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + struct dma_info *info = get_dma_info(i); + struct dma_channel *channel = &info->channels[i]; + + len += sprintf(buf + len, "%2d: %14s %s\n", + channel->chan, info->name, + channel->dev_id); + } + + return len; +} + +static SYSDEV_ATTR(devices, S_IRUGO, dma_show_devices, NULL); + +static int __init dma_sysclass_init(void) +{ + int ret; + + ret = sysdev_class_register(&dma_sysclass); + if (ret == 0) + sysfs_create_file(&dma_sysclass.kset.kobj, &attr_devices.attr); + + return ret; +} + +postcore_initcall(dma_sysclass_init); + +static ssize_t dma_show_dev_id(struct sys_device *dev, 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 sys_device *dev, + const char *buf, size_t count) +{ + struct dma_channel *channel = to_dma_channel(dev); + strcpy(channel->dev_id, buf); + return count; +} + +static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id); + +static ssize_t dma_store_config(struct sys_device *dev, + 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->chan, config); + + return count; +} + +static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config); + +static ssize_t dma_show_mode(struct sys_device *dev, 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 sys_device *dev, + const char *buf, size_t count) +{ + struct dma_channel *channel = to_dma_channel(dev); + channel->mode = simple_strtoul(buf, NULL, 0); + return count; +} + +static SYSDEV_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 sys_device *dev, char *buf) \ +{ \ + struct dma_channel *channel = to_dma_channel(dev); \ + return sprintf(buf, fmt, channel->field); \ +} \ +static SYSDEV_ATTR(field, S_IRUGO, dma_show_##field, NULL); + +dma_ro_attr(count, "0x%08x\n"); +dma_ro_attr(flags, "0x%08lx\n"); + +int __init dma_create_sysfs_files(struct dma_channel *chan) +{ + struct sys_device *dev = &chan->dev; + int ret; + + dev->id = chan->chan; + dev->cls = &dma_sysclass; + + ret = sysdev_register(dev); + if (ret) + return ret; + + sysdev_create_file(dev, &attr_dev_id); + sysdev_create_file(dev, &attr_count); + sysdev_create_file(dev, &attr_mode); + sysdev_create_file(dev, &attr_flags); + sysdev_create_file(dev, &attr_config); + + return 0; +} + diff -puN arch/sh/drivers/dma/Makefile~sh-dma-driver-updates arch/sh/drivers/dma/Makefile --- 25/arch/sh/drivers/dma/Makefile~sh-dma-driver-updates 2004-06-23 20:00:14.623466920 -0700 +++ 25-akpm/arch/sh/drivers/dma/Makefile 2004-06-23 20:00:14.634465248 -0700 @@ -3,6 +3,7 @@ # obj-y += dma-api.o dma-isa.o +obj-$(CONFIG_SYSFS) += dma-sysfs.o obj-$(CONFIG_SH_DMA) += dma-sh.o obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o diff -puN include/asm-sh/cpu-sh4/dma.h~sh-dma-driver-updates include/asm-sh/cpu-sh4/dma.h --- 25/include/asm-sh/cpu-sh4/dma.h~sh-dma-driver-updates 2004-06-23 20:00:14.624466768 -0700 +++ 25-akpm/include/asm-sh/cpu-sh4/dma.h 2004-06-23 20:00:14.635465096 -0700 @@ -3,5 +3,15 @@ #define SH_DMAC_BASE 0xffa00000 +#define SAR ((unsigned long[]){SH_DMAC_BASE + 0x00, SH_DMAC_BASE + 0x10, \ + SH_DMAC_BASE + 0x20, SH_DMAC_BASE + 0x30}) +#define DAR ((unsigned long[]){SH_DMAC_BASE + 0x04, SH_DMAC_BASE + 0x14, \ + SH_DMAC_BASE + 0x24, SH_DMAC_BASE + 0x34}) +#define DMATCR ((unsigned long[]){SH_DMAC_BASE + 0x08, SH_DMAC_BASE + 0x18, \ + SH_DMAC_BASE + 0x28, SH_DMAC_BASE + 0x38}) +#define CHCR ((unsigned long[]){SH_DMAC_BASE + 0x0c, SH_DMAC_BASE + 0x1c, \ + SH_DMAC_BASE + 0x2c, SH_DMAC_BASE + 0x3c}) +#define DMAOR (SH_DMAC_BASE + 0x40) + #endif /* __ASM_CPU_SH4_DMA_H */ diff -puN include/asm-sh/dma.h~sh-dma-driver-updates include/asm-sh/dma.h --- 25/include/asm-sh/dma.h~sh-dma-driver-updates 2004-06-23 20:00:14.626466464 -0700 +++ 25-akpm/include/asm-sh/dma.h 2004-06-23 20:00:14.635465096 -0700 @@ -1,7 +1,7 @@ /* * include/asm-sh/dma.h * - * Copyright (C) 2003 Paul Mundt + * 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 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,7 @@ # define MAX_DMA_CHANNELS (CONFIG_NR_ONCHIP_DMA_CHANNELS) #endif -/* +/* * Read and write modes can mean drastically different things depending on the * channel configuration. Consult your DMAC documentation and module * implementation for further clues. @@ -38,40 +39,69 @@ #define DMA_MODE_WRITE 0x01 #define DMA_MODE_MASK 0x01 +#define DMA_AUTOINIT 0x10 + +/* + * DMAC (dma_info) flags + */ +enum { + DMAC_CHANNELS_CONFIGURED = 0x00, + DMAC_CHANNELS_TEI_CAPABLE = 0x01, +}; + +/* + * DMA channel capabilities / flags + */ +enum { + DMA_CONFIGURED = 0x00, + DMA_TEI_CAPABLE = 0x01, +}; + extern spinlock_t dma_spin_lock; -struct dma_info; +struct dma_channel; struct dma_ops { - const char *name; - - int (*request)(struct dma_info *info); - void (*free)(struct dma_info *info); + int (*request)(struct dma_channel *chan); + void (*free)(struct dma_channel *chan); - int (*get_residue)(struct dma_info *info); - int (*xfer)(struct dma_info *info); - void (*configure)(struct dma_info *info, unsigned long flags); + int (*get_residue)(struct dma_channel *chan); + int (*xfer)(struct dma_channel *chan); + void (*configure)(struct dma_channel *chan, unsigned long flags); }; -struct dma_info { - const char *dev_id; +struct dma_channel { + char dev_id[16]; unsigned int chan; unsigned int mode; unsigned int count; - + unsigned long sar; unsigned long dar; - unsigned int configured:1; - unsigned int tei_capable:1; + unsigned long flags; atomic_t busy; struct semaphore sem; wait_queue_head_t wait_queue; + + struct sys_device dev; +}; + +struct dma_info { + const char *name; + unsigned int nr_channels; + unsigned long flags; + struct dma_ops *ops; + struct dma_channel *channels; + + struct list_head list; }; +#define to_dma_channel(channel) container_of(channel, struct dma_channel, dev) + /* arch/sh/drivers/dma/dma-api.c */ extern int dma_xfer(unsigned int chan, unsigned long from, unsigned long to, size_t size, unsigned int mode); @@ -90,17 +120,22 @@ extern int request_dma(unsigned int chan extern void free_dma(unsigned int chan); extern int get_dma_residue(unsigned int chan); extern struct dma_info *get_dma_info(unsigned int chan); +extern struct dma_channel *get_dma_channel(unsigned int chan); extern void dma_wait_for_completion(unsigned int chan); extern void dma_configure_channel(unsigned int chan, unsigned long flags); -extern int register_dmac(struct dma_ops *ops); +extern int register_dmac(struct dma_info *info); +extern void unregister_dmac(struct dma_info *info); -extern struct dma_info dma_info[]; +#ifdef CONFIG_SYSFS +/* arch/sh/drivers/dma/dma-sysfs.c */ +extern int dma_create_sysfs_files(struct dma_channel *); +#endif #ifdef CONFIG_PCI extern int isa_dma_bridge_buggy; #else -#define isa_dma_bridge_buggy (0) +#define isa_dma_bridge_buggy (0) #endif #endif /* __ASM_SH_DMA_H */ _