linux/arch/arm/mach-rpc/dma.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-rpc/dma.c
   3 *
   4 *  Copyright (C) 1998 Russell King
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 *  DMA functions specific to RiscPC architecture
  11 */
  12#include <linux/slab.h>
  13#include <linux/mman.h>
  14#include <linux/init.h>
  15#include <linux/interrupt.h>
  16#include <linux/dma-mapping.h>
  17#include <linux/io.h>
  18
  19#include <asm/page.h>
  20#include <asm/dma.h>
  21#include <asm/fiq.h>
  22#include <asm/irq.h>
  23#include <mach/hardware.h>
  24#include <asm/uaccess.h>
  25
  26#include <asm/mach/dma.h>
  27#include <asm/hardware/iomd.h>
  28
  29#if 0
  30typedef enum {
  31        dma_size_8      = 1,
  32        dma_size_16     = 2,
  33        dma_size_32     = 4,
  34        dma_size_128    = 16
  35} dma_size_t;
  36#endif
  37
  38#define TRANSFER_SIZE   2
  39
  40#define CURA    (0)
  41#define ENDA    (IOMD_IO0ENDA - IOMD_IO0CURA)
  42#define CURB    (IOMD_IO0CURB - IOMD_IO0CURA)
  43#define ENDB    (IOMD_IO0ENDB - IOMD_IO0CURA)
  44#define CR      (IOMD_IO0CR - IOMD_IO0CURA)
  45#define ST      (IOMD_IO0ST - IOMD_IO0CURA)
  46
  47static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma)
  48{
  49        unsigned long end, offset, flags = 0;
  50
  51        if (dma->sg) {
  52                sg->dma_address = dma->sg->dma_address;
  53                offset = sg->dma_address & ~PAGE_MASK;
  54
  55                end = offset + dma->sg->length;
  56
  57                if (end > PAGE_SIZE)
  58                        end = PAGE_SIZE;
  59
  60                if (offset + TRANSFER_SIZE >= end)
  61                        flags |= DMA_END_L;
  62
  63                sg->length = end - TRANSFER_SIZE;
  64
  65                dma->sg->length -= end - offset;
  66                dma->sg->dma_address += end - offset;
  67
  68                if (dma->sg->length == 0) {
  69                        if (dma->sgcount > 1) {
  70                                dma->sg++;
  71                                dma->sgcount--;
  72                        } else {
  73                                dma->sg = NULL;
  74                                flags |= DMA_END_S;
  75                        }
  76                }
  77        } else {
  78                flags = DMA_END_S | DMA_END_L;
  79                sg->dma_address = 0;
  80                sg->length = 0;
  81        }
  82
  83        sg->length |= flags;
  84}
  85
  86static irqreturn_t iomd_dma_handle(int irq, void *dev_id)
  87{
  88        dma_t *dma = (dma_t *)dev_id;
  89        unsigned long base = dma->dma_base;
  90
  91        do {
  92                unsigned int status;
  93
  94                status = iomd_readb(base + ST);
  95                if (!(status & DMA_ST_INT))
  96                        return IRQ_HANDLED;
  97
  98                if ((dma->state ^ status) & DMA_ST_AB)
  99                        iomd_get_next_sg(&dma->cur_sg, dma);
 100
 101                switch (status & (DMA_ST_OFL | DMA_ST_AB)) {
 102                case DMA_ST_OFL:                        /* OIA */
 103                case DMA_ST_AB:                         /* .IB */
 104                        iomd_writel(dma->cur_sg.dma_address, base + CURA);
 105                        iomd_writel(dma->cur_sg.length, base + ENDA);
 106                        dma->state = DMA_ST_AB;
 107                        break;
 108
 109                case DMA_ST_OFL | DMA_ST_AB:            /* OIB */
 110                case 0:                                 /* .IA */
 111                        iomd_writel(dma->cur_sg.dma_address, base + CURB);
 112                        iomd_writel(dma->cur_sg.length, base + ENDB);
 113                        dma->state = 0;
 114                        break;
 115                }
 116
 117                if (status & DMA_ST_OFL &&
 118                    dma->cur_sg.length == (DMA_END_S|DMA_END_L))
 119                        break;
 120        } while (1);
 121
 122        dma->state = ~DMA_ST_AB;
 123        disable_irq(irq);
 124
 125        return IRQ_HANDLED;
 126}
 127
 128static int iomd_request_dma(dmach_t channel, dma_t *dma)
 129{
 130        return request_irq(dma->dma_irq, iomd_dma_handle,
 131                           IRQF_DISABLED, dma->device_id, dma);
 132}
 133
 134static void iomd_free_dma(dmach_t channel, dma_t *dma)
 135{
 136        free_irq(dma->dma_irq, dma);
 137}
 138
 139static void iomd_enable_dma(dmach_t channel, dma_t *dma)
 140{
 141        unsigned long dma_base = dma->dma_base;
 142        unsigned int ctrl = TRANSFER_SIZE | DMA_CR_E;
 143
 144        if (dma->invalid) {
 145                dma->invalid = 0;
 146
 147                /*
 148                 * Cope with ISA-style drivers which expect cache
 149                 * coherence.
 150                 */
 151                if (!dma->sg) {
 152                        dma->sg = &dma->buf;
 153                        dma->sgcount = 1;
 154                        dma->buf.length = dma->count;
 155                        dma->buf.dma_address = dma_map_single(NULL,
 156                                dma->addr, dma->count,
 157                                dma->dma_mode == DMA_MODE_READ ?
 158                                DMA_FROM_DEVICE : DMA_TO_DEVICE);
 159                }
 160
 161                iomd_writeb(DMA_CR_C, dma_base + CR);
 162                dma->state = DMA_ST_AB;
 163        }
 164                
 165        if (dma->dma_mode == DMA_MODE_READ)
 166                ctrl |= DMA_CR_D;
 167
 168        iomd_writeb(ctrl, dma_base + CR);
 169        enable_irq(dma->dma_irq);
 170}
 171
 172static void iomd_disable_dma(dmach_t channel, dma_t *dma)
 173{
 174        unsigned long dma_base = dma->dma_base;
 175        unsigned long flags;
 176
 177        local_irq_save(flags);
 178        if (dma->state != ~DMA_ST_AB)
 179                disable_irq(dma->dma_irq);
 180        iomd_writeb(0, dma_base + CR);
 181        local_irq_restore(flags);
 182}
 183
 184static int iomd_set_dma_speed(dmach_t channel, dma_t *dma, int cycle)
 185{
 186        int tcr, speed;
 187
 188        if (cycle < 188)
 189                speed = 3;
 190        else if (cycle <= 250)
 191                speed = 2;
 192        else if (cycle < 438)
 193                speed = 1;
 194        else
 195                speed = 0;
 196
 197        tcr = iomd_readb(IOMD_DMATCR);
 198        speed &= 3;
 199
 200        switch (channel) {
 201        case DMA_0:
 202                tcr = (tcr & ~0x03) | speed;
 203                break;
 204
 205        case DMA_1:
 206                tcr = (tcr & ~0x0c) | (speed << 2);
 207                break;
 208
 209        case DMA_2:
 210                tcr = (tcr & ~0x30) | (speed << 4);
 211                break;
 212
 213        case DMA_3:
 214                tcr = (tcr & ~0xc0) | (speed << 6);
 215                break;
 216
 217        default:
 218                break;
 219        }
 220
 221        iomd_writeb(tcr, IOMD_DMATCR);
 222
 223        return speed;
 224}
 225
 226static struct dma_ops iomd_dma_ops = {
 227        .type           = "IOMD",
 228        .request        = iomd_request_dma,
 229        .free           = iomd_free_dma,
 230        .enable         = iomd_enable_dma,
 231        .disable        = iomd_disable_dma,
 232        .setspeed       = iomd_set_dma_speed,
 233};
 234
 235static struct fiq_handler fh = {
 236        .name   = "floppydma"
 237};
 238
 239static void floppy_enable_dma(dmach_t channel, dma_t *dma)
 240{
 241        void *fiqhandler_start;
 242        unsigned int fiqhandler_length;
 243        struct pt_regs regs;
 244
 245        if (dma->sg)
 246                BUG();
 247
 248        if (dma->dma_mode == DMA_MODE_READ) {
 249                extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
 250                fiqhandler_start = &floppy_fiqin_start;
 251                fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start;
 252        } else {
 253                extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
 254                fiqhandler_start = &floppy_fiqout_start;
 255                fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start;
 256        }
 257
 258        regs.ARM_r9  = dma->count;
 259        regs.ARM_r10 = (unsigned long)dma->addr;
 260        regs.ARM_fp  = (unsigned long)FLOPPYDMA_BASE;
 261
 262        if (claim_fiq(&fh)) {
 263                printk("floppydma: couldn't claim FIQ.\n");
 264                return;
 265        }
 266
 267        set_fiq_handler(fiqhandler_start, fiqhandler_length);
 268        set_fiq_regs(&regs);
 269        enable_fiq(dma->dma_irq);
 270}
 271
 272static void floppy_disable_dma(dmach_t channel, dma_t *dma)
 273{
 274        disable_fiq(dma->dma_irq);
 275        release_fiq(&fh);
 276}
 277
 278static int floppy_get_residue(dmach_t channel, dma_t *dma)
 279{
 280        struct pt_regs regs;
 281        get_fiq_regs(&regs);
 282        return regs.ARM_r9;
 283}
 284
 285static struct dma_ops floppy_dma_ops = {
 286        .type           = "FIQDMA",
 287        .enable         = floppy_enable_dma,
 288        .disable        = floppy_disable_dma,
 289        .residue        = floppy_get_residue,
 290};
 291
 292/*
 293 * This is virtual DMA - we don't need anything here.
 294 */
 295static void sound_enable_disable_dma(dmach_t channel, dma_t *dma)
 296{
 297}
 298
 299static struct dma_ops sound_dma_ops = {
 300        .type           = "VIRTUAL",
 301        .enable         = sound_enable_disable_dma,
 302        .disable        = sound_enable_disable_dma,
 303};
 304
 305void __init arch_dma_init(dma_t *dma)
 306{
 307        iomd_writeb(0, IOMD_IO0CR);
 308        iomd_writeb(0, IOMD_IO1CR);
 309        iomd_writeb(0, IOMD_IO2CR);
 310        iomd_writeb(0, IOMD_IO3CR);
 311
 312        iomd_writeb(0xa0, IOMD_DMATCR);
 313
 314        dma[DMA_0].dma_base             = IOMD_IO0CURA;
 315        dma[DMA_0].dma_irq              = IRQ_DMA0;
 316        dma[DMA_0].d_ops                = &iomd_dma_ops;
 317        dma[DMA_1].dma_base             = IOMD_IO1CURA;
 318        dma[DMA_1].dma_irq              = IRQ_DMA1;
 319        dma[DMA_1].d_ops                = &iomd_dma_ops;
 320        dma[DMA_2].dma_base             = IOMD_IO2CURA;
 321        dma[DMA_2].dma_irq              = IRQ_DMA2;
 322        dma[DMA_2].d_ops                = &iomd_dma_ops;
 323        dma[DMA_3].dma_base             = IOMD_IO3CURA;
 324        dma[DMA_3].dma_irq              = IRQ_DMA3;
 325        dma[DMA_3].d_ops                = &iomd_dma_ops;
 326        dma[DMA_S0].dma_base            = IOMD_SD0CURA;
 327        dma[DMA_S0].dma_irq             = IRQ_DMAS0;
 328        dma[DMA_S0].d_ops               = &iomd_dma_ops;
 329        dma[DMA_S1].dma_base            = IOMD_SD1CURA;
 330        dma[DMA_S1].dma_irq             = IRQ_DMAS1;
 331        dma[DMA_S1].d_ops               = &iomd_dma_ops;
 332        dma[DMA_VIRTUAL_FLOPPY].dma_irq = FIQ_FLOPPYDATA;
 333        dma[DMA_VIRTUAL_FLOPPY].d_ops   = &floppy_dma_ops;
 334        dma[DMA_VIRTUAL_SOUND].d_ops    = &sound_dma_ops;
 335
 336        /*
 337         * Setup DMA channels 2,3 to be for podules
 338         * and channels 0,1 for internal devices
 339         */
 340        iomd_writeb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT);
 341}
 342
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.