18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mips Jazz DMA controller support 48c2ecf20Sopenharmony_ci * Copyright (C) 1995, 1996 by Andreas Busse 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * NOTE: Some of the argument checking could be removed when 78c2ecf20Sopenharmony_ci * things have settled down. Also, instead of returning 0xffffffff 88c2ecf20Sopenharmony_ci * on failure of vdma_alloc() one could leave page #0 unused 98c2ecf20Sopenharmony_ci * and return the more usual NULL pointer as logical address. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/export.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/memblock.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/gfp.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h> 208c2ecf20Sopenharmony_ci#include <asm/mipsregs.h> 218c2ecf20Sopenharmony_ci#include <asm/jazz.h> 228c2ecf20Sopenharmony_ci#include <asm/io.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci#include <asm/dma.h> 258c2ecf20Sopenharmony_ci#include <asm/jazzdma.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Set this to one to enable additional vdma debug code. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define CONF_DEBUG_VDMA 0 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic VDMA_PGTBL_ENTRY *pgtbl; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vdma_lock); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Debug stuff 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci#define vdma_debug ((CONF_DEBUG_VDMA) ? debuglvl : 0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int debuglvl = 3; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * Initialize the pagetable with a one-to-one mapping of 458c2ecf20Sopenharmony_ci * the first 16 Mbytes of main memory and declare all 468c2ecf20Sopenharmony_ci * entries to be unused. Using this method will at least 478c2ecf20Sopenharmony_ci * allow some early device driver operations to work. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistatic inline void vdma_pgtbl_init(void) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned long paddr = 0; 528c2ecf20Sopenharmony_ci int i; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci for (i = 0; i < VDMA_PGTBL_ENTRIES; i++) { 558c2ecf20Sopenharmony_ci pgtbl[i].frame = paddr; 568c2ecf20Sopenharmony_ci pgtbl[i].owner = VDMA_PAGE_EMPTY; 578c2ecf20Sopenharmony_ci paddr += VDMA_PAGESIZE; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * Initialize the Jazz R4030 dma controller 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic int __init vdma_init(void) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci /* 678c2ecf20Sopenharmony_ci * Allocate 32k of memory for DMA page tables. This needs to be page 688c2ecf20Sopenharmony_ci * aligned and should be uncached to avoid cache flushing after every 698c2ecf20Sopenharmony_ci * update. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci pgtbl = (VDMA_PGTBL_ENTRY *)__get_free_pages(GFP_KERNEL | GFP_DMA, 728c2ecf20Sopenharmony_ci get_order(VDMA_PGTBL_SIZE)); 738c2ecf20Sopenharmony_ci BUG_ON(!pgtbl); 748c2ecf20Sopenharmony_ci dma_cache_wback_inv((unsigned long)pgtbl, VDMA_PGTBL_SIZE); 758c2ecf20Sopenharmony_ci pgtbl = (VDMA_PGTBL_ENTRY *)CKSEG1ADDR((unsigned long)pgtbl); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Clear the R4030 translation table 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci vdma_pgtbl_init(); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE, 838c2ecf20Sopenharmony_ci CPHYSADDR((unsigned long)pgtbl)); 848c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM, VDMA_PGTBL_SIZE); 858c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci printk(KERN_INFO "VDMA: R4030 DMA pagetables initialized.\n"); 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciarch_initcall(vdma_init); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Allocate DMA pagetables using a simple first-fit algorithm 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ciunsigned long vdma_alloc(unsigned long paddr, unsigned long size) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int first, last, pages, frame, i; 988c2ecf20Sopenharmony_ci unsigned long laddr, flags; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* check arguments */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (paddr > 0x1fffffff) { 1038c2ecf20Sopenharmony_ci if (vdma_debug) 1048c2ecf20Sopenharmony_ci printk("vdma_alloc: Invalid physical address: %08lx\n", 1058c2ecf20Sopenharmony_ci paddr); 1068c2ecf20Sopenharmony_ci return DMA_MAPPING_ERROR; /* invalid physical address */ 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci if (size > 0x400000 || size == 0) { 1098c2ecf20Sopenharmony_ci if (vdma_debug) 1108c2ecf20Sopenharmony_ci printk("vdma_alloc: Invalid size: %08lx\n", size); 1118c2ecf20Sopenharmony_ci return DMA_MAPPING_ERROR; /* invalid physical address */ 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci spin_lock_irqsave(&vdma_lock, flags); 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * Find free chunk 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci pages = VDMA_PAGE(paddr + size) - VDMA_PAGE(paddr) + 1; 1198c2ecf20Sopenharmony_ci first = 0; 1208c2ecf20Sopenharmony_ci while (1) { 1218c2ecf20Sopenharmony_ci while (pgtbl[first].owner != VDMA_PAGE_EMPTY && 1228c2ecf20Sopenharmony_ci first < VDMA_PGTBL_ENTRIES) first++; 1238c2ecf20Sopenharmony_ci if (first + pages > VDMA_PGTBL_ENTRIES) { /* nothing free */ 1248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdma_lock, flags); 1258c2ecf20Sopenharmony_ci return DMA_MAPPING_ERROR; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci last = first + 1; 1298c2ecf20Sopenharmony_ci while (pgtbl[last].owner == VDMA_PAGE_EMPTY 1308c2ecf20Sopenharmony_ci && last - first < pages) 1318c2ecf20Sopenharmony_ci last++; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (last - first == pages) 1348c2ecf20Sopenharmony_ci break; /* found */ 1358c2ecf20Sopenharmony_ci first = last + 1; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* 1398c2ecf20Sopenharmony_ci * Mark pages as allocated 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci laddr = (first << 12) + (paddr & (VDMA_PAGESIZE - 1)); 1428c2ecf20Sopenharmony_ci frame = paddr & ~(VDMA_PAGESIZE - 1); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci for (i = first; i < last; i++) { 1458c2ecf20Sopenharmony_ci pgtbl[i].frame = frame; 1468c2ecf20Sopenharmony_ci pgtbl[i].owner = laddr; 1478c2ecf20Sopenharmony_ci frame += VDMA_PAGESIZE; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Update translation table and return logical start address 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (vdma_debug > 1) 1568c2ecf20Sopenharmony_ci printk("vdma_alloc: Allocated %d pages starting from %08lx\n", 1578c2ecf20Sopenharmony_ci pages, laddr); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (vdma_debug > 2) { 1608c2ecf20Sopenharmony_ci printk("LADDR: "); 1618c2ecf20Sopenharmony_ci for (i = first; i < last; i++) 1628c2ecf20Sopenharmony_ci printk("%08x ", i << 12); 1638c2ecf20Sopenharmony_ci printk("\nPADDR: "); 1648c2ecf20Sopenharmony_ci for (i = first; i < last; i++) 1658c2ecf20Sopenharmony_ci printk("%08x ", pgtbl[i].frame); 1668c2ecf20Sopenharmony_ci printk("\nOWNER: "); 1678c2ecf20Sopenharmony_ci for (i = first; i < last; i++) 1688c2ecf20Sopenharmony_ci printk("%08x ", pgtbl[i].owner); 1698c2ecf20Sopenharmony_ci printk("\n"); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdma_lock, flags); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return laddr; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_alloc); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Free previously allocated dma translation pages 1818c2ecf20Sopenharmony_ci * Note that this does NOT change the translation table, 1828c2ecf20Sopenharmony_ci * it just marks the free'd pages as unused! 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ciint vdma_free(unsigned long laddr) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int i; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci i = laddr >> 12; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (pgtbl[i].owner != laddr) { 1918c2ecf20Sopenharmony_ci printk 1928c2ecf20Sopenharmony_ci ("vdma_free: trying to free other's dma pages, laddr=%8lx\n", 1938c2ecf20Sopenharmony_ci laddr); 1948c2ecf20Sopenharmony_ci return -1; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci while (i < VDMA_PGTBL_ENTRIES && pgtbl[i].owner == laddr) { 1988c2ecf20Sopenharmony_ci pgtbl[i].owner = VDMA_PAGE_EMPTY; 1998c2ecf20Sopenharmony_ci i++; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (vdma_debug > 1) 2038c2ecf20Sopenharmony_ci printk("vdma_free: freed %ld pages starting from %08lx\n", 2048c2ecf20Sopenharmony_ci i - (laddr >> 12), laddr); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_free); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * Translate a physical address to a logical address. 2138c2ecf20Sopenharmony_ci * This will return the logical address of the first 2148c2ecf20Sopenharmony_ci * match. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ciunsigned long vdma_phys2log(unsigned long paddr) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int i; 2198c2ecf20Sopenharmony_ci int frame; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci frame = paddr & ~(VDMA_PAGESIZE - 1); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci for (i = 0; i < VDMA_PGTBL_ENTRIES; i++) { 2248c2ecf20Sopenharmony_ci if (pgtbl[i].frame == frame) 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (i == VDMA_PGTBL_ENTRIES) 2298c2ecf20Sopenharmony_ci return ~0UL; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return (i << 12) + (paddr & (VDMA_PAGESIZE - 1)); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_phys2log); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Translate a logical DMA address to a physical address 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ciunsigned long vdma_log2phys(unsigned long laddr) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci return pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE - 1)); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_log2phys); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/* 2478c2ecf20Sopenharmony_ci * Print DMA statistics 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_civoid vdma_stats(void) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci int i; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci printk("vdma_stats: CONFIG: %08x\n", 2548c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CONFIG)); 2558c2ecf20Sopenharmony_ci printk("R4030 translation table base: %08x\n", 2568c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_TRSTBL_BASE)); 2578c2ecf20Sopenharmony_ci printk("R4030 translation table limit: %08x\n", 2588c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_TRSTBL_LIM)); 2598c2ecf20Sopenharmony_ci printk("vdma_stats: INV_ADDR: %08x\n", 2608c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_INV_ADDR)); 2618c2ecf20Sopenharmony_ci printk("vdma_stats: R_FAIL_ADDR: %08x\n", 2628c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_R_FAIL_ADDR)); 2638c2ecf20Sopenharmony_ci printk("vdma_stats: M_FAIL_ADDR: %08x\n", 2648c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_M_FAIL_ADDR)); 2658c2ecf20Sopenharmony_ci printk("vdma_stats: IRQ_SOURCE: %08x\n", 2668c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_IRQ_SOURCE)); 2678c2ecf20Sopenharmony_ci printk("vdma_stats: I386_ERROR: %08x\n", 2688c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_I386_ERROR)); 2698c2ecf20Sopenharmony_ci printk("vdma_chnl_modes: "); 2708c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 2718c2ecf20Sopenharmony_ci printk("%04x ", 2728c2ecf20Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_MODE + 2738c2ecf20Sopenharmony_ci (i << 5))); 2748c2ecf20Sopenharmony_ci printk("\n"); 2758c2ecf20Sopenharmony_ci printk("vdma_chnl_enables: "); 2768c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 2778c2ecf20Sopenharmony_ci printk("%04x ", 2788c2ecf20Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 2798c2ecf20Sopenharmony_ci (i << 5))); 2808c2ecf20Sopenharmony_ci printk("\n"); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * DMA transfer functions 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * Enable a DMA channel. Also clear any error conditions. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_civoid vdma_enable(int channel) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int status; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (vdma_debug) 2958c2ecf20Sopenharmony_ci printk("vdma_enable: channel %d\n", channel); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* 2988c2ecf20Sopenharmony_ci * Check error conditions first 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5)); 3018c2ecf20Sopenharmony_ci if (status & 0x400) 3028c2ecf20Sopenharmony_ci printk("VDMA: Channel %d: Address error!\n", channel); 3038c2ecf20Sopenharmony_ci if (status & 0x200) 3048c2ecf20Sopenharmony_ci printk("VDMA: Channel %d: Memory error!\n", channel); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* 3078c2ecf20Sopenharmony_ci * Clear all interrupt flags 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 3108c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 3118c2ecf20Sopenharmony_ci (channel << 5)) | R4030_TC_INTR 3128c2ecf20Sopenharmony_ci | R4030_MEM_INTR | R4030_ADDR_INTR); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * Enable the desired channel 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 3188c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 3198c2ecf20Sopenharmony_ci (channel << 5)) | 3208c2ecf20Sopenharmony_ci R4030_CHNL_ENABLE); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_enable); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * Disable a DMA channel 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_civoid vdma_disable(int channel) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci if (vdma_debug) { 3318c2ecf20Sopenharmony_ci int status = 3328c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 3338c2ecf20Sopenharmony_ci (channel << 5)); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci printk("vdma_disable: channel %d\n", channel); 3368c2ecf20Sopenharmony_ci printk("VDMA: channel %d status: %04x (%s) mode: " 3378c2ecf20Sopenharmony_ci "%02x addr: %06x count: %06x\n", 3388c2ecf20Sopenharmony_ci channel, status, 3398c2ecf20Sopenharmony_ci ((status & 0x600) ? "ERROR" : "OK"), 3408c2ecf20Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_MODE + 3418c2ecf20Sopenharmony_ci (channel << 5)), 3428c2ecf20Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_ADDR + 3438c2ecf20Sopenharmony_ci (channel << 5)), 3448c2ecf20Sopenharmony_ci (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_COUNT + 3458c2ecf20Sopenharmony_ci (channel << 5))); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 3498c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 3508c2ecf20Sopenharmony_ci (channel << 5)) & 3518c2ecf20Sopenharmony_ci ~R4030_CHNL_ENABLE); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * After disabling a DMA channel a remote bus register should be 3558c2ecf20Sopenharmony_ci * read to ensure that the current DMA acknowledge cycle is completed. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci *((volatile unsigned int *) JAZZ_DUMMY_DEVICE); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_disable); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* 3638c2ecf20Sopenharmony_ci * Set DMA mode. This function accepts the mode values used 3648c2ecf20Sopenharmony_ci * to set a PC-style DMA controller. For the SCSI and FDC 3658c2ecf20Sopenharmony_ci * channels, we also set the default modes each time we're 3668c2ecf20Sopenharmony_ci * called. 3678c2ecf20Sopenharmony_ci * NOTE: The FAST and BURST dma modes are supported by the 3688c2ecf20Sopenharmony_ci * R4030 Rev. 2 and PICA chipsets only. I leave them disabled 3698c2ecf20Sopenharmony_ci * for now. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_civoid vdma_set_mode(int channel, int mode) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci if (vdma_debug) 3748c2ecf20Sopenharmony_ci printk("vdma_set_mode: channel %d, mode 0x%x\n", channel, 3758c2ecf20Sopenharmony_ci mode); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci switch (channel) { 3788c2ecf20Sopenharmony_ci case JAZZ_SCSI_DMA: /* scsi */ 3798c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_MODE + (channel << 5), 3808c2ecf20Sopenharmony_ci/* R4030_MODE_FAST | */ 3818c2ecf20Sopenharmony_ci/* R4030_MODE_BURST | */ 3828c2ecf20Sopenharmony_ci R4030_MODE_INTR_EN | 3838c2ecf20Sopenharmony_ci R4030_MODE_WIDTH_16 | 3848c2ecf20Sopenharmony_ci R4030_MODE_ATIME_80); 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci case JAZZ_FLOPPY_DMA: /* floppy */ 3888c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_MODE + (channel << 5), 3898c2ecf20Sopenharmony_ci/* R4030_MODE_FAST | */ 3908c2ecf20Sopenharmony_ci/* R4030_MODE_BURST | */ 3918c2ecf20Sopenharmony_ci R4030_MODE_INTR_EN | 3928c2ecf20Sopenharmony_ci R4030_MODE_WIDTH_8 | 3938c2ecf20Sopenharmony_ci R4030_MODE_ATIME_120); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case JAZZ_AUDIOL_DMA: 3978c2ecf20Sopenharmony_ci case JAZZ_AUDIOR_DMA: 3988c2ecf20Sopenharmony_ci printk("VDMA: Audio DMA not supported yet.\n"); 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci default: 4028c2ecf20Sopenharmony_ci printk 4038c2ecf20Sopenharmony_ci ("VDMA: vdma_set_mode() called with unsupported channel %d!\n", 4048c2ecf20Sopenharmony_ci channel); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci switch (mode) { 4088c2ecf20Sopenharmony_ci case DMA_MODE_READ: 4098c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 4108c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 4118c2ecf20Sopenharmony_ci (channel << 5)) & 4128c2ecf20Sopenharmony_ci ~R4030_CHNL_WRITE); 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci case DMA_MODE_WRITE: 4168c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5), 4178c2ecf20Sopenharmony_ci r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + 4188c2ecf20Sopenharmony_ci (channel << 5)) | 4198c2ecf20Sopenharmony_ci R4030_CHNL_WRITE); 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci default: 4238c2ecf20Sopenharmony_ci printk 4248c2ecf20Sopenharmony_ci ("VDMA: vdma_set_mode() called with unknown dma mode 0x%x\n", 4258c2ecf20Sopenharmony_ci mode); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_set_mode); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* 4328c2ecf20Sopenharmony_ci * Set Transfer Address 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_civoid vdma_set_addr(int channel, long addr) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci if (vdma_debug) 4378c2ecf20Sopenharmony_ci printk("vdma_set_addr: channel %d, addr %lx\n", channel, 4388c2ecf20Sopenharmony_ci addr); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_ADDR + (channel << 5), addr); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_set_addr); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/* 4468c2ecf20Sopenharmony_ci * Set Transfer Count 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_civoid vdma_set_count(int channel, int count) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci if (vdma_debug) 4518c2ecf20Sopenharmony_ci printk("vdma_set_count: channel %d, count %08x\n", channel, 4528c2ecf20Sopenharmony_ci (unsigned) count); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci r4030_write_reg32(JAZZ_R4030_CHNL_COUNT + (channel << 5), count); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vdma_set_count); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* 4608c2ecf20Sopenharmony_ci * Get Residual 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ciint vdma_get_residue(int channel) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int residual; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci residual = r4030_read_reg32(JAZZ_R4030_CHNL_COUNT + (channel << 5)); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (vdma_debug) 4698c2ecf20Sopenharmony_ci printk("vdma_get_residual: channel %d: residual=%d\n", 4708c2ecf20Sopenharmony_ci channel, residual); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return residual; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* 4768c2ecf20Sopenharmony_ci * Get DMA channel enable register 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ciint vdma_get_enable(int channel) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci int enable; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci enable = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5)); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (vdma_debug) 4858c2ecf20Sopenharmony_ci printk("vdma_get_enable: channel %d: enable=%d\n", channel, 4868c2ecf20Sopenharmony_ci enable); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return enable; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void *jazz_dma_alloc(struct device *dev, size_t size, 4928c2ecf20Sopenharmony_ci dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct page *page; 4958c2ecf20Sopenharmony_ci void *ret; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (attrs & DMA_ATTR_NO_WARN) 4988c2ecf20Sopenharmony_ci gfp |= __GFP_NOWARN; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci size = PAGE_ALIGN(size); 5018c2ecf20Sopenharmony_ci page = alloc_pages(gfp, get_order(size)); 5028c2ecf20Sopenharmony_ci if (!page) 5038c2ecf20Sopenharmony_ci return NULL; 5048c2ecf20Sopenharmony_ci ret = page_address(page); 5058c2ecf20Sopenharmony_ci memset(ret, 0, size); 5068c2ecf20Sopenharmony_ci *dma_handle = vdma_alloc(virt_to_phys(ret), size); 5078c2ecf20Sopenharmony_ci if (*dma_handle == DMA_MAPPING_ERROR) 5088c2ecf20Sopenharmony_ci goto out_free_pages; 5098c2ecf20Sopenharmony_ci arch_dma_prep_coherent(page, size); 5108c2ecf20Sopenharmony_ci return (void *)(UNCAC_BASE + __pa(ret)); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ciout_free_pages: 5138c2ecf20Sopenharmony_ci __free_pages(page, get_order(size)); 5148c2ecf20Sopenharmony_ci return NULL; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic void jazz_dma_free(struct device *dev, size_t size, void *vaddr, 5188c2ecf20Sopenharmony_ci dma_addr_t dma_handle, unsigned long attrs) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci vdma_free(dma_handle); 5218c2ecf20Sopenharmony_ci __free_pages(virt_to_page(vaddr), get_order(size)); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic dma_addr_t jazz_dma_map_page(struct device *dev, struct page *page, 5258c2ecf20Sopenharmony_ci unsigned long offset, size_t size, enum dma_data_direction dir, 5268c2ecf20Sopenharmony_ci unsigned long attrs) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci phys_addr_t phys = page_to_phys(page) + offset; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 5318c2ecf20Sopenharmony_ci arch_sync_dma_for_device(phys, size, dir); 5328c2ecf20Sopenharmony_ci return vdma_alloc(phys, size); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void jazz_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, 5368c2ecf20Sopenharmony_ci size_t size, enum dma_data_direction dir, unsigned long attrs) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 5398c2ecf20Sopenharmony_ci arch_sync_dma_for_cpu(vdma_log2phys(dma_addr), size, dir); 5408c2ecf20Sopenharmony_ci vdma_free(dma_addr); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int jazz_dma_map_sg(struct device *dev, struct scatterlist *sglist, 5448c2ecf20Sopenharmony_ci int nents, enum dma_data_direction dir, unsigned long attrs) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci int i; 5478c2ecf20Sopenharmony_ci struct scatterlist *sg; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci for_each_sg(sglist, sg, nents, i) { 5508c2ecf20Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 5518c2ecf20Sopenharmony_ci arch_sync_dma_for_device(sg_phys(sg), sg->length, 5528c2ecf20Sopenharmony_ci dir); 5538c2ecf20Sopenharmony_ci sg->dma_address = vdma_alloc(sg_phys(sg), sg->length); 5548c2ecf20Sopenharmony_ci if (sg->dma_address == DMA_MAPPING_ERROR) 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci sg_dma_len(sg) = sg->length; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return nents; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic void jazz_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, 5638c2ecf20Sopenharmony_ci int nents, enum dma_data_direction dir, unsigned long attrs) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci int i; 5668c2ecf20Sopenharmony_ci struct scatterlist *sg; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci for_each_sg(sglist, sg, nents, i) { 5698c2ecf20Sopenharmony_ci if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) 5708c2ecf20Sopenharmony_ci arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); 5718c2ecf20Sopenharmony_ci vdma_free(sg->dma_address); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void jazz_dma_sync_single_for_device(struct device *dev, 5768c2ecf20Sopenharmony_ci dma_addr_t addr, size_t size, enum dma_data_direction dir) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci arch_sync_dma_for_device(vdma_log2phys(addr), size, dir); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic void jazz_dma_sync_single_for_cpu(struct device *dev, 5828c2ecf20Sopenharmony_ci dma_addr_t addr, size_t size, enum dma_data_direction dir) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci arch_sync_dma_for_cpu(vdma_log2phys(addr), size, dir); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic void jazz_dma_sync_sg_for_device(struct device *dev, 5888c2ecf20Sopenharmony_ci struct scatterlist *sgl, int nents, enum dma_data_direction dir) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct scatterlist *sg; 5918c2ecf20Sopenharmony_ci int i; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 5948c2ecf20Sopenharmony_ci arch_sync_dma_for_device(sg_phys(sg), sg->length, dir); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void jazz_dma_sync_sg_for_cpu(struct device *dev, 5988c2ecf20Sopenharmony_ci struct scatterlist *sgl, int nents, enum dma_data_direction dir) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct scatterlist *sg; 6018c2ecf20Sopenharmony_ci int i; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 6048c2ecf20Sopenharmony_ci arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciconst struct dma_map_ops jazz_dma_ops = { 6088c2ecf20Sopenharmony_ci .alloc = jazz_dma_alloc, 6098c2ecf20Sopenharmony_ci .free = jazz_dma_free, 6108c2ecf20Sopenharmony_ci .map_page = jazz_dma_map_page, 6118c2ecf20Sopenharmony_ci .unmap_page = jazz_dma_unmap_page, 6128c2ecf20Sopenharmony_ci .map_sg = jazz_dma_map_sg, 6138c2ecf20Sopenharmony_ci .unmap_sg = jazz_dma_unmap_sg, 6148c2ecf20Sopenharmony_ci .sync_single_for_cpu = jazz_dma_sync_single_for_cpu, 6158c2ecf20Sopenharmony_ci .sync_single_for_device = jazz_dma_sync_single_for_device, 6168c2ecf20Sopenharmony_ci .sync_sg_for_cpu = jazz_dma_sync_sg_for_cpu, 6178c2ecf20Sopenharmony_ci .sync_sg_for_device = jazz_dma_sync_sg_for_device, 6188c2ecf20Sopenharmony_ci .mmap = dma_common_mmap, 6198c2ecf20Sopenharmony_ci .get_sgtable = dma_common_get_sgtable, 6208c2ecf20Sopenharmony_ci .alloc_pages = dma_common_alloc_pages, 6218c2ecf20Sopenharmony_ci .free_pages = dma_common_free_pages, 6228c2ecf20Sopenharmony_ci}; 6238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(jazz_dma_ops); 624