18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * BRIEF MODULE DESCRIPTION 48c2ecf20Sopenharmony_ci * A DMA channel allocator for Au1x00. API is modeled loosely off of 58c2ecf20Sopenharmony_ci * linux/kernel/dma.c. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2000, 2008 MontaVista Software Inc. 88c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc. <source@mvista.com> 98c2ecf20Sopenharmony_ci * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 128c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 138c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 148c2ecf20Sopenharmony_ci * option) any later version. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 178c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 188c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 198c2ecf20Sopenharmony_ci * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 208c2ecf20Sopenharmony_ci * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 218c2ecf20Sopenharmony_ci * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 228c2ecf20Sopenharmony_ci * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 238c2ecf20Sopenharmony_ci * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 248c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 258c2ecf20Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along 288c2ecf20Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc., 298c2ecf20Sopenharmony_ci * 675 Mass Ave, Cambridge, MA 02139, USA. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci#include <linux/export.h> 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/errno.h> 378c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 388c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 418c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000_dma.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * A note on resource allocation: 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * All drivers needing DMA channels, should allocate and release them 478c2ecf20Sopenharmony_ci * through the public routines `request_dma()' and `free_dma()'. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * In order to avoid problems, all processes should allocate resources in 508c2ecf20Sopenharmony_ci * the same sequence and release them in the reverse order. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. 538c2ecf20Sopenharmony_ci * When releasing them, first release the IRQ, then release the DMA. The 548c2ecf20Sopenharmony_ci * main reason for this order is that, if you are requesting the DMA buffer 558c2ecf20Sopenharmony_ci * done interrupt, you won't know the irq number until the DMA channel is 568c2ecf20Sopenharmony_ci * returned from request_dma. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* DMA Channel register block spacing */ 608c2ecf20Sopenharmony_ci#define DMA_CHANNEL_LEN 0x00000100 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(au1000_dma_spin_lock); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct dma_chan au1000_dma_table[NUM_AU1000_DMA_CHANNELS] = { 658c2ecf20Sopenharmony_ci {.dev_id = -1,}, 668c2ecf20Sopenharmony_ci {.dev_id = -1,}, 678c2ecf20Sopenharmony_ci {.dev_id = -1,}, 688c2ecf20Sopenharmony_ci {.dev_id = -1,}, 698c2ecf20Sopenharmony_ci {.dev_id = -1,}, 708c2ecf20Sopenharmony_ci {.dev_id = -1,}, 718c2ecf20Sopenharmony_ci {.dev_id = -1,}, 728c2ecf20Sopenharmony_ci {.dev_id = -1,} 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1000_dma_table); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Device FIFO addresses and default DMA modes */ 778c2ecf20Sopenharmony_cistatic const struct dma_dev { 788c2ecf20Sopenharmony_ci unsigned int fifo_addr; 798c2ecf20Sopenharmony_ci unsigned int dma_mode; 808c2ecf20Sopenharmony_ci} dma_dev_table[DMA_NUM_DEV] = { 818c2ecf20Sopenharmony_ci { AU1000_UART0_PHYS_ADDR + 0x04, DMA_DW8 }, /* UART0_TX */ 828c2ecf20Sopenharmony_ci { AU1000_UART0_PHYS_ADDR + 0x00, DMA_DW8 | DMA_DR }, /* UART0_RX */ 838c2ecf20Sopenharmony_ci { 0, 0 }, /* DMA_REQ0 */ 848c2ecf20Sopenharmony_ci { 0, 0 }, /* DMA_REQ1 */ 858c2ecf20Sopenharmony_ci { AU1000_AC97_PHYS_ADDR + 0x08, DMA_DW16 }, /* AC97 TX c */ 868c2ecf20Sopenharmony_ci { AU1000_AC97_PHYS_ADDR + 0x08, DMA_DW16 | DMA_DR }, /* AC97 RX c */ 878c2ecf20Sopenharmony_ci { AU1000_UART3_PHYS_ADDR + 0x04, DMA_DW8 | DMA_NC }, /* UART3_TX */ 888c2ecf20Sopenharmony_ci { AU1000_UART3_PHYS_ADDR + 0x00, DMA_DW8 | DMA_NC | DMA_DR }, /* UART3_RX */ 898c2ecf20Sopenharmony_ci { AU1000_USB_UDC_PHYS_ADDR + 0x00, DMA_DW8 | DMA_NC | DMA_DR }, /* EP0RD */ 908c2ecf20Sopenharmony_ci { AU1000_USB_UDC_PHYS_ADDR + 0x04, DMA_DW8 | DMA_NC }, /* EP0WR */ 918c2ecf20Sopenharmony_ci { AU1000_USB_UDC_PHYS_ADDR + 0x08, DMA_DW8 | DMA_NC }, /* EP2WR */ 928c2ecf20Sopenharmony_ci { AU1000_USB_UDC_PHYS_ADDR + 0x0c, DMA_DW8 | DMA_NC }, /* EP3WR */ 938c2ecf20Sopenharmony_ci { AU1000_USB_UDC_PHYS_ADDR + 0x10, DMA_DW8 | DMA_NC | DMA_DR }, /* EP4RD */ 948c2ecf20Sopenharmony_ci { AU1000_USB_UDC_PHYS_ADDR + 0x14, DMA_DW8 | DMA_NC | DMA_DR }, /* EP5RD */ 958c2ecf20Sopenharmony_ci /* on Au1500, these 2 are DMA_REQ2/3 (GPIO208/209) instead! */ 968c2ecf20Sopenharmony_ci { AU1000_I2S_PHYS_ADDR + 0x00, DMA_DW32 | DMA_NC}, /* I2S TX */ 978c2ecf20Sopenharmony_ci { AU1000_I2S_PHYS_ADDR + 0x00, DMA_DW32 | DMA_NC | DMA_DR}, /* I2S RX */ 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciint au1000_dma_read_proc(char *buf, char **start, off_t fpos, 1018c2ecf20Sopenharmony_ci int length, int *eof, void *data) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int i, len = 0; 1048c2ecf20Sopenharmony_ci struct dma_chan *chan; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { 1078c2ecf20Sopenharmony_ci chan = get_dma_chan(i); 1088c2ecf20Sopenharmony_ci if (chan != NULL) 1098c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%2d: %s\n", 1108c2ecf20Sopenharmony_ci i, chan->dev_str); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (fpos >= len) { 1148c2ecf20Sopenharmony_ci *start = buf; 1158c2ecf20Sopenharmony_ci *eof = 1; 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci *start = buf + fpos; 1198c2ecf20Sopenharmony_ci len -= fpos; 1208c2ecf20Sopenharmony_ci if (len > length) 1218c2ecf20Sopenharmony_ci return length; 1228c2ecf20Sopenharmony_ci *eof = 1; 1238c2ecf20Sopenharmony_ci return len; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* Device FIFO addresses and default DMA modes - 2nd bank */ 1278c2ecf20Sopenharmony_cistatic const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = { 1288c2ecf20Sopenharmony_ci { AU1100_SD0_PHYS_ADDR + 0x00, DMA_DS | DMA_DW8 }, /* coherent */ 1298c2ecf20Sopenharmony_ci { AU1100_SD0_PHYS_ADDR + 0x04, DMA_DS | DMA_DW8 | DMA_DR }, /* coherent */ 1308c2ecf20Sopenharmony_ci { AU1100_SD1_PHYS_ADDR + 0x00, DMA_DS | DMA_DW8 }, /* coherent */ 1318c2ecf20Sopenharmony_ci { AU1100_SD1_PHYS_ADDR + 0x04, DMA_DS | DMA_DW8 | DMA_DR } /* coherent */ 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_civoid dump_au1000_dma_channel(unsigned int dmanr) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct dma_chan *chan; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (dmanr >= NUM_AU1000_DMA_CHANNELS) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci chan = &au1000_dma_table[dmanr]; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci printk(KERN_INFO "Au1000 DMA%d Register Dump:\n", dmanr); 1438c2ecf20Sopenharmony_ci printk(KERN_INFO " mode = 0x%08x\n", 1448c2ecf20Sopenharmony_ci __raw_readl(chan->io + DMA_MODE_SET)); 1458c2ecf20Sopenharmony_ci printk(KERN_INFO " addr = 0x%08x\n", 1468c2ecf20Sopenharmony_ci __raw_readl(chan->io + DMA_PERIPHERAL_ADDR)); 1478c2ecf20Sopenharmony_ci printk(KERN_INFO " start0 = 0x%08x\n", 1488c2ecf20Sopenharmony_ci __raw_readl(chan->io + DMA_BUFFER0_START)); 1498c2ecf20Sopenharmony_ci printk(KERN_INFO " start1 = 0x%08x\n", 1508c2ecf20Sopenharmony_ci __raw_readl(chan->io + DMA_BUFFER1_START)); 1518c2ecf20Sopenharmony_ci printk(KERN_INFO " count0 = 0x%08x\n", 1528c2ecf20Sopenharmony_ci __raw_readl(chan->io + DMA_BUFFER0_COUNT)); 1538c2ecf20Sopenharmony_ci printk(KERN_INFO " count1 = 0x%08x\n", 1548c2ecf20Sopenharmony_ci __raw_readl(chan->io + DMA_BUFFER1_COUNT)); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * Finds a free channel, and binds the requested device to it. 1598c2ecf20Sopenharmony_ci * Returns the allocated channel number, or negative on error. 1608c2ecf20Sopenharmony_ci * Requests the DMA done IRQ if irqhandler != NULL. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ciint request_au1000_dma(int dev_id, const char *dev_str, 1638c2ecf20Sopenharmony_ci irq_handler_t irqhandler, 1648c2ecf20Sopenharmony_ci unsigned long irqflags, 1658c2ecf20Sopenharmony_ci void *irq_dev_id) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct dma_chan *chan; 1688c2ecf20Sopenharmony_ci const struct dma_dev *dev; 1698c2ecf20Sopenharmony_ci int i, ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (alchemy_get_cputype() == ALCHEMY_CPU_AU1100) { 1728c2ecf20Sopenharmony_ci if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) 1738c2ecf20Sopenharmony_ci return -EINVAL; 1748c2ecf20Sopenharmony_ci } else { 1758c2ecf20Sopenharmony_ci if (dev_id < 0 || dev_id >= DMA_NUM_DEV) 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) 1808c2ecf20Sopenharmony_ci if (au1000_dma_table[i].dev_id < 0) 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (i == NUM_AU1000_DMA_CHANNELS) 1848c2ecf20Sopenharmony_ci return -ENODEV; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci chan = &au1000_dma_table[i]; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (dev_id >= DMA_NUM_DEV) { 1898c2ecf20Sopenharmony_ci dev_id -= DMA_NUM_DEV; 1908c2ecf20Sopenharmony_ci dev = &dma_dev_table_bank2[dev_id]; 1918c2ecf20Sopenharmony_ci } else 1928c2ecf20Sopenharmony_ci dev = &dma_dev_table[dev_id]; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (irqhandler) { 1958c2ecf20Sopenharmony_ci chan->irq_dev = irq_dev_id; 1968c2ecf20Sopenharmony_ci ret = request_irq(chan->irq, irqhandler, irqflags, dev_str, 1978c2ecf20Sopenharmony_ci chan->irq_dev); 1988c2ecf20Sopenharmony_ci if (ret) { 1998c2ecf20Sopenharmony_ci chan->irq_dev = NULL; 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } else { 2038c2ecf20Sopenharmony_ci chan->irq_dev = NULL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* fill it in */ 2078c2ecf20Sopenharmony_ci chan->io = (void __iomem *)(KSEG1ADDR(AU1000_DMA_PHYS_ADDR) + 2088c2ecf20Sopenharmony_ci i * DMA_CHANNEL_LEN); 2098c2ecf20Sopenharmony_ci chan->dev_id = dev_id; 2108c2ecf20Sopenharmony_ci chan->dev_str = dev_str; 2118c2ecf20Sopenharmony_ci chan->fifo_addr = dev->fifo_addr; 2128c2ecf20Sopenharmony_ci chan->mode = dev->dma_mode; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* initialize the channel before returning */ 2158c2ecf20Sopenharmony_ci init_dma(i); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return i; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(request_au1000_dma); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_civoid free_au1000_dma(unsigned int dmanr) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct dma_chan *chan = get_dma_chan(dmanr); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!chan) { 2268c2ecf20Sopenharmony_ci printk(KERN_ERR "Error trying to free DMA%d\n", dmanr); 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci disable_dma(dmanr); 2318c2ecf20Sopenharmony_ci if (chan->irq_dev) 2328c2ecf20Sopenharmony_ci free_irq(chan->irq, chan->irq_dev); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci chan->irq_dev = NULL; 2358c2ecf20Sopenharmony_ci chan->dev_id = -1; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(free_au1000_dma); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int __init au1000_dma_init(void) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci int base, i; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci switch (alchemy_get_cputype()) { 2448c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 2458c2ecf20Sopenharmony_ci base = AU1000_DMA_INT_BASE; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 2488c2ecf20Sopenharmony_ci base = AU1500_DMA_INT_BASE; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1100: 2518c2ecf20Sopenharmony_ci base = AU1100_DMA_INT_BASE; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci default: 2548c2ecf20Sopenharmony_ci goto out; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) 2588c2ecf20Sopenharmony_ci au1000_dma_table[i].irq = base + i; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci printk(KERN_INFO "Alchemy DMA initialized\n"); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciout: 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ciarch_initcall(au1000_dma_init); 266