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