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