1e8c7c482SRalf Baechle /* 2e8c7c482SRalf Baechle * 3e8c7c482SRalf Baechle * BRIEF MODULE DESCRIPTION 4e8c7c482SRalf Baechle * A DMA channel allocator for Au1x00. API is modeled loosely off of 5e8c7c482SRalf Baechle * linux/kernel/dma.c. 6e8c7c482SRalf Baechle * 7e8c7c482SRalf Baechle * Copyright 2000, 2008 MontaVista Software Inc. 8e8c7c482SRalf Baechle * Author: MontaVista Software, Inc. <source@mvista.com> 9e8c7c482SRalf Baechle * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) 10e8c7c482SRalf Baechle * 11e8c7c482SRalf Baechle * This program is free software; you can redistribute it and/or modify it 12e8c7c482SRalf Baechle * under the terms of the GNU General Public License as published by the 13e8c7c482SRalf Baechle * Free Software Foundation; either version 2 of the License, or (at your 14e8c7c482SRalf Baechle * option) any later version. 15e8c7c482SRalf Baechle * 16e8c7c482SRalf Baechle * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 17e8c7c482SRalf Baechle * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18e8c7c482SRalf Baechle * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 19e8c7c482SRalf Baechle * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20e8c7c482SRalf Baechle * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21e8c7c482SRalf Baechle * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 22e8c7c482SRalf Baechle * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23e8c7c482SRalf Baechle * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24e8c7c482SRalf Baechle * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25e8c7c482SRalf Baechle * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26e8c7c482SRalf Baechle * 27e8c7c482SRalf Baechle * You should have received a copy of the GNU General Public License along 28e8c7c482SRalf Baechle * with this program; if not, write to the Free Software Foundation, Inc., 29e8c7c482SRalf Baechle * 675 Mass Ave, Cambridge, MA 02139, USA. 30e8c7c482SRalf Baechle * 31e8c7c482SRalf Baechle */ 32e8c7c482SRalf Baechle #include <linux/module.h> 33e8c7c482SRalf Baechle #include <linux/kernel.h> 34e8c7c482SRalf Baechle #include <linux/errno.h> 35e8c7c482SRalf Baechle #include <linux/spinlock.h> 36e8c7c482SRalf Baechle #include <linux/interrupt.h> 37e8c7c482SRalf Baechle 38e8c7c482SRalf Baechle #include <asm/mach-au1x00/au1000.h> 39e8c7c482SRalf Baechle #include <asm/mach-au1x00/au1000_dma.h> 40e8c7c482SRalf Baechle 41e8c7c482SRalf Baechle #if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1500) || \ 42e8c7c482SRalf Baechle defined(CONFIG_SOC_AU1100) 43e8c7c482SRalf Baechle /* 44e8c7c482SRalf Baechle * A note on resource allocation: 45e8c7c482SRalf Baechle * 46e8c7c482SRalf Baechle * All drivers needing DMA channels, should allocate and release them 47e8c7c482SRalf Baechle * through the public routines `request_dma()' and `free_dma()'. 48e8c7c482SRalf Baechle * 49e8c7c482SRalf Baechle * In order to avoid problems, all processes should allocate resources in 50e8c7c482SRalf Baechle * the same sequence and release them in the reverse order. 51e8c7c482SRalf Baechle * 52e8c7c482SRalf Baechle * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. 53e8c7c482SRalf Baechle * When releasing them, first release the IRQ, then release the DMA. The 54e8c7c482SRalf Baechle * main reason for this order is that, if you are requesting the DMA buffer 55e8c7c482SRalf Baechle * done interrupt, you won't know the irq number until the DMA channel is 56e8c7c482SRalf Baechle * returned from request_dma. 57e8c7c482SRalf Baechle */ 58e8c7c482SRalf Baechle 59e8c7c482SRalf Baechle DEFINE_SPINLOCK(au1000_dma_spin_lock); 60e8c7c482SRalf Baechle 61e8c7c482SRalf Baechle struct dma_chan au1000_dma_table[NUM_AU1000_DMA_CHANNELS] = { 62e8c7c482SRalf Baechle {.dev_id = -1,}, 63e8c7c482SRalf Baechle {.dev_id = -1,}, 64e8c7c482SRalf Baechle {.dev_id = -1,}, 65e8c7c482SRalf Baechle {.dev_id = -1,}, 66e8c7c482SRalf Baechle {.dev_id = -1,}, 67e8c7c482SRalf Baechle {.dev_id = -1,}, 68e8c7c482SRalf Baechle {.dev_id = -1,}, 69e8c7c482SRalf Baechle {.dev_id = -1,} 70e8c7c482SRalf Baechle }; 71e8c7c482SRalf Baechle EXPORT_SYMBOL(au1000_dma_table); 72e8c7c482SRalf Baechle 73e8c7c482SRalf Baechle /* Device FIFO addresses and default DMA modes */ 74e8c7c482SRalf Baechle static const struct dma_dev { 75e8c7c482SRalf Baechle unsigned int fifo_addr; 76e8c7c482SRalf Baechle unsigned int dma_mode; 77e8c7c482SRalf Baechle } dma_dev_table[DMA_NUM_DEV] = { 78e8c7c482SRalf Baechle {UART0_ADDR + UART_TX, 0}, 79e8c7c482SRalf Baechle {UART0_ADDR + UART_RX, 0}, 80e8c7c482SRalf Baechle {0, 0}, 81e8c7c482SRalf Baechle {0, 0}, 82e8c7c482SRalf Baechle {AC97C_DATA, DMA_DW16 }, /* coherent */ 83e8c7c482SRalf Baechle {AC97C_DATA, DMA_DR | DMA_DW16 }, /* coherent */ 84e8c7c482SRalf Baechle {UART3_ADDR + UART_TX, DMA_DW8 | DMA_NC}, 85e8c7c482SRalf Baechle {UART3_ADDR + UART_RX, DMA_DR | DMA_DW8 | DMA_NC}, 86e8c7c482SRalf Baechle {USBD_EP0RD, DMA_DR | DMA_DW8 | DMA_NC}, 87e8c7c482SRalf Baechle {USBD_EP0WR, DMA_DW8 | DMA_NC}, 88e8c7c482SRalf Baechle {USBD_EP2WR, DMA_DW8 | DMA_NC}, 89e8c7c482SRalf Baechle {USBD_EP3WR, DMA_DW8 | DMA_NC}, 90e8c7c482SRalf Baechle {USBD_EP4RD, DMA_DR | DMA_DW8 | DMA_NC}, 91e8c7c482SRalf Baechle {USBD_EP5RD, DMA_DR | DMA_DW8 | DMA_NC}, 92e8c7c482SRalf Baechle {I2S_DATA, DMA_DW32 | DMA_NC}, 93e8c7c482SRalf Baechle {I2S_DATA, DMA_DR | DMA_DW32 | DMA_NC} 94e8c7c482SRalf Baechle }; 95e8c7c482SRalf Baechle 96e8c7c482SRalf Baechle int au1000_dma_read_proc(char *buf, char **start, off_t fpos, 97e8c7c482SRalf Baechle int length, int *eof, void *data) 98e8c7c482SRalf Baechle { 99e8c7c482SRalf Baechle int i, len = 0; 100e8c7c482SRalf Baechle struct dma_chan *chan; 101e8c7c482SRalf Baechle 102e8c7c482SRalf Baechle for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { 103e8c7c482SRalf Baechle chan = get_dma_chan(i); 104e8c7c482SRalf Baechle if (chan != NULL) 105e8c7c482SRalf Baechle len += sprintf(buf + len, "%2d: %s\n", 106e8c7c482SRalf Baechle i, chan->dev_str); 107e8c7c482SRalf Baechle } 108e8c7c482SRalf Baechle 109e8c7c482SRalf Baechle if (fpos >= len) { 110e8c7c482SRalf Baechle *start = buf; 111e8c7c482SRalf Baechle *eof = 1; 112e8c7c482SRalf Baechle return 0; 113e8c7c482SRalf Baechle } 114e8c7c482SRalf Baechle *start = buf + fpos; 115e8c7c482SRalf Baechle len -= fpos; 116e8c7c482SRalf Baechle if (len > length) 117e8c7c482SRalf Baechle return length; 118e8c7c482SRalf Baechle *eof = 1; 119e8c7c482SRalf Baechle return len; 120e8c7c482SRalf Baechle } 121e8c7c482SRalf Baechle 122e8c7c482SRalf Baechle /* Device FIFO addresses and default DMA modes - 2nd bank */ 123e8c7c482SRalf Baechle static const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = { 124e8c7c482SRalf Baechle { SD0_XMIT_FIFO, DMA_DS | DMA_DW8 }, /* coherent */ 125e8c7c482SRalf Baechle { SD0_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8 }, /* coherent */ 126e8c7c482SRalf Baechle { SD1_XMIT_FIFO, DMA_DS | DMA_DW8 }, /* coherent */ 127e8c7c482SRalf Baechle { SD1_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8 } /* coherent */ 128e8c7c482SRalf Baechle }; 129e8c7c482SRalf Baechle 130e8c7c482SRalf Baechle void dump_au1000_dma_channel(unsigned int dmanr) 131e8c7c482SRalf Baechle { 132e8c7c482SRalf Baechle struct dma_chan *chan; 133e8c7c482SRalf Baechle 134e8c7c482SRalf Baechle if (dmanr >= NUM_AU1000_DMA_CHANNELS) 135e8c7c482SRalf Baechle return; 136e8c7c482SRalf Baechle chan = &au1000_dma_table[dmanr]; 137e8c7c482SRalf Baechle 138e8c7c482SRalf Baechle printk(KERN_INFO "Au1000 DMA%d Register Dump:\n", dmanr); 139e8c7c482SRalf Baechle printk(KERN_INFO " mode = 0x%08x\n", 140e8c7c482SRalf Baechle au_readl(chan->io + DMA_MODE_SET)); 141e8c7c482SRalf Baechle printk(KERN_INFO " addr = 0x%08x\n", 142e8c7c482SRalf Baechle au_readl(chan->io + DMA_PERIPHERAL_ADDR)); 143e8c7c482SRalf Baechle printk(KERN_INFO " start0 = 0x%08x\n", 144e8c7c482SRalf Baechle au_readl(chan->io + DMA_BUFFER0_START)); 145e8c7c482SRalf Baechle printk(KERN_INFO " start1 = 0x%08x\n", 146e8c7c482SRalf Baechle au_readl(chan->io + DMA_BUFFER1_START)); 147e8c7c482SRalf Baechle printk(KERN_INFO " count0 = 0x%08x\n", 148e8c7c482SRalf Baechle au_readl(chan->io + DMA_BUFFER0_COUNT)); 149e8c7c482SRalf Baechle printk(KERN_INFO " count1 = 0x%08x\n", 150e8c7c482SRalf Baechle au_readl(chan->io + DMA_BUFFER1_COUNT)); 151e8c7c482SRalf Baechle } 152e8c7c482SRalf Baechle 153e8c7c482SRalf Baechle /* 154e8c7c482SRalf Baechle * Finds a free channel, and binds the requested device to it. 155e8c7c482SRalf Baechle * Returns the allocated channel number, or negative on error. 156e8c7c482SRalf Baechle * Requests the DMA done IRQ if irqhandler != NULL. 157e8c7c482SRalf Baechle */ 158e8c7c482SRalf Baechle int request_au1000_dma(int dev_id, const char *dev_str, 159e8c7c482SRalf Baechle irq_handler_t irqhandler, 160e8c7c482SRalf Baechle unsigned long irqflags, 161e8c7c482SRalf Baechle void *irq_dev_id) 162e8c7c482SRalf Baechle { 163e8c7c482SRalf Baechle struct dma_chan *chan; 164e8c7c482SRalf Baechle const struct dma_dev *dev; 165e8c7c482SRalf Baechle int i, ret; 166e8c7c482SRalf Baechle 167e8c7c482SRalf Baechle #if defined(CONFIG_SOC_AU1100) 168e8c7c482SRalf Baechle if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) 169e8c7c482SRalf Baechle return -EINVAL; 170e8c7c482SRalf Baechle #else 171e8c7c482SRalf Baechle if (dev_id < 0 || dev_id >= DMA_NUM_DEV) 172e8c7c482SRalf Baechle return -EINVAL; 173e8c7c482SRalf Baechle #endif 174e8c7c482SRalf Baechle 175e8c7c482SRalf Baechle for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) 176e8c7c482SRalf Baechle if (au1000_dma_table[i].dev_id < 0) 177e8c7c482SRalf Baechle break; 178e8c7c482SRalf Baechle 179e8c7c482SRalf Baechle if (i == NUM_AU1000_DMA_CHANNELS) 180e8c7c482SRalf Baechle return -ENODEV; 181e8c7c482SRalf Baechle 182e8c7c482SRalf Baechle chan = &au1000_dma_table[i]; 183e8c7c482SRalf Baechle 184e8c7c482SRalf Baechle if (dev_id >= DMA_NUM_DEV) { 185e8c7c482SRalf Baechle dev_id -= DMA_NUM_DEV; 186e8c7c482SRalf Baechle dev = &dma_dev_table_bank2[dev_id]; 187e8c7c482SRalf Baechle } else 188e8c7c482SRalf Baechle dev = &dma_dev_table[dev_id]; 189e8c7c482SRalf Baechle 190e8c7c482SRalf Baechle if (irqhandler) { 191e8c7c482SRalf Baechle chan->irq = AU1000_DMA_INT_BASE + i; 192e8c7c482SRalf Baechle chan->irq_dev = irq_dev_id; 193e8c7c482SRalf Baechle ret = request_irq(chan->irq, irqhandler, irqflags, dev_str, 194e8c7c482SRalf Baechle chan->irq_dev); 195e8c7c482SRalf Baechle if (ret) { 196e8c7c482SRalf Baechle chan->irq = 0; 197e8c7c482SRalf Baechle chan->irq_dev = NULL; 198e8c7c482SRalf Baechle return ret; 199e8c7c482SRalf Baechle } 200e8c7c482SRalf Baechle } else { 201e8c7c482SRalf Baechle chan->irq = 0; 202e8c7c482SRalf Baechle chan->irq_dev = NULL; 203e8c7c482SRalf Baechle } 204e8c7c482SRalf Baechle 205e8c7c482SRalf Baechle /* fill it in */ 206e8c7c482SRalf Baechle chan->io = DMA_CHANNEL_BASE + i * DMA_CHANNEL_LEN; 207e8c7c482SRalf Baechle chan->dev_id = dev_id; 208e8c7c482SRalf Baechle chan->dev_str = dev_str; 209e8c7c482SRalf Baechle chan->fifo_addr = dev->fifo_addr; 210e8c7c482SRalf Baechle chan->mode = dev->dma_mode; 211e8c7c482SRalf Baechle 212e8c7c482SRalf Baechle /* initialize the channel before returning */ 213e8c7c482SRalf Baechle init_dma(i); 214e8c7c482SRalf Baechle 215e8c7c482SRalf Baechle return i; 216e8c7c482SRalf Baechle } 217e8c7c482SRalf Baechle EXPORT_SYMBOL(request_au1000_dma); 218e8c7c482SRalf Baechle 219e8c7c482SRalf Baechle void free_au1000_dma(unsigned int dmanr) 220e8c7c482SRalf Baechle { 221e8c7c482SRalf Baechle struct dma_chan *chan = get_dma_chan(dmanr); 222e8c7c482SRalf Baechle 223e8c7c482SRalf Baechle if (!chan) { 224e8c7c482SRalf Baechle printk(KERN_ERR "Error trying to free DMA%d\n", dmanr); 225e8c7c482SRalf Baechle return; 226e8c7c482SRalf Baechle } 227e8c7c482SRalf Baechle 228e8c7c482SRalf Baechle disable_dma(dmanr); 229e8c7c482SRalf Baechle if (chan->irq) 230e8c7c482SRalf Baechle free_irq(chan->irq, chan->irq_dev); 231e8c7c482SRalf Baechle 232e8c7c482SRalf Baechle chan->irq = 0; 233e8c7c482SRalf Baechle chan->irq_dev = NULL; 234e8c7c482SRalf Baechle chan->dev_id = -1; 235e8c7c482SRalf Baechle } 236e8c7c482SRalf Baechle EXPORT_SYMBOL(free_au1000_dma); 237e8c7c482SRalf Baechle 238e8c7c482SRalf Baechle #endif /* AU1000 AU1500 AU1100 */ 239