/** @file ahci_elphel_ext.h
 *
 * @brief Elphel AHCI SATA platform driver for Elphel393 camera. This module provides
 * additional functions which allows to use a part of a disk (or entire disk) as a
 * raw circular buffer.
 *
 * @copyright Copyright (C) 2016 Elphel, Inc
 *
 * @par <b>License</b>
 * 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 3 of the License, or
 * (at your option) any later version.
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include <linux/timer.h>
#include <uapi/elphel/ahci_cmd.h>
#include <uapi/elphel/c313a.h>
#include "../elphel/circbuf.h"
#include "../elphel/x393_fpga_functions.h"

#ifndef _AHCI_ELPHEL_EXT
#define _AHCI_ELPHEL_EXT


#define IRQ_SIMPLE                (1 << 0)       ///< Flag indicating that IRQ corresponds to internal command and should not be
                                                 ///< processed in ahci_handle_port_interrupt
#define DISK_BUSY                 (1 << 1)       ///< Flag indicating that disk is currently busy. Access to this flag should be protected by
                                                 ///< spin locks to prevent race conditions
#define PROC_CMD                  (1 << 2)       ///< Processing driver's internal command is in progress
#define LAST_BLOCK                (1 << 3)       ///< Flag indicating that the remaining chunk of data will be recorded
#define DELAYED_FINISH            (1 << 4)       ///< Flag indicating that recording should be stopped right after the last chunk of data is written
#define LOCK_TAIL                 (1 << 5)       ///< Lock current command slot until all data buffers are assigned and the frame is aligned
#define START_EH                  (1 << 6)       ///< Start error handling procedure
#define WAIT_EH                   (1 << 7)       ///< Wait for error handler completion
#define DELAYED_IRQ_RCVD          (1 << 8)       ///< Delayed interrupt received and should be processed
#define IRQ_PROCESSED             (1 << 9)       ///< Tell command guard timer that IRQ is received and EH should not be started
#define CMD_FIS_LEN               5              ///< The length of a command FIS in double words
#define ADDR_MASK_28_BIT          ((u64)0xfffffff)///< This is used to get 28-bit address from 64-bit value
#define MAX_PRDT_LEN              0x3fffff       ///< A maximum of length of 4MB may exist for PRDT entry
#define MAX_DATA_CHUNKS           9              ///< An array or JPEG frame chunks contains pointers to JPEG leading marker,
                                                 ///< JPEG header, Exif data if present, stuffing bytes chunk which aligns
                                                 ///< the frame size to disk sector boundary, JPEG data which
                                                 ///< can be split into two chunks, align buffers, JPEG
                                                 ///< trailing marker, and pointer to a buffer containing the remainder of a
                                                 ///< frame. Nine chunks of data in total.
#define DEFAULT_PORT_NUM          0              ///< Default port number
#define ALIGNMENT_SIZE            32             ///< Align buffers length to this amount of bytes
#define MAX_SGL_LEN               168            ///< Maximum number of entries in PRDT table. HW max is 64k.
                                                 ///< Set this value the same as AHCI_MAX_SG in ahci.h
#define MAX_CMD_SLOTS             4              ///< Maximum number of frames which will be processed at the same time
#define MAX_LBA_COUNT             0xff           ///< Maximum number of sectors for READ DMA or WRITE DMA commands
#define MAX_LBA_COUNT_EXT         0xffff         ///< Maximum number of sectors for READ DMA EXT or WRITE_DMA EXT commands
#define PHY_BLOCK_SIZE            512            ///< Physical disk block size
#define JPEG_MARKER_LEN           2              ///< The size in bytes of JPEG marker
#define JPEG_SIZE_LEN             2              ///< The size in bytes of JPEG marker length field
#define INCLUDE_REM               1              ///< Include REM buffer to total size calculation
#define EXCLUDE_REM               0              ///< Exclude REM buffer from total size calculation
#define SPEED_SAMPLES_NUM         5              ///< Maximum number of samples for disk recording speed measurement
#define DEFAULT_CMD_TIMEOUT       200            ///< Default timeout for commands, in ms
#define WAIT_IRQ_TIMEOUT          5000           ///< Wait delayed interrupt for this amount of time, in ms

#define TSTMP_CMD_SYS             1              ///< command issued by system
#define TSTMP_CMD_DRV             2              ///< command issued by driver
#define TSTMP_IRQ_SYS             3              ///< irq processed by system
#define TSTMP_IRQ_DRV             4              ///< irq processed by driver
#define TSTMP_IRQ_MARK_1          5              ///< additional time stamp mark
enum {
    PORT_VS                       = 0x70,        ///< vendor specific port address
    PORT_TIMESTAMP_ADDR           = 0x78         ///< datascope timestamp register
};
enum {
	REG_NUM = 128,                               ///< total number of DWs in status buffer
	REG_HOST_IS = 0,                             ///< value of host IS register in data buffer
	REG_PxIS,                                    ///< value of PxIS in data buffer
	REG_PxIE,                                    ///< value of PxIE in data buffer
	REG_PxSERR,                                  ///< value of PxSERR in data buffer
	IRQ_COUNTER,                                 ///< interrupts counter for internal commands
	IRQ_COUNTER_SYS,                             ///< interrupts counter for system commands
	CMD_SENT,                                    ///< number of commands sent
	CMD_RCVD                                     ///< number of commands received
};
struct datascope {
    void __iomem *timestamp_reg;                 ///< register in vendor specific address range (PxVS) where timestamp can be written
    unsigned int cmd_cntr;                       ///< command counter, its value can be found in timestamp (2 bits only)
    unsigned int enable;                         ///< enable/disable timestamps
    uint32_t reg_stat[REG_NUM];                  ///< buffer for registers' status, filled in in IRQ
};

/** Data for error handler */
struct error_handler {
	uint32_t s_error;                            ///< the content of PxSERR register
	uint32_t irq_stat;                           ///< the content of PxIS register
	wait_queue_head_t wait;                      ///< wait queue for delayed interrupts
};

/** This structure is for collecting some recording statistics */
struct rec_stat {
	unsigned int samples_ptr;                    ///< pointer to next sample in rec_stat::samples
	unsigned int samples[SPEED_SAMPLES_NUM];     ///< calculated recording speed samples, the value of recording speed
	                                             ///< presented via sysfs is a median of this array
	sec_usec_t start_time;                       ///< time when current command has been issued
	unsigned long last_irq_delay;                ///< late interrupt delay, in ms
	unsigned int stat_ready;                     ///< flag indicating that new statisics sample is ready
};

/** This structure holds raw device buffer pointers */
struct drv_pointers {
	uint64_t lba_start;                          ///< raw buffer starting LBA
	uint64_t lba_end;                            ///< raw buffer ending LBA
	uint64_t lba_write;                          ///< current write pointer inside raw buffer
	uint16_t wr_count;                           ///< the number of LBA to write next time
};

/** Container structure for frame buffers */
struct frame_buffers {
	struct fvec exif_buff;                       ///< Exif buffer
	struct fvec jpheader_buff;                   ///< JPEG header buffer
	struct fvec trailer_buff;                    ///< buffer for trailing marker
	struct fvec common_buff;                     ///< common buffer where other parts are combined
	struct fvec rem_buff;                        ///< remainder from previous frame
};

/** Symbolic names for slots in buffer pointers. Buffer alignment function relies on the order of these names, so
 * new names can be added but the overall order should not be changed */
enum {
	CHUNK_LEADER,                                ///< pointer to JPEG leading marker
	CHUNK_EXIF,                                  ///< pointer to Exif buffer
	CHUNK_HEADER,                                ///< pointer to JPEG header data excluding leading marker
	CHUNK_COMMON,                                ///< pointer to common buffer
	CHUNK_DATA_0,                                ///< pointer to JPEG data
	CHUNK_DATA_1,                                ///< pointer to the second half of JPEG data if a frame crosses circbuf boundary
	CHUNK_TRAILER,                               ///< pointer to JPEG trailing marker
	CHUNK_ALIGN,                                 ///< pointer to buffer where the second part of JPEG data should be aligned
	CHUNK_REM                                    ///< pointer to buffer containing the remainder of current frame. It will be recorded during next transaction
};

/** AHCI driver private structure */
struct elphel_ahci_priv {
	u32 clb_offs;                                ///< CLB offset, received from device tree
	u32 fb_offs;                                 ///< FB offset, received from device tree
	u32 base_addr;                               ///< controller base address
	u32 flags;                                   ///< flags indicating current state of the driver. Access to #DISK_BUSY flags is protected with
	                                             ///< a spin lock
	int curr_cmd;                                ///< current ATA command
	size_t max_data_sz;                          ///< maximum data size (in bytes) which can be processed with current ATA command
	struct drv_pointers lba_ptr;                 ///< disk buffer pointers
	struct frame_buffers fbuffs[MAX_CMD_SLOTS];  ///< a set of buffers for each command
	struct fvec data_chunks[MAX_CMD_SLOTS][MAX_DATA_CHUNKS];///< a set of vectors pointing to data buffers for each command
	struct fvec sgl[MAX_SGL_LEN];                ///< an array of data buffers mapped for next transaction
	int sg_elems;                                ///< the number of S/G vectors mapped for next transaction in @e sgl array
	int curr_data_chunk;                         ///< index of a data chunk used during last transaction
	size_t curr_data_offset;                     ///< offset of the last byte in a data chunk pointed to by @e curr_data_chunk
	size_t head_ptr;                             ///< pointer to command slot which will be written next
	size_t tail_ptr;                             ///< pointer to next free command slot
	spinlock_t flags_lock;                       ///< controls access to #DISK_BUSY flag in @e flags variable.
	                                             ///< This flag controls access to disk write operations either from
	                                             ///< the the driver itself or from the system.  Mutex is not used
	                                             ///< because this flag is accessed from interrupt context
	struct tasklet_struct bh;                    ///< command processing tasklet
	struct device *dev;                          ///< pointer to parent device structure
	struct rec_stat stat;                        ///< recording statistics

	struct timer_list cmd_timer;                 ///< command execution guard timer
	unsigned int cmd_timeout;                    ///< command timeout, in ms
	unsigned int io_error_flag;                  ///< flag indicating IO error was detected, this is flag is exported via sysfs

	struct datascope datascope;
	struct error_handler eh;                     ///< error handler data
};

#endif /* _AHCI_ELPHEL_EXT */