18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * BRIEF MODULE DESCRIPTION 48c2ecf20Sopenharmony_ci * The Descriptor Based DMA channel manager that first appeared 58c2ecf20Sopenharmony_ci * on the Au1550. I started with dma.c, but I think all that is 68c2ecf20Sopenharmony_ci * left is this initial comment :-) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright 2004 Embedded Edge, LLC 98c2ecf20Sopenharmony_ci * dan@embeddededge.com 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/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/slab.h> 368c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 378c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 388c2ecf20Sopenharmony_ci#include <linux/export.h> 398c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 408c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 418c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1xxx_dbdma.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * The Descriptor Based DMA supports up to 16 channels. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * There are 32 devices defined. We keep an internal structure 478c2ecf20Sopenharmony_ci * of devices using these channels, along with additional 488c2ecf20Sopenharmony_ci * information. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * We allocate the descriptors and allow access to them through various 518c2ecf20Sopenharmony_ci * functions. The drivers allocate the data buffers and assign them 528c2ecf20Sopenharmony_ci * to the descriptors. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(au1xxx_dbdma_spin_lock); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* I couldn't find a macro that did this... */ 578c2ecf20Sopenharmony_ci#define ALIGN_ADDR(x, a) ((((u32)(x)) + (a-1)) & ~(a-1)) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic dbdma_global_t *dbdma_gptr = 608c2ecf20Sopenharmony_ci (dbdma_global_t *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); 618c2ecf20Sopenharmony_cistatic int dbdma_initialized; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic dbdev_tab_t *dbdev_tab; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic dbdev_tab_t au1550_dbdev_tab[] __initdata = { 668c2ecf20Sopenharmony_ci /* UARTS */ 678c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, 688c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, 698c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x11400004, 0, 0 }, 708c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x11400000, 0, 0 }, 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* EXT DMA */ 738c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, 748c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, 758c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_DMA_REQ2, 0, 0, 0, 0x00000000, 0, 0 }, 768c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_DMA_REQ3, 0, 0, 0, 0x00000000, 0, 0 }, 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* USB DEV */ 798c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_USBDEV_RX0, DEV_FLAGS_IN, 4, 8, 0x10200000, 0, 0 }, 808c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_USBDEV_TX0, DEV_FLAGS_OUT, 4, 8, 0x10200004, 0, 0 }, 818c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_USBDEV_TX1, DEV_FLAGS_OUT, 4, 8, 0x10200008, 0, 0 }, 828c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_USBDEV_TX2, DEV_FLAGS_OUT, 4, 8, 0x1020000c, 0, 0 }, 838c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_USBDEV_RX3, DEV_FLAGS_IN, 4, 8, 0x10200010, 0, 0 }, 848c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_USBDEV_RX4, DEV_FLAGS_IN, 4, 8, 0x10200014, 0, 0 }, 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* PSCs */ 878c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 }, 888c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 }, 898c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 }, 908c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 }, 918c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 0, 0x10a0001c, 0, 0 }, 928c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 0, 0x10a0001c, 0, 0 }, 938c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 0, 0x10b0001c, 0, 0 }, 948c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 0, 0x10b0001c, 0, 0 }, 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_PCI_WRITE, 0, 0, 0, 0x00000000, 0, 0 }, /* PCI */ 978c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_NAND_FLASH, 0, 0, 0, 0x00000000, 0, 0 }, /* NAND */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* MAC 0 */ 1008c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_MAC0_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, 1018c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_MAC0_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* MAC 1 */ 1048c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_MAC1_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, 1058c2ecf20Sopenharmony_ci { AU1550_DSCR_CMD0_MAC1_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1088c2ecf20Sopenharmony_ci { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic dbdev_tab_t au1200_dbdev_tab[] __initdata = { 1128c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, 1138c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, 1148c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x11200004, 0, 0 }, 1158c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x11200000, 0, 0 }, 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, 1188c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_MAE_BE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1218c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_MAE_FE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1228c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1238c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, 1268c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, 1278c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 }, 1288c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 }, 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, 1318c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x11a0001c, 0, 0 }, 1348c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x11a0001c, 0, 0 }, 1358c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1368c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x11b0001c, 0, 0 }, 1378c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x11b0001c, 0, 0 }, 1388c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 }, 1418c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 }, 1428c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 }, 1438c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci { AU1200_DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1488c2ecf20Sopenharmony_ci { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic dbdev_tab_t au1300_dbdev_tab[] __initdata = { 1528c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x10100004, 0, 0 }, 1538c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x10100000, 0, 0 }, 1548c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x10101004, 0, 0 }, 1558c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x10101000, 0, 0 }, 1568c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART2_TX, DEV_FLAGS_OUT, 0, 8, 0x10102004, 0, 0 }, 1578c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART2_RX, DEV_FLAGS_IN, 0, 8, 0x10102000, 0, 0 }, 1588c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x10103004, 0, 0 }, 1598c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x10103000, 0, 0 }, 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, 1628c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, 1638c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 8, 8, 0x10601000, 0, 0 }, 1648c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 8, 8, 0x10601004, 0, 0 }, 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, 1678c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0001c, 0, 0 }, 1708c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x10a0001c, 0, 0 }, 1718c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0101c, 0, 0 }, 1728c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x10a0101c, 0, 0 }, 1738c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0201c, 0, 0 }, 1748c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 16, 0x10a0201c, 0, 0 }, 1758c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0301c, 0, 0 }, 1768c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 16, 0x10a0301c, 0, 0 }, 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1798c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_SDMS_TX2, DEV_FLAGS_OUT, 4, 8, 0x10602000, 0, 0 }, 1828c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_SDMS_RX2, DEV_FLAGS_IN, 4, 8, 0x10602004, 0, 0 }, 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_UDMA, DEV_FLAGS_ANYUSE, 0, 32, 0x14001810, 0, 0 }, 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, 1898c2ecf20Sopenharmony_ci { AU1300_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1928c2ecf20Sopenharmony_ci { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 32 predefined plus 32 custom */ 1968c2ecf20Sopenharmony_ci#define DBDEV_TAB_SIZE 64 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic chan_tab_t *chan_tab_ptr[NUM_DBDMA_CHANS]; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic dbdev_tab_t *find_dbdev_id(u32 id) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci dbdev_tab_t *p; 2048c2ecf20Sopenharmony_ci for (i = 0; i < DBDEV_TAB_SIZE; ++i) { 2058c2ecf20Sopenharmony_ci p = &dbdev_tab[i]; 2068c2ecf20Sopenharmony_ci if (p->dev_id == id) 2078c2ecf20Sopenharmony_ci return p; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci return NULL; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid *au1xxx_ddma_get_nextptr_virt(au1x_ddma_desc_t *dp) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_ddma_get_nextptr_virt); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciu32 au1xxx_ddma_add_device(dbdev_tab_t *dev) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci u32 ret = 0; 2218c2ecf20Sopenharmony_ci dbdev_tab_t *p; 2228c2ecf20Sopenharmony_ci static u16 new_id = 0x1000; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci p = find_dbdev_id(~0); 2258c2ecf20Sopenharmony_ci if (NULL != p) { 2268c2ecf20Sopenharmony_ci memcpy(p, dev, sizeof(dbdev_tab_t)); 2278c2ecf20Sopenharmony_ci p->dev_id = DSCR_DEV2CUSTOM_ID(new_id, dev->dev_id); 2288c2ecf20Sopenharmony_ci ret = p->dev_id; 2298c2ecf20Sopenharmony_ci new_id++; 2308c2ecf20Sopenharmony_ci#if 0 2318c2ecf20Sopenharmony_ci printk(KERN_DEBUG "add_device: id:%x flags:%x padd:%x\n", 2328c2ecf20Sopenharmony_ci p->dev_id, p->dev_flags, p->dev_physaddr); 2338c2ecf20Sopenharmony_ci#endif 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_ddma_add_device); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_civoid au1xxx_ddma_del_device(u32 devid) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci dbdev_tab_t *p = find_dbdev_id(devid); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (p != NULL) { 2458c2ecf20Sopenharmony_ci memset(p, 0, sizeof(dbdev_tab_t)); 2468c2ecf20Sopenharmony_ci p->dev_id = ~0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_ddma_del_device); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* Allocate a channel and return a non-zero descriptor if successful. */ 2528c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, 2538c2ecf20Sopenharmony_ci void (*callback)(int, void *), void *callparam) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci unsigned long flags; 2568c2ecf20Sopenharmony_ci u32 used, chan; 2578c2ecf20Sopenharmony_ci u32 dcp; 2588c2ecf20Sopenharmony_ci int i; 2598c2ecf20Sopenharmony_ci dbdev_tab_t *stp, *dtp; 2608c2ecf20Sopenharmony_ci chan_tab_t *ctp; 2618c2ecf20Sopenharmony_ci au1x_dma_chan_t *cp; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * We do the initialization on the first channel allocation. 2658c2ecf20Sopenharmony_ci * We have to wait because of the interrupt handler initialization 2668c2ecf20Sopenharmony_ci * which can't be done successfully during board set up. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci if (!dbdma_initialized) 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci stp = find_dbdev_id(srcid); 2728c2ecf20Sopenharmony_ci if (stp == NULL) 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci dtp = find_dbdev_id(destid); 2758c2ecf20Sopenharmony_ci if (dtp == NULL) 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci used = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Check to see if we can get both channels. */ 2818c2ecf20Sopenharmony_ci spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); 2828c2ecf20Sopenharmony_ci if (!(stp->dev_flags & DEV_FLAGS_INUSE) || 2838c2ecf20Sopenharmony_ci (stp->dev_flags & DEV_FLAGS_ANYUSE)) { 2848c2ecf20Sopenharmony_ci /* Got source */ 2858c2ecf20Sopenharmony_ci stp->dev_flags |= DEV_FLAGS_INUSE; 2868c2ecf20Sopenharmony_ci if (!(dtp->dev_flags & DEV_FLAGS_INUSE) || 2878c2ecf20Sopenharmony_ci (dtp->dev_flags & DEV_FLAGS_ANYUSE)) { 2888c2ecf20Sopenharmony_ci /* Got destination */ 2898c2ecf20Sopenharmony_ci dtp->dev_flags |= DEV_FLAGS_INUSE; 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci /* Can't get dest. Release src. */ 2928c2ecf20Sopenharmony_ci stp->dev_flags &= ~DEV_FLAGS_INUSE; 2938c2ecf20Sopenharmony_ci used++; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } else 2968c2ecf20Sopenharmony_ci used++; 2978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (used) 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Let's see if we can allocate a channel for it. */ 3038c2ecf20Sopenharmony_ci ctp = NULL; 3048c2ecf20Sopenharmony_ci chan = 0; 3058c2ecf20Sopenharmony_ci spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); 3068c2ecf20Sopenharmony_ci for (i = 0; i < NUM_DBDMA_CHANS; i++) 3078c2ecf20Sopenharmony_ci if (chan_tab_ptr[i] == NULL) { 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * If kmalloc fails, it is caught below same 3108c2ecf20Sopenharmony_ci * as a channel not available. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci ctp = kmalloc(sizeof(chan_tab_t), GFP_ATOMIC); 3138c2ecf20Sopenharmony_ci chan_tab_ptr[i] = ctp; 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (ctp != NULL) { 3198c2ecf20Sopenharmony_ci memset(ctp, 0, sizeof(chan_tab_t)); 3208c2ecf20Sopenharmony_ci ctp->chan_index = chan = i; 3218c2ecf20Sopenharmony_ci dcp = KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); 3228c2ecf20Sopenharmony_ci dcp += (0x0100 * chan); 3238c2ecf20Sopenharmony_ci ctp->chan_ptr = (au1x_dma_chan_t *)dcp; 3248c2ecf20Sopenharmony_ci cp = (au1x_dma_chan_t *)dcp; 3258c2ecf20Sopenharmony_ci ctp->chan_src = stp; 3268c2ecf20Sopenharmony_ci ctp->chan_dest = dtp; 3278c2ecf20Sopenharmony_ci ctp->chan_callback = callback; 3288c2ecf20Sopenharmony_ci ctp->chan_callparam = callparam; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Initialize channel configuration. */ 3318c2ecf20Sopenharmony_ci i = 0; 3328c2ecf20Sopenharmony_ci if (stp->dev_intlevel) 3338c2ecf20Sopenharmony_ci i |= DDMA_CFG_SED; 3348c2ecf20Sopenharmony_ci if (stp->dev_intpolarity) 3358c2ecf20Sopenharmony_ci i |= DDMA_CFG_SP; 3368c2ecf20Sopenharmony_ci if (dtp->dev_intlevel) 3378c2ecf20Sopenharmony_ci i |= DDMA_CFG_DED; 3388c2ecf20Sopenharmony_ci if (dtp->dev_intpolarity) 3398c2ecf20Sopenharmony_ci i |= DDMA_CFG_DP; 3408c2ecf20Sopenharmony_ci if ((stp->dev_flags & DEV_FLAGS_SYNC) || 3418c2ecf20Sopenharmony_ci (dtp->dev_flags & DEV_FLAGS_SYNC)) 3428c2ecf20Sopenharmony_ci i |= DDMA_CFG_SYNC; 3438c2ecf20Sopenharmony_ci cp->ddma_cfg = i; 3448c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * Return a non-zero value that can be used to find the channel 3488c2ecf20Sopenharmony_ci * information in subsequent operations. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci return (u32)(&chan_tab_ptr[chan]); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Release devices */ 3548c2ecf20Sopenharmony_ci stp->dev_flags &= ~DEV_FLAGS_INUSE; 3558c2ecf20Sopenharmony_ci dtp->dev_flags &= ~DEV_FLAGS_INUSE; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_chan_alloc); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 3628c2ecf20Sopenharmony_ci * Set the device width if source or destination is a FIFO. 3638c2ecf20Sopenharmony_ci * Should be 8, 16, or 32 bits. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_set_devwidth(u32 chanid, int bits) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci u32 rv; 3688c2ecf20Sopenharmony_ci chan_tab_t *ctp; 3698c2ecf20Sopenharmony_ci dbdev_tab_t *stp, *dtp; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 3728c2ecf20Sopenharmony_ci stp = ctp->chan_src; 3738c2ecf20Sopenharmony_ci dtp = ctp->chan_dest; 3748c2ecf20Sopenharmony_ci rv = 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (stp->dev_flags & DEV_FLAGS_IN) { /* Source in fifo */ 3778c2ecf20Sopenharmony_ci rv = stp->dev_devwidth; 3788c2ecf20Sopenharmony_ci stp->dev_devwidth = bits; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci if (dtp->dev_flags & DEV_FLAGS_OUT) { /* Destination out fifo */ 3818c2ecf20Sopenharmony_ci rv = dtp->dev_devwidth; 3828c2ecf20Sopenharmony_ci dtp->dev_devwidth = bits; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return rv; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_set_devwidth); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* Allocate a descriptor ring, initializing as much as possible. */ 3908c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_ring_alloc(u32 chanid, int entries) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int i; 3938c2ecf20Sopenharmony_ci u32 desc_base, srcid, destid; 3948c2ecf20Sopenharmony_ci u32 cmd0, cmd1, src1, dest1; 3958c2ecf20Sopenharmony_ci u32 src0, dest0; 3968c2ecf20Sopenharmony_ci chan_tab_t *ctp; 3978c2ecf20Sopenharmony_ci dbdev_tab_t *stp, *dtp; 3988c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * I guess we could check this to be within the 4028c2ecf20Sopenharmony_ci * range of the table...... 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 4058c2ecf20Sopenharmony_ci stp = ctp->chan_src; 4068c2ecf20Sopenharmony_ci dtp = ctp->chan_dest; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * The descriptors must be 32-byte aligned. There is a 4108c2ecf20Sopenharmony_ci * possibility the allocation will give us such an address, 4118c2ecf20Sopenharmony_ci * and if we try that first we are likely to not waste larger 4128c2ecf20Sopenharmony_ci * slabs of memory. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci desc_base = (u32)kmalloc_array(entries, sizeof(au1x_ddma_desc_t), 4158c2ecf20Sopenharmony_ci GFP_KERNEL|GFP_DMA); 4168c2ecf20Sopenharmony_ci if (desc_base == 0) 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (desc_base & 0x1f) { 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Lost....do it again, allocate extra, and round 4228c2ecf20Sopenharmony_ci * the address base. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci kfree((const void *)desc_base); 4258c2ecf20Sopenharmony_ci i = entries * sizeof(au1x_ddma_desc_t); 4268c2ecf20Sopenharmony_ci i += (sizeof(au1x_ddma_desc_t) - 1); 4278c2ecf20Sopenharmony_ci desc_base = (u32)kmalloc(i, GFP_KERNEL|GFP_DMA); 4288c2ecf20Sopenharmony_ci if (desc_base == 0) 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ctp->cdb_membase = desc_base; 4328c2ecf20Sopenharmony_ci desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t)); 4338c2ecf20Sopenharmony_ci } else 4348c2ecf20Sopenharmony_ci ctp->cdb_membase = desc_base; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci dp = (au1x_ddma_desc_t *)desc_base; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Keep track of the base descriptor. */ 4398c2ecf20Sopenharmony_ci ctp->chan_desc_base = dp; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Initialize the rings with as much information as we know. */ 4428c2ecf20Sopenharmony_ci srcid = stp->dev_id; 4438c2ecf20Sopenharmony_ci destid = dtp->dev_id; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci cmd0 = cmd1 = src1 = dest1 = 0; 4468c2ecf20Sopenharmony_ci src0 = dest0 = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_SID(srcid); 4498c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_DID(destid); 4508c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_IE | DSCR_CMD0_CV; 4518c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_NOCHANGE); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Is it mem to mem transfer? */ 4548c2ecf20Sopenharmony_ci if (((DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_THROTTLE) || 4558c2ecf20Sopenharmony_ci (DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_ALWAYS)) && 4568c2ecf20Sopenharmony_ci ((DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_THROTTLE) || 4578c2ecf20Sopenharmony_ci (DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_ALWAYS))) 4588c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_MEM; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci switch (stp->dev_devwidth) { 4618c2ecf20Sopenharmony_ci case 8: 4628c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_BYTE); 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci case 16: 4658c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_HALFWORD); 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci case 32: 4688c2ecf20Sopenharmony_ci default: 4698c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_WORD); 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci switch (dtp->dev_devwidth) { 4748c2ecf20Sopenharmony_ci case 8: 4758c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_BYTE); 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci case 16: 4788c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_HALFWORD); 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci case 32: 4818c2ecf20Sopenharmony_ci default: 4828c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_WORD); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci * If the device is marked as an in/out FIFO, ensure it is 4888c2ecf20Sopenharmony_ci * set non-coherent. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci if (stp->dev_flags & DEV_FLAGS_IN) 4918c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_SN; /* Source in FIFO */ 4928c2ecf20Sopenharmony_ci if (dtp->dev_flags & DEV_FLAGS_OUT) 4938c2ecf20Sopenharmony_ci cmd0 |= DSCR_CMD0_DN; /* Destination out FIFO */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* 4968c2ecf20Sopenharmony_ci * Set up source1. For now, assume no stride and increment. 4978c2ecf20Sopenharmony_ci * A channel attribute update can change this later. 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ci switch (stp->dev_tsize) { 5008c2ecf20Sopenharmony_ci case 1: 5018c2ecf20Sopenharmony_ci src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE1); 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case 2: 5048c2ecf20Sopenharmony_ci src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE2); 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci case 4: 5078c2ecf20Sopenharmony_ci src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE4); 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci case 8: 5108c2ecf20Sopenharmony_ci default: 5118c2ecf20Sopenharmony_ci src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE8); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* If source input is FIFO, set static address. */ 5168c2ecf20Sopenharmony_ci if (stp->dev_flags & DEV_FLAGS_IN) { 5178c2ecf20Sopenharmony_ci if (stp->dev_flags & DEV_FLAGS_BURSTABLE) 5188c2ecf20Sopenharmony_ci src1 |= DSCR_SRC1_SAM(DSCR_xAM_BURST); 5198c2ecf20Sopenharmony_ci else 5208c2ecf20Sopenharmony_ci src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (stp->dev_physaddr) 5248c2ecf20Sopenharmony_ci src0 = stp->dev_physaddr; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * Set up dest1. For now, assume no stride and increment. 5288c2ecf20Sopenharmony_ci * A channel attribute update can change this later. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci switch (dtp->dev_tsize) { 5318c2ecf20Sopenharmony_ci case 1: 5328c2ecf20Sopenharmony_ci dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE1); 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci case 2: 5358c2ecf20Sopenharmony_ci dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE2); 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci case 4: 5388c2ecf20Sopenharmony_ci dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE4); 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci case 8: 5418c2ecf20Sopenharmony_ci default: 5428c2ecf20Sopenharmony_ci dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE8); 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* If destination output is FIFO, set static address. */ 5478c2ecf20Sopenharmony_ci if (dtp->dev_flags & DEV_FLAGS_OUT) { 5488c2ecf20Sopenharmony_ci if (dtp->dev_flags & DEV_FLAGS_BURSTABLE) 5498c2ecf20Sopenharmony_ci dest1 |= DSCR_DEST1_DAM(DSCR_xAM_BURST); 5508c2ecf20Sopenharmony_ci else 5518c2ecf20Sopenharmony_ci dest1 |= DSCR_DEST1_DAM(DSCR_xAM_STATIC); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (dtp->dev_physaddr) 5558c2ecf20Sopenharmony_ci dest0 = dtp->dev_physaddr; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci#if 0 5588c2ecf20Sopenharmony_ci printk(KERN_DEBUG "did:%x sid:%x cmd0:%x cmd1:%x source0:%x " 5598c2ecf20Sopenharmony_ci "source1:%x dest0:%x dest1:%x\n", 5608c2ecf20Sopenharmony_ci dtp->dev_id, stp->dev_id, cmd0, cmd1, src0, 5618c2ecf20Sopenharmony_ci src1, dest0, dest1); 5628c2ecf20Sopenharmony_ci#endif 5638c2ecf20Sopenharmony_ci for (i = 0; i < entries; i++) { 5648c2ecf20Sopenharmony_ci dp->dscr_cmd0 = cmd0; 5658c2ecf20Sopenharmony_ci dp->dscr_cmd1 = cmd1; 5668c2ecf20Sopenharmony_ci dp->dscr_source0 = src0; 5678c2ecf20Sopenharmony_ci dp->dscr_source1 = src1; 5688c2ecf20Sopenharmony_ci dp->dscr_dest0 = dest0; 5698c2ecf20Sopenharmony_ci dp->dscr_dest1 = dest1; 5708c2ecf20Sopenharmony_ci dp->dscr_stat = 0; 5718c2ecf20Sopenharmony_ci dp->sw_context = 0; 5728c2ecf20Sopenharmony_ci dp->sw_status = 0; 5738c2ecf20Sopenharmony_ci dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(dp + 1)); 5748c2ecf20Sopenharmony_ci dp++; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Make last descrptor point to the first. */ 5788c2ecf20Sopenharmony_ci dp--; 5798c2ecf20Sopenharmony_ci dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(ctp->chan_desc_base)); 5808c2ecf20Sopenharmony_ci ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return (u32)ctp->chan_desc_base; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_ring_alloc); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/* 5878c2ecf20Sopenharmony_ci * Put a source buffer into the DMA ring. 5888c2ecf20Sopenharmony_ci * This updates the source pointer and byte count. Normally used 5898c2ecf20Sopenharmony_ci * for memory to fifo transfers. 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_put_source(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci chan_tab_t *ctp; 5948c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* 5978c2ecf20Sopenharmony_ci * I guess we could check this to be within the 5988c2ecf20Sopenharmony_ci * range of the table...... 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci ctp = *(chan_tab_t **)chanid; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * We should have multiple callers for a particular channel, 6048c2ecf20Sopenharmony_ci * an interrupt doesn't affect this pointer nor the descriptor, 6058c2ecf20Sopenharmony_ci * so no locking should be needed. 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_ci dp = ctp->put_ptr; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* 6108c2ecf20Sopenharmony_ci * If the descriptor is valid, we are way ahead of the DMA 6118c2ecf20Sopenharmony_ci * engine, so just return an error condition. 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci if (dp->dscr_cmd0 & DSCR_CMD0_V) 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Load up buffer address and byte count. */ 6178c2ecf20Sopenharmony_ci dp->dscr_source0 = buf & ~0UL; 6188c2ecf20Sopenharmony_ci dp->dscr_cmd1 = nbytes; 6198c2ecf20Sopenharmony_ci /* Check flags */ 6208c2ecf20Sopenharmony_ci if (flags & DDMA_FLAGS_IE) 6218c2ecf20Sopenharmony_ci dp->dscr_cmd0 |= DSCR_CMD0_IE; 6228c2ecf20Sopenharmony_ci if (flags & DDMA_FLAGS_NOIE) 6238c2ecf20Sopenharmony_ci dp->dscr_cmd0 &= ~DSCR_CMD0_IE; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* 6268c2ecf20Sopenharmony_ci * There is an errata on the Au1200/Au1550 parts that could result 6278c2ecf20Sopenharmony_ci * in "stale" data being DMA'ed. It has to do with the snoop logic on 6288c2ecf20Sopenharmony_ci * the cache eviction buffer. DMA_NONCOHERENT is on by default for 6298c2ecf20Sopenharmony_ci * these parts. If it is fixed in the future, these dma_cache_inv will 6308c2ecf20Sopenharmony_ci * just be nothing more than empty macros. See io.h. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci dma_cache_wback_inv((unsigned long)buf, nbytes); 6338c2ecf20Sopenharmony_ci dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ 6348c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 6358c2ecf20Sopenharmony_ci dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); 6368c2ecf20Sopenharmony_ci ctp->chan_ptr->ddma_dbell = 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* Get next descriptor pointer. */ 6398c2ecf20Sopenharmony_ci ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Return something non-zero. */ 6428c2ecf20Sopenharmony_ci return nbytes; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_put_source); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/* Put a destination buffer into the DMA ring. 6478c2ecf20Sopenharmony_ci * This updates the destination pointer and byte count. Normally used 6488c2ecf20Sopenharmony_ci * to place an empty buffer into the ring for fifo to memory transfers. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_put_dest(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci chan_tab_t *ctp; 6538c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* I guess we could check this to be within the 6568c2ecf20Sopenharmony_ci * range of the table...... 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* We should have multiple callers for a particular channel, 6618c2ecf20Sopenharmony_ci * an interrupt doesn't affect this pointer nor the descriptor, 6628c2ecf20Sopenharmony_ci * so no locking should be needed. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci dp = ctp->put_ptr; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* If the descriptor is valid, we are way ahead of the DMA 6678c2ecf20Sopenharmony_ci * engine, so just return an error condition. 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_ci if (dp->dscr_cmd0 & DSCR_CMD0_V) 6708c2ecf20Sopenharmony_ci return 0; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Load up buffer address and byte count */ 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Check flags */ 6758c2ecf20Sopenharmony_ci if (flags & DDMA_FLAGS_IE) 6768c2ecf20Sopenharmony_ci dp->dscr_cmd0 |= DSCR_CMD0_IE; 6778c2ecf20Sopenharmony_ci if (flags & DDMA_FLAGS_NOIE) 6788c2ecf20Sopenharmony_ci dp->dscr_cmd0 &= ~DSCR_CMD0_IE; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dp->dscr_dest0 = buf & ~0UL; 6818c2ecf20Sopenharmony_ci dp->dscr_cmd1 = nbytes; 6828c2ecf20Sopenharmony_ci#if 0 6838c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n", 6848c2ecf20Sopenharmony_ci dp->dscr_cmd0, dp->dscr_cmd1, dp->dscr_source0, 6858c2ecf20Sopenharmony_ci dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); 6868c2ecf20Sopenharmony_ci#endif 6878c2ecf20Sopenharmony_ci /* 6888c2ecf20Sopenharmony_ci * There is an errata on the Au1200/Au1550 parts that could result in 6898c2ecf20Sopenharmony_ci * "stale" data being DMA'ed. It has to do with the snoop logic on the 6908c2ecf20Sopenharmony_ci * cache eviction buffer. DMA_NONCOHERENT is on by default for these 6918c2ecf20Sopenharmony_ci * parts. If it is fixed in the future, these dma_cache_inv will just 6928c2ecf20Sopenharmony_ci * be nothing more than empty macros. See io.h. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci dma_cache_inv((unsigned long)buf, nbytes); 6958c2ecf20Sopenharmony_ci dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ 6968c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 6978c2ecf20Sopenharmony_ci dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); 6988c2ecf20Sopenharmony_ci ctp->chan_ptr->ddma_dbell = 0; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* Get next descriptor pointer. */ 7018c2ecf20Sopenharmony_ci ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* Return something non-zero. */ 7048c2ecf20Sopenharmony_ci return nbytes; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_put_dest); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci/* 7098c2ecf20Sopenharmony_ci * Get a destination buffer into the DMA ring. 7108c2ecf20Sopenharmony_ci * Normally used to get a full buffer from the ring during fifo 7118c2ecf20Sopenharmony_ci * to memory transfers. This does not set the valid bit, you will 7128c2ecf20Sopenharmony_ci * have to put another destination buffer to keep the DMA going. 7138c2ecf20Sopenharmony_ci */ 7148c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_get_dest(u32 chanid, void **buf, int *nbytes) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci chan_tab_t *ctp; 7178c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 7188c2ecf20Sopenharmony_ci u32 rv; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* 7218c2ecf20Sopenharmony_ci * I guess we could check this to be within the 7228c2ecf20Sopenharmony_ci * range of the table...... 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* 7278c2ecf20Sopenharmony_ci * We should have multiple callers for a particular channel, 7288c2ecf20Sopenharmony_ci * an interrupt doesn't affect this pointer nor the descriptor, 7298c2ecf20Sopenharmony_ci * so no locking should be needed. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_ci dp = ctp->get_ptr; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* 7348c2ecf20Sopenharmony_ci * If the descriptor is valid, we are way ahead of the DMA 7358c2ecf20Sopenharmony_ci * engine, so just return an error condition. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci if (dp->dscr_cmd0 & DSCR_CMD0_V) 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* Return buffer address and byte count. */ 7418c2ecf20Sopenharmony_ci *buf = (void *)(phys_to_virt(dp->dscr_dest0)); 7428c2ecf20Sopenharmony_ci *nbytes = dp->dscr_cmd1; 7438c2ecf20Sopenharmony_ci rv = dp->dscr_stat; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* Get next descriptor pointer. */ 7468c2ecf20Sopenharmony_ci ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* Return something non-zero. */ 7498c2ecf20Sopenharmony_ci return rv; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(au1xxx_dbdma_get_dest); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_civoid au1xxx_dbdma_stop(u32 chanid) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci chan_tab_t *ctp; 7568c2ecf20Sopenharmony_ci au1x_dma_chan_t *cp; 7578c2ecf20Sopenharmony_ci int halt_timeout = 0; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci cp = ctp->chan_ptr; 7628c2ecf20Sopenharmony_ci cp->ddma_cfg &= ~DDMA_CFG_EN; /* Disable channel */ 7638c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 7648c2ecf20Sopenharmony_ci while (!(cp->ddma_stat & DDMA_STAT_H)) { 7658c2ecf20Sopenharmony_ci udelay(1); 7668c2ecf20Sopenharmony_ci halt_timeout++; 7678c2ecf20Sopenharmony_ci if (halt_timeout > 100) { 7688c2ecf20Sopenharmony_ci printk(KERN_WARNING "warning: DMA channel won't halt\n"); 7698c2ecf20Sopenharmony_ci break; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci /* clear current desc valid and doorbell */ 7738c2ecf20Sopenharmony_ci cp->ddma_stat |= (DDMA_STAT_DB | DDMA_STAT_V); 7748c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_stop); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci/* 7798c2ecf20Sopenharmony_ci * Start using the current descriptor pointer. If the DBDMA encounters 7808c2ecf20Sopenharmony_ci * a non-valid descriptor, it will stop. In this case, we can just 7818c2ecf20Sopenharmony_ci * continue by adding a buffer to the list and starting again. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_civoid au1xxx_dbdma_start(u32 chanid) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci chan_tab_t *ctp; 7868c2ecf20Sopenharmony_ci au1x_dma_chan_t *cp; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 7898c2ecf20Sopenharmony_ci cp = ctp->chan_ptr; 7908c2ecf20Sopenharmony_ci cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); 7918c2ecf20Sopenharmony_ci cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ 7928c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 7938c2ecf20Sopenharmony_ci cp->ddma_dbell = 0; 7948c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_start); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_civoid au1xxx_dbdma_reset(u32 chanid) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci chan_tab_t *ctp; 8018c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci au1xxx_dbdma_stop(chanid); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 8068c2ecf20Sopenharmony_ci ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* Run through the descriptors and reset the valid indicator. */ 8098c2ecf20Sopenharmony_ci dp = ctp->chan_desc_base; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci do { 8128c2ecf20Sopenharmony_ci dp->dscr_cmd0 &= ~DSCR_CMD0_V; 8138c2ecf20Sopenharmony_ci /* 8148c2ecf20Sopenharmony_ci * Reset our software status -- this is used to determine 8158c2ecf20Sopenharmony_ci * if a descriptor is in use by upper level software. Since 8168c2ecf20Sopenharmony_ci * posting can reset 'V' bit. 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_ci dp->sw_status = 0; 8198c2ecf20Sopenharmony_ci dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 8208c2ecf20Sopenharmony_ci } while (dp != ctp->chan_desc_base); 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_reset); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ciu32 au1xxx_get_dma_residue(u32 chanid) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci chan_tab_t *ctp; 8278c2ecf20Sopenharmony_ci au1x_dma_chan_t *cp; 8288c2ecf20Sopenharmony_ci u32 rv; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 8318c2ecf20Sopenharmony_ci cp = ctp->chan_ptr; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* This is only valid if the channel is stopped. */ 8348c2ecf20Sopenharmony_ci rv = cp->ddma_bytecnt; 8358c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return rv; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(au1xxx_get_dma_residue); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_civoid au1xxx_dbdma_chan_free(u32 chanid) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci chan_tab_t *ctp; 8448c2ecf20Sopenharmony_ci dbdev_tab_t *stp, *dtp; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 8478c2ecf20Sopenharmony_ci stp = ctp->chan_src; 8488c2ecf20Sopenharmony_ci dtp = ctp->chan_dest; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci au1xxx_dbdma_stop(chanid); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci kfree((void *)ctp->cdb_membase); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci stp->dev_flags &= ~DEV_FLAGS_INUSE; 8558c2ecf20Sopenharmony_ci dtp->dev_flags &= ~DEV_FLAGS_INUSE; 8568c2ecf20Sopenharmony_ci chan_tab_ptr[ctp->chan_index] = NULL; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci kfree(ctp); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(au1xxx_dbdma_chan_free); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic irqreturn_t dbdma_interrupt(int irq, void *dev_id) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci u32 intstat; 8658c2ecf20Sopenharmony_ci u32 chan_index; 8668c2ecf20Sopenharmony_ci chan_tab_t *ctp; 8678c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 8688c2ecf20Sopenharmony_ci au1x_dma_chan_t *cp; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci intstat = dbdma_gptr->ddma_intstat; 8718c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 8728c2ecf20Sopenharmony_ci chan_index = __ffs(intstat); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci ctp = chan_tab_ptr[chan_index]; 8758c2ecf20Sopenharmony_ci cp = ctp->chan_ptr; 8768c2ecf20Sopenharmony_ci dp = ctp->cur_ptr; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* Reset interrupt. */ 8798c2ecf20Sopenharmony_ci cp->ddma_irq = 0; 8808c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (ctp->chan_callback) 8838c2ecf20Sopenharmony_ci ctp->chan_callback(irq, ctp->chan_callparam); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 8868c2ecf20Sopenharmony_ci return IRQ_RETVAL(1); 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_civoid au1xxx_dbdma_dump(u32 chanid) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci chan_tab_t *ctp; 8928c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 8938c2ecf20Sopenharmony_ci dbdev_tab_t *stp, *dtp; 8948c2ecf20Sopenharmony_ci au1x_dma_chan_t *cp; 8958c2ecf20Sopenharmony_ci u32 i = 0; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 8988c2ecf20Sopenharmony_ci stp = ctp->chan_src; 8998c2ecf20Sopenharmony_ci dtp = ctp->chan_dest; 9008c2ecf20Sopenharmony_ci cp = ctp->chan_ptr; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Chan %x, stp %x (dev %d) dtp %x (dev %d)\n", 9038c2ecf20Sopenharmony_ci (u32)ctp, (u32)stp, stp - dbdev_tab, (u32)dtp, 9048c2ecf20Sopenharmony_ci dtp - dbdev_tab); 9058c2ecf20Sopenharmony_ci printk(KERN_DEBUG "desc base %x, get %x, put %x, cur %x\n", 9068c2ecf20Sopenharmony_ci (u32)(ctp->chan_desc_base), (u32)(ctp->get_ptr), 9078c2ecf20Sopenharmony_ci (u32)(ctp->put_ptr), (u32)(ctp->cur_ptr)); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci printk(KERN_DEBUG "dbdma chan %x\n", (u32)cp); 9108c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cfg %08x, desptr %08x, statptr %08x\n", 9118c2ecf20Sopenharmony_ci cp->ddma_cfg, cp->ddma_desptr, cp->ddma_statptr); 9128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "dbell %08x, irq %08x, stat %08x, bytecnt %08x\n", 9138c2ecf20Sopenharmony_ci cp->ddma_dbell, cp->ddma_irq, cp->ddma_stat, 9148c2ecf20Sopenharmony_ci cp->ddma_bytecnt); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* Run through the descriptors */ 9178c2ecf20Sopenharmony_ci dp = ctp->chan_desc_base; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci do { 9208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Dp[%d]= %08x, cmd0 %08x, cmd1 %08x\n", 9218c2ecf20Sopenharmony_ci i++, (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1); 9228c2ecf20Sopenharmony_ci printk(KERN_DEBUG "src0 %08x, src1 %08x, dest0 %08x, dest1 %08x\n", 9238c2ecf20Sopenharmony_ci dp->dscr_source0, dp->dscr_source1, 9248c2ecf20Sopenharmony_ci dp->dscr_dest0, dp->dscr_dest1); 9258c2ecf20Sopenharmony_ci printk(KERN_DEBUG "stat %08x, nxtptr %08x\n", 9268c2ecf20Sopenharmony_ci dp->dscr_stat, dp->dscr_nxtptr); 9278c2ecf20Sopenharmony_ci dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 9288c2ecf20Sopenharmony_ci } while (dp != ctp->chan_desc_base); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/* Put a descriptor into the DMA ring. 9328c2ecf20Sopenharmony_ci * This updates the source/destination pointers and byte count. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ciu32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci chan_tab_t *ctp; 9378c2ecf20Sopenharmony_ci au1x_ddma_desc_t *dp; 9388c2ecf20Sopenharmony_ci u32 nbytes = 0; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* 9418c2ecf20Sopenharmony_ci * I guess we could check this to be within the 9428c2ecf20Sopenharmony_ci * range of the table...... 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci ctp = *((chan_tab_t **)chanid); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* 9478c2ecf20Sopenharmony_ci * We should have multiple callers for a particular channel, 9488c2ecf20Sopenharmony_ci * an interrupt doesn't affect this pointer nor the descriptor, 9498c2ecf20Sopenharmony_ci * so no locking should be needed. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ci dp = ctp->put_ptr; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* 9548c2ecf20Sopenharmony_ci * If the descriptor is valid, we are way ahead of the DMA 9558c2ecf20Sopenharmony_ci * engine, so just return an error condition. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci if (dp->dscr_cmd0 & DSCR_CMD0_V) 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Load up buffer addresses and byte count. */ 9618c2ecf20Sopenharmony_ci dp->dscr_dest0 = dscr->dscr_dest0; 9628c2ecf20Sopenharmony_ci dp->dscr_source0 = dscr->dscr_source0; 9638c2ecf20Sopenharmony_ci dp->dscr_dest1 = dscr->dscr_dest1; 9648c2ecf20Sopenharmony_ci dp->dscr_source1 = dscr->dscr_source1; 9658c2ecf20Sopenharmony_ci dp->dscr_cmd1 = dscr->dscr_cmd1; 9668c2ecf20Sopenharmony_ci nbytes = dscr->dscr_cmd1; 9678c2ecf20Sopenharmony_ci /* Allow the caller to specify if an interrupt is generated */ 9688c2ecf20Sopenharmony_ci dp->dscr_cmd0 &= ~DSCR_CMD0_IE; 9698c2ecf20Sopenharmony_ci dp->dscr_cmd0 |= dscr->dscr_cmd0 | DSCR_CMD0_V; 9708c2ecf20Sopenharmony_ci ctp->chan_ptr->ddma_dbell = 0; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* Get next descriptor pointer. */ 9738c2ecf20Sopenharmony_ci ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Return something non-zero. */ 9768c2ecf20Sopenharmony_ci return nbytes; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic unsigned long alchemy_dbdma_pm_data[NUM_DBDMA_CHANS + 1][6]; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int alchemy_dbdma_suspend(void) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci int i; 9858c2ecf20Sopenharmony_ci void __iomem *addr; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); 9888c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[0][0] = __raw_readl(addr + 0x00); 9898c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[0][1] = __raw_readl(addr + 0x04); 9908c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[0][2] = __raw_readl(addr + 0x08); 9918c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[0][3] = __raw_readl(addr + 0x0c); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* save channel configurations */ 9948c2ecf20Sopenharmony_ci addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); 9958c2ecf20Sopenharmony_ci for (i = 1; i <= NUM_DBDMA_CHANS; i++) { 9968c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[i][0] = __raw_readl(addr + 0x00); 9978c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[i][1] = __raw_readl(addr + 0x04); 9988c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[i][2] = __raw_readl(addr + 0x08); 9998c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[i][3] = __raw_readl(addr + 0x0c); 10008c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[i][4] = __raw_readl(addr + 0x10); 10018c2ecf20Sopenharmony_ci alchemy_dbdma_pm_data[i][5] = __raw_readl(addr + 0x14); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* halt channel */ 10048c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][0] & ~1, addr + 0x00); 10058c2ecf20Sopenharmony_ci wmb(); 10068c2ecf20Sopenharmony_ci while (!(__raw_readl(addr + 0x14) & 1)) 10078c2ecf20Sopenharmony_ci wmb(); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci addr += 0x100; /* next channel base */ 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci /* disable channel interrupts */ 10128c2ecf20Sopenharmony_ci addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); 10138c2ecf20Sopenharmony_ci __raw_writel(0, addr + 0x0c); 10148c2ecf20Sopenharmony_ci wmb(); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic void alchemy_dbdma_resume(void) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci int i; 10228c2ecf20Sopenharmony_ci void __iomem *addr; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); 10258c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[0][0], addr + 0x00); 10268c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[0][1], addr + 0x04); 10278c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[0][2], addr + 0x08); 10288c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[0][3], addr + 0x0c); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* restore channel configurations */ 10318c2ecf20Sopenharmony_ci addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); 10328c2ecf20Sopenharmony_ci for (i = 1; i <= NUM_DBDMA_CHANS; i++) { 10338c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][0], addr + 0x00); 10348c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][1], addr + 0x04); 10358c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][2], addr + 0x08); 10368c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][3], addr + 0x0c); 10378c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][4], addr + 0x10); 10388c2ecf20Sopenharmony_ci __raw_writel(alchemy_dbdma_pm_data[i][5], addr + 0x14); 10398c2ecf20Sopenharmony_ci wmb(); 10408c2ecf20Sopenharmony_ci addr += 0x100; /* next channel base */ 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic struct syscore_ops alchemy_dbdma_syscore_ops = { 10458c2ecf20Sopenharmony_ci .suspend = alchemy_dbdma_suspend, 10468c2ecf20Sopenharmony_ci .resume = alchemy_dbdma_resume, 10478c2ecf20Sopenharmony_ci}; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic int __init dbdma_setup(unsigned int irq, dbdev_tab_t *idtable) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci int ret; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci dbdev_tab = kcalloc(DBDEV_TAB_SIZE, sizeof(dbdev_tab_t), GFP_KERNEL); 10548c2ecf20Sopenharmony_ci if (!dbdev_tab) 10558c2ecf20Sopenharmony_ci return -ENOMEM; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci memcpy(dbdev_tab, idtable, 32 * sizeof(dbdev_tab_t)); 10588c2ecf20Sopenharmony_ci for (ret = 32; ret < DBDEV_TAB_SIZE; ret++) 10598c2ecf20Sopenharmony_ci dbdev_tab[ret].dev_id = ~0; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci dbdma_gptr->ddma_config = 0; 10628c2ecf20Sopenharmony_ci dbdma_gptr->ddma_throttle = 0; 10638c2ecf20Sopenharmony_ci dbdma_gptr->ddma_inten = 0xffff; 10648c2ecf20Sopenharmony_ci wmb(); /* drain writebuffer */ 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci ret = request_irq(irq, dbdma_interrupt, 0, "dbdma", (void *)dbdma_gptr); 10678c2ecf20Sopenharmony_ci if (ret) 10688c2ecf20Sopenharmony_ci printk(KERN_ERR "Cannot grab DBDMA interrupt!\n"); 10698c2ecf20Sopenharmony_ci else { 10708c2ecf20Sopenharmony_ci dbdma_initialized = 1; 10718c2ecf20Sopenharmony_ci register_syscore_ops(&alchemy_dbdma_syscore_ops); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci return ret; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic int __init alchemy_dbdma_init(void) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci switch (alchemy_get_cputype()) { 10808c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 10818c2ecf20Sopenharmony_ci return dbdma_setup(AU1550_DDMA_INT, au1550_dbdev_tab); 10828c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1200: 10838c2ecf20Sopenharmony_ci return dbdma_setup(AU1200_DDMA_INT, au1200_dbdev_tab); 10848c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 10858c2ecf20Sopenharmony_ci return dbdma_setup(AU1300_DDMA_INT, au1300_dbdev_tab); 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_cisubsys_initcall(alchemy_dbdma_init); 1090