/*
 * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *  * Neither the name of The Linux Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef IPA_TABLE_H
#define IPA_TABLE_H

#include <stdint.h>
#include <stdbool.h>
#include <linux/msm_ipa.h>

#define IPA_TABLE_MAX_ENTRIES 5120

#define IPA_TABLE_INVALID_ENTRY 0x0

#undef  VALID_INDEX
#define VALID_INDEX(idx) \
	( (idx) != IPA_TABLE_INVALID_ENTRY )

#undef  VALID_RULE_HDL
#define VALID_RULE_HDL(hdl) \
	( (hdl) != IPA_TABLE_INVALID_ENTRY )

#undef GOTO_REC
#define GOTO_REC(tbl, rec_idx) \
	( (tbl)->table_addr + ((rec_idx) * (tbl)->entry_size) )

typedef enum
{
	IPA_NAT_BASE_TBL       = 0,
	IPA_NAT_EXPN_TBL       = 1,
	IPA_NAT_INDX_TBL       = 2,
	IPA_NAT_INDEX_EXPN_TBL = 3,
	IPA_IPV6CT_BASE_TBL    = 4,
	IPA_IPV6CT_EXPN_TBL    = 5,
} ipa_table_dma_type;

#define VALID_IPA_TABLE_DMA_TYPE(t) \
	( (t) >= IPA_NAT_BASE_TBL && (t) <= IPA_IPV6CT_EXPN_TBL )

/*
 *    --------- NAT Rule Handle Entry ID structure ---------
 *
 * +-----------+-----------+------------------+----------------+
 * |   1 bit   |  2 bits   |    12 bits       |     1 bit      |
 * +-----------+-----------+------------------+----------------+
 * | 0 - DDR   | reserved  | index into table |  0 - base      |
 * | 1 - SRAM  |           |                  |  1 - expansion |
 * +-----------+-----------+------------------+----------------+
 */
#define IPA_TABLE_TYPE_BITS      0x00000001
#define IPA_TABLE_TYPE_MASK      0x00000001
#define IPA_TABLE_INDX_MASK      0x00000FFF
#define IPA_TABLE_TYPE_MEM_SHIFT 15

#undef BREAK_RULE_HDL
#define BREAK_RULE_HDL(tbl, hdl, mt, iet, indx) \
	do { \
		mt    = ((hdl) >> IPA_TABLE_TYPE_MEM_SHIFT) & IPA_TABLE_TYPE_MASK; \
		iet   =  (hdl)                              & IPA_TABLE_TYPE_MASK; \
		indx  = ((hdl) >> IPA_TABLE_TYPE_BITS)      & IPA_TABLE_INDX_MASK; \
		indx += (iet) ? tbl->table_entries : 0; \
		/*IPADBG("hdl(%u) -> mt(%u) iet(%u) indx(%u)\n", hdl, mt, iet, indx);*/ \
	} while ( 0 )

typedef int (*entry_validity_checker)(
	void* entry);

typedef uint16_t (*entry_next_index_getter)(
	void* entry);

typedef uint16_t (*entry_prev_index_getter)(
	void*    entry,
	uint16_t entry_index,
	void*    meta,
	uint16_t base_table_size);

typedef void (*entry_prev_index_setter)(
	void*    entry,
	uint16_t entry_index,
	uint16_t prev_index,
	void*    meta,
	uint16_t base_table_size);

typedef int (*entry_head_inserter)(
	void*     entry,
	void*     user_data,
	uint16_t* dma_command_data);

typedef int (*entry_tail_inserter)(
	void* entry,
	void* user_data);

typedef uint16_t (*entry_delete_head_dma_command_data_getter)(
	void* head,
	void* next_entry);

typedef struct
{
	entry_validity_checker  entry_is_valid;
	entry_next_index_getter entry_get_next_index;
	entry_prev_index_getter entry_get_prev_index;
	entry_prev_index_setter entry_set_prev_index;
	entry_head_inserter     entry_head_insert;
	entry_tail_inserter     entry_tail_insert;
	entry_delete_head_dma_command_data_getter
	  entry_get_delete_head_dma_command_data;
} ipa_table_entry_interface;

typedef enum
{
	HELP_UPDATE_HEAD  = 0,
	HELP_UPDATE_ENTRY = 1,
	HELP_DELETE_HEAD  = 2,

	HELP_UPDATE_MAX,
} dma_help_type;

#undef VALID_DMA_HELP_TYPE
#define VALID_DMA_HELP_TYPE(t) \
	( (t) >=  HELP_UPDATE_HEAD && (t) < HELP_UPDATE_MAX )

typedef struct
{
	uint32_t           offset;
	ipa_table_dma_type table_type;
	ipa_table_dma_type expn_table_type;
	uint8_t            table_indx;
} ipa_table_dma_cmd_helper;

typedef struct
{
	char                       name[IPA_RESOURCE_NAME_MAX];

	enum ipa3_nat_mem_in       nmi;

	int                        entry_size;

	uint16_t                   table_entries;
	uint16_t                   expn_table_entries;
	uint32_t                   tot_tbl_ents;

	uint8_t*                   table_addr;
	uint8_t*                   expn_table_addr;

	uint16_t                   cur_tbl_cnt;
	uint16_t                   cur_expn_tbl_cnt;

	ipa_table_entry_interface* entry_interface;

	ipa_table_dma_cmd_helper*  dma_help[HELP_UPDATE_MAX];

	void*                      meta;
	int                        meta_entry_size;
} ipa_table;

typedef struct
{
	uint16_t prev_index;
	void*    prev_entry;

	uint16_t curr_index;
	void*    curr_entry;

	uint16_t next_index;
	void*    next_entry;
} ipa_table_iterator;


void ipa_table_init(
	ipa_table*                 table,
	const char*                table_name,
	enum ipa3_nat_mem_in       nmi,
	int                        entry_size,
	void*                      meta,
	int                        meta_entry_size,
	ipa_table_entry_interface* entry_interface);

int ipa_table_calculate_entries_num(
	ipa_table*           table,
	uint16_t             number_of_entries,
	enum ipa3_nat_mem_in nmi);

int ipa_table_calculate_size(
	ipa_table* table);

uint8_t* ipa_table_calculate_addresses(
	ipa_table* table,
	uint8_t*   base_addr);

void ipa_table_reset(
	ipa_table* table);

int ipa_table_add_entry(
	ipa_table*                  table,
	void*                       user_data,
	uint16_t*                   index,
	uint32_t*                   rule_hdl,
	struct ipa_ioc_nat_dma_cmd* cmd);

void ipa_table_create_delete_command(
	ipa_table*                  table,
	struct ipa_ioc_nat_dma_cmd* cmd,
	ipa_table_iterator*         iterator);

void ipa_table_delete_entry(
	ipa_table*          table,
	ipa_table_iterator* iterator,
	uint8_t             is_prev_empty);

void ipa_table_erase_entry(
	ipa_table* table,
	uint16_t   index);

int ipa_table_get_entry(
	ipa_table* table,
	uint32_t   entry_handle,
	void**     entry,
	uint16_t*  entry_index);

void* ipa_table_get_entry_by_index(
	ipa_table* table,
	uint16_t   index);

void ipa_table_dma_cmd_helper_init(
	ipa_table_dma_cmd_helper* dma_cmd_helper,
	uint8_t                   table_indx,
	ipa_table_dma_type        table_type,
	ipa_table_dma_type        expn_table_type,
	uint32_t                  offset);

void ipa_table_dma_cmd_generate(
	ipa_table_dma_cmd_helper*   dma_cmd_helper,
	uint8_t                     is_expn,
	uint32_t                    entry_offset,
	uint16_t                    data,
	struct ipa_ioc_nat_dma_cmd* cmd);

int ipa_table_iterator_init(
	ipa_table_iterator* iterator,
	ipa_table*          table,
	void*               curr_entry,
	uint16_t            curr_index);

int ipa_table_iterator_next(
	ipa_table_iterator* iterator,
	ipa_table*          table);

int ipa_table_iterator_end(
	ipa_table_iterator* iterator,
	ipa_table*          table,
	uint16_t            head_index,
	void*               head);

int ipa_table_iterator_is_head_with_tail(
	ipa_table_iterator* iterator);

int ipa_calc_num_sram_table_entries(
	uint32_t  sram_size,
	uint32_t  table1_ent_size,
	uint32_t  table2_ent_size,
	uint16_t* num_entries_ptr);

typedef int (*ipa_table_walk_cb)(
	ipa_table*      table_ptr,
	uint32_t        rule_hdl,
	void*           record_ptr,
	uint16_t        record_index,
	void*           meta_record_ptr,
	uint16_t        meta_record_index,
	void*           arb_data_ptr );

typedef enum
{
	WHEN_SLOT_EMPTY  = 0,
	WHEN_SLOT_FILLED = 1,

	WHEN_SLOT_MAX
} When2Callback;

#define VALID_WHEN2CALLBACK(w) \
	( (w) >= WHEN_SLOT_EMPTY && (w) < WHEN_SLOT_MAX )

int ipa_table_walk(
	ipa_table*        table,
	uint16_t          start_index,
	When2Callback     when,
	ipa_table_walk_cb walk_cb,
	void*             arb_data_ptr );

int ipa_table_add_dma_cmd(
	ipa_table*                  tbl_ptr,
	dma_help_type               help_type,
	void*                       rec_ptr,
	uint16_t                    rec_index,
	uint16_t                    data_for_entry,
	struct ipa_ioc_nat_dma_cmd* cmd_ptr );

#endif