mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	[ARM] 5414/2: ep93xx m2p dma core
Add m2p dma support to the ep93xx Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> Acked-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									1c8daabe1d
								
							
						
					
					
						commit
						6d831c6554
					
				
					 4 changed files with 463 additions and 2 deletions
				
			
		| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Makefile for the linux kernel.
 | 
					# Makefile for the linux kernel.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
obj-y			:= core.o clock.o gpio.o
 | 
					obj-y			:= core.o clock.o dma-m2p.o gpio.o
 | 
				
			||||||
obj-m			:=
 | 
					obj-m			:=
 | 
				
			||||||
obj-n			:=
 | 
					obj-n			:=
 | 
				
			||||||
obj-			:=
 | 
					obj-			:=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										408
									
								
								arch/arm/mach-ep93xx/dma-m2p.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										408
									
								
								arch/arm/mach-ep93xx/dma-m2p.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,408 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * arch/arm/mach-ep93xx/dma-m2p.c
 | 
				
			||||||
 | 
					 * M2P DMA handling for Cirrus EP93xx chips.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
 | 
				
			||||||
 | 
					 * Copyright (C) 2006 Applied Data Systems
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2009 Ryan Mallon <ryan@bluewatersys.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or (at
 | 
				
			||||||
 | 
					 * your option) any later version.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * On the EP93xx chip the following peripherals my be allocated to the 10
 | 
				
			||||||
 | 
					 * Memory to Internal Peripheral (M2P) channels (5 transmit + 5 receive).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	I2S	contains 3 Tx and 3 Rx DMA Channels
 | 
				
			||||||
 | 
					 *	AAC	contains 3 Tx and 3 Rx DMA Channels
 | 
				
			||||||
 | 
					 *	UART1	contains 1 Tx and 1 Rx DMA Channels
 | 
				
			||||||
 | 
					 *	UART2	contains 1 Tx and 1 Rx DMA Channels
 | 
				
			||||||
 | 
					 *	UART3	contains 1 Tx and 1 Rx DMA Channels
 | 
				
			||||||
 | 
					 *	IrDA	contains 1 Tx and 1 Rx DMA Channels
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SSP and IDE use the Memory to Memory (M2M) channels and are not covered
 | 
				
			||||||
 | 
					 * with this implementation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/clk.h>
 | 
				
			||||||
 | 
					#include <linux/err.h>
 | 
				
			||||||
 | 
					#include <linux/interrupt.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <mach/dma.h>
 | 
				
			||||||
 | 
					#include <mach/hardware.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define M2P_CONTROL			0x00
 | 
				
			||||||
 | 
					#define  M2P_CONTROL_STALL_IRQ_EN	(1 << 0)
 | 
				
			||||||
 | 
					#define  M2P_CONTROL_NFB_IRQ_EN		(1 << 1)
 | 
				
			||||||
 | 
					#define  M2P_CONTROL_ERROR_IRQ_EN	(1 << 3)
 | 
				
			||||||
 | 
					#define  M2P_CONTROL_ENABLE		(1 << 4)
 | 
				
			||||||
 | 
					#define M2P_INTERRUPT			0x04
 | 
				
			||||||
 | 
					#define  M2P_INTERRUPT_STALL		(1 << 0)
 | 
				
			||||||
 | 
					#define  M2P_INTERRUPT_NFB		(1 << 1)
 | 
				
			||||||
 | 
					#define  M2P_INTERRUPT_ERROR		(1 << 3)
 | 
				
			||||||
 | 
					#define M2P_PPALLOC			0x08
 | 
				
			||||||
 | 
					#define M2P_STATUS			0x0c
 | 
				
			||||||
 | 
					#define M2P_REMAIN			0x14
 | 
				
			||||||
 | 
					#define M2P_MAXCNT0			0x20
 | 
				
			||||||
 | 
					#define M2P_BASE0			0x24
 | 
				
			||||||
 | 
					#define M2P_MAXCNT1			0x30
 | 
				
			||||||
 | 
					#define M2P_BASE1			0x34
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define STATE_IDLE	0	/* Channel is inactive.  */
 | 
				
			||||||
 | 
					#define STATE_STALL	1	/* Channel is active, no buffers pending.  */
 | 
				
			||||||
 | 
					#define STATE_ON	2	/* Channel is active, one buffer pending.  */
 | 
				
			||||||
 | 
					#define STATE_NEXT	3	/* Channel is active, two buffers pending.  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct m2p_channel {
 | 
				
			||||||
 | 
						char				*name;
 | 
				
			||||||
 | 
						void __iomem			*base;
 | 
				
			||||||
 | 
						int				irq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct clk			*clk;
 | 
				
			||||||
 | 
						spinlock_t			lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void				*client;
 | 
				
			||||||
 | 
						unsigned			next_slot:1;
 | 
				
			||||||
 | 
						struct ep93xx_dma_buffer	*buffer_xfer;
 | 
				
			||||||
 | 
						struct ep93xx_dma_buffer	*buffer_next;
 | 
				
			||||||
 | 
						struct list_head		buffers_pending;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct m2p_channel m2p_rx[] = {
 | 
				
			||||||
 | 
						{"m2p1", EP93XX_DMA_BASE + 0x0040, IRQ_EP93XX_DMAM2P1},
 | 
				
			||||||
 | 
						{"m2p3", EP93XX_DMA_BASE + 0x00c0, IRQ_EP93XX_DMAM2P3},
 | 
				
			||||||
 | 
						{"m2p5", EP93XX_DMA_BASE + 0x0200, IRQ_EP93XX_DMAM2P5},
 | 
				
			||||||
 | 
						{"m2p7", EP93XX_DMA_BASE + 0x0280, IRQ_EP93XX_DMAM2P7},
 | 
				
			||||||
 | 
						{"m2p9", EP93XX_DMA_BASE + 0x0300, IRQ_EP93XX_DMAM2P9},
 | 
				
			||||||
 | 
						{NULL},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct m2p_channel m2p_tx[] = {
 | 
				
			||||||
 | 
						{"m2p0", EP93XX_DMA_BASE + 0x0000, IRQ_EP93XX_DMAM2P0},
 | 
				
			||||||
 | 
						{"m2p2", EP93XX_DMA_BASE + 0x0080, IRQ_EP93XX_DMAM2P2},
 | 
				
			||||||
 | 
						{"m2p4", EP93XX_DMA_BASE + 0x0240, IRQ_EP93XX_DMAM2P4},
 | 
				
			||||||
 | 
						{"m2p6", EP93XX_DMA_BASE + 0x02c0, IRQ_EP93XX_DMAM2P6},
 | 
				
			||||||
 | 
						{"m2p8", EP93XX_DMA_BASE + 0x0340, IRQ_EP93XX_DMAM2P8},
 | 
				
			||||||
 | 
						{NULL},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void feed_buf(struct m2p_channel *ch, struct ep93xx_dma_buffer *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ch->next_slot == 0) {
 | 
				
			||||||
 | 
							writel(buf->size, ch->base + M2P_MAXCNT0);
 | 
				
			||||||
 | 
							writel(buf->bus_addr, ch->base + M2P_BASE0);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							writel(buf->size, ch->base + M2P_MAXCNT1);
 | 
				
			||||||
 | 
							writel(buf->bus_addr, ch->base + M2P_BASE1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ch->next_slot ^= 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void choose_buffer_xfer(struct m2p_channel *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ep93xx_dma_buffer *buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch->buffer_xfer = NULL;
 | 
				
			||||||
 | 
						if (!list_empty(&ch->buffers_pending)) {
 | 
				
			||||||
 | 
							buf = list_entry(ch->buffers_pending.next,
 | 
				
			||||||
 | 
									 struct ep93xx_dma_buffer, list);
 | 
				
			||||||
 | 
							list_del(&buf->list);
 | 
				
			||||||
 | 
							feed_buf(ch, buf);
 | 
				
			||||||
 | 
							ch->buffer_xfer = buf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void choose_buffer_next(struct m2p_channel *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ep93xx_dma_buffer *buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch->buffer_next = NULL;
 | 
				
			||||||
 | 
						if (!list_empty(&ch->buffers_pending)) {
 | 
				
			||||||
 | 
							buf = list_entry(ch->buffers_pending.next,
 | 
				
			||||||
 | 
									 struct ep93xx_dma_buffer, list);
 | 
				
			||||||
 | 
							list_del(&buf->list);
 | 
				
			||||||
 | 
							feed_buf(ch, buf);
 | 
				
			||||||
 | 
							ch->buffer_next = buf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void m2p_set_control(struct m2p_channel *ch, u32 v)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * The control register must be read immediately after being written so
 | 
				
			||||||
 | 
						 * that the internal state machine is correctly updated. See the ep93xx
 | 
				
			||||||
 | 
						 * users' guide for details.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						writel(v, ch->base + M2P_CONTROL);
 | 
				
			||||||
 | 
						readl(ch->base + M2P_CONTROL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int m2p_channel_state(struct m2p_channel *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (readl(ch->base + M2P_STATUS) >> 4) & 0x3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static irqreturn_t m2p_irq(int irq, void *dev_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch = dev_id;
 | 
				
			||||||
 | 
						struct ep93xx_dma_m2p_client *cl;
 | 
				
			||||||
 | 
						u32 irq_status, v;
 | 
				
			||||||
 | 
						int error = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl = ch->client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&ch->lock);
 | 
				
			||||||
 | 
						irq_status = readl(ch->base + M2P_INTERRUPT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (irq_status & M2P_INTERRUPT_ERROR) {
 | 
				
			||||||
 | 
							writel(M2P_INTERRUPT_ERROR, ch->base + M2P_INTERRUPT);
 | 
				
			||||||
 | 
							error = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) == 0) {
 | 
				
			||||||
 | 
							spin_unlock(&ch->lock);
 | 
				
			||||||
 | 
							return IRQ_NONE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (m2p_channel_state(ch)) {
 | 
				
			||||||
 | 
						case STATE_IDLE:
 | 
				
			||||||
 | 
							pr_crit("m2p_irq: dma interrupt without a dma buffer\n");
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case STATE_STALL:
 | 
				
			||||||
 | 
							cl->buffer_finished(cl->cookie, ch->buffer_xfer, 0, error);
 | 
				
			||||||
 | 
							if (ch->buffer_next != NULL) {
 | 
				
			||||||
 | 
								cl->buffer_finished(cl->cookie, ch->buffer_next,
 | 
				
			||||||
 | 
										    0, error);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							choose_buffer_xfer(ch);
 | 
				
			||||||
 | 
							choose_buffer_next(ch);
 | 
				
			||||||
 | 
							if (ch->buffer_xfer != NULL)
 | 
				
			||||||
 | 
								cl->buffer_started(cl->cookie, ch->buffer_xfer);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case STATE_ON:
 | 
				
			||||||
 | 
							cl->buffer_finished(cl->cookie, ch->buffer_xfer, 0, error);
 | 
				
			||||||
 | 
							ch->buffer_xfer = ch->buffer_next;
 | 
				
			||||||
 | 
							choose_buffer_next(ch);
 | 
				
			||||||
 | 
							cl->buffer_started(cl->cookie, ch->buffer_xfer);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case STATE_NEXT:
 | 
				
			||||||
 | 
							pr_crit("m2p_irq: dma interrupt while next\n");
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = readl(ch->base + M2P_CONTROL) & ~(M2P_CONTROL_STALL_IRQ_EN |
 | 
				
			||||||
 | 
										      M2P_CONTROL_NFB_IRQ_EN);
 | 
				
			||||||
 | 
						if (ch->buffer_xfer != NULL)
 | 
				
			||||||
 | 
							v |= M2P_CONTROL_STALL_IRQ_EN;
 | 
				
			||||||
 | 
						if (ch->buffer_next != NULL)
 | 
				
			||||||
 | 
							v |= M2P_CONTROL_NFB_IRQ_EN;
 | 
				
			||||||
 | 
						m2p_set_control(ch, v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_unlock(&ch->lock);
 | 
				
			||||||
 | 
						return IRQ_HANDLED;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct m2p_channel *find_free_channel(struct ep93xx_dma_m2p_client *cl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cl->flags & EP93XX_DMA_M2P_RX)
 | 
				
			||||||
 | 
							ch = m2p_rx;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ch = m2p_tx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; ch[i].base; i++) {
 | 
				
			||||||
 | 
							struct ep93xx_dma_m2p_client *client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client = ch[i].client;
 | 
				
			||||||
 | 
							if (client != NULL) {
 | 
				
			||||||
 | 
								int port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								port = cl->flags & EP93XX_DMA_M2P_PORT_MASK;
 | 
				
			||||||
 | 
								if (port == (client->flags &
 | 
				
			||||||
 | 
									     EP93XX_DMA_M2P_PORT_MASK)) {
 | 
				
			||||||
 | 
									pr_warning("DMA channel already used by %s\n",
 | 
				
			||||||
 | 
										   cl->name ? : "unknown client");
 | 
				
			||||||
 | 
									return ERR_PTR(-EBUSY);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; ch[i].base; i++) {
 | 
				
			||||||
 | 
							if (ch[i].client == NULL)
 | 
				
			||||||
 | 
								return ch + i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pr_warning("No free DMA channel for %s\n",
 | 
				
			||||||
 | 
							   cl->name ? : "unknown client");
 | 
				
			||||||
 | 
						return ERR_PTR(-ENODEV);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void channel_enable(struct m2p_channel *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ep93xx_dma_m2p_client *cl = ch->client;
 | 
				
			||||||
 | 
						u32 v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_enable(ch->clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = cl->flags & EP93XX_DMA_M2P_PORT_MASK;
 | 
				
			||||||
 | 
						writel(v, ch->base + M2P_PPALLOC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = cl->flags & EP93XX_DMA_M2P_ERROR_MASK;
 | 
				
			||||||
 | 
						v |= M2P_CONTROL_ENABLE | M2P_CONTROL_ERROR_IRQ_EN;
 | 
				
			||||||
 | 
						m2p_set_control(ch, v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void channel_disable(struct m2p_channel *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = readl(ch->base + M2P_CONTROL);
 | 
				
			||||||
 | 
						v &= ~(M2P_CONTROL_STALL_IRQ_EN | M2P_CONTROL_NFB_IRQ_EN);
 | 
				
			||||||
 | 
						m2p_set_control(ch, v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (m2p_channel_state(ch) == STATE_ON)
 | 
				
			||||||
 | 
							cpu_relax();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m2p_set_control(ch, 0x0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (m2p_channel_state(ch) == STATE_STALL)
 | 
				
			||||||
 | 
							cpu_relax();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_disable(ch->clk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ep93xx_dma_m2p_client_register(struct ep93xx_dma_m2p_client *cl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch = find_free_channel(cl);
 | 
				
			||||||
 | 
						if (IS_ERR(ch))
 | 
				
			||||||
 | 
							return PTR_ERR(ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = request_irq(ch->irq, m2p_irq, 0, cl->name ? : "dma-m2p", ch);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch->client = cl;
 | 
				
			||||||
 | 
						ch->next_slot = 0;
 | 
				
			||||||
 | 
						ch->buffer_xfer = NULL;
 | 
				
			||||||
 | 
						ch->buffer_next = NULL;
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&ch->buffers_pending);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl->channel = ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						channel_enable(ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_client_register);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_client_unregister(struct ep93xx_dma_m2p_client *cl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch = cl->channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						channel_disable(ch);
 | 
				
			||||||
 | 
						free_irq(ch->irq, ch);
 | 
				
			||||||
 | 
						ch->client = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_client_unregister);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_submit(struct ep93xx_dma_m2p_client *cl,
 | 
				
			||||||
 | 
								   struct ep93xx_dma_buffer *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch = cl->channel;
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
						u32 v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irqsave(&ch->lock, flags);
 | 
				
			||||||
 | 
						v = readl(ch->base + M2P_CONTROL);
 | 
				
			||||||
 | 
						if (ch->buffer_xfer == NULL) {
 | 
				
			||||||
 | 
							ch->buffer_xfer = buf;
 | 
				
			||||||
 | 
							feed_buf(ch, buf);
 | 
				
			||||||
 | 
							cl->buffer_started(cl->cookie, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							v |= M2P_CONTROL_STALL_IRQ_EN;
 | 
				
			||||||
 | 
							m2p_set_control(ch, v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else if (ch->buffer_next == NULL) {
 | 
				
			||||||
 | 
							ch->buffer_next = buf;
 | 
				
			||||||
 | 
							feed_buf(ch, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							v |= M2P_CONTROL_NFB_IRQ_EN;
 | 
				
			||||||
 | 
							m2p_set_control(ch, v);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							list_add_tail(&buf->list, &ch->buffers_pending);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&ch->lock, flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_submit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *cl,
 | 
				
			||||||
 | 
									     struct ep93xx_dma_buffer *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch = cl->channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_add_tail(&buf->list, &ch->buffers_pending);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_submit_recursive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *cl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct m2p_channel *ch = cl->channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						channel_disable(ch);
 | 
				
			||||||
 | 
						ch->next_slot = 0;
 | 
				
			||||||
 | 
						ch->buffer_xfer = NULL;
 | 
				
			||||||
 | 
						ch->buffer_next = NULL;
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&ch->buffers_pending);
 | 
				
			||||||
 | 
						channel_enable(ch);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_flush);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int init_channel(struct m2p_channel *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ch->clk = clk_get(NULL, ch->name);
 | 
				
			||||||
 | 
						if (IS_ERR(ch->clk))
 | 
				
			||||||
 | 
							return PTR_ERR(ch->clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_init(&ch->lock);
 | 
				
			||||||
 | 
						ch->client = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __init ep93xx_dma_m2p_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; m2p_rx[i].base; i++) {
 | 
				
			||||||
 | 
							ret = init_channel(m2p_rx + i);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; m2p_tx[i].base; i++) {
 | 
				
			||||||
 | 
							ret = init_channel(m2p_tx + i);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pr_info("M2P DMA subsystem initialized\n");
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					arch_initcall(ep93xx_dma_m2p_init);
 | 
				
			||||||
							
								
								
									
										52
									
								
								arch/arm/mach-ep93xx/include/mach/dma.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								arch/arm/mach-ep93xx/include/mach/dma.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					#ifndef __ASM_ARCH_DMA_H
 | 
				
			||||||
 | 
					#define __ASM_ARCH_DMA_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/list.h>
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ep93xx_dma_buffer {
 | 
				
			||||||
 | 
						struct list_head	list;
 | 
				
			||||||
 | 
						u32			bus_addr;
 | 
				
			||||||
 | 
						u16			size;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ep93xx_dma_m2p_client {
 | 
				
			||||||
 | 
						char			*name;
 | 
				
			||||||
 | 
						u8			flags;
 | 
				
			||||||
 | 
						void			*cookie;
 | 
				
			||||||
 | 
						void			(*buffer_started)(void *cookie,
 | 
				
			||||||
 | 
										struct ep93xx_dma_buffer *buf);
 | 
				
			||||||
 | 
						void			(*buffer_finished)(void *cookie,
 | 
				
			||||||
 | 
										struct ep93xx_dma_buffer *buf,
 | 
				
			||||||
 | 
										int bytes, int error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Internal to the DMA code.  */
 | 
				
			||||||
 | 
						void			*channel;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_I2S1	0x00
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_I2S2	0x01
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_AAC1	0x02
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_AAC2	0x03
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_AAC3	0x04
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_I2S3	0x05
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_UART1	0x06
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_UART2	0x07
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_UART3	0x08
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_IRDA	0x09
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_PORT_MASK	0x0f
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_TX		0x00
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_RX		0x10
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_ABORT_ON_ERROR	0x20
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_IGNORE_ERROR	0x40
 | 
				
			||||||
 | 
					#define EP93XX_DMA_M2P_ERROR_MASK	0x60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int  ep93xx_dma_m2p_client_register(struct ep93xx_dma_m2p_client *m2p);
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_client_unregister(struct ep93xx_dma_m2p_client *m2p);
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_submit(struct ep93xx_dma_m2p_client *m2p,
 | 
				
			||||||
 | 
								   struct ep93xx_dma_buffer *buf);
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p,
 | 
				
			||||||
 | 
									     struct ep93xx_dma_buffer *buf);
 | 
				
			||||||
 | 
					void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __ASM_ARCH_DMA_H */
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* AHB peripherals */
 | 
					/* AHB peripherals */
 | 
				
			||||||
#define EP93XX_DMA_BASE			(EP93XX_AHB_VIRT_BASE + 0x00000000)
 | 
					#define EP93XX_DMA_BASE			((void __iomem *)		\
 | 
				
			||||||
 | 
										 (EP93XX_AHB_VIRT_BASE + 0x00000000))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define EP93XX_ETHERNET_BASE		(EP93XX_AHB_VIRT_BASE + 0x00010000)
 | 
					#define EP93XX_ETHERNET_BASE		(EP93XX_AHB_VIRT_BASE + 0x00010000)
 | 
				
			||||||
#define EP93XX_ETHERNET_PHYS_BASE	(EP93XX_AHB_PHYS_BASE + 0x00010000)
 | 
					#define EP93XX_ETHERNET_PHYS_BASE	(EP93XX_AHB_PHYS_BASE + 0x00010000)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue