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