xref: /openbmc/linux/arch/mips/jazz/jazzdma.c (revision f3c3091b98d5d52df40aaf27f11530701d02ac56)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Mips Jazz DMA controller support
41da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 by Andreas Busse
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * NOTE: Some of the argument checking could be removed when
71da177e4SLinus Torvalds  * things have settled down. Also, instead of returning 0xffffffff
81da177e4SLinus Torvalds  * on failure of vdma_alloc() one could leave page #0 unused
91da177e4SLinus Torvalds  * and return the more usual NULL pointer as logical address.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/init.h>
1326dd3e4fSPaul Gortmaker #include <linux/export.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
151da177e4SLinus Torvalds #include <linux/mm.h>
1657c8a661SMike Rapoport #include <linux/memblock.h>
171da177e4SLinus Torvalds #include <linux/spinlock.h>
185a0e3ad6STejun Heo #include <linux/gfp.h>
190a0f0d8bSChristoph Hellwig #include <linux/dma-map-ops.h>
201da177e4SLinus Torvalds #include <asm/mipsregs.h>
211da177e4SLinus Torvalds #include <asm/jazz.h>
221da177e4SLinus Torvalds #include <asm/io.h>
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
241da177e4SLinus Torvalds #include <asm/dma.h>
251da177e4SLinus Torvalds #include <asm/jazzdma.h>
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds /*
281da177e4SLinus Torvalds  * Set this to one to enable additional vdma debug code.
291da177e4SLinus Torvalds  */
301da177e4SLinus Torvalds #define CONF_DEBUG_VDMA 0
311da177e4SLinus Torvalds 
32ea202c63SThomas Bogendoerfer static VDMA_PGTBL_ENTRY *pgtbl;
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static DEFINE_SPINLOCK(vdma_lock);
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds /*
371da177e4SLinus Torvalds  * Debug stuff
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds #define vdma_debug     ((CONF_DEBUG_VDMA) ? debuglvl : 0)
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds static int debuglvl = 3;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds /*
441da177e4SLinus Torvalds  * Initialize the pagetable with a one-to-one mapping of
451da177e4SLinus Torvalds  * the first 16 Mbytes of main memory and declare all
461da177e4SLinus Torvalds  * entries to be unused. Using this method will at least
471da177e4SLinus Torvalds  * allow some early device driver operations to work.
481da177e4SLinus Torvalds  */
vdma_pgtbl_init(void)491da177e4SLinus Torvalds static inline void vdma_pgtbl_init(void)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	unsigned long paddr = 0;
521da177e4SLinus Torvalds 	int i;
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	for (i = 0; i < VDMA_PGTBL_ENTRIES; i++) {
551da177e4SLinus Torvalds 		pgtbl[i].frame = paddr;
561da177e4SLinus Torvalds 		pgtbl[i].owner = VDMA_PAGE_EMPTY;
571da177e4SLinus Torvalds 		paddr += VDMA_PAGESIZE;
581da177e4SLinus Torvalds 	}
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds /*
621da177e4SLinus Torvalds  * Initialize the Jazz R4030 dma controller
631da177e4SLinus Torvalds  */
vdma_init(void)64ea202c63SThomas Bogendoerfer static int __init vdma_init(void)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds 	/*
671da177e4SLinus Torvalds 	 * Allocate 32k of memory for DMA page tables.	This needs to be page
681da177e4SLinus Torvalds 	 * aligned and should be uncached to avoid cache flushing after every
691da177e4SLinus Torvalds 	 * update.
701da177e4SLinus Torvalds 	 */
71ea202c63SThomas Bogendoerfer 	pgtbl = (VDMA_PGTBL_ENTRY *)__get_free_pages(GFP_KERNEL | GFP_DMA,
72ea202c63SThomas Bogendoerfer 						    get_order(VDMA_PGTBL_SIZE));
73b72b7092SRalf Baechle 	BUG_ON(!pgtbl);
74ea202c63SThomas Bogendoerfer 	dma_cache_wback_inv((unsigned long)pgtbl, VDMA_PGTBL_SIZE);
7541af167fSThomas Bogendoerfer 	pgtbl = (VDMA_PGTBL_ENTRY *)CKSEG1ADDR((unsigned long)pgtbl);
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	/*
781da177e4SLinus Torvalds 	 * Clear the R4030 translation table
791da177e4SLinus Torvalds 	 */
801da177e4SLinus Torvalds 	vdma_pgtbl_init();
811da177e4SLinus Torvalds 
8241af167fSThomas Bogendoerfer 	r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE,
8341af167fSThomas Bogendoerfer 			  CPHYSADDR((unsigned long)pgtbl));
841da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM, VDMA_PGTBL_SIZE);
851da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0);
861da177e4SLinus Torvalds 
87ea202c63SThomas Bogendoerfer 	printk(KERN_INFO "VDMA: R4030 DMA pagetables initialized.\n");
88ea202c63SThomas Bogendoerfer 	return 0;
891da177e4SLinus Torvalds }
90c5e2bbb4SChristoph Hellwig arch_initcall(vdma_init);
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /*
931da177e4SLinus Torvalds  * Allocate DMA pagetables using a simple first-fit algorithm
941da177e4SLinus Torvalds  */
vdma_alloc(unsigned long paddr,unsigned long size)951da177e4SLinus Torvalds unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds 	int first, last, pages, frame, i;
981da177e4SLinus Torvalds 	unsigned long laddr, flags;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	/* check arguments */
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	if (paddr > 0x1fffffff) {
1031da177e4SLinus Torvalds 		if (vdma_debug)
1041da177e4SLinus Torvalds 			printk("vdma_alloc: Invalid physical address: %08lx\n",
1051da177e4SLinus Torvalds 			       paddr);
106122da4e0SChristoph Hellwig 		return DMA_MAPPING_ERROR;	/* invalid physical address */
1071da177e4SLinus Torvalds 	}
1081da177e4SLinus Torvalds 	if (size > 0x400000 || size == 0) {
1091da177e4SLinus Torvalds 		if (vdma_debug)
1101da177e4SLinus Torvalds 			printk("vdma_alloc: Invalid size: %08lx\n", size);
111122da4e0SChristoph Hellwig 		return DMA_MAPPING_ERROR;	/* invalid physical address */
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	spin_lock_irqsave(&vdma_lock, flags);
1151da177e4SLinus Torvalds 	/*
1161da177e4SLinus Torvalds 	 * Find free chunk
1171da177e4SLinus Torvalds 	 */
118ea202c63SThomas Bogendoerfer 	pages = VDMA_PAGE(paddr + size) - VDMA_PAGE(paddr) + 1;
1191da177e4SLinus Torvalds 	first = 0;
1201da177e4SLinus Torvalds 	while (1) {
121ea202c63SThomas Bogendoerfer 		while (pgtbl[first].owner != VDMA_PAGE_EMPTY &&
1221da177e4SLinus Torvalds 		       first < VDMA_PGTBL_ENTRIES) first++;
1231da177e4SLinus Torvalds 		if (first + pages > VDMA_PGTBL_ENTRIES) {	/* nothing free */
1241da177e4SLinus Torvalds 			spin_unlock_irqrestore(&vdma_lock, flags);
125122da4e0SChristoph Hellwig 			return DMA_MAPPING_ERROR;
1261da177e4SLinus Torvalds 		}
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 		last = first + 1;
129ea202c63SThomas Bogendoerfer 		while (pgtbl[last].owner == VDMA_PAGE_EMPTY
1301da177e4SLinus Torvalds 		       && last - first < pages)
1311da177e4SLinus Torvalds 			last++;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 		if (last - first == pages)
1341da177e4SLinus Torvalds 			break;	/* found */
135ea202c63SThomas Bogendoerfer 		first = last + 1;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	/*
1391da177e4SLinus Torvalds 	 * Mark pages as allocated
1401da177e4SLinus Torvalds 	 */
1411da177e4SLinus Torvalds 	laddr = (first << 12) + (paddr & (VDMA_PAGESIZE - 1));
1421da177e4SLinus Torvalds 	frame = paddr & ~(VDMA_PAGESIZE - 1);
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	for (i = first; i < last; i++) {
145ea202c63SThomas Bogendoerfer 		pgtbl[i].frame = frame;
146ea202c63SThomas Bogendoerfer 		pgtbl[i].owner = laddr;
1471da177e4SLinus Torvalds 		frame += VDMA_PAGESIZE;
1481da177e4SLinus Torvalds 	}
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	/*
1511da177e4SLinus Torvalds 	 * Update translation table and return logical start address
1521da177e4SLinus Torvalds 	 */
1531da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0);
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	if (vdma_debug > 1)
1561da177e4SLinus Torvalds 		printk("vdma_alloc: Allocated %d pages starting from %08lx\n",
1571da177e4SLinus Torvalds 		     pages, laddr);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if (vdma_debug > 2) {
1601da177e4SLinus Torvalds 		printk("LADDR: ");
1611da177e4SLinus Torvalds 		for (i = first; i < last; i++)
1621da177e4SLinus Torvalds 			printk("%08x ", i << 12);
1631da177e4SLinus Torvalds 		printk("\nPADDR: ");
1641da177e4SLinus Torvalds 		for (i = first; i < last; i++)
165ea202c63SThomas Bogendoerfer 			printk("%08x ", pgtbl[i].frame);
1661da177e4SLinus Torvalds 		printk("\nOWNER: ");
1671da177e4SLinus Torvalds 		for (i = first; i < last; i++)
168ea202c63SThomas Bogendoerfer 			printk("%08x ", pgtbl[i].owner);
1691da177e4SLinus Torvalds 		printk("\n");
1701da177e4SLinus Torvalds 	}
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	spin_unlock_irqrestore(&vdma_lock, flags);
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	return laddr;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_alloc);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds /*
1801da177e4SLinus Torvalds  * Free previously allocated dma translation pages
1811da177e4SLinus Torvalds  * Note that this does NOT change the translation table,
1821da177e4SLinus Torvalds  * it just marks the free'd pages as unused!
1831da177e4SLinus Torvalds  */
vdma_free(unsigned long laddr)1841da177e4SLinus Torvalds int vdma_free(unsigned long laddr)
1851da177e4SLinus Torvalds {
1861da177e4SLinus Torvalds 	int i;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	i = laddr >> 12;
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds 	if (pgtbl[i].owner != laddr) {
1911da177e4SLinus Torvalds 		printk
1921da177e4SLinus Torvalds 		    ("vdma_free: trying to free other's dma pages, laddr=%8lx\n",
1931da177e4SLinus Torvalds 		     laddr);
1941da177e4SLinus Torvalds 		return -1;
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds 
1973d4656d6SRoel Kluin 	while (i < VDMA_PGTBL_ENTRIES && pgtbl[i].owner == laddr) {
1981da177e4SLinus Torvalds 		pgtbl[i].owner = VDMA_PAGE_EMPTY;
1991da177e4SLinus Torvalds 		i++;
2001da177e4SLinus Torvalds 	}
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	if (vdma_debug > 1)
2031da177e4SLinus Torvalds 		printk("vdma_free: freed %ld pages starting from %08lx\n",
2041da177e4SLinus Torvalds 		       i - (laddr >> 12), laddr);
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	return 0;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_free);
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds /*
2121da177e4SLinus Torvalds  * Translate a physical address to a logical address.
2131da177e4SLinus Torvalds  * This will return the logical address of the first
2141da177e4SLinus Torvalds  * match.
2151da177e4SLinus Torvalds  */
vdma_phys2log(unsigned long paddr)2161da177e4SLinus Torvalds unsigned long vdma_phys2log(unsigned long paddr)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	int i;
2191da177e4SLinus Torvalds 	int frame;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	frame = paddr & ~(VDMA_PAGESIZE - 1);
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	for (i = 0; i < VDMA_PGTBL_ENTRIES; i++) {
2241da177e4SLinus Torvalds 		if (pgtbl[i].frame == frame)
2251da177e4SLinus Torvalds 			break;
2261da177e4SLinus Torvalds 	}
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	if (i == VDMA_PGTBL_ENTRIES)
2291da177e4SLinus Torvalds 		return ~0UL;
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 	return (i << 12) + (paddr & (VDMA_PAGESIZE - 1));
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_phys2log);
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds /*
2371da177e4SLinus Torvalds  * Translate a logical DMA address to a physical address
2381da177e4SLinus Torvalds  */
vdma_log2phys(unsigned long laddr)2391da177e4SLinus Torvalds unsigned long vdma_log2phys(unsigned long laddr)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds 	return pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE - 1));
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_log2phys);
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds /*
2471da177e4SLinus Torvalds  * Print DMA statistics
2481da177e4SLinus Torvalds  */
vdma_stats(void)2491da177e4SLinus Torvalds void vdma_stats(void)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 	int i;
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	printk("vdma_stats: CONFIG: %08x\n",
2541da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_CONFIG));
2551da177e4SLinus Torvalds 	printk("R4030 translation table base: %08x\n",
2561da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_TRSTBL_BASE));
2571da177e4SLinus Torvalds 	printk("R4030 translation table limit: %08x\n",
2581da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_TRSTBL_LIM));
2591da177e4SLinus Torvalds 	printk("vdma_stats: INV_ADDR: %08x\n",
2601da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_INV_ADDR));
2611da177e4SLinus Torvalds 	printk("vdma_stats: R_FAIL_ADDR: %08x\n",
2621da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_R_FAIL_ADDR));
2631da177e4SLinus Torvalds 	printk("vdma_stats: M_FAIL_ADDR: %08x\n",
2641da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_M_FAIL_ADDR));
2651da177e4SLinus Torvalds 	printk("vdma_stats: IRQ_SOURCE: %08x\n",
2661da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_IRQ_SOURCE));
2671da177e4SLinus Torvalds 	printk("vdma_stats: I386_ERROR: %08x\n",
2681da177e4SLinus Torvalds 	       r4030_read_reg32(JAZZ_R4030_I386_ERROR));
2691da177e4SLinus Torvalds 	printk("vdma_chnl_modes:   ");
2701da177e4SLinus Torvalds 	for (i = 0; i < 8; i++)
2711da177e4SLinus Torvalds 		printk("%04x ",
2721da177e4SLinus Torvalds 		       (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_MODE +
2731da177e4SLinus Torvalds 						   (i << 5)));
2741da177e4SLinus Torvalds 	printk("\n");
2751da177e4SLinus Torvalds 	printk("vdma_chnl_enables: ");
2761da177e4SLinus Torvalds 	for (i = 0; i < 8; i++)
2771da177e4SLinus Torvalds 		printk("%04x ",
2781da177e4SLinus Torvalds 		       (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
2791da177e4SLinus Torvalds 						   (i << 5)));
2801da177e4SLinus Torvalds 	printk("\n");
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds /*
2841da177e4SLinus Torvalds  * DMA transfer functions
2851da177e4SLinus Torvalds  */
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds /*
2881da177e4SLinus Torvalds  * Enable a DMA channel. Also clear any error conditions.
2891da177e4SLinus Torvalds  */
vdma_enable(int channel)2901da177e4SLinus Torvalds void vdma_enable(int channel)
2911da177e4SLinus Torvalds {
2921da177e4SLinus Torvalds 	int status;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	if (vdma_debug)
2951da177e4SLinus Torvalds 		printk("vdma_enable: channel %d\n", channel);
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	/*
2981da177e4SLinus Torvalds 	 * Check error conditions first
2991da177e4SLinus Torvalds 	 */
3001da177e4SLinus Torvalds 	status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5));
3011da177e4SLinus Torvalds 	if (status & 0x400)
3021da177e4SLinus Torvalds 		printk("VDMA: Channel %d: Address error!\n", channel);
3031da177e4SLinus Torvalds 	if (status & 0x200)
3041da177e4SLinus Torvalds 		printk("VDMA: Channel %d: Memory error!\n", channel);
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	/*
3071da177e4SLinus Torvalds 	 * Clear all interrupt flags
3081da177e4SLinus Torvalds 	 */
3091da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5),
3101da177e4SLinus Torvalds 			  r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
3111da177e4SLinus Torvalds 					   (channel << 5)) | R4030_TC_INTR
3121da177e4SLinus Torvalds 			  | R4030_MEM_INTR | R4030_ADDR_INTR);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	/*
3151da177e4SLinus Torvalds 	 * Enable the desired channel
3161da177e4SLinus Torvalds 	 */
3171da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5),
3181da177e4SLinus Torvalds 			  r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
3191da177e4SLinus Torvalds 					   (channel << 5)) |
3201da177e4SLinus Torvalds 			  R4030_CHNL_ENABLE);
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_enable);
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds /*
3261da177e4SLinus Torvalds  * Disable a DMA channel
3271da177e4SLinus Torvalds  */
vdma_disable(int channel)3281da177e4SLinus Torvalds void vdma_disable(int channel)
3291da177e4SLinus Torvalds {
3301da177e4SLinus Torvalds 	if (vdma_debug) {
3311da177e4SLinus Torvalds 		int status =
3321da177e4SLinus Torvalds 		    r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
3331da177e4SLinus Torvalds 				     (channel << 5));
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 		printk("vdma_disable: channel %d\n", channel);
3361da177e4SLinus Torvalds 		printk("VDMA: channel %d status: %04x (%s) mode: "
3371da177e4SLinus Torvalds 		       "%02x addr: %06x count: %06x\n",
3381da177e4SLinus Torvalds 		       channel, status,
3391da177e4SLinus Torvalds 		       ((status & 0x600) ? "ERROR" : "OK"),
3401da177e4SLinus Torvalds 		       (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_MODE +
3411da177e4SLinus Torvalds 						   (channel << 5)),
3421da177e4SLinus Torvalds 		       (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_ADDR +
3431da177e4SLinus Torvalds 						   (channel << 5)),
3441da177e4SLinus Torvalds 		       (unsigned) r4030_read_reg32(JAZZ_R4030_CHNL_COUNT +
3451da177e4SLinus Torvalds 						   (channel << 5)));
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5),
3491da177e4SLinus Torvalds 			  r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
3501da177e4SLinus Torvalds 					   (channel << 5)) &
3511da177e4SLinus Torvalds 			  ~R4030_CHNL_ENABLE);
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	/*
3541da177e4SLinus Torvalds 	 * After disabling a DMA channel a remote bus register should be
3551da177e4SLinus Torvalds 	 * read to ensure that the current DMA acknowledge cycle is completed.
3561da177e4SLinus Torvalds 	 */
3571da177e4SLinus Torvalds 	*((volatile unsigned int *) JAZZ_DUMMY_DEVICE);
3581da177e4SLinus Torvalds }
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_disable);
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds /*
3631da177e4SLinus Torvalds  * Set DMA mode. This function accepts the mode values used
3641da177e4SLinus Torvalds  * to set a PC-style DMA controller. For the SCSI and FDC
3651da177e4SLinus Torvalds  * channels, we also set the default modes each time we're
3661da177e4SLinus Torvalds  * called.
3671da177e4SLinus Torvalds  * NOTE: The FAST and BURST dma modes are supported by the
3681da177e4SLinus Torvalds  * R4030 Rev. 2 and PICA chipsets only. I leave them disabled
3691da177e4SLinus Torvalds  * for now.
3701da177e4SLinus Torvalds  */
vdma_set_mode(int channel,int mode)3711da177e4SLinus Torvalds void vdma_set_mode(int channel, int mode)
3721da177e4SLinus Torvalds {
3731da177e4SLinus Torvalds 	if (vdma_debug)
3741da177e4SLinus Torvalds 		printk("vdma_set_mode: channel %d, mode 0x%x\n", channel,
3751da177e4SLinus Torvalds 		       mode);
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	switch (channel) {
3781da177e4SLinus Torvalds 	case JAZZ_SCSI_DMA:	/* scsi */
3791da177e4SLinus Torvalds 		r4030_write_reg32(JAZZ_R4030_CHNL_MODE + (channel << 5),
3801da177e4SLinus Torvalds /*			  R4030_MODE_FAST | */
3811da177e4SLinus Torvalds /*			  R4030_MODE_BURST | */
3821da177e4SLinus Torvalds 				  R4030_MODE_INTR_EN |
3831da177e4SLinus Torvalds 				  R4030_MODE_WIDTH_16 |
3841da177e4SLinus Torvalds 				  R4030_MODE_ATIME_80);
3851da177e4SLinus Torvalds 		break;
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	case JAZZ_FLOPPY_DMA:	/* floppy */
3881da177e4SLinus Torvalds 		r4030_write_reg32(JAZZ_R4030_CHNL_MODE + (channel << 5),
3891da177e4SLinus Torvalds /*			  R4030_MODE_FAST | */
3901da177e4SLinus Torvalds /*			  R4030_MODE_BURST | */
3911da177e4SLinus Torvalds 				  R4030_MODE_INTR_EN |
3921da177e4SLinus Torvalds 				  R4030_MODE_WIDTH_8 |
3931da177e4SLinus Torvalds 				  R4030_MODE_ATIME_120);
3941da177e4SLinus Torvalds 		break;
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	case JAZZ_AUDIOL_DMA:
3971da177e4SLinus Torvalds 	case JAZZ_AUDIOR_DMA:
3981da177e4SLinus Torvalds 		printk("VDMA: Audio DMA not supported yet.\n");
3991da177e4SLinus Torvalds 		break;
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	default:
4021da177e4SLinus Torvalds 		printk
4031da177e4SLinus Torvalds 		    ("VDMA: vdma_set_mode() called with unsupported channel %d!\n",
4041da177e4SLinus Torvalds 		     channel);
4051da177e4SLinus Torvalds 	}
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	switch (mode) {
4081da177e4SLinus Torvalds 	case DMA_MODE_READ:
4091da177e4SLinus Torvalds 		r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5),
4101da177e4SLinus Torvalds 				  r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
4111da177e4SLinus Torvalds 						   (channel << 5)) &
4121da177e4SLinus Torvalds 				  ~R4030_CHNL_WRITE);
4131da177e4SLinus Torvalds 		break;
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	case DMA_MODE_WRITE:
4161da177e4SLinus Torvalds 		r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5),
4171da177e4SLinus Torvalds 				  r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE +
4181da177e4SLinus Torvalds 						   (channel << 5)) |
4191da177e4SLinus Torvalds 				  R4030_CHNL_WRITE);
4201da177e4SLinus Torvalds 		break;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	default:
4231da177e4SLinus Torvalds 		printk
4241da177e4SLinus Torvalds 		    ("VDMA: vdma_set_mode() called with unknown dma mode 0x%x\n",
4251da177e4SLinus Torvalds 		     mode);
4261da177e4SLinus Torvalds 	}
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_set_mode);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds /*
4321da177e4SLinus Torvalds  * Set Transfer Address
4331da177e4SLinus Torvalds  */
vdma_set_addr(int channel,long addr)4341da177e4SLinus Torvalds void vdma_set_addr(int channel, long addr)
4351da177e4SLinus Torvalds {
4361da177e4SLinus Torvalds 	if (vdma_debug)
4371da177e4SLinus Torvalds 		printk("vdma_set_addr: channel %d, addr %lx\n", channel,
4381da177e4SLinus Torvalds 		       addr);
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_CHNL_ADDR + (channel << 5), addr);
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_set_addr);
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds /*
4461da177e4SLinus Torvalds  * Set Transfer Count
4471da177e4SLinus Torvalds  */
vdma_set_count(int channel,int count)4481da177e4SLinus Torvalds void vdma_set_count(int channel, int count)
4491da177e4SLinus Torvalds {
4501da177e4SLinus Torvalds 	if (vdma_debug)
4511da177e4SLinus Torvalds 		printk("vdma_set_count: channel %d, count %08x\n", channel,
4521da177e4SLinus Torvalds 		       (unsigned) count);
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	r4030_write_reg32(JAZZ_R4030_CHNL_COUNT + (channel << 5), count);
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds EXPORT_SYMBOL(vdma_set_count);
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds /*
4601da177e4SLinus Torvalds  * Get Residual
4611da177e4SLinus Torvalds  */
vdma_get_residue(int channel)4621da177e4SLinus Torvalds int vdma_get_residue(int channel)
4631da177e4SLinus Torvalds {
4641da177e4SLinus Torvalds 	int residual;
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	residual = r4030_read_reg32(JAZZ_R4030_CHNL_COUNT + (channel << 5));
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	if (vdma_debug)
4691da177e4SLinus Torvalds 		printk("vdma_get_residual: channel %d: residual=%d\n",
4701da177e4SLinus Torvalds 		       channel, residual);
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 	return residual;
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds /*
4761da177e4SLinus Torvalds  * Get DMA channel enable register
4771da177e4SLinus Torvalds  */
vdma_get_enable(int channel)4781da177e4SLinus Torvalds int vdma_get_enable(int channel)
4791da177e4SLinus Torvalds {
4801da177e4SLinus Torvalds 	int enable;
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	enable = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE + (channel << 5));
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 	if (vdma_debug)
4851da177e4SLinus Torvalds 		printk("vdma_get_enable: channel %d: enable=%d\n", channel,
4861da177e4SLinus Torvalds 		       enable);
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	return enable;
4891da177e4SLinus Torvalds }
490ea202c63SThomas Bogendoerfer 
jazz_dma_alloc(struct device * dev,size_t size,dma_addr_t * dma_handle,gfp_t gfp,unsigned long attrs)491c5e2bbb4SChristoph Hellwig static void *jazz_dma_alloc(struct device *dev, size_t size,
492c5e2bbb4SChristoph Hellwig 		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
493c5e2bbb4SChristoph Hellwig {
494170780beSChristoph Hellwig 	struct page *page;
495c5e2bbb4SChristoph Hellwig 	void *ret;
496c5e2bbb4SChristoph Hellwig 
497170780beSChristoph Hellwig 	if (attrs & DMA_ATTR_NO_WARN)
498170780beSChristoph Hellwig 		gfp |= __GFP_NOWARN;
499c5e2bbb4SChristoph Hellwig 
500170780beSChristoph Hellwig 	size = PAGE_ALIGN(size);
501170780beSChristoph Hellwig 	page = alloc_pages(gfp, get_order(size));
502170780beSChristoph Hellwig 	if (!page)
503170780beSChristoph Hellwig 		return NULL;
504170780beSChristoph Hellwig 	ret = page_address(page);
505170780beSChristoph Hellwig 	memset(ret, 0, size);
506c5e2bbb4SChristoph Hellwig 	*dma_handle = vdma_alloc(virt_to_phys(ret), size);
507170780beSChristoph Hellwig 	if (*dma_handle == DMA_MAPPING_ERROR)
508170780beSChristoph Hellwig 		goto out_free_pages;
509170780beSChristoph Hellwig 	arch_dma_prep_coherent(page, size);
510170780beSChristoph Hellwig 	return (void *)(UNCAC_BASE + __pa(ret));
511170780beSChristoph Hellwig 
512170780beSChristoph Hellwig out_free_pages:
513170780beSChristoph Hellwig 	__free_pages(page, get_order(size));
514170780beSChristoph Hellwig 	return NULL;
515c5e2bbb4SChristoph Hellwig }
516c5e2bbb4SChristoph Hellwig 
jazz_dma_free(struct device * dev,size_t size,void * vaddr,dma_addr_t dma_handle,unsigned long attrs)517c5e2bbb4SChristoph Hellwig static void jazz_dma_free(struct device *dev, size_t size, void *vaddr,
518c5e2bbb4SChristoph Hellwig 		dma_addr_t dma_handle, unsigned long attrs)
519c5e2bbb4SChristoph Hellwig {
520c5e2bbb4SChristoph Hellwig 	vdma_free(dma_handle);
521170780beSChristoph Hellwig 	__free_pages(virt_to_page(vaddr), get_order(size));
522c5e2bbb4SChristoph Hellwig }
523c5e2bbb4SChristoph Hellwig 
jazz_dma_map_page(struct device * dev,struct page * page,unsigned long offset,size_t size,enum dma_data_direction dir,unsigned long attrs)524c5e2bbb4SChristoph Hellwig static dma_addr_t jazz_dma_map_page(struct device *dev, struct page *page,
525c5e2bbb4SChristoph Hellwig 		unsigned long offset, size_t size, enum dma_data_direction dir,
526c5e2bbb4SChristoph Hellwig 		unsigned long attrs)
527c5e2bbb4SChristoph Hellwig {
528c5e2bbb4SChristoph Hellwig 	phys_addr_t phys = page_to_phys(page) + offset;
529c5e2bbb4SChristoph Hellwig 
530c5e2bbb4SChristoph Hellwig 	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
53156e35f9cSChristoph Hellwig 		arch_sync_dma_for_device(phys, size, dir);
532c5e2bbb4SChristoph Hellwig 	return vdma_alloc(phys, size);
533c5e2bbb4SChristoph Hellwig }
534c5e2bbb4SChristoph Hellwig 
jazz_dma_unmap_page(struct device * dev,dma_addr_t dma_addr,size_t size,enum dma_data_direction dir,unsigned long attrs)535c5e2bbb4SChristoph Hellwig static void jazz_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
536c5e2bbb4SChristoph Hellwig 		size_t size, enum dma_data_direction dir, unsigned long attrs)
537c5e2bbb4SChristoph Hellwig {
538c5e2bbb4SChristoph Hellwig 	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
53956e35f9cSChristoph Hellwig 		arch_sync_dma_for_cpu(vdma_log2phys(dma_addr), size, dir);
540c5e2bbb4SChristoph Hellwig 	vdma_free(dma_addr);
541c5e2bbb4SChristoph Hellwig }
542c5e2bbb4SChristoph Hellwig 
jazz_dma_map_sg(struct device * dev,struct scatterlist * sglist,int nents,enum dma_data_direction dir,unsigned long attrs)543c5e2bbb4SChristoph Hellwig static int jazz_dma_map_sg(struct device *dev, struct scatterlist *sglist,
544c5e2bbb4SChristoph Hellwig 		int nents, enum dma_data_direction dir, unsigned long attrs)
545c5e2bbb4SChristoph Hellwig {
546c5e2bbb4SChristoph Hellwig 	int i;
547c5e2bbb4SChristoph Hellwig 	struct scatterlist *sg;
548c5e2bbb4SChristoph Hellwig 
549c5e2bbb4SChristoph Hellwig 	for_each_sg(sglist, sg, nents, i) {
550c5e2bbb4SChristoph Hellwig 		if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
55156e35f9cSChristoph Hellwig 			arch_sync_dma_for_device(sg_phys(sg), sg->length,
552c5e2bbb4SChristoph Hellwig 				dir);
553c5e2bbb4SChristoph Hellwig 		sg->dma_address = vdma_alloc(sg_phys(sg), sg->length);
554122da4e0SChristoph Hellwig 		if (sg->dma_address == DMA_MAPPING_ERROR)
555af82fe85SMartin Oliveira 			return -EIO;
556c5e2bbb4SChristoph Hellwig 		sg_dma_len(sg) = sg->length;
557c5e2bbb4SChristoph Hellwig 	}
558c5e2bbb4SChristoph Hellwig 
559c5e2bbb4SChristoph Hellwig 	return nents;
560c5e2bbb4SChristoph Hellwig }
561c5e2bbb4SChristoph Hellwig 
jazz_dma_unmap_sg(struct device * dev,struct scatterlist * sglist,int nents,enum dma_data_direction dir,unsigned long attrs)562c5e2bbb4SChristoph Hellwig static void jazz_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
563c5e2bbb4SChristoph Hellwig 		int nents, enum dma_data_direction dir, unsigned long attrs)
564c5e2bbb4SChristoph Hellwig {
565c5e2bbb4SChristoph Hellwig 	int i;
566c5e2bbb4SChristoph Hellwig 	struct scatterlist *sg;
567c5e2bbb4SChristoph Hellwig 
568c5e2bbb4SChristoph Hellwig 	for_each_sg(sglist, sg, nents, i) {
569c5e2bbb4SChristoph Hellwig 		if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
57056e35f9cSChristoph Hellwig 			arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
571c5e2bbb4SChristoph Hellwig 		vdma_free(sg->dma_address);
572c5e2bbb4SChristoph Hellwig 	}
573c5e2bbb4SChristoph Hellwig }
574c5e2bbb4SChristoph Hellwig 
jazz_dma_sync_single_for_device(struct device * dev,dma_addr_t addr,size_t size,enum dma_data_direction dir)575c5e2bbb4SChristoph Hellwig static void jazz_dma_sync_single_for_device(struct device *dev,
576c5e2bbb4SChristoph Hellwig 		dma_addr_t addr, size_t size, enum dma_data_direction dir)
577c5e2bbb4SChristoph Hellwig {
57856e35f9cSChristoph Hellwig 	arch_sync_dma_for_device(vdma_log2phys(addr), size, dir);
579c5e2bbb4SChristoph Hellwig }
580c5e2bbb4SChristoph Hellwig 
jazz_dma_sync_single_for_cpu(struct device * dev,dma_addr_t addr,size_t size,enum dma_data_direction dir)581c5e2bbb4SChristoph Hellwig static void jazz_dma_sync_single_for_cpu(struct device *dev,
582c5e2bbb4SChristoph Hellwig 		dma_addr_t addr, size_t size, enum dma_data_direction dir)
583c5e2bbb4SChristoph Hellwig {
58456e35f9cSChristoph Hellwig 	arch_sync_dma_for_cpu(vdma_log2phys(addr), size, dir);
585c5e2bbb4SChristoph Hellwig }
586c5e2bbb4SChristoph Hellwig 
jazz_dma_sync_sg_for_device(struct device * dev,struct scatterlist * sgl,int nents,enum dma_data_direction dir)587c5e2bbb4SChristoph Hellwig static void jazz_dma_sync_sg_for_device(struct device *dev,
588c5e2bbb4SChristoph Hellwig 		struct scatterlist *sgl, int nents, enum dma_data_direction dir)
589c5e2bbb4SChristoph Hellwig {
590c5e2bbb4SChristoph Hellwig 	struct scatterlist *sg;
591c5e2bbb4SChristoph Hellwig 	int i;
592c5e2bbb4SChristoph Hellwig 
593c5e2bbb4SChristoph Hellwig 	for_each_sg(sgl, sg, nents, i)
59456e35f9cSChristoph Hellwig 		arch_sync_dma_for_device(sg_phys(sg), sg->length, dir);
595c5e2bbb4SChristoph Hellwig }
596c5e2bbb4SChristoph Hellwig 
jazz_dma_sync_sg_for_cpu(struct device * dev,struct scatterlist * sgl,int nents,enum dma_data_direction dir)597c5e2bbb4SChristoph Hellwig static void jazz_dma_sync_sg_for_cpu(struct device *dev,
598c5e2bbb4SChristoph Hellwig 		struct scatterlist *sgl, int nents, enum dma_data_direction dir)
599c5e2bbb4SChristoph Hellwig {
600c5e2bbb4SChristoph Hellwig 	struct scatterlist *sg;
601c5e2bbb4SChristoph Hellwig 	int i;
602c5e2bbb4SChristoph Hellwig 
603c5e2bbb4SChristoph Hellwig 	for_each_sg(sgl, sg, nents, i)
60456e35f9cSChristoph Hellwig 		arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
605c5e2bbb4SChristoph Hellwig }
606c5e2bbb4SChristoph Hellwig 
607c5e2bbb4SChristoph Hellwig const struct dma_map_ops jazz_dma_ops = {
608c5e2bbb4SChristoph Hellwig 	.alloc			= jazz_dma_alloc,
609c5e2bbb4SChristoph Hellwig 	.free			= jazz_dma_free,
610c5e2bbb4SChristoph Hellwig 	.map_page		= jazz_dma_map_page,
611c5e2bbb4SChristoph Hellwig 	.unmap_page		= jazz_dma_unmap_page,
612c5e2bbb4SChristoph Hellwig 	.map_sg			= jazz_dma_map_sg,
613c5e2bbb4SChristoph Hellwig 	.unmap_sg		= jazz_dma_unmap_sg,
614c5e2bbb4SChristoph Hellwig 	.sync_single_for_cpu	= jazz_dma_sync_single_for_cpu,
615c5e2bbb4SChristoph Hellwig 	.sync_single_for_device	= jazz_dma_sync_single_for_device,
616c5e2bbb4SChristoph Hellwig 	.sync_sg_for_cpu	= jazz_dma_sync_sg_for_cpu,
617c5e2bbb4SChristoph Hellwig 	.sync_sg_for_device	= jazz_dma_sync_sg_for_device,
618f9f3232aSChristoph Hellwig 	.mmap			= dma_common_mmap,
619f9f3232aSChristoph Hellwig 	.get_sgtable		= dma_common_get_sgtable,
620*05d2e16aSGreg Kroah-Hartman 	.alloc_pages		= dma_common_alloc_pages,
621efa70f2fSChristoph Hellwig 	.free_pages		= dma_common_free_pages,
622c5e2bbb4SChristoph Hellwig };
623c5e2bbb4SChristoph Hellwig EXPORT_SYMBOL(jazz_dma_ops);
624