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