162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Mips Jazz DMA controller support 462306a36Sopenharmony_ci * Copyright (C) 1995, 1996 by Andreas Busse 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * NOTE: Some of the argument checking could be removed when 762306a36Sopenharmony_ci * things have settled down. Also, instead of returning 0xffffffff 862306a36Sopenharmony_ci * on failure of vdma_alloc() one could leave page #0 unused 962306a36Sopenharmony_ci * and return the more usual NULL pointer as logical address. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/memblock.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/gfp.h> 1962306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 2062306a36Sopenharmony_ci#include <asm/mipsregs.h> 2162306a36Sopenharmony_ci#include <asm/jazz.h> 2262306a36Sopenharmony_ci#include <asm/io.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <asm/dma.h> 2562306a36Sopenharmony_ci#include <asm/jazzdma.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Set this to one to enable additional vdma debug code. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci#define CONF_DEBUG_VDMA 0 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic VDMA_PGTBL_ENTRY *pgtbl; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vdma_lock); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Debug stuff 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci#define vdma_debug ((CONF_DEBUG_VDMA) ? debuglvl : 0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int debuglvl = 3; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Initialize the pagetable with a one-to-one mapping of 4562306a36Sopenharmony_ci * the first 16 Mbytes of main memory and declare all 4662306a36Sopenharmony_ci * entries to be unused. Using this method will at least 4762306a36Sopenharmony_ci * allow some early device driver operations to work. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic inline void vdma_pgtbl_init(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci unsigned long paddr = 0; 5262306a36Sopenharmony_ci int i; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (i = 0; i < VDMA_PGTBL_ENTRIES; i++) { 5562306a36Sopenharmony_ci pgtbl[i].frame = paddr; 5662306a36Sopenharmony_ci pgtbl[i].owner = VDMA_PAGE_EMPTY; 5762306a36Sopenharmony_ci paddr += VDMA_PAGESIZE; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * Initialize the Jazz R4030 dma controller 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int __init vdma_init(void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * Allocate 32k of memory for DMA page tables. This needs to be page 6862306a36Sopenharmony_ci * aligned and should be uncached to avoid cache flushing after every 6962306a36Sopenharmony_ci * update. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci pgtbl = (VDMA_PGTBL_ENTRY *)__get_free_pages(GFP_KERNEL | GFP_DMA, 7262306a36Sopenharmony_ci get_order(VDMA_PGTBL_SIZE)); 7362306a36Sopenharmony_ci BUG_ON(!pgtbl); 7462306a36Sopenharmony_ci dma_cache_wback_inv((unsigned long)pgtbl, VDMA_PGTBL_SIZE); 7562306a36Sopenharmony_ci pgtbl = (VDMA_PGTBL_ENTRY *)CKSEG1ADDR((unsigned long)pgtbl); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Clear the R4030 translation table 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci vdma_pgtbl_init(); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE, 8362306a36Sopenharmony_ci CPHYSADDR((unsigned long)pgtbl)); 8462306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM, VDMA_PGTBL_SIZE); 8562306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci printk(KERN_INFO "VDMA: R4030 DMA pagetables initialized.\n"); 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciarch_initcall(vdma_init); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Allocate DMA pagetables using a simple first-fit algorithm 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ciunsigned long vdma_alloc(unsigned long paddr, unsigned long size) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int first, last, pages, frame, i; 9862306a36Sopenharmony_ci unsigned long laddr, flags; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* check arguments */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (paddr > 0x1fffffff) { 10362306a36Sopenharmony_ci if (vdma_debug) 10462306a36Sopenharmony_ci printk("vdma_alloc: Invalid physical address: %08lx\n", 10562306a36Sopenharmony_ci paddr); 10662306a36Sopenharmony_ci return DMA_MAPPING_ERROR; /* invalid physical address */ 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci if (size > 0x400000 || size == 0) { 10962306a36Sopenharmony_ci if (vdma_debug) 11062306a36Sopenharmony_ci printk("vdma_alloc: Invalid size: %08lx\n", size); 11162306a36Sopenharmony_ci return DMA_MAPPING_ERROR; /* invalid physical address */ 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci spin_lock_irqsave(&vdma_lock, flags); 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * Find free chunk 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci pages = VDMA_PAGE(paddr + size) - VDMA_PAGE(paddr) + 1; 11962306a36Sopenharmony_ci first = 0; 12062306a36Sopenharmony_ci while (1) { 12162306a36Sopenharmony_ci while (pgtbl[first].owner != VDMA_PAGE_EMPTY && 12262306a36Sopenharmony_ci first < VDMA_PGTBL_ENTRIES) first++; 12362306a36Sopenharmony_ci if (first + pages > VDMA_PGTBL_ENTRIES) { /* nothing free */ 12462306a36Sopenharmony_ci spin_unlock_irqrestore(&vdma_lock, flags); 12562306a36Sopenharmony_ci return DMA_MAPPING_ERROR; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci last = first + 1; 12962306a36Sopenharmony_ci while (pgtbl[last].owner == VDMA_PAGE_EMPTY 13062306a36Sopenharmony_ci && last - first < pages) 13162306a36Sopenharmony_ci last++; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (last - first == pages) 13462306a36Sopenharmony_ci break; /* found */ 13562306a36Sopenharmony_ci first = last + 1; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* 13962306a36Sopenharmony_ci * Mark pages as allocated 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci laddr = (first << 12) + (paddr & (VDMA_PAGESIZE - 1)); 14262306a36Sopenharmony_ci frame = paddr & ~(VDMA_PAGESIZE - 1); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for (i = first; i < last; i++) { 14562306a36Sopenharmony_ci pgtbl[i].frame = frame; 14662306a36Sopenharmony_ci pgtbl[i].owner = laddr; 14762306a36Sopenharmony_ci frame += VDMA_PAGESIZE; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Update translation table and return logical start address 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (vdma_debug > 1) 15662306a36Sopenharmony_ci printk("vdma_alloc: Allocated %d pages starting from %08lx\n", 15762306a36Sopenharmony_ci pages, laddr); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (vdma_debug > 2) { 16062306a36Sopenharmony_ci printk("LADDR: "); 16162306a36Sopenharmony_ci for (i = first; i < last; i++) 16262306a36Sopenharmony_ci printk("%08x ", i << 12); 16362306a36Sopenharmony_ci printk("\nPADDR: "); 16462306a36Sopenharmony_ci for (i = first; i < last; i++) 16562306a36Sopenharmony_ci printk("%08x ", pgtbl[i].frame); 16662306a36Sopenharmony_ci printk("\nOWNER: "); 16762306a36Sopenharmony_ci for (i = first; i < last; i++) 16862306a36Sopenharmony_ci printk("%08x ", pgtbl[i].owner); 16962306a36Sopenharmony_ci printk("\n"); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_unlock_irqrestore(&vdma_lock, flags); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return laddr; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_alloc); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Free previously allocated dma translation pages 18162306a36Sopenharmony_ci * Note that this does NOT change the translation table, 18262306a36Sopenharmony_ci * it just marks the free'd pages as unused! 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ciint vdma_free(unsigned long laddr) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci i = laddr >> 12; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (pgtbl[i].owner != laddr) { 19162306a36Sopenharmony_ci printk 19262306a36Sopenharmony_ci ("vdma_free: trying to free other's dma pages, laddr=%8lx\n", 19362306a36Sopenharmony_ci laddr); 19462306a36Sopenharmony_ci return -1; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci while (i < VDMA_PGTBL_ENTRIES && pgtbl[i].owner == laddr) { 19862306a36Sopenharmony_ci pgtbl[i].owner = VDMA_PAGE_EMPTY; 19962306a36Sopenharmony_ci i++; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (vdma_debug > 1) 20362306a36Sopenharmony_ci printk("vdma_free: freed %ld pages starting from %08lx\n", 20462306a36Sopenharmony_ci i - (laddr >> 12), laddr); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_free); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * Translate a physical address to a logical address. 21362306a36Sopenharmony_ci * This will return the logical address of the first 21462306a36Sopenharmony_ci * match. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ciunsigned long vdma_phys2log(unsigned long paddr) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci int i; 21962306a36Sopenharmony_ci int frame; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci frame = paddr & ~(VDMA_PAGESIZE - 1); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci for (i = 0; i < VDMA_PGTBL_ENTRIES; i++) { 22462306a36Sopenharmony_ci if (pgtbl[i].frame == frame) 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (i == VDMA_PGTBL_ENTRIES) 22962306a36Sopenharmony_ci return ~0UL; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return (i << 12) + (paddr & (VDMA_PAGESIZE - 1)); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_phys2log); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * Translate a logical DMA address to a physical address 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ciunsigned long vdma_log2phys(unsigned long laddr) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci return pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE - 1)); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_log2phys); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* 24762306a36Sopenharmony_ci * Print DMA statistics 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_civoid vdma_stats(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci printk("vdma_stats: CONFIG: %08x\n", 25462306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CONFIG)); 25562306a36Sopenharmony_ci printk("R4030 translation table base: %08x\n", 25662306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_TRSTBL_BASE)); 25762306a36Sopenharmony_ci printk("R4030 translation table limit: %08x\n", 25862306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_TRSTBL_LIM)); 25962306a36Sopenharmony_ci printk("vdma_stats: INV_ADDR: %08x\n", 26062306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_INV_ADDR)); 26162306a36Sopenharmony_ci printk("vdma_stats: R_FAIL_ADDR: %08x\n", 26262306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_R_FAIL_ADDR)); 26362306a36Sopenharmony_ci printk("vdma_stats: M_FAIL_ADDR: %08x\n", 26462306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_M_FAIL_ADDR)); 26562306a36Sopenharmony_ci printk("vdma_stats: IRQ_SOURCE: %08x\n", 26662306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_IRQ_SOURCE)); 26762306a36Sopenharmony_ci printk("vdma_stats: I386_ERROR: %08x\n", 26862306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_I386_ERROR)); 26962306a36Sopenharmony_ci printk("vdma_chnl_modes: "); 27062306a36Sopenharmony_ci for (i = 0; i < 8; i++) 27162306a36Sopenharmony_ci printk("%04x ", 27262306a36Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_MODE + 27362306a36Sopenharmony_ci (i << 5))); 27462306a36Sopenharmony_ci printk("\n"); 27562306a36Sopenharmony_ci printk("vdma_chnl_enables: "); 27662306a36Sopenharmony_ci for (i = 0; i < 8; i++) 27762306a36Sopenharmony_ci printk("%04x ", 27862306a36Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 27962306a36Sopenharmony_ci (i << 5))); 28062306a36Sopenharmony_ci printk("\n"); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * DMA transfer functions 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* 28862306a36Sopenharmony_ci * Enable a DMA channel. Also clear any error conditions. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_civoid vdma_enable(int channel) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci int status; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (vdma_debug) 29562306a36Sopenharmony_ci printk("vdma_enable: channel %d\n", channel); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * Check error conditions first 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5)); 30162306a36Sopenharmony_ci if (status & 0x400) 30262306a36Sopenharmony_ci printk("VDMA: Channel %d: Address error!\n", channel); 30362306a36Sopenharmony_ci if (status & 0x200) 30462306a36Sopenharmony_ci printk("VDMA: Channel %d: Memory error!\n", channel); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * Clear all interrupt flags 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 31062306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 31162306a36Sopenharmony_ci (channel << 5)) | R4030_TC_INTR 31262306a36Sopenharmony_ci | R4030_MEM_INTR | R4030_ADDR_INTR); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * Enable the desired channel 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 31862306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 31962306a36Sopenharmony_ci (channel << 5)) | 32062306a36Sopenharmony_ci R4030_CHNL_ENABLE); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_enable); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Disable a DMA channel 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_civoid vdma_disable(int channel) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci if (vdma_debug) { 33162306a36Sopenharmony_ci int status = 33262306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 33362306a36Sopenharmony_ci (channel << 5)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci printk("vdma_disable: channel %d\n", channel); 33662306a36Sopenharmony_ci printk("VDMA: channel %d status: %04x (%s) mode: " 33762306a36Sopenharmony_ci "%02x addr: %06x count: %06x\n", 33862306a36Sopenharmony_ci channel, status, 33962306a36Sopenharmony_ci ((status & 0x600) ? "ERROR" : "OK"), 34062306a36Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_MODE + 34162306a36Sopenharmony_ci (channel << 5)), 34262306a36Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_ADDR + 34362306a36Sopenharmony_ci (channel << 5)), 34462306a36Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_COUNT + 34562306a36Sopenharmony_ci (channel << 5))); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 34962306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 35062306a36Sopenharmony_ci (channel << 5)) & 35162306a36Sopenharmony_ci ~R4030_CHNL_ENABLE); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * After disabling a DMA channel a remote bus register should be 35562306a36Sopenharmony_ci * read to ensure that the current DMA acknowledge cycle is completed. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci *((volatile unsigned int *) JAZZ_DUMMY_DEVICE); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_disable); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * Set DMA mode. This function accepts the mode values used 36462306a36Sopenharmony_ci * to set a PC-style DMA controller. For the SCSI and FDC 36562306a36Sopenharmony_ci * channels, we also set the default modes each time we're 36662306a36Sopenharmony_ci * called. 36762306a36Sopenharmony_ci * NOTE: The FAST and BURST dma modes are supported by the 36862306a36Sopenharmony_ci * R4030 Rev. 2 and PICA chipsets only. I leave them disabled 36962306a36Sopenharmony_ci * for now. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_civoid vdma_set_mode(int channel, int mode) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci if (vdma_debug) 37462306a36Sopenharmony_ci printk("vdma_set_mode: channel %d, mode 0x%x\n", channel, 37562306a36Sopenharmony_ci mode); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci switch (channel) { 37862306a36Sopenharmony_ci case JAZZ_SCSI_DMA: /* scsi */ 37962306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_MODE + (channel << 5), 38062306a36Sopenharmony_ci/* R4030_MODE_FAST | */ 38162306a36Sopenharmony_ci/* R4030_MODE_BURST | */ 38262306a36Sopenharmony_ci R4030_MODE_INTR_EN | 38362306a36Sopenharmony_ci R4030_MODE_WIDTH_16 | 38462306a36Sopenharmony_ci R4030_MODE_ATIME_80); 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci case JAZZ_FLOPPY_DMA: /* floppy */ 38862306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_MODE + (channel << 5), 38962306a36Sopenharmony_ci/* R4030_MODE_FAST | */ 39062306a36Sopenharmony_ci/* R4030_MODE_BURST | */ 39162306a36Sopenharmony_ci R4030_MODE_INTR_EN | 39262306a36Sopenharmony_ci R4030_MODE_WIDTH_8 | 39362306a36Sopenharmony_ci R4030_MODE_ATIME_120); 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci case JAZZ_AUDIOL_DMA: 39762306a36Sopenharmony_ci case JAZZ_AUDIOR_DMA: 39862306a36Sopenharmony_ci printk("VDMA: Audio DMA not supported yet.\n"); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci default: 40262306a36Sopenharmony_ci printk 40362306a36Sopenharmony_ci ("VDMA: vdma_set_mode() called with unsupported channel %d!\n", 40462306a36Sopenharmony_ci channel); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci switch (mode) { 40862306a36Sopenharmony_ci case DMA_MODE_READ: 40962306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 41062306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 41162306a36Sopenharmony_ci (channel << 5)) & 41262306a36Sopenharmony_ci ~R4030_CHNL_WRITE); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci case DMA_MODE_WRITE: 41662306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 41762306a36Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 41862306a36Sopenharmony_ci (channel << 5)) | 41962306a36Sopenharmony_ci R4030_CHNL_WRITE); 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci default: 42362306a36Sopenharmony_ci printk 42462306a36Sopenharmony_ci ("VDMA: vdma_set_mode() called with unknown dma mode 0x%x\n", 42562306a36Sopenharmony_ci mode); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_set_mode); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* 43262306a36Sopenharmony_ci * Set Transfer Address 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_civoid vdma_set_addr(int channel, long addr) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci if (vdma_debug) 43762306a36Sopenharmony_ci printk("vdma_set_addr: channel %d, addr %lx\n", channel, 43862306a36Sopenharmony_ci addr); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ADDR + (channel << 5), addr); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_set_addr); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* 44662306a36Sopenharmony_ci * Set Transfer Count 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_civoid vdma_set_count(int channel, int count) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci if (vdma_debug) 45162306a36Sopenharmony_ci printk("vdma_set_count: channel %d, count %08x\n", channel, 45262306a36Sopenharmony_ci (unsigned) count); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_COUNT + (channel << 5), count); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciEXPORT_SYMBOL(vdma_set_count); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* 46062306a36Sopenharmony_ci * Get Residual 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ciint vdma_get_residue(int channel) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci int residual; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci residual = r4030_read_reg32(JAZZ_R4030_CHNL_COUNT + (channel << 5)); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (vdma_debug) 46962306a36Sopenharmony_ci printk("vdma_get_residual: channel %d: residual=%d\n", 47062306a36Sopenharmony_ci channel, residual); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return residual; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/* 47662306a36Sopenharmony_ci * Get DMA channel enable register 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ciint vdma_get_enable(int channel) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int enable; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci enable = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5)); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (vdma_debug) 48562306a36Sopenharmony_ci printk("vdma_get_enable: channel %d: enable=%d\n", channel, 48662306a36Sopenharmony_ci enable); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return enable; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void *jazz_dma_alloc(struct device *dev, size_t size, 49262306a36Sopenharmony_ci dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct page *page; 49562306a36Sopenharmony_ci void *ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (attrs & DMA_ATTR_NO_WARN) 49862306a36Sopenharmony_ci gfp |= __GFP_NOWARN; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci size = PAGE_ALIGN(size); 50162306a36Sopenharmony_ci page = alloc_pages(gfp, get_order(size)); 50262306a36Sopenharmony_ci if (!page) 50362306a36Sopenharmony_ci return NULL; 50462306a36Sopenharmony_ci ret = page_address(page); 50562306a36Sopenharmony_ci memset(ret, 0, size); 50662306a36Sopenharmony_ci *dma_handle = vdma_alloc(virt_to_phys(ret), size); 50762306a36Sopenharmony_ci if (*dma_handle == DMA_MAPPING_ERROR) 50862306a36Sopenharmony_ci goto out_free_pages; 50962306a36Sopenharmony_ci arch_dma_prep_coherent(page, size); 51062306a36Sopenharmony_ci return (void *)(UNCAC_BASE + __pa(ret)); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ciout_free_pages: 51362306a36Sopenharmony_ci __free_pages(page, get_order(size)); 51462306a36Sopenharmony_ci return NULL; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void jazz_dma_free(struct device *dev, size_t size, void *vaddr, 51862306a36Sopenharmony_ci dma_addr_t dma_handle, unsigned long attrs) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci vdma_free(dma_handle); 52162306a36Sopenharmony_ci __free_pages(virt_to_page(vaddr), get_order(size)); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic dma_addr_t jazz_dma_map_page(struct device *dev, struct page *page, 52562306a36Sopenharmony_ci unsigned long offset, size_t size, enum dma_data_direction dir, 52662306a36Sopenharmony_ci unsigned long attrs) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci phys_addr_t phys = page_to_phys(page) + offset; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 53162306a36Sopenharmony_ci arch_sync_dma_for_device(phys, size, dir); 53262306a36Sopenharmony_ci return vdma_alloc(phys, size); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void jazz_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, 53662306a36Sopenharmony_ci size_t size, enum dma_data_direction dir, unsigned long attrs) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 53962306a36Sopenharmony_ci arch_sync_dma_for_cpu(vdma_log2phys(dma_addr), size, dir); 54062306a36Sopenharmony_ci vdma_free(dma_addr); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int jazz_dma_map_sg(struct device *dev, struct scatterlist *sglist, 54462306a36Sopenharmony_ci int nents, enum dma_data_direction dir, unsigned long attrs) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci int i; 54762306a36Sopenharmony_ci struct scatterlist *sg; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci for_each_sg(sglist, sg, nents, i) { 55062306a36Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 55162306a36Sopenharmony_ci arch_sync_dma_for_device(sg_phys(sg), sg->length, 55262306a36Sopenharmony_ci dir); 55362306a36Sopenharmony_ci sg->dma_address = vdma_alloc(sg_phys(sg), sg->length); 55462306a36Sopenharmony_ci if (sg->dma_address == DMA_MAPPING_ERROR) 55562306a36Sopenharmony_ci return -EIO; 55662306a36Sopenharmony_ci sg_dma_len(sg) = sg->length; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return nents; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void jazz_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, 56362306a36Sopenharmony_ci int nents, enum dma_data_direction dir, unsigned long attrs) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci int i; 56662306a36Sopenharmony_ci struct scatterlist *sg; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci for_each_sg(sglist, sg, nents, i) { 56962306a36Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 57062306a36Sopenharmony_ci arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); 57162306a36Sopenharmony_ci vdma_free(sg->dma_address); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic void jazz_dma_sync_single_for_device(struct device *dev, 57662306a36Sopenharmony_ci dma_addr_t addr, size_t size, enum dma_data_direction dir) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci arch_sync_dma_for_device(vdma_log2phys(addr), size, dir); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void jazz_dma_sync_single_for_cpu(struct device *dev, 58262306a36Sopenharmony_ci dma_addr_t addr, size_t size, enum dma_data_direction dir) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci arch_sync_dma_for_cpu(vdma_log2phys(addr), size, dir); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic void jazz_dma_sync_sg_for_device(struct device *dev, 58862306a36Sopenharmony_ci struct scatterlist *sgl, int nents, enum dma_data_direction dir) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct scatterlist *sg; 59162306a36Sopenharmony_ci int i; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 59462306a36Sopenharmony_ci arch_sync_dma_for_device(sg_phys(sg), sg->length, dir); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void jazz_dma_sync_sg_for_cpu(struct device *dev, 59862306a36Sopenharmony_ci struct scatterlist *sgl, int nents, enum dma_data_direction dir) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct scatterlist *sg; 60162306a36Sopenharmony_ci int i; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 60462306a36Sopenharmony_ci arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciconst struct dma_map_ops jazz_dma_ops = { 60862306a36Sopenharmony_ci .alloc = jazz_dma_alloc, 60962306a36Sopenharmony_ci .free = jazz_dma_free, 61062306a36Sopenharmony_ci .map_page = jazz_dma_map_page, 61162306a36Sopenharmony_ci .unmap_page = jazz_dma_unmap_page, 61262306a36Sopenharmony_ci .map_sg = jazz_dma_map_sg, 61362306a36Sopenharmony_ci .unmap_sg = jazz_dma_unmap_sg, 61462306a36Sopenharmony_ci .sync_single_for_cpu = jazz_dma_sync_single_for_cpu, 61562306a36Sopenharmony_ci .sync_single_for_device = jazz_dma_sync_single_for_device, 61662306a36Sopenharmony_ci .sync_sg_for_cpu = jazz_dma_sync_sg_for_cpu, 61762306a36Sopenharmony_ci .sync_sg_for_device = jazz_dma_sync_sg_for_device, 61862306a36Sopenharmony_ci .mmap = dma_common_mmap, 61962306a36Sopenharmony_ci .get_sgtable = dma_common_get_sgtable, 62062306a36Sopenharmony_ci .alloc_pages = dma_common_alloc_pages, 62162306a36Sopenharmony_ci .free_pages = dma_common_free_pages, 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ciEXPORT_SYMBOL(jazz_dma_ops); 624