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