/*
 *  intel_mid_dma_regs.h - Intel MID DMA Drivers
 *
 *  Copyright (C) 2008-10 Intel Corp
 *  Author: Vinod Koul <vinod.koul@intel.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; version 2 of the License.
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *
 */
#ifndef __INTEL_MID_DMAC_REGS_H__
#define __INTEL_MID_DMAC_REGS_H__

#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/pci_ids.h>

#define INTEL_MID_DMA_DRIVER_VERSION "1.1.0"

#define	REG_BIT0		0x00000001
#define	REG_BIT8		0x00000100
#define INT_MASK_WE		0x8
#define CLEAR_DONE		0xFFFFEFFF
#define UNMASK_INTR_REG(chan_num) \
	((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num))
#define MASK_INTR_REG(chan_num) (REG_BIT8 << chan_num)

#define ENABLE_CHANNEL(chan_num) \
	((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num))

#define DISABLE_CHANNEL(chan_num) \
	(REG_BIT8 << chan_num)

#define DESCS_PER_CHANNEL	16
/*DMA Registers*/
/*registers associated with channel programming*/
#define DMA_REG_SIZE		0x400
#define DMA_CH_SIZE		0x58

/*CH X REG = (DMA_CH_SIZE)*CH_NO + REG*/
#define SAR			0x00 /* Source Address Register*/
#define DAR			0x08 /* Destination Address Register*/
#define LLP			0x10 /* Linked List Pointer Register*/
#define CTL_LOW			0x18 /* Control Register*/
#define CTL_HIGH		0x1C /* Control Register*/
#define CFG_LOW			0x40 /* Configuration Register Low*/
#define CFG_HIGH		0x44 /* Configuration Register high*/

#define STATUS_TFR		0x2E8
#define STATUS_BLOCK		0x2F0
#define STATUS_ERR		0x308

#define RAW_TFR			0x2C0
#define RAW_BLOCK		0x2C8
#define RAW_ERR			0x2E0

#define MASK_TFR		0x310
#define MASK_BLOCK		0x318
#define MASK_SRC_TRAN		0x320
#define MASK_DST_TRAN		0x328
#define MASK_ERR		0x330

#define CLEAR_TFR		0x338
#define CLEAR_BLOCK		0x340
#define CLEAR_SRC_TRAN		0x348
#define CLEAR_DST_TRAN		0x350
#define CLEAR_ERR		0x358

#define INTR_STATUS		0x360
#define DMA_CFG			0x398
#define DMA_CHAN_EN		0x3A0

/*DMA channel control registers*/
union intel_mid_dma_ctl_lo {
	struct {
		u32	int_en:1;	/*enable or disable interrupts*/
					/*should be 0*/
		u32	dst_tr_width:3;	/*destination transfer width*/
					/*usually 32 bits = 010*/
		u32	src_tr_width:3; /*source transfer width*/
					/*usually 32 bits = 010*/
		u32	dinc:2;		/*destination address inc/dec*/
					/*For mem:INC=00, Periphral NoINC=11*/
		u32	sinc:2;		/*source address inc or dec, as above*/
		u32	dst_msize:3;	/*destination burst transaction length*/
					/*always = 16 ie 011*/
		u32	src_msize:3;	/*source burst transaction length*/
					/*always = 16 ie 011*/
		u32	reser1:3;
		u32	tt_fc:3;	/*transfer type and flow controller*/
					/*M-M = 000
					  P-M = 010
					  M-P = 001*/
		u32	dms:2;		/*destination master select = 0*/
		u32	sms:2;		/*source master select = 0*/
		u32	llp_dst_en:1;	/*enable/disable destination LLP = 0*/
		u32	llp_src_en:1;	/*enable/disable source LLP = 0*/
		u32	reser2:3;
	} ctlx;
	u32	ctl_lo;
};

union intel_mid_dma_ctl_hi {
	struct {
		u32	block_ts:12;	/*block transfer size*/
		u32	done:1;		/*Done - updated by DMAC*/
		u32	reser:19;	/*configured by DMAC*/
	} ctlx;
	u32	ctl_hi;

};

/*DMA channel configuration registers*/
union intel_mid_dma_cfg_lo {
	struct {
		u32	reser1:5;
		u32	ch_prior:3;	/*channel priority = 0*/
		u32	ch_susp:1;	/*channel suspend = 0*/
		u32	fifo_empty:1;	/*FIFO empty or not R bit = 0*/
		u32	hs_sel_dst:1;	/*select HW/SW destn handshaking*/
					/*HW = 0, SW = 1*/
		u32	hs_sel_src:1;	/*select HW/SW src handshaking*/
		u32	reser2:6;
		u32	dst_hs_pol:1;	/*dest HS interface polarity*/
		u32	src_hs_pol:1;	/*src HS interface polarity*/
		u32	max_abrst:10;	/*max AMBA burst len = 0 (no sw limit*/
		u32	reload_src:1;	/*auto reload src addr =1 if src is P*/
		u32	reload_dst:1;	/*AR destn addr =1 if dstn is P*/
	} cfgx;
	u32	cfg_lo;
};

union intel_mid_dma_cfg_hi {
	struct {
		u32	fcmode:1;	/*flow control mode = 1*/
		u32	fifo_mode:1;	/*FIFO mode select = 1*/
		u32	protctl:3;	/*protection control = 0*/
		u32	rsvd:2;
		u32	src_per:4;	/*src hw HS interface*/
		u32	dst_per:4;	/*dstn hw HS interface*/
		u32	reser2:17;
	} cfgx;
	u32	cfg_hi;
};


/**
 * struct intel_mid_dma_chan - internal mid representation of a DMA channel
 * @chan: dma_chan strcture represetation for mid chan
 * @ch_regs: MMIO register space pointer to channel register
 * @dma_base: MMIO register space DMA engine base pointer
 * @ch_id: DMA channel id
 * @lock: channel spinlock
 * @completed: DMA cookie
 * @active_list: current active descriptors
 * @queue: current queued up descriptors
 * @free_list: current free descriptors
 * @slave: dma slave struture
 * @descs_allocated: total number of decsiptors allocated
 * @dma: dma device struture pointer
 * @busy: bool representing if ch is busy (active txn) or not
 * @in_use: bool representing if ch is in use or not
 * @raw_tfr: raw trf interrupt received
 * @raw_block: raw block interrupt received
 */
struct intel_mid_dma_chan {
	struct dma_chan		chan;
	void __iomem		*ch_regs;
	void __iomem		*dma_base;
	int			ch_id;
	spinlock_t		lock;
	dma_cookie_t		completed;
	struct list_head	active_list;
	struct list_head	queue;
	struct list_head	free_list;
	unsigned int		descs_allocated;
	struct middma_device	*dma;
	bool			busy;
	bool			in_use;
	u32			raw_tfr;
	u32			raw_block;
	struct intel_mid_dma_slave *mid_slave;
};

static inline struct intel_mid_dma_chan *to_intel_mid_dma_chan(
						struct dma_chan *chan)
{
	return container_of(chan, struct intel_mid_dma_chan, chan);
}

enum intel_mid_dma_state {
	RUNNING = 0,
	SUSPENDED,
};
/**
 * struct middma_device - internal representation of a DMA device
 * @pdev: PCI device
 * @dma_base: MMIO register space pointer of DMA
 * @dma_pool: for allocating DMA descriptors
 * @common: embedded struct dma_device
 * @tasklet: dma tasklet for processing interrupts
 * @ch: per channel data
 * @pci_id: DMA device PCI ID
 * @intr_mask: Interrupt mask to be used
 * @mask_reg: MMIO register for periphral mask
 * @chan_base: Base ch index (read from driver data)
 * @max_chan: max number of chs supported (from drv_data)
 * @block_size: Block size of DMA transfer supported (from drv_data)
 * @pimr_mask: MMIO register addr for periphral interrupt (from drv_data)
 * @state: dma PM device state
 */
struct middma_device {
	struct pci_dev		*pdev;
	void __iomem		*dma_base;
	struct pci_pool		*dma_pool;
	struct dma_device	common;
	struct tasklet_struct   tasklet;
	struct intel_mid_dma_chan ch[MAX_CHAN];
	unsigned int		pci_id;
	unsigned int		intr_mask;
	void __iomem		*mask_reg;
	int			chan_base;
	int			max_chan;
	int			block_size;
	unsigned int		pimr_mask;
	enum intel_mid_dma_state state;
};

static inline struct middma_device *to_middma_device(struct dma_device *common)
{
	return container_of(common, struct middma_device, common);
}

struct intel_mid_dma_desc {
	void __iomem			*block; /*ch ptr*/
	struct list_head		desc_node;
	struct dma_async_tx_descriptor	txd;
	size_t				len;
	dma_addr_t			sar;
	dma_addr_t			dar;
	u32				cfg_hi;
	u32				cfg_lo;
	u32				ctl_lo;
	u32				ctl_hi;
	struct pci_pool			*lli_pool;
	struct intel_mid_dma_lli	*lli;
	dma_addr_t			lli_phys;
	unsigned int			lli_length;
	unsigned int			current_lli;
	dma_addr_t			next;
	enum dma_data_direction		dirn;
	enum dma_status			status;
	enum dma_slave_buswidth		width; /*width of DMA txn*/
	enum intel_mid_dma_mode		cfg_mode; /*mode configuration*/

};

struct intel_mid_dma_lli {
	dma_addr_t			sar;
	dma_addr_t			dar;
	dma_addr_t			llp;
	u32				ctl_lo;
	u32				ctl_hi;
} __attribute__ ((packed));

static inline int test_ch_en(void __iomem *dma, u32 ch_no)
{
	u32 en_reg = ioread32(dma + DMA_CHAN_EN);
	return (en_reg >> ch_no) & 0x1;
}

static inline struct intel_mid_dma_desc *to_intel_mid_dma_desc
		(struct dma_async_tx_descriptor *txd)
{
	return container_of(txd, struct intel_mid_dma_desc, txd);
}

static inline struct intel_mid_dma_slave *to_intel_mid_dma_slave
		(struct dma_slave_config *slave)
{
	return container_of(slave, struct intel_mid_dma_slave, dma_slave);
}


int dma_resume(struct pci_dev *pci);

#endif /*__INTEL_MID_DMAC_REGS_H__*/
