linux/arch/arm/mach-s3c2410/dma.c
<<
>>
Prefs
   1/* linux/arch/arm/mach-bast/dma.c
   2 *
   3 * (c) 2003,2004 Simtec Electronics
   4 *      Ben Dooks <ben@simtec.co.uk>
   5 *
   6 * S3C2410 DMA core
   7 *
   8 * http://www.simtec.co.uk/products/EB2410ITX/
   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 * Changelog:
  15 *  18-Nov-2004 BJD  Removed error for loading onto stopped channel
  16 *  10-Nov-2004 BJD  Ensure all external symbols exported for modules
  17 *  10-Nov-2004 BJD  Use sys_device and sysdev_class for power management
  18 *  08-Aug-2004 BJD  Apply rmk's suggestions
  19 *  21-Jul-2004 BJD  Ported to linux 2.6
  20 *  12-Jul-2004 BJD  Finished re-write and change of API
  21 *  06-Jul-2004 BJD  Rewrote dma code to try and cope with various problems
  22 *  23-May-2003 BJD  Created file
  23 *  19-Aug-2003 BJD  Cleanup, header fix, added URL
  24 *
  25 * This file is based on the Sangwook Lee/Samsung patches, re-written due
  26 * to various ommisions from the code (such as flexible dma configuration)
  27 * for use with the BAST system board.
  28 *
  29 * The re-write is pretty much complete, and should be good enough for any
  30 * possible DMA function
  31 */
  32
  33#include <linux/config.h>
  34
  35#ifdef CONFIG_S3C2410_DMA_DEBUG
  36#define DEBUG
  37#endif
  38
  39#include <linux/module.h>
  40#include <linux/init.h>
  41#include <linux/sched.h>
  42#include <linux/spinlock.h>
  43#include <linux/interrupt.h>
  44#include <linux/sysdev.h>
  45#include <linux/slab.h>
  46#include <linux/errno.h>
  47#include <linux/delay.h>
  48
  49#include <asm/system.h>
  50#include <asm/irq.h>
  51#include <asm/hardware.h>
  52#include <asm/io.h>
  53#include <asm/dma.h>
  54
  55#include <asm/mach/dma.h>
  56#include <asm/arch/map.h>
  57
  58/* io map for dma */
  59static void __iomem *dma_base;
  60
  61/* dma channel state information */
  62s3c2410_dma_chan_t s3c2410_chans[S3C2410_DMA_CHANNELS];
  63
  64/* debugging functions */
  65
  66#define BUF_MAGIC (0xcafebabe)
  67
  68#define dmawarn(fmt...) printk(KERN_DEBUG fmt)
  69
  70#define dma_regaddr(chan, reg) ((chan)->regs + (reg))
  71
  72#if 1
  73#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg))
  74#else
  75static inline void
  76dma_wrreg(s3c2410_dma_chan_t *chan, int reg, unsigned long val)
  77{
  78        pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg);
  79        writel(val, dma_regaddr(chan, reg));
  80}
  81
  82#endif
  83
  84#define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
  85
  86/* captured register state for debug */
  87
  88struct s3c2410_dma_regstate {
  89        unsigned long         dcsrc;
  90        unsigned long         disrc;
  91        unsigned long         dstat;
  92        unsigned long         dcon;
  93        unsigned long         dmsktrig;
  94};
  95
  96#ifdef CONFIG_S3C2410_DMA_DEBUG
  97
  98/* dmadbg_showregs
  99 *
 100 * simple debug routine to print the current state of the dma registers
 101*/
 102
 103static void
 104dmadbg_capture(s3c2410_dma_chan_t *chan, struct s3c2410_dma_regstate *regs)
 105{
 106        regs->dcsrc    = dma_rdreg(chan, S3C2410_DMA_DCSRC);
 107        regs->disrc    = dma_rdreg(chan, S3C2410_DMA_DISRC);
 108        regs->dstat    = dma_rdreg(chan, S3C2410_DMA_DSTAT);
 109        regs->dcon     = dma_rdreg(chan, S3C2410_DMA_DCON);
 110        regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
 111}
 112
 113static void
 114dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan,
 115                 struct s3c2410_dma_regstate *regs)
 116{
 117        printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
 118               chan->number, fname, line,
 119               regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig,
 120               regs->dcon);
 121}
 122
 123static void
 124dmadbg_showchan(const char *fname, int line, s3c2410_dma_chan_t *chan)
 125{
 126        struct s3c2410_dma_regstate state;
 127
 128        dmadbg_capture(chan, &state);
 129
 130        printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n",
 131               chan->number, fname, line, chan->load_state,
 132               chan->curr, chan->next, chan->end);
 133
 134        dmadbg_showregs(fname, line, chan, &state);
 135}
 136
 137#define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan))
 138#define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan))
 139#else
 140#define dbg_showregs(chan) do { } while(0)
 141#define dbg_showchan(chan) do { } while(0)
 142#endif /* CONFIG_S3C2410_DMA_DEBUG */
 143
 144#define check_channel(chan) \
 145  do { if ((chan) >= S3C2410_DMA_CHANNELS) { \
 146    printk(KERN_ERR "%s: invalid channel %d\n", __FUNCTION__, (chan)); \
 147    return -EINVAL; \
 148  } } while(0)
 149
 150
 151/* s3c2410_dma_stats_timeout
 152 *
 153 * Update DMA stats from timeout info
 154*/
 155
 156static void
 157s3c2410_dma_stats_timeout(s3c2410_dma_stats_t *stats, int val)
 158{
 159        if (stats == NULL)
 160                return;
 161
 162        if (val > stats->timeout_longest)
 163                stats->timeout_longest = val;
 164        if (val < stats->timeout_shortest)
 165                stats->timeout_shortest = val;
 166
 167        stats->timeout_avg += val;
 168}
 169
 170/* s3c2410_dma_waitforload
 171 *
 172 * wait for the DMA engine to load a buffer, and update the state accordingly
 173*/
 174
 175static int
 176s3c2410_dma_waitforload(s3c2410_dma_chan_t *chan, int line)
 177{
 178        int timeout = chan->load_timeout;
 179        int took;
 180
 181        if (chan->load_state != S3C2410_DMALOAD_1LOADED) {
 182                printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line);
 183                return 0;
 184        }
 185
 186        if (chan->stats != NULL)
 187                chan->stats->loads++;
 188
 189        while (--timeout > 0) {
 190                if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) {
 191                        took = chan->load_timeout - timeout;
 192
 193                        s3c2410_dma_stats_timeout(chan->stats, took);
 194
 195                        switch (chan->load_state) {
 196                        case S3C2410_DMALOAD_1LOADED:
 197                                chan->load_state = S3C2410_DMALOAD_1RUNNING;
 198                                break;
 199
 200                        default:
 201                                printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state);
 202                        }
 203
 204                        return 1;
 205                }
 206        }
 207
 208        if (chan->stats != NULL) {
 209                chan->stats->timeout_failed++;
 210        }
 211
 212        return 0;
 213}
 214
 215
 216
 217/* s3c2410_dma_loadbuffer
 218 *
 219 * load a buffer, and update the channel state
 220*/
 221
 222static inline int
 223s3c2410_dma_loadbuffer(s3c2410_dma_chan_t *chan,
 224                       s3c2410_dma_buf_t *buf)
 225{
 226        unsigned long reload;
 227
 228        pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n",
 229                 buf, (unsigned long)buf->data, buf->size);
 230
 231        if (buf == NULL) {
 232                dmawarn("buffer is NULL\n");
 233                return -EINVAL;
 234        }
 235
 236        /* check the state of the channel before we do anything */
 237
 238        if (chan->load_state == S3C2410_DMALOAD_1LOADED) {
 239                dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n");
 240        }
 241
 242        if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {
 243                dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n");
 244        }
 245
 246        /* it would seem sensible if we are the last buffer to not bother
 247         * with the auto-reload bit, so that the DMA engine will not try
 248         * and load another transfer after this one has finished...
 249         */
 250        if (chan->load_state == S3C2410_DMALOAD_NONE) {
 251                pr_debug("load_state is none, checking for noreload (next=%p)\n",
 252                         buf->next);
 253                reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0;
 254        } else {
 255                pr_debug("load_state is %d => autoreload\n", chan->load_state);
 256                reload = S3C2410_DCON_AUTORELOAD;
 257        }
 258
 259        writel(buf->data, chan->addr_reg);
 260
 261        dma_wrreg(chan, S3C2410_DMA_DCON,
 262                  chan->dcon | reload | (buf->size/chan->xfer_unit));
 263
 264        chan->next = buf->next;
 265
 266        /* update the state of the channel */
 267
 268        switch (chan->load_state) {
 269        case S3C2410_DMALOAD_NONE:
 270                chan->load_state = S3C2410_DMALOAD_1LOADED;
 271                break;
 272
 273        case S3C2410_DMALOAD_1RUNNING:
 274                chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
 275                break;
 276
 277        default:
 278                dmawarn("dmaload: unknown state %d in loadbuffer\n",
 279                        chan->load_state);
 280                break;
 281        }
 282
 283        return 0;
 284}
 285
 286/* s3c2410_dma_call_op
 287 *
 288 * small routine to call the op routine with the given op if it has been
 289 * registered
 290*/
 291
 292static void
 293s3c2410_dma_call_op(s3c2410_dma_chan_t *chan, s3c2410_chan_op_t op)
 294{
 295        if (chan->op_fn != NULL) {
 296                (chan->op_fn)(chan, op);
 297        }
 298}
 299
 300/* s3c2410_dma_buffdone
 301 *
 302 * small wrapper to check if callback routine needs to be called, and
 303 * if so, call it
 304*/
 305
 306static inline void
 307s3c2410_dma_buffdone(s3c2410_dma_chan_t *chan, s3c2410_dma_buf_t *buf,
 308                     s3c2410_dma_buffresult_t result)
 309{
 310        pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n",
 311                 chan->callback_fn, buf, buf->id, buf->size, result);
 312
 313        if (chan->callback_fn != NULL) {
 314                (chan->callback_fn)(chan, buf->id, buf->size, result);
 315        }
 316}
 317
 318/* s3c2410_dma_start
 319 *
 320 * start a dma channel going
 321*/
 322
 323static int s3c2410_dma_start(s3c2410_dma_chan_t *chan)
 324{
 325        unsigned long tmp;
 326        unsigned long flags;
 327
 328        pr_debug("s3c2410_start_dma: channel=%d\n", chan->number);
 329
 330        local_irq_save(flags);
 331
 332        if (chan->state == S3C2410_DMA_RUNNING) {
 333                pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state);
 334                local_irq_restore(flags);
 335                return 0;
 336        }
 337
 338        chan->state = S3C2410_DMA_RUNNING;
 339
 340        /* check wether there is anything to load, and if not, see
 341         * if we can find anything to load
 342         */
 343
 344        if (chan->load_state == S3C2410_DMALOAD_NONE) {
 345                if (chan->next == NULL) {
 346                        printk(KERN_ERR "dma%d: channel has nothing loaded\n",
 347                               chan->number);
 348                        chan->state = S3C2410_DMA_IDLE;
 349                        local_irq_restore(flags);
 350                        return -EINVAL;
 351                }
 352
 353                s3c2410_dma_loadbuffer(chan, chan->next);
 354        }
 355
 356        dbg_showchan(chan);
 357
 358        /* enable the channel */
 359
 360        if (!chan->irq_enabled) {
 361                enable_irq(chan->irq);
 362                chan->irq_enabled = 1;
 363        }
 364
 365        /* start the channel going */
 366
 367        tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
 368        tmp &= ~S3C2410_DMASKTRIG_STOP;
 369        tmp |= S3C2410_DMASKTRIG_ON;
 370        dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
 371
 372        pr_debug("wrote %08lx to DMASKTRIG\n", tmp);
 373
 374#if 0
 375        /* the dma buffer loads should take care of clearing the AUTO
 376         * reloading feature */
 377        tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
 378        tmp &= ~S3C2410_DCON_NORELOAD;
 379        dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
 380#endif
 381
 382        s3c2410_dma_call_op(chan, S3C2410_DMAOP_START);
 383
 384        dbg_showchan(chan);
 385
 386        local_irq_restore(flags);
 387        return 0;
 388}
 389
 390/* s3c2410_dma_canload
 391 *
 392 * work out if we can queue another buffer into the DMA engine
 393*/
 394
 395static int
 396s3c2410_dma_canload(s3c2410_dma_chan_t *chan)
 397{
 398        if (chan->load_state == S3C2410_DMALOAD_NONE ||
 399            chan->load_state == S3C2410_DMALOAD_1RUNNING)
 400                return 1;
 401
 402        return 0;
 403}
 404
 405
 406/* s3c2410_dma_enqueue
 407 *
 408 * queue an given buffer for dma transfer.
 409 *
 410 * id         the device driver's id information for this buffer
 411 * data       the physical address of the buffer data
 412 * size       the size of the buffer in bytes
 413 *
 414 * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART
 415 * is checked, and if set, the channel is started. If this flag isn't set,
 416 * then an error will be returned.
 417 *
 418 * It is possible to queue more than one DMA buffer onto a channel at
 419 * once, and the code will deal with the re-loading of the next buffer
 420 * when necessary.
 421*/
 422
 423int s3c2410_dma_enqueue(unsigned int channel, void *id,
 424                        dma_addr_t data, int size)
 425{
 426        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 427        s3c2410_dma_buf_t *buf;
 428        unsigned long flags;
 429
 430        check_channel(channel);
 431
 432        pr_debug("%s: id=%p, data=%08x, size=%d\n",
 433                 __FUNCTION__, id, (unsigned int)data, size);
 434
 435        buf = (s3c2410_dma_buf_t *)kmalloc(sizeof(*buf), GFP_ATOMIC);
 436        if (buf == NULL) {
 437                pr_debug("%s: out of memory (%d alloc)\n",
 438                         __FUNCTION__, sizeof(*buf));
 439                return -ENOMEM;
 440        }
 441
 442        pr_debug("%s: new buffer %p\n", __FUNCTION__, buf);
 443
 444        //dbg_showchan(chan);
 445
 446        buf->next  = NULL;
 447        buf->data  = buf->ptr = data;
 448        buf->size  = size;
 449        buf->id    = id;
 450        buf->magic = BUF_MAGIC;
 451
 452        local_irq_save(flags);
 453
 454        if (chan->curr == NULL) {
 455                /* we've got nothing loaded... */
 456                pr_debug("%s: buffer %p queued onto empty channel\n",
 457                         __FUNCTION__, buf);
 458
 459                chan->curr = buf;
 460                chan->end  = buf;
 461                chan->next = NULL;
 462        } else {
 463                pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n",
 464                         chan->number, __FUNCTION__, buf);
 465
 466                if (chan->end == NULL)
 467                        pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n",
 468                                 chan->number, __FUNCTION__, chan);
 469
 470                chan->end->next = buf;
 471                chan->end = buf;
 472        }
 473
 474        /* if necessary, update the next buffer field */
 475        if (chan->next == NULL)
 476                chan->next = buf;
 477
 478        /* check to see if we can load a buffer */
 479        if (chan->state == S3C2410_DMA_RUNNING) {
 480                if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {
 481                        if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
 482                                printk(KERN_ERR "dma%d: loadbuffer:"
 483                                       "timeout loading buffer\n",
 484                                       chan->number);
 485                                dbg_showchan(chan);
 486                                local_irq_restore(flags);
 487                                return -EINVAL;
 488                        }
 489                }
 490
 491                while (s3c2410_dma_canload(chan) && chan->next != NULL) {
 492                        s3c2410_dma_loadbuffer(chan, chan->next);
 493                }
 494        } else if (chan->state == S3C2410_DMA_IDLE) {
 495                if (chan->flags & S3C2410_DMAF_AUTOSTART) {
 496                        s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START);
 497                }
 498        }
 499
 500        local_irq_restore(flags);
 501        return 0;
 502}
 503
 504EXPORT_SYMBOL(s3c2410_dma_enqueue);
 505
 506static inline void
 507s3c2410_dma_freebuf(s3c2410_dma_buf_t *buf)
 508{
 509        int magicok = (buf->magic == BUF_MAGIC);
 510
 511        buf->magic = -1;
 512
 513        if (magicok) {
 514                kfree(buf);
 515        } else {
 516                printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf);
 517        }
 518}
 519
 520/* s3c2410_dma_lastxfer
 521 *
 522 * called when the system is out of buffers, to ensure that the channel
 523 * is prepared for shutdown.
 524*/
 525
 526static inline void
 527s3c2410_dma_lastxfer(s3c2410_dma_chan_t *chan)
 528{
 529        pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n",
 530                 chan->number, chan->load_state);
 531
 532        switch (chan->load_state) {
 533        case S3C2410_DMALOAD_NONE:
 534                break;
 535
 536        case S3C2410_DMALOAD_1LOADED:
 537                if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
 538                                /* flag error? */
 539                        printk(KERN_ERR "dma%d: timeout waiting for load\n",
 540                               chan->number);
 541                        return;
 542                }
 543                break;
 544
 545        default:
 546                pr_debug("dma%d: lastxfer: unhandled load_state %d with no next",
 547                         chan->number, chan->load_state);
 548                return;
 549
 550        }
 551
 552        /* hopefully this'll shut the damned thing up after the transfer... */
 553        dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD);
 554}
 555
 556
 557#define dmadbg2(x...)
 558
 559static irqreturn_t
 560s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs)
 561{
 562        s3c2410_dma_chan_t *chan = (s3c2410_dma_chan_t *)devpw;
 563        s3c2410_dma_buf_t  *buf;
 564
 565        buf = chan->curr;
 566
 567        dbg_showchan(chan);
 568
 569        /* modify the channel state */
 570
 571        switch (chan->load_state) {
 572        case S3C2410_DMALOAD_1RUNNING:
 573                /* TODO - if we are running only one buffer, we probably
 574                 * want to reload here, and then worry about the buffer
 575                 * callback */
 576
 577                chan->load_state = S3C2410_DMALOAD_NONE;
 578                break;
 579
 580        case S3C2410_DMALOAD_1LOADED:
 581                /* iirc, we should go back to NONE loaded here, we
 582                 * had a buffer, and it was never verified as being
 583                 * loaded.
 584                 */
 585
 586                chan->load_state = S3C2410_DMALOAD_NONE;
 587                break;
 588
 589        case S3C2410_DMALOAD_1LOADED_1RUNNING:
 590                /* we'll worry about checking to see if another buffer is
 591                 * ready after we've called back the owner. This should
 592                 * ensure we do not wait around too long for the DMA
 593                 * engine to start the next transfer
 594                 */
 595
 596                chan->load_state = S3C2410_DMALOAD_1LOADED;
 597                break;
 598
 599        case S3C2410_DMALOAD_NONE:
 600                printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n",
 601                       chan->number);
 602                break;
 603
 604        default:
 605                printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n",
 606                       chan->number, chan->load_state);
 607                break;
 608        }
 609
 610        if (buf != NULL) {
 611                /* update the chain to make sure that if we load any more
 612                 * buffers when we call the callback function, things should
 613                 * work properly */
 614
 615                chan->curr = buf->next;
 616                buf->next  = NULL;
 617
 618                if (buf->magic != BUF_MAGIC) {
 619                        printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n",
 620                               chan->number, __FUNCTION__, buf);
 621                        return IRQ_HANDLED;
 622                }
 623
 624                s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);
 625
 626                /* free resouces */
 627                s3c2410_dma_freebuf(buf);
 628        } else {
 629        }
 630
 631        if (chan->next != NULL) {
 632                unsigned long flags;
 633
 634                switch (chan->load_state) {
 635                case S3C2410_DMALOAD_1RUNNING:
 636                        /* don't need to do anything for this state */
 637                        break;
 638
 639                case S3C2410_DMALOAD_NONE:
 640                        /* can load buffer immediately */
 641                        break;
 642
 643                case S3C2410_DMALOAD_1LOADED:
 644                        if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
 645                                /* flag error? */
 646                                printk(KERN_ERR "dma%d: timeout waiting for load\n",
 647                                       chan->number);
 648                                return IRQ_HANDLED;
 649                        }
 650
 651                        break;
 652
 653                case S3C2410_DMALOAD_1LOADED_1RUNNING:
 654                        goto no_load;
 655
 656                default:
 657                        printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n",
 658                               chan->number, chan->load_state);
 659                        return IRQ_HANDLED;
 660                }
 661
 662                local_irq_save(flags);
 663                s3c2410_dma_loadbuffer(chan, chan->next);
 664                local_irq_restore(flags);
 665        } else {
 666                s3c2410_dma_lastxfer(chan);
 667
 668                /* see if we can stop this channel.. */
 669                if (chan->load_state == S3C2410_DMALOAD_NONE) {
 670                        pr_debug("dma%d: end of transfer, stopping channel (%ld)\n",
 671                                 chan->number, jiffies);
 672                        s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);
 673                }
 674        }
 675
 676 no_load:
 677        return IRQ_HANDLED;
 678}
 679
 680
 681
 682/* s3c2410_request_dma
 683 *
 684 * get control of an dma channel
 685*/
 686
 687int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client,
 688                        void *dev)
 689{
 690        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 691        unsigned long flags;
 692        int err;
 693
 694        pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",
 695                 channel, client->name, dev);
 696
 697        check_channel(channel);
 698
 699        local_irq_save(flags);
 700
 701        dbg_showchan(chan);
 702
 703        if (chan->in_use) {
 704                if (client != chan->client) {
 705                        printk(KERN_ERR "dma%d: already in use\n", channel);
 706                        local_irq_restore(flags);
 707                        return -EBUSY;
 708                } else {
 709                        printk(KERN_ERR "dma%d: client already has channel\n", channel);
 710                }
 711        }
 712
 713        chan->client = client;
 714        chan->in_use = 1;
 715
 716        if (!chan->irq_claimed) {
 717                pr_debug("dma%d: %s : requesting irq %d\n",
 718                         channel, __FUNCTION__, chan->irq);
 719
 720                err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT,
 721                                  client->name, (void *)chan);
 722
 723                if (err) {
 724                        chan->in_use = 0;
 725                        local_irq_restore(flags);
 726
 727                        printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",
 728                               client->name, chan->irq, chan->number);
 729                        return err;
 730                }
 731
 732                chan->irq_claimed = 1;
 733                chan->irq_enabled = 1;
 734        }
 735
 736        local_irq_restore(flags);
 737
 738        /* need to setup */
 739
 740        pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan);
 741
 742        return 0;
 743}
 744
 745EXPORT_SYMBOL(s3c2410_dma_request);
 746
 747/* s3c2410_dma_free
 748 *
 749 * release the given channel back to the system, will stop and flush
 750 * any outstanding transfers, and ensure the channel is ready for the
 751 * next claimant.
 752 *
 753 * Note, although a warning is currently printed if the freeing client
 754 * info is not the same as the registrant's client info, the free is still
 755 * allowed to go through.
 756*/
 757
 758int s3c2410_dma_free(dmach_t channel, s3c2410_dma_client_t *client)
 759{
 760        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 761        unsigned long flags;
 762
 763        check_channel(channel);
 764
 765        local_irq_save(flags);
 766
 767
 768        if (chan->client != client) {
 769                printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
 770                       channel, chan->client, client);
 771        }
 772
 773        /* sort out stopping and freeing the channel */
 774
 775        if (chan->state != S3C2410_DMA_IDLE) {
 776                pr_debug("%s: need to stop dma channel %p\n",
 777                       __FUNCTION__, chan);
 778
 779                /* possibly flush the channel */
 780                s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP);
 781        }
 782
 783        chan->client = NULL;
 784        chan->in_use = 0;
 785
 786        local_irq_restore(flags);
 787
 788        return 0;
 789}
 790
 791EXPORT_SYMBOL(s3c2410_dma_free);
 792
 793static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan)
 794{
 795        unsigned long tmp;
 796        unsigned long flags;
 797
 798        pr_debug("%s:\n", __FUNCTION__);
 799
 800        dbg_showchan(chan);
 801
 802        local_irq_save(flags);
 803
 804        s3c2410_dma_call_op(chan,  S3C2410_DMAOP_STOP);
 805
 806        tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
 807        tmp |= S3C2410_DMASKTRIG_STOP;
 808        dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
 809
 810#if 0
 811        /* should also clear interrupts, according to WinCE BSP */
 812        tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
 813        tmp |= S3C2410_DCON_NORELOAD;
 814        dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
 815#endif
 816
 817        chan->state      = S3C2410_DMA_IDLE;
 818        chan->load_state = S3C2410_DMALOAD_NONE;
 819
 820        local_irq_restore(flags);
 821
 822        return 0;
 823}
 824
 825/* s3c2410_dma_flush
 826 *
 827 * stop the channel, and remove all current and pending transfers
 828*/
 829
 830static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan)
 831{
 832        s3c2410_dma_buf_t *buf, *next;
 833        unsigned long flags;
 834
 835        pr_debug("%s:\n", __FUNCTION__);
 836
 837        local_irq_save(flags);
 838
 839        if (chan->state != S3C2410_DMA_IDLE) {
 840                pr_debug("%s: stopping channel...\n", __FUNCTION__ );
 841                s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);
 842        }
 843
 844        buf = chan->curr;
 845        if (buf == NULL)
 846                buf = chan->next;
 847
 848        chan->curr = chan->next = chan->end = NULL;
 849
 850        if (buf != NULL) {
 851                for ( ; buf != NULL; buf = next) {
 852                        next = buf->next;
 853
 854                        pr_debug("%s: free buffer %p, next %p\n",
 855                               __FUNCTION__, buf, buf->next);
 856
 857                        s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT);
 858                        s3c2410_dma_freebuf(buf);
 859                }
 860        }
 861
 862        local_irq_restore(flags);
 863
 864        return 0;
 865}
 866
 867
 868int
 869s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op)
 870{
 871        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 872
 873        check_channel(channel);
 874
 875        switch (op) {
 876        case S3C2410_DMAOP_START:
 877                return s3c2410_dma_start(chan);
 878
 879        case S3C2410_DMAOP_STOP:
 880                return s3c2410_dma_dostop(chan);
 881
 882        case S3C2410_DMAOP_PAUSE:
 883                return -ENOENT;
 884
 885        case S3C2410_DMAOP_RESUME:
 886                return -ENOENT;
 887
 888        case S3C2410_DMAOP_FLUSH:
 889                return s3c2410_dma_flush(chan);
 890
 891        case S3C2410_DMAOP_TIMEOUT:
 892                return 0;
 893
 894        }
 895
 896        return -ENOENT;      /* unknown, don't bother */
 897}
 898
 899EXPORT_SYMBOL(s3c2410_dma_ctrl);
 900
 901/* DMA configuration for each channel
 902 *
 903 * DISRCC -> source of the DMA (AHB,APB)
 904 * DISRC  -> source address of the DMA
 905 * DIDSTC -> destination of the DMA (AHB,APD)
 906 * DIDST  -> destination address of the DMA
 907*/
 908
 909/* s3c2410_dma_config
 910 *
 911 * xfersize:     size of unit in bytes (1,2,4)
 912 * dcon:         base value of the DCONx register
 913*/
 914
 915int s3c2410_dma_config(dmach_t channel,
 916                       int xferunit,
 917                       int dcon)
 918{
 919        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 920
 921        pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n",
 922                 __FUNCTION__, channel, xferunit, dcon);
 923
 924        check_channel(channel);
 925
 926        switch (xferunit) {
 927        case 1:
 928                dcon |= S3C2410_DCON_BYTE;
 929                break;
 930
 931        case 2:
 932                dcon |= S3C2410_DCON_HALFWORD;
 933                break;
 934
 935        case 4:
 936                dcon |= S3C2410_DCON_WORD;
 937                break;
 938
 939        default:
 940                pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit);
 941                return -EINVAL;
 942        }
 943
 944        dcon |= S3C2410_DCON_HWTRIG;
 945        dcon |= S3C2410_DCON_INTREQ;
 946
 947        pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon);
 948
 949        chan->dcon = dcon;
 950        chan->xfer_unit = xferunit;
 951
 952        return 0;
 953}
 954
 955EXPORT_SYMBOL(s3c2410_dma_config);
 956
 957int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
 958{
 959        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 960
 961        check_channel(channel);
 962
 963        pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags);
 964
 965        chan->flags = flags;
 966
 967        return 0;
 968}
 969
 970EXPORT_SYMBOL(s3c2410_dma_setflags);
 971
 972
 973/* do we need to protect the settings of the fields from
 974 * irq?
 975*/
 976
 977int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn)
 978{
 979        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 980
 981        check_channel(channel);
 982
 983        pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn);
 984
 985        chan->op_fn = rtn;
 986
 987        return 0;
 988}
 989
 990EXPORT_SYMBOL(s3c2410_dma_set_opfn);
 991
 992int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
 993{
 994        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
 995
 996        check_channel(channel);
 997
 998        pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn);
 999
1000        chan->callback_fn = rtn;
1001
1002        return 0;
1003}
1004
1005EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn);
1006
1007/* s3c2410_dma_devconfig
1008 *
1009 * configure the dma source/destination hardware type and address
1010 *
1011 * source:    S3C2410_DMASRC_HW: source is hardware
1012 *            S3C2410_DMASRC_MEM: source is memory
1013 *
1014 * hwcfg:     the value for xxxSTCn register,
1015 *            bit 0: 0=increment pointer, 1=leave pointer
1016 *            bit 1: 0=soucre is AHB, 1=soucre is APB
1017 *
1018 * devaddr:   physical address of the source
1019*/
1020
1021int s3c2410_dma_devconfig(int channel,
1022                          s3c2410_dmasrc_t source,
1023                          int hwcfg,
1024                          unsigned long devaddr)
1025{
1026        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
1027
1028        check_channel(channel);
1029
1030        pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n",
1031                 __FUNCTION__, (int)source, hwcfg, devaddr);
1032
1033        chan->source = source;
1034        chan->dev_addr = devaddr;
1035
1036        switch (source) {
1037        case S3C2410_DMASRC_HW:
1038                /* source is hardware */
1039                pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n",
1040                         __FUNCTION__, devaddr, hwcfg);
1041                dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);
1042                dma_wrreg(chan, S3C2410_DMA_DISRC,  devaddr);
1043                dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));
1044
1045                chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
1046                return 0;
1047
1048        case S3C2410_DMASRC_MEM:
1049                /* source is memory */
1050                pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n",
1051                          __FUNCTION__, devaddr, hwcfg);
1052                dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));
1053                dma_wrreg(chan, S3C2410_DMA_DIDST,  devaddr);
1054                dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);
1055
1056                chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
1057                return 0;
1058        }
1059
1060        printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source);
1061        return -EINVAL;
1062}
1063
1064EXPORT_SYMBOL(s3c2410_dma_devconfig);
1065
1066/* s3c2410_dma_getposition
1067 *
1068 * returns the current transfer points for the dma source and destination
1069*/
1070
1071int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst)
1072{
1073        s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
1074
1075        check_channel(channel);
1076
1077        if (src != NULL)
1078                *src = dma_rdreg(chan, S3C2410_DMA_DCSRC);
1079
1080        if (dst != NULL)
1081                *dst = dma_rdreg(chan, S3C2410_DMA_DCDST);
1082
1083        return 0;
1084}
1085
1086EXPORT_SYMBOL(s3c2410_dma_getposition);
1087
1088
1089/* system device class */
1090
1091#ifdef CONFIG_PM
1092
1093static int s3c2410_dma_suspend(struct sys_device *dev, u32 state)
1094{
1095        s3c2410_dma_chan_t *cp = container_of(dev, s3c2410_dma_chan_t, dev);
1096
1097        printk(KERN_DEBUG "suspending dma channel %d\n", cp->number);
1098
1099        if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) {
1100                /* the dma channel is still working, which is probably
1101                 * a bad thing to do over suspend/resume. We stop the
1102                 * channel and assume that the client is either going to
1103                 * retry after resume, or that it is broken.
1104                 */
1105
1106                printk(KERN_INFO "dma: stopping channel %d due to suspend\n",
1107                       cp->number);
1108
1109                s3c2410_dma_dostop(cp);
1110        }
1111
1112        return 0;
1113}
1114
1115static int s3c2410_dma_resume(struct sys_device *dev)
1116{
1117        return 0;
1118}
1119
1120#else
1121#define s3c2410_dma_suspend NULL
1122#define s3c2410_dma_resume  NULL
1123#endif /* CONFIG_PM */
1124
1125static struct sysdev_class dma_sysclass = {
1126        set_kset_name("s3c24xx-dma"),
1127        .suspend        = s3c2410_dma_suspend,
1128        .resume         = s3c2410_dma_resume,
1129};
1130
1131/* initialisation code */
1132
1133static int __init s3c2410_init_dma(void)
1134{
1135        s3c2410_dma_chan_t *cp;
1136        int channel;
1137        int ret;
1138
1139        printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n");
1140
1141        dma_base = ioremap(S3C2410_PA_DMA, 0x200);
1142        if (dma_base == NULL) {
1143                printk(KERN_ERR "dma failed to remap register block\n");
1144                return -ENOMEM;
1145        }
1146
1147        ret = sysdev_class_register(&dma_sysclass);
1148        if (ret != 0) {
1149                printk(KERN_ERR "dma sysclass registration failed\n");
1150                goto err;
1151        }
1152
1153        for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) {
1154                cp = &s3c2410_chans[channel];
1155
1156                memset(cp, 0, sizeof(s3c2410_dma_chan_t));
1157
1158                /* dma channel irqs are in order.. */
1159                cp->number = channel;
1160                cp->irq    = channel + IRQ_DMA0;
1161                cp->regs   = dma_base + (channel*0x40);
1162
1163                /* point current stats somewhere */
1164                cp->stats  = &cp->stats_store;
1165                cp->stats_store.timeout_shortest = LONG_MAX;
1166
1167                /* basic channel configuration */
1168
1169                cp->load_timeout = 1<<18;
1170
1171                /* register system device */
1172
1173                cp->dev.cls = &dma_sysclass;
1174                cp->dev.id  = channel;
1175                ret = sysdev_register(&cp->dev);
1176
1177                printk("DMA channel %d at %p, irq %d\n",
1178                       cp->number, cp->regs, cp->irq);
1179        }
1180
1181        return 0;
1182
1183 err:
1184        iounmap(dma_base);
1185        dma_base = NULL;
1186        return ret;
1187}
1188
1189__initcall(s3c2410_init_dma);
1190
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.