/*
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* QCOM BAM DMA engine driver
*
* QCOM BAM DMA blocks are distributed amongst a number of the on-chip
* peripherals on the MSM 8x74. The configuration of the channels are dependent
* on the way they are hard wired to that specific peripheral. The peripheral
* device tree entries specify the configuration of each channel.
*
* The DMA controller requires the use of external memory for storage of the
* hardware descriptors for each channel. The descriptor FIFO is accessed as a
* circular buffer and operations are managed according to the offset within the
* FIFO. After pipe/channel reset, all of the pipe registers and internal state
* are back to defaults.
*
* During DMA operations, we write descriptors to the FIFO, being careful to
* handle wrapping and then write the last FIFO offset to that channel's
* P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register
* indicates the current FIFO offset that is being processed, so there is some
* indication of where the hardware is currently working.
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_dma.h>
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include "dmaengine.h"
#include "virt-dma.h"
struct bam_desc_hw {
u32 addr; /* Buffer physical address */
u16 size; /* Buffer size in bytes */
u16 flags;
};
#define DESC_FLAG_INT BIT(15)
#define DESC_FLAG_EOT BIT(14)
#define DESC_FLAG_EOB BIT(13)
#define DESC_FLAG_NWD BIT(12)
struct bam_async_desc {
struct virt_dma_desc vd;
u32 num_desc;
u32 xfer_len;
/* transaction flags, EOT|EOB|NWD */
u16 flags;
struct bam_desc_hw *curr_desc;
enum dma_transfer_direction dir;
size_t length;
struct bam_desc_hw desc[0];
};
enum bam_reg {
BAM_CTRL,
BAM_REVISION,
BAM_NUM_PIPES,
BAM_DESC_CNT_TRSHLD,
BAM_IRQ_SRCS,
BAM_IRQ_SRCS_MSK,
BAM_IRQ_SRCS_UNMASKED,
BAM_IRQ_STTS,
BAM_IRQ_CLR,
BAM_IRQ_EN,
BAM_CNFG_BITS,
BAM_IRQ_SRCS_EE,
BAM_IRQ_SRCS_MSK_EE,
BAM_P_CTRL,
BAM_P_RST,
BAM_P_HALT,
BAM_P_IRQ_STTS,
BAM_P_IRQ_CLR,
BAM_P_IRQ_EN,
BAM_P_EVNT_DEST_ADDR,
BAM_P_EVNT_REG,
BAM_P_SW_OFSTS,
BAM_P_DATA_FIFO_ADDR,
BAM_P_DESC_FIFO_ADDR,
BAM_P_EVNT_GEN_TRSHLD,
BAM_P_FIFO_SIZES,
};
struct reg_offset_data {
u32 base_offset;
unsigned int pipe_mult, evnt_mult, ee_mult;
};
static const struct reg_offset_data bam_v1_3_reg_info[] = {
[BAM_CTRL] = { 0x0F80, 0x00, 0x00, 0x00 },
[BAM_REVISION] = { 0x0F84, 0x00, 0x00, 0x00 },
[BAM_NUM_PIPES] = { 0x0FBC, 0x00, 0x00, 0x00 },
[BAM_DESC_CNT_TRSHLD] = { 0x0F88, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS] = { 0x0F8C, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS_MSK] = { 0x0F90, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS_UNMASKED] = { 0x0FB0, 0x00, 0x00, 0x00 },
[BAM_IRQ_STTS] = { 0x0F94, 0x00, 0x00, 0x00 },
[BAM_IRQ_CLR] = { 0x0F98, 0x00, 0x00, 0x00 },
[BAM_IRQ_EN] = { 0x0F9C, 0x00, 0x00, 0x00 },
[BAM_CNFG_BITS] = { 0x0FFC, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS_EE] = { 0x1800, 0x00, 0x00, 0x80 },
[BAM_IRQ_SRCS_MSK_EE] = { 0x1804, 0x00, 0x00, 0x80 },
[BAM_P_C