1/* 2 * arch/sh/kernel/dma.c 3 * 4 * Copyright (C) 2000 Takashi YOSHII 5 * 6 * PC like DMA API for SuperH's DMAC. 7 */ 8 9#include <linux/config.h> 10#include <linux/init.h> 11#include <linux/irq.h> 12#include <linux/interrupt.h> 13 14#include <asm/signal.h> 15#include <asm/dma.h> 16 17static struct dma_info_t *dma_info[MAX_DMA_CHANNELS]; 18static struct dma_info_t *autoinit_info[SH_MAX_DMA_CHANNELS] = {0}; 19static spinlock_t dma_spin_lock; 20 21static unsigned int calc_chcr(struct dma_info_t *info) 22{ 23 unsigned int chcr; 24 25 chcr = ( info->mode & DMA_MODE_WRITE )? info->mode_write : info->mode_read; 26 if( info->mode & DMA_AUTOINIT ) 27 chcr |= CHCR_IE; 28 return chcr; 29} 30 31static __inline__ int ts_shift(unsigned long chcr) 32{ 33 return ((int[]){3,0,1,2,5,0,0,0})[(chcr>>4)&0x000007]; 34} 35 36static void dma_tei(int irq, void *dev_id, struct pt_regs *regs) 37{ 38 int chan = irq - DMTE_IRQ[0]; 39 struct dma_info_t *info = autoinit_info[chan]; 40 41 if( info->mode & DMA_MODE_WRITE ) 42 ctrl_outl(info->mem_addr, SAR[info->chan]); 43 else 44 ctrl_outl(info->mem_addr, DAR[info->chan]); 45 46 ctrl_outl(info->count>>ts_shift(calc_chcr(info)), DMATCR[info->chan]); 47 ctrl_outl(ctrl_inl(CHCR[info->chan])&~CHCR_TE, CHCR[info->chan]); 48} 49 50static struct irqaction irq_tei = { dma_tei, SA_INTERRUPT, 0, "dma_tei", NULL, NULL}; 51 52void setup_dma(unsigned int dmanr, struct dma_info_t *info) 53{ 54 make_ipr_irq(DMTE_IRQ[info->chan], DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY); 55 setup_irq(DMTE_IRQ[info->chan], &irq_tei); 56 dma_info[dmanr] = info; 57} 58 59unsigned long claim_dma_lock(void) 60{ 61 unsigned long flags; 62 spin_lock_irqsave(&dma_spin_lock, flags); 63 return flags; 64} 65 66void release_dma_lock(unsigned long flags) 67{ 68 spin_unlock_irqrestore(&dma_spin_lock, flags); 69} 70 71void enable_dma(unsigned int dmanr) 72{ 73 struct dma_info_t *info = dma_info[dmanr]; 74 ctrl_outl(calc_chcr(info)|CHCR_DE, CHCR[info->chan]); 75} 76 77void disable_dma(unsigned int dmanr) 78{ 79 struct dma_info_t *info = dma_info[dmanr]; 80 ctrl_outl(calc_chcr(info)&~CHCR_DE, CHCR[info->chan]); 81} 82 83void set_dma_mode(unsigned int dmanr, char mode) 84{ 85 struct dma_info_t *info = dma_info[dmanr]; 86 87 info->mode = mode; 88 set_dma_addr(dmanr, info->mem_addr); 89 set_dma_count(dmanr, info->count); 90 autoinit_info[info->chan] = info; 91} 92 93void set_dma_addr(unsigned int dmanr, unsigned int a) 94{ 95 struct dma_info_t *info = dma_info[dmanr]; 96 unsigned long sar, dar; 97 98 info->mem_addr = a; 99 sar = (info->mode & DMA_MODE_WRITE)? info->mem_addr: info->dev_addr; 100 dar = (info->mode & DMA_MODE_WRITE)? info->dev_addr: info->mem_addr; 101 ctrl_outl(sar, SAR[info->chan]); 102 ctrl_outl(dar, DAR[info->chan]); 103} 104 105void set_dma_count(unsigned int dmanr, unsigned int count) 106{ 107 struct dma_info_t *info = dma_info[dmanr]; 108 info->count = count; 109 ctrl_outl(count>>ts_shift(calc_chcr(info)), DMATCR[info->chan]); 110} 111 112int get_dma_residue(unsigned int dmanr) 113{ 114 struct dma_info_t *info = dma_info[dmanr]; 115 return ctrl_inl(DMATCR[info->chan])<<ts_shift(calc_chcr(info)); 116} 117 118#if defined(__SH4__) 119static void dma_err(int irq, void *dev_id, struct pt_regs *regs) 120{ 121 printk(KERN_WARNING "DMAE: DMAOR=%lx\n",ctrl_inl(DMAOR)); 122 ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_NMIF, DMAOR); 123 ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_AE, DMAOR); 124 ctrl_outl(ctrl_inl(DMAOR)|DMAOR_DME, DMAOR); 125} 126static struct irqaction irq_err = { dma_err, SA_INTERRUPT, 0, "dma_err", NULL, NULL}; 127#endif 128 129int __init init_dma(void) 130{ 131#if defined(__SH4__) 132 make_ipr_irq(DMAE_IRQ, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY); 133 setup_irq(DMAE_IRQ, &irq_err); 134#endif 135 136 ctrl_outl(DMAOR_DME, DMAOR); 137 return 0; 138} 139 140module_init(init_dma); 141 142/**/ 143