/* Copyright (c) 2011-2015, 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.
 */

/*
 * Function and data structure declarations for SPS BAM handling.
 */


#ifndef _SPSBAM_H_
#define _SPSBAM_H_

#include <linux/types.h>
#include <linux/completion.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>

#include "spsi.h"

#define BAM_HANDLE_INVALID         0

#define to_sps_bam_dev(x) \
	container_of((x), struct sps_bam, base)

enum bam_irq {
	BAM_DEV_IRQ_RDY_TO_SLEEP = 0x00000001,
	BAM_DEV_IRQ_HRESP_ERROR = 0x00000002,
	BAM_DEV_IRQ_ERROR = 0x00000004,
	BAM_DEV_IRQ_TIMER = 0x00000010,
};

/* Pipe interrupt mask */
enum bam_pipe_irq {
	/* BAM finishes descriptor which has INT bit selected */
	BAM_PIPE_IRQ_DESC_INT = 0x00000001,
	/* Inactivity timer Expires */
	BAM_PIPE_IRQ_TIMER = 0x00000002,
	/* Wakeup peripheral (i.e. USB) */
	BAM_PIPE_IRQ_WAKE = 0x00000004,
	/* Producer - no free space for adding a descriptor */
	/* Consumer - no descriptors for processing */
	BAM_PIPE_IRQ_OUT_OF_DESC = 0x00000008,
	/* Pipe Error interrupt */
	BAM_PIPE_IRQ_ERROR = 0x00000010,
	/* End-Of-Transfer */
	BAM_PIPE_IRQ_EOT = 0x00000020,
	/* Pipe RESET unsuccessful */
	BAM_PIPE_IRQ_RST_ERROR = 0x00000040,
	/* Errorneous Hresponse by AHB MASTER */
	BAM_PIPE_IRQ_HRESP_ERROR = 0x00000080,
};

/* Halt Type */
enum bam_halt {
	BAM_HALT_OFF = 0,
	BAM_HALT_ON = 1,
};

/* Threshold values of the DMA channels */
enum bam_dma_thresh_dma {
	BAM_DMA_THRESH_512 = 0x3,
	BAM_DMA_THRESH_256 = 0x2,
	BAM_DMA_THRESH_128 = 0x1,
	BAM_DMA_THRESH_64 = 0x0,
};

/* Weight values of the DMA channels */
enum bam_dma_weight_dma {
	BAM_DMA_WEIGHT_HIGH = 7,
	BAM_DMA_WEIGHT_MED = 3,
	BAM_DMA_WEIGHT_LOW = 1,
	BAM_DMA_WEIGHT_DEFAULT = BAM_DMA_WEIGHT_LOW,
	BAM_DMA_WEIGHT_DISABLE = 0,
};


/* Invalid pipe index value */
#define SPS_BAM_PIPE_INVALID  ((u32)(-1))

/* Parameters for sps_bam_pipe_connect() */
struct sps_bam_connect_param {
	/* which end point must be initialized */
	enum sps_mode mode;

	/* OR'd connection end point options (see SPS_O defines) */
	u32 options;

	/* SETPEND/MTI interrupt generation parameters */
	u32 irq_gen_addr;
	u32 irq_gen_data;

};

/* Event registration struct */
struct sps_bam_event_reg {
	/* Client's event object handle */
	struct completion *xfer_done;
	void (*callback)(struct sps_event_notify *notify);

	/* Event trigger mode */
	enum sps_trigger mode;

	/* User pointer that will be provided in event payload data */
	void *user;

};

/* Descriptor FIFO cache entry */
struct sps_bam_desc_cache {
	struct sps_iovec iovec;
	void *user; /* User pointer registered with this transfer */
};

/* Forward declaration */
struct sps_bam;

/* System mode control */
struct sps_bam_sys_mode {
	/* Descriptor FIFO control */
	u8 *desc_buf; /* Descriptor FIFO for BAM pipe */
	u32 desc_offset; /* Next new descriptor to be written to hardware */
	u32 acked_offset; /* Next descriptor to be retired by software */

	/* Descriptor cache control (!no_queue only) */
	u8 *desc_cache; /* Software cache of descriptor FIFO contents */
	u32 cache_offset; /* Next descriptor to be cached (ack_xfers only) */

	/* User pointers associated with cached descriptors */
	void **user_ptrs;

	/* Event handling */
	struct sps_bam_event_reg event_regs[SPS_EVENT_INDEX(SPS_EVENT_MAX)];
	struct list_head events_q;

	struct sps_q_event event;	/* Temp storage for event creation */
	int no_queue;	/* Whether events are queued */
	int ack_xfers;	/* Whether client must ACK all descriptors */
	int handler_eot; /* Whether EOT handling is in progress (debug) */

	/* Statistics */
#ifdef SPS_BAM_STATISTICS
	u32 desc_wr_count;
	u32 desc_rd_count;
	u32 user_ptrs_count;
	u32 user_found;
	u32 int_flags;
	u32 eot_flags;
	u32 callback_events;
	u32 wait_events;
	u32 queued_events;
	u32 get_events;
	u32 get_iovecs;
#endif /* SPS_BAM_STATISTICS */
};

/* BAM pipe descriptor */
struct sps_pipe {
	struct list_head list;

	/* Client state */
	u32 client_state;
	struct sps_bam *bam;
	struct sps_connect connect;
	const struct sps_connection *map;

	/* Pipe parameters */
	u32 state;
	u32 pipe_index;
	u32 pipe_index_mask;
	u32 irq_mask;
	int polled;
	int hybrid;
	bool late_eot;
	u32 irq_gen_addr;
	enum sps_mode mode;
	u32 num_descs; /* Size (number of elements) of descriptor FIFO */
	u32 desc_size; /* Size (bytes) of descriptor FIFO */
	int wake_up_is_one_shot; /* Whether WAKEUP event is a one-shot or not */

	/* System mode control */
	struct sps_bam_sys_mode sys;

	bool disconnecting;
};

/* BAM device descriptor */
struct sps_bam {
	struct list_head list;

	/* BAM device properties, including connection defaults */
	struct sps_bam_props props;

	/* BAM device state */
	u32 state;
	struct mutex lock;
	void *base; /* BAM virtual base address */
	u32 version;
	spinlock_t isr_lock;
	spinlock_t connection_lock;
	unsigned long irqsave_flags;

	/* Pipe state */
	u32 pipe_active_mask;
	u32 pipe_remote_mask;
	struct sps_pipe *pipes[BAM_MAX_PIPES];
	struct list_head pipes_q;

	/* Statistics */
	u32 irq_from_disabled_pipe;
	u32 event_trigger_failures;

	void *ipc_log0;
	void *ipc_log1;
	void *ipc_log2;
	void *ipc_log3;
	void *ipc_log4;

	u32 ipc_loglevel;

	/* Desc cache pointers */
	u8 *desc_cache_pointers[BAM_MAX_PIPES];
};

/**
 * BAM driver initialization
 *
 * This function initializes the BAM driver.
 *
 * @options - driver options bitflags (see SPS_OPT_*)
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_driver_init(u32 options);

/**
 * BAM device initialization
 *
 * This function initializes a BAM device.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_device_init(struct sps_bam *dev);

/**
 * BAM device de-initialization
 *
 * This function de-initializes a BAM device.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_device_de_init(struct sps_bam *dev);

/**
 * BAM device reset
 *
 * This Function resets a BAM device.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_reset(struct sps_bam *dev);

/**
 * BAM device enable
 *
 * This function enables a BAM device.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_enable(struct sps_bam *dev);

/**
 * BAM device disable
 *
 * This Function disables a BAM device.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_disable(struct sps_bam *dev);

/**
 * Allocate a BAM pipe
 *
 * This function allocates a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - client-specified pipe index, or SPS_BAM_PIPE_INVALID if
 *    any available pipe is acceptable
 *
 * @return - allocated pipe index, or SPS_BAM_PIPE_INVALID on error
 *
 */
u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index);

/**
 * Free a BAM pipe
 *
 * This function frees a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 */
void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index);

/**
 * Establish BAM pipe connection
 *
 * This function establishes a connection for a BAM pipe (end point).
 *
 * @client - pointer to client pipe state struct
 *
 * @params - connection parameters
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_connect(struct sps_pipe *client,
			const struct sps_bam_connect_param *params);

/**
 * Disconnect a BAM pipe connection
 *
 * This function disconnects a connection for a BAM pipe (end point).
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index);

/**
 * Set BAM pipe parameters
 *
 * This function sets parameters for a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @options - bitflag options (see SPS_O_*)
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options);

/**
 * Enable a BAM pipe
 *
 * This function enables a BAM pipe.  Note that this function
 *    is separate from the pipe connect function to allow proper
 *    sequencing of consumer enable followed by producer enable.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index);

/**
 * Disable a BAM pipe
 *
 * This function disables a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index);

/**
 * Register an event for a BAM pipe
 *
 * This function registers an event for a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @reg - pointer to event registration struct
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_reg_event(struct sps_bam *dev, u32 pipe_index,
			   struct sps_register_event *reg);

/**
 * Submit a transfer of a single buffer to a BAM pipe
 *
 * This function submits a transfer of a single buffer to a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @addr - physical address of buffer to transfer
 *
 * @size - number of bytes to transfer
 *
 * @user - user pointer to register for event
 *
 * @flags - descriptor flags (see SPS_IOVEC_FLAG defines)
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_transfer_one(struct sps_bam *dev, u32 pipe_index, u32 addr,
			      u32 size, void *user, u32 flags);

/**
 * Submit a transfer to a BAM pipe
 *
 * This function submits a transfer to a BAM pipe.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @transfer - pointer to transfer struct
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_transfer(struct sps_bam *dev, u32 pipe_index,
			 struct sps_transfer *transfer);

/**
 * Get a BAM pipe event
 *
 * This function polls for a BAM pipe event.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @notify - pointer to event notification struct
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_get_event(struct sps_bam *dev, u32 pipe_index,
			   struct sps_event_notify *notify);

/**
 * Get processed I/O vector
 *
 * This function fetches the next processed I/O vector.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @iovec - Pointer to I/O vector struct (output).
 *   This struct will be zeroed if there are no more processed I/O vectors.
 *
 * @return 0 on success, negative value on error
 */
int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index,
			   struct sps_iovec *iovec);

/**
 * Determine whether a BAM pipe descriptor FIFO is empty
 *
 * This function returns the empty state of a BAM pipe descriptor FIFO.
 *
 * The pipe mutex must be locked before calling this function.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @empty - pointer to client's empty status word (boolean)
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index, u32 *empty);

/**
 * Get number of free slots in a BAM pipe descriptor FIFO
 *
 * This function returns the number of free slots in a BAM pipe descriptor FIFO.
 *
 * The pipe mutex must be locked before calling this function.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @count - pointer to count status
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index, u32 *count);

/**
 * Set BAM pipe to satellite ownership
 *
 * This function sets the BAM pipe to satellite ownership.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index);

/**
 * Perform BAM pipe timer control
 *
 * This function performs BAM pipe timer control operations.
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @timer_ctrl - Pointer to timer control specification
 *
 * @timer_result - Pointer to buffer for timer operation result.
 *    This argument can be NULL if no result is expected for the operation.
 *    If non-NULL, the current timer value will always provided.
 *
 * @return 0 on success, negative value on error
 *
 */
int sps_bam_pipe_timer_ctrl(struct sps_bam *dev, u32 pipe_index,
			    struct sps_timer_ctrl *timer_ctrl,
			    struct sps_timer_result *timer_result);


/**
 * Get the number of unused descriptors in the descriptor FIFO
 * of a pipe
 *
 * @dev - pointer to BAM device descriptor
 *
 * @pipe_index - pipe index
 *
 * @desc_num - number of unused descriptors
 *
 */
int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index,
					u32 *desc_num);

/*
 * sps_bam_check_irq - check IRQ of a BAM device.
 * @dev - pointer to BAM device descriptor
 *
 * This function checks any pending interrupt of a BAM device.
 *
 * Return: 0 on success, negative value on error
 */
int sps_bam_check_irq(struct sps_bam *dev);

/*
 * sps_bam_pipe_pending_desc - checking pending descriptor.
 * @dev:	BAM device handle
 * @pipe_index:	pipe index
 *
 * This function checks if a pipe of a BAM has any pending descriptor.
 *
 * @return true if there is any desc pending
 */
bool sps_bam_pipe_pending_desc(struct sps_bam *dev, u32 pipe_index);

/*
 * sps_bam_pipe_inject_zlt - inject a ZLT with EOT.
 * @dev:	BAM device handle
 * @pipe_index:	pipe index
 *
 * This function injects a ZLT with EOT for a pipe of a BAM.
 *
 * Return: 0 on success, negative value on error
 */
int sps_bam_pipe_inject_zlt(struct sps_bam *dev, u32 pipe_index);
#endif	/* _SPSBAM_H_ */