linux/arch/arm/plat-pxa/dma.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/plat-pxa/dma.c
   3 *
   4 *  PXA DMA registration and IRQ dispatching
   5 *
   6 *  Author:     Nicolas Pitre
   7 *  Created:    Nov 15, 2001
   8 *  Copyright:  MontaVista Software Inc.
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License version 2 as
  12 *  published by the Free Software Foundation.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/slab.h>
  18#include <linux/kernel.h>
  19#include <linux/interrupt.h>
  20#include <linux/errno.h>
  21#include <linux/dma-mapping.h>
  22
  23#include <asm/system.h>
  24#include <asm/irq.h>
  25#include <asm/memory.h>
  26#include <mach/hardware.h>
  27#include <mach/dma.h>
  28
  29#define DMA_DEBUG_NAME          "pxa_dma"
  30#define DMA_MAX_REQUESTERS      64
  31
  32struct dma_channel {
  33        char *name;
  34        pxa_dma_prio prio;
  35        void (*irq_handler)(int, void *);
  36        void *data;
  37        spinlock_t lock;
  38};
  39
  40static struct dma_channel *dma_channels;
  41static int num_dma_channels;
  42
  43/*
  44 * Debug fs
  45 */
  46#ifdef CONFIG_DEBUG_FS
  47#include <linux/debugfs.h>
  48#include <linux/uaccess.h>
  49#include <linux/seq_file.h>
  50
  51static struct dentry *dbgfs_root, *dbgfs_state, **dbgfs_chan;
  52
  53static int dbg_show_requester_chan(struct seq_file *s, void *p)
  54{
  55        int pos = 0;
  56        int chan = (int)s->private;
  57        int i;
  58        u32 drcmr;
  59
  60        pos += seq_printf(s, "DMA channel %d requesters list :\n", chan);
  61        for (i = 0; i < DMA_MAX_REQUESTERS; i++) {
  62                drcmr = DRCMR(i);
  63                if ((drcmr & DRCMR_CHLNUM) == chan)
  64                        pos += seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i,
  65                                          !!(drcmr & DRCMR_MAPVLD));
  66        }
  67        return pos;
  68}
  69
  70static inline int dbg_burst_from_dcmd(u32 dcmd)
  71{
  72        int burst = (dcmd >> 16) & 0x3;
  73
  74        return burst ? 4 << burst : 0;
  75}
  76
  77static int is_phys_valid(unsigned long addr)
  78{
  79        return pfn_valid(__phys_to_pfn(addr));
  80}
  81
  82#define DCSR_STR(flag) (dcsr & DCSR_##flag ? #flag" " : "")
  83#define DCMD_STR(flag) (dcmd & DCMD_##flag ? #flag" " : "")
  84
  85static int dbg_show_descriptors(struct seq_file *s, void *p)
  86{
  87        int pos = 0;
  88        int chan = (int)s->private;
  89        int i, max_show = 20, burst, width;
  90        u32 dcmd;
  91        unsigned long phys_desc;
  92        struct pxa_dma_desc *desc;
  93        unsigned long flags;
  94
  95        spin_lock_irqsave(&dma_channels[chan].lock, flags);
  96        phys_desc = DDADR(chan);
  97
  98        pos += seq_printf(s, "DMA channel %d descriptors :\n", chan);
  99        pos += seq_printf(s, "[%03d] First descriptor unknown\n", 0);
 100        for (i = 1; i < max_show && is_phys_valid(phys_desc); i++) {
 101                desc = phys_to_virt(phys_desc);
 102                dcmd = desc->dcmd;
 103                burst = dbg_burst_from_dcmd(dcmd);
 104                width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
 105
 106                pos += seq_printf(s, "[%03d] Desc at %08lx(virt %p)\n",
 107                                  i, phys_desc, desc);
 108                pos += seq_printf(s, "\tDDADR = %08x\n", desc->ddadr);
 109                pos += seq_printf(s, "\tDSADR = %08x\n", desc->dsadr);
 110                pos += seq_printf(s, "\tDTADR = %08x\n", desc->dtadr);
 111                pos += seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d"
 112                                  " width=%d len=%d)\n",
 113                                  dcmd,
 114                                  DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR),
 115                                  DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG),
 116                                  DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN),
 117                                  DCMD_STR(ENDIAN), burst, width,
 118                                  dcmd & DCMD_LENGTH);
 119                phys_desc = desc->ddadr;
 120        }
 121        if (i == max_show)
 122                pos += seq_printf(s, "[%03d] Desc at %08lx ... max display reached\n",
 123                                  i, phys_desc);
 124        else
 125                pos += seq_printf(s, "[%03d] Desc at %08lx is %s\n",
 126                                  i, phys_desc, phys_desc == DDADR_STOP ?
 127                                  "DDADR_STOP" : "invalid");
 128
 129        spin_unlock_irqrestore(&dma_channels[chan].lock, flags);
 130        return pos;
 131}
 132
 133static int dbg_show_chan_state(struct seq_file *s, void *p)
 134{
 135        int pos = 0;
 136        int chan = (int)s->private;
 137        u32 dcsr, dcmd;
 138        int burst, width;
 139        static char *str_prio[] = { "high", "normal", "low" };
 140
 141        dcsr = DCSR(chan);
 142        dcmd = DCMD(chan);
 143        burst = dbg_burst_from_dcmd(dcmd);
 144        width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
 145
 146        pos += seq_printf(s, "DMA channel %d\n", chan);
 147        pos += seq_printf(s, "\tPriority : %s\n",
 148                          str_prio[dma_channels[chan].prio]);
 149        pos += seq_printf(s, "\tUnaligned transfer bit: %s\n",
 150                          DALGN & (1 << chan) ? "yes" : "no");
 151        pos += seq_printf(s, "\tDCSR  = %08x (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
 152                          dcsr, DCSR_STR(RUN), DCSR_STR(NODESC),
 153                          DCSR_STR(STOPIRQEN), DCSR_STR(EORIRQEN),
 154                          DCSR_STR(EORJMPEN), DCSR_STR(EORSTOPEN),
 155                          DCSR_STR(SETCMPST), DCSR_STR(CLRCMPST),
 156                          DCSR_STR(CMPST), DCSR_STR(EORINTR), DCSR_STR(REQPEND),
 157                          DCSR_STR(STOPSTATE), DCSR_STR(ENDINTR),
 158                          DCSR_STR(STARTINTR), DCSR_STR(BUSERR));
 159
 160        pos += seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d width=%d"
 161                          " len=%d)\n",
 162                          dcmd,
 163                          DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR),
 164                          DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG),
 165                          DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN),
 166                          DCMD_STR(ENDIAN), burst, width, dcmd & DCMD_LENGTH);
 167        pos += seq_printf(s, "\tDSADR = %08x\n", DSADR(chan));
 168        pos += seq_printf(s, "\tDTADR = %08x\n", DTADR(chan));
 169        pos += seq_printf(s, "\tDDADR = %08x\n", DDADR(chan));
 170        return pos;
 171}
 172
 173static int dbg_show_state(struct seq_file *s, void *p)
 174{
 175        int pos = 0;
 176
 177        /* basic device status */
 178        pos += seq_printf(s, "DMA engine status\n");
 179        pos += seq_printf(s, "\tChannel number: %d\n", num_dma_channels);
 180
 181        return pos;
 182}
 183
 184#define DBGFS_FUNC_DECL(name) \
 185static int dbg_open_##name(struct inode *inode, struct file *file) \
 186{ \
 187        return single_open(file, dbg_show_##name, inode->i_private); \
 188} \
 189static const struct file_operations dbg_fops_##name = { \
 190        .owner          = THIS_MODULE, \
 191        .open           = dbg_open_##name, \
 192        .llseek         = seq_lseek, \
 193        .read           = seq_read, \
 194        .release        = single_release, \
 195}
 196
 197DBGFS_FUNC_DECL(state);
 198DBGFS_FUNC_DECL(chan_state);
 199DBGFS_FUNC_DECL(descriptors);
 200DBGFS_FUNC_DECL(requester_chan);
 201
 202static struct dentry *pxa_dma_dbg_alloc_chan(int ch, struct dentry *chandir)
 203{
 204        char chan_name[11];
 205        struct dentry *chan, *chan_state = NULL, *chan_descr = NULL;
 206        struct dentry *chan_reqs = NULL;
 207        void *dt;
 208
 209        scnprintf(chan_name, sizeof(chan_name), "%d", ch);
 210        chan = debugfs_create_dir(chan_name, chandir);
 211        dt = (void *)ch;
 212
 213        if (chan)
 214                chan_state = debugfs_create_file("state", 0400, chan, dt,
 215                                                 &dbg_fops_chan_state);
 216        if (chan_state)
 217                chan_descr = debugfs_create_file("descriptors", 0400, chan, dt,
 218                                                 &dbg_fops_descriptors);
 219        if (chan_descr)
 220                chan_reqs = debugfs_create_file("requesters", 0400, chan, dt,
 221                                                &dbg_fops_requester_chan);
 222        if (!chan_reqs)
 223                goto err_state;
 224
 225        return chan;
 226
 227err_state:
 228        debugfs_remove_recursive(chan);
 229        return NULL;
 230}
 231
 232static void pxa_dma_init_debugfs(void)
 233{
 234        int i;
 235        struct dentry *chandir;
 236
 237        dbgfs_root = debugfs_create_dir(DMA_DEBUG_NAME, NULL);
 238        if (IS_ERR(dbgfs_root) || !dbgfs_root)
 239                goto err_root;
 240
 241        dbgfs_state = debugfs_create_file("state", 0400, dbgfs_root, NULL,
 242                                          &dbg_fops_state);
 243        if (!dbgfs_state)
 244                goto err_state;
 245
 246        dbgfs_chan = kmalloc(sizeof(*dbgfs_state) * num_dma_channels,
 247                             GFP_KERNEL);
 248        if (!dbgfs_chan)
 249                goto err_alloc;
 250
 251        chandir = debugfs_create_dir("channels", dbgfs_root);
 252        if (!chandir)
 253                goto err_chandir;
 254
 255        for (i = 0; i < num_dma_channels; i++) {
 256                dbgfs_chan[i] = pxa_dma_dbg_alloc_chan(i, chandir);
 257                if (!dbgfs_chan[i])
 258                        goto err_chans;
 259        }
 260
 261        return;
 262err_chans:
 263err_chandir:
 264        kfree(dbgfs_chan);
 265err_alloc:
 266err_state:
 267        debugfs_remove_recursive(dbgfs_root);
 268err_root:
 269        pr_err("pxa_dma: debugfs is not available\n");
 270}
 271
 272static void __exit pxa_dma_cleanup_debugfs(void)
 273{
 274        debugfs_remove_recursive(dbgfs_root);
 275}
 276#else
 277static inline void pxa_dma_init_debugfs(void) {}
 278static inline void pxa_dma_cleanup_debugfs(void) {}
 279#endif
 280
 281int pxa_request_dma (char *name, pxa_dma_prio prio,
 282                        void (*irq_handler)(int, void *),
 283                        void *data)
 284{
 285        unsigned long flags;
 286        int i, found = 0;
 287
 288        /* basic sanity checks */
 289        if (!name || !irq_handler)
 290                return -EINVAL;
 291
 292        local_irq_save(flags);
 293
 294        do {
 295                /* try grabbing a DMA channel with the requested priority */
 296                for (i = 0; i < num_dma_channels; i++) {
 297                        if ((dma_channels[i].prio == prio) &&
 298                            !dma_channels[i].name) {
 299                                found = 1;
 300                                break;
 301                        }
 302                }
 303                /* if requested prio group is full, try a hier priority */
 304        } while (!found && prio--);
 305
 306        if (found) {
 307                DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
 308                dma_channels[i].name = name;
 309                dma_channels[i].irq_handler = irq_handler;
 310                dma_channels[i].data = data;
 311        } else {
 312                printk (KERN_WARNING "No more available DMA channels for %s\n", name);
 313                i = -ENODEV;
 314        }
 315
 316        local_irq_restore(flags);
 317        return i;
 318}
 319EXPORT_SYMBOL(pxa_request_dma);
 320
 321void pxa_free_dma (int dma_ch)
 322{
 323        unsigned long flags;
 324
 325        if (!dma_channels[dma_ch].name) {
 326                printk (KERN_CRIT
 327                        "%s: trying to free channel %d which is already freed\n",
 328                        __func__, dma_ch);
 329                return;
 330        }
 331
 332        local_irq_save(flags);
 333        DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
 334        dma_channels[dma_ch].name = NULL;
 335        local_irq_restore(flags);
 336}
 337EXPORT_SYMBOL(pxa_free_dma);
 338
 339static irqreturn_t dma_irq_handler(int irq, void *dev_id)
 340{
 341        int i, dint = DINT;
 342        struct dma_channel *channel;
 343
 344        while (dint) {
 345                i = __ffs(dint);
 346                dint &= (dint - 1);
 347                channel = &dma_channels[i];
 348                if (channel->name && channel->irq_handler) {
 349                        channel->irq_handler(i, channel->data);
 350                } else {
 351                        /*
 352                         * IRQ for an unregistered DMA channel:
 353                         * let's clear the interrupts and disable it.
 354                         */
 355                        printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
 356                        DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
 357                }
 358        }
 359        return IRQ_HANDLED;
 360}
 361
 362int __init pxa_init_dma(int irq, int num_ch)
 363{
 364        int i, ret;
 365
 366        dma_channels = kzalloc(sizeof(struct dma_channel) * num_ch, GFP_KERNEL);
 367        if (dma_channels == NULL)
 368                return -ENOMEM;
 369
 370        /* dma channel priorities on pxa2xx processors:
 371         * ch 0 - 3,  16 - 19  <--> (0) DMA_PRIO_HIGH
 372         * ch 4 - 7,  20 - 23  <--> (1) DMA_PRIO_MEDIUM
 373         * ch 8 - 15, 24 - 31  <--> (2) DMA_PRIO_LOW
 374         */
 375        for (i = 0; i < num_ch; i++) {
 376                DCSR(i) = 0;
 377                dma_channels[i].prio = min((i & 0xf) >> 2, DMA_PRIO_LOW);
 378                spin_lock_init(&dma_channels[i].lock);
 379        }
 380
 381        ret = request_irq(irq, dma_irq_handler, IRQF_DISABLED, "DMA", NULL);
 382        if (ret) {
 383                printk (KERN_CRIT "Wow!  Can't register IRQ for DMA\n");
 384                kfree(dma_channels);
 385                return ret;
 386        }
 387        num_dma_channels = num_ch;
 388
 389        pxa_dma_init_debugfs();
 390
 391        return 0;
 392}
 393
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.