/* SPDX-License-Identifier: GPL-2.0-only
 * This file is part of the CAN-CTRL IP Core driver for Linux
 *
 * Copyright (C) Fraunhofer-Institut for Photonic Microsystems (IPMS)
 * Maria-Reiche-Str. 2
 * 01109 Dresden
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 *
 * @file can_lib.h
 * @author bog, sep, svt
 * @date 14/05/2024
 * @brief CAN-CTRL driver functions
 */

#ifndef CAN_LIB_H
#define CAN_LIB_H

#include <linux/types.h>
#include "can_frame.h"
#include "can_linux_registers.h"

#define CAN_MAX_FRAME_WORDLEN \
	(2u) /**< maximum frame length of CAN classic frame in words */
#define CAN_MAX_FD_FRAME_WORDLEN \
	(16u) /**< maximum frame length of CAN FD frame in words */
#define CAN_MAX_XL_FRAME_WORDLEN \
	(512u) /**< maximum frame length of CAN XL frame in words */

#define CAN_MAX_FRAME_BYTELEN    \
	(CAN_MAX_FRAME_WORDLEN * \
	 (sizeof(uint32_t) /     \
	  sizeof(uint8_t))) /**< maximum frame length of CAN classic frame in Byte */
#define CAN_MAX_FD_FRAME_BYTELEN    \
	(CAN_MAX_FD_FRAME_WORDLEN * \
	 (sizeof(uint32_t) /        \
	  sizeof(uint8_t))) /**< maximum frame length of CAN FD frame in Byte */
#define CAN_MAX_XL_FRAME_BYTELEN    \
	(CAN_MAX_XL_FRAME_WORDLEN * \
	 (sizeof(uint32_t) /        \
	  sizeof(uint8_t))) /**< maximum frame length of CAN XL frame in Byte */
/**
 * @brief CAN driver error codes
 *
 */
typedef enum {
	CAN_NO_ERROR = 0, /**< no error */
	CAN_ERR_PARAMETER = 1, /**< parameter error in function call */
	CAN_ERR_RESETSTATE = 2, /**< invalid reset state for this function */
	CAN_ERR_NO_DATA = 3, /**< no receive data available */
	CAN_ERR_HW_LIMIT = 4, /**< hardware limit is reached */
	CAN_ERR_MEM_LIMIT =
		5, /**< memory limit on receive reached - data loss occured */
	CAN_ERR_TBUF_FULL =
		6, /**< no empty STB-slots - transmission cancelled  */
} can_linux_error_code_t;

/**
 * @brief CAN driver interrupt flags
 *
 * @note CAN_INTERRUPT_MASK defines a mask for all interrupt flags, CAN_INTERRUPT_SET_MASK defines a mask for all interrupt flags that can
 * enabled/disabled
 *
 */
typedef enum {
	CAN_INTERRUPT_ABORT = (1u << 0), /**< frame abort done */
	CAN_INTERRUPT_ERROR = (1u << 1), /**< error indication */
	CAN_INTERRUPT_TRANS_SECOND =
		(1u << 2), /**< transmission of secondary buffer completed */
	CAN_INTERRUPT_TRANS_PRIMARY =
		(1u << 3), /**< transmission of primary buffer completed */
	CAN_INTERRUPT_RECV_ALMOST_FULL =
		(1u << 4), /**< receive buffer almost full */
	CAN_INTERRUPT_RECV_FULL = (1u << 5), /**< receive buffer full */
	CAN_INTERRUPT_RECV_OVERFLOW = (1u << 6), /**< receive buffer overflow */
	CAN_INTERRUPT_RECV = (1u << 7), /**< received frame */
	CAN_INTERRUPT_BUS_ERROR = (1u << 8), /**< bus error */
	CAN_INTERRUPT_ARBITRATION_LOST = (1u << 9), /**< arbitration lost */
	CAN_INTERRUPT_ERROR_PASSIV = (1u << 10), /**< error passive */
	CAN_INTERRUPT_TIME_TRIGGER = (1u << 11), /**< time trigger */
	CAN_INTERRUPT_TRIGGER_ERROR = (1u << 12), /**< trigger error */
	CAN_INTERRUPT_WATCH_TRIGGER = (1u << 13), /**< watch trigger */
	CAN_INTERRUPT_ECC_DATA_WARNING =
		(1u << 14), /**< memory protection data warning */
	CAN_INTERRUPT_ECC_DATA_ERROR =
		(1u << 15), /**< memory protection data error */
	CAN_INTERRUPT_ECC_ADDR_ERROR =
		(1u << 16), /**< memory protection address error */
	CAN_INTERRUPT_SAFETY_ERROR = (1u << 17), /**< safety error */
	CAN_INTERRUPT_SAFETY_WARNING = (1u << 18), /**< safety warning */

	/** mask for all interrupt flags */
	CAN_INTERRUPT_MASK = ((CAN_INTERRUPT_SAFETY_WARNING << 1) - 1u),

	/** mask for interrupt flags that can be enabled/disabled */
	CAN_INTERRUPT_SET_MASK =
		(CAN_INTERRUPT_MASK &
		 ~(CAN_INTERRUPT_ABORT | CAN_INTERRUPT_TRIGGER_ERROR |
		   CAN_INTERRUPT_ECC_DATA_ERROR | CAN_INTERRUPT_ECC_ADDR_ERROR |
		   CAN_INTERRUPT_SAFETY_ERROR)),
} can_interrupt_t;

/*!
 * @brief CAN driver Values for TSSTAT-Register (Transmit Secondary Status)
 *
 */
typedef enum {
	TSSTAT_STB_empty = 0, /**< Secondary Transmit Buffer empty*/
	TSSTAT_STB_halfempty =
		1, /**< Secondary Transmit Buffer less than or half full*/
	TSSTAT_STB_halffull =
		2, /**< Secondary Transmit Buffer more than half full*/
	TSSTAT_STB_full = 3, /**< Secondary Transmit Buffer full*/
} can_tsstat_t;

/**
 * @brief CAN driver frame types
 *
 */
typedef enum {
	CAN_FRAME_CLASSIC, /**< CAN classic frame*/
	CAN_FRAME_FD, /**< CAN FD frame */
	CAN_FRAME_XL, /**< CAN XL frame */
} can_frame_type_t;

/**
 * @brief CAN driver transmission buffer selection
 *
 */
typedef enum {
	TBUF_SEL_PTB, /**< primary (priority) buffer */
	TBUF_SEL_STB, /**< secondary buffer */
} can_tbuf_sel_t;

/**
 * @brief CAN driver transmission buffer trigger mode
 *
 */
typedef enum {
	TBUF_TRIGGER_PTB_ONE, /**< transmit frame from the PTB */
	TBUF_TRIGGER_STB_ONE, /**< transmit one frame from the STB */
	TBUF_TRIGGER_STB_ALL, /**< transmit all frames from the STB */
} can_tbuf_trigger_t;

/**
 * @brief CAN driver loopback mode
 *
 */
typedef enum {
	CAN_LOOPBACK_DISABLE, /**< disable loopback mode */
	CAN_LOOPBACK_EXTERNAL, /**< external loopback mode */
	CAN_LOOPBACK_INTERNAL, /**< internal loopback mode */
} can_loopback_mode_t;

/**
 * @brief CAN driver bittiming types
 *
 */
typedef enum {
	CAN_BITTIMING_AC, /**< arbitration phase and classic CAN */
	CAN_BITTIMING_FD, /**< CAN FD data phase */
	CAN_BITTIMING_XL, /**< CAN XL data phase */
} can_bittiming_type_t;

/*!
 * @brief CAN driver autoactions on transmit
 *
 */
typedef enum {
	CAN_AUTOACTION_NONE, /**< no action on transmit - separate trigger necessary */
	CAN_AUTOACTION_SECONDARY_NEXT, /**< autoselect next slot in STB - separate trigger necessary */
	CAN_AUTOACTION_PRIMARY_SEND, /**< autosend the transmitted frame from PTB */
} can_autoaction_type_t;

/**
 * @brief CAN driver bittiming structure
 *
 */
typedef struct {
	uint32_t prop_seg; /**< propagation segment in TQs */
	uint32_t phase_seg1; /**< phase segment 1 in TQs */
	uint32_t phase_seg2; /**< phase segment 2 in TQs */
	uint32_t sjw; /**< synchronization jump width */
	bool tdc_enable; /**< transmitter delay compensation: false - disabled, only used for CAN_BITTIMING_FD and CAN_BITTIMING_XL */
	uint8_t tdc; /**< optional: specify transmitter delay compensation, otherwise it's calculated as specified in DES; in any case tdc_enable must be true */
	uint32_t prescaler; /**< bitrate prescaler, only set for CAN_BITTIMING_AC */
	uint32_t cclk; /**< base clock for the IP Core */
} can_bittiming_t;

/**
 * @brief CAN driver acceptance filter structure
 *
 */
typedef struct {
	uint32_t id; /**< CAN frame identifier */
	bool use_ext_id; /**< CAN frame extended identifier: 0 - 11 bit, otherwise 29 bit */
} can_acceptance_filter_t;

/**
 * @brief CAN driver PWM structure
 *
 */
typedef struct {
	uint32_t PWMO; /**< PWM offset */
	uint32_t PWMS; /**< PWM short phase */
	uint32_t PWML; /**< PWM long phase */
} can_pwm_t;

/**
 * @brief activate/deactivate the reset state of the CAN core
 *
 * @param can       pointer to CAN core register map
 * @param active    activate reset: false - deactivate, true activate
 * @return can_linux_error_code_t
 *
 * @note activate the reset result in reset if some registers
 * @note see CAN-CTRL Design Specification for additional details
 */
can_linux_error_code_t can_reset(volatile CAN_CTRL_t *can, bool active);

/**
 * @brief start the transmit of classic CAN frame
 *
 * @param can           pointer to CAN core register map
 * @param frame         pointer to structure of classic CAN frame
 * @param data          pointer to transmit data buffer, must be of length can_frame_info_t.len
 * @param auto_action   determines the action after filling the Transmit Buffer from can_autoaction_type_t
 * @param handle        unique identifier for the transmission request
 * @return can_linux_error_code_t
 *
 * @note deactivate auto_trigger if additional CAN-SEC sequence is used
 */
can_linux_error_code_t can_transmit(volatile CAN_CTRL_t *can,
				    can_frame_info_t *frame, uint32_t *data,
				    can_autoaction_type_t auto_action, uint32_t handle);

/**
 * @brief start the transmit of CAN FD frame
 *
 * @param can           pointer to CAN core register map
 * @param frame         pointer to structure of CAN FD frame
 * @param data          pointer to transmit data buffer, must be of length can_fd_frame_info_t.len
 * @param auto_action   determines the action after filling the Transmit Buffer from can_autoaction_type_t
 * @param handle        unique identifier for the transmission request
 * @return can_linux_error_code_t
 *
 * @note deactivate auto_trigger if additional CAN-SEC sequence is used
 */
can_linux_error_code_t can_fd_transmit(volatile CAN_CTRL_t *can,
				       can_fd_frame_info_t *frame,
				       uint32_t *data,
				       can_autoaction_type_t auto_action,
					   uint32_t handle);

/**
 * @brief start the transmit of CAN XL frame
 *
 * @param can           pointer to CAN core register map
 * @param frame         pointer to structure of CAN XL frame
 * @param data          pointer to transmit data buffer, must be of length can_xl_frame_info_t.len
 * @param auto_action   determines the action after filling the Transmit Buffer from can_autoaction_type_t
 * @param handle        unique identifier for the transmission request
 * @return can_linux_error_code_t
 *
 * @note deactivate auto_trigger if additional CAN-SEC sequence is used
 */
can_linux_error_code_t can_xl_transmit(volatile CAN_CTRL_t *can,
				       can_xl_frame_info_t *frame,
				       uint32_t *data,
				       can_autoaction_type_t auto_action,
					   uint32_t handle);

/**
 * @brief manually trigger the transmit of CAN frame
 *
 * @param can       pointer to CAN core register map
 * @param trigger   trigger mode from can_tbuf_trigger_t
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_transmit_buffer_trigger(volatile CAN_CTRL_t *can,
						   can_tbuf_trigger_t trigger);

/**
 * @brief select the transmission buffer (primary/secondary)
 *
 * @param can       pointer to CAN core register map
 * @param tbsel     transmission buffer selection from can_tbuf_sel_t
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_transmit_buffer_select(volatile CAN_CTRL_t *can,
						  can_tbuf_sel_t tbsel);

/**
 * @brief set the loopback mode
 *
 * @param can   pointer to CAN core register map
 * @param mode  loopback mode from can_loopback_mode_t
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_loopback_set(volatile CAN_CTRL_t *can,
					can_loopback_mode_t mode);

/**
 * @brief set the bittiming for CAN protocol
 *
 * @param can       pointer to CAN core register map
 * @param type      bittiming type from can_bittiming_type_t
 * @param timing    pointer to bittiming from can_bittiming_t
 * @return can_linux_error_code_t
 *
 * @note requires CAN core reset active
 */
can_linux_error_code_t can_bittiming_set(volatile CAN_CTRL_t *can,
					 can_bittiming_type_t type,
					 can_bittiming_t *timing);

/**
 * @brief read the current interrupt flags
 *
 * @param can   pointer to CAN core register map
 * @param interrupt_flags   pointer to resulting mask of all flags from can_interrupt_t
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_interrupt_get(volatile CAN_CTRL_t *can,
					 uint32_t *interrupt_flags);

/**
 * @brief clear interrupt flags
 *
 * @param can   pointer to CAN core register map
 * @param mask  mask of flags to clear from can_interrupt_t
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_interrupt_clear(volatile CAN_CTRL_t *can,
					   uint32_t mask);

/**
 * @brief enable interrupt
 *
 * @param can   pointer to CAN core register map
 * @param mask  mask of interrupts to enable from can_interrupt_t
 * @return can_linux_error_code_t
 *
 * @note only flags in CAN_INTERRUPT_SET_MASK are allowed
 */
can_linux_error_code_t can_interrupt_enable(volatile CAN_CTRL_t *can,
					    uint32_t mask);

/**
 * @brief disable interrupt
 *
 * @param can   pointer to CAN core register map
 * @param mask  mask of interrupts to disable from can_interrupt_t
 * @return can_linux_error_code_t
 *
 * @note only flags in CAN_INTERRUPT_SET_MASK are allowed
 */
can_linux_error_code_t can_interrupt_disable(volatile CAN_CTRL_t *can,
					     uint32_t mask);

/**
 * @brief determine frame type from receive buffer
 *
 * This function is used to determine the current frame type in the receive buffer. The application can than allocate a sufficient buffer
 * for the frame. Afterwards the frame data can read with the functions can_receive_data, can_fd_receive_data or can_xl_receive_data.
 *
 * @param can   pointer to CAN core register map
 * @param type  pointer to frame type, return the frame type of the current frame in receive buffer
 * @return can_linux_error_code_t CAN_ERR_NO_DATA if no data available, otherwise CAN_NO_ERROR
 *
 * @note frame type is only valid, if function return with CAN_NO_ERROR
 */
can_linux_error_code_t can_determine_rbuf_frame_type(volatile CAN_CTRL_t *can,
						     can_frame_type_t *type);

/**
 * @brief receive classic CAN frame
 *
 * @param can   pointer to CAN core register map
 * @param frame pointer to structure of classic CAN frame to return frame information
 * @param data  pointer to receive data buffer
 * @param buf_len   maximum size of data buffer in byte
 * @return can_linux_error_code_t
 * @note data and sizeof(data) must be word-aligned. messages longer then buf_len will be truncated
 */
can_linux_error_code_t can_receive_data(volatile CAN_CTRL_t *can,
					can_frame_info_t *frame, uint32_t *data,
					uint8_t buf_len);

/**
 * @brief receive CAN FD frame
 *
 * @param can   pointer to CAN core register map
 * @param frame pointer to structure of CAN FD frame to return frame information
 * @param data  pointer to receive data buffer
 * @param buf_len   maximum size of data buffer in byte
 * @return can_linux_error_code_t
 * @note data and sizeof(data) must be word-aligned. messages longer then buf_len will be truncated
 */
can_linux_error_code_t can_fd_receive_data(volatile CAN_CTRL_t *can,
					   can_fd_frame_info_t *frame,
					   uint32_t *data, uint8_t buf_len);

/**
 * @brief receive CAN XL frame
 *
 * @param can   pointer to CAN core register map
 * @param frame pointer to structure of CAN XL frame to return frame information
 * @param data  pointer to receive data buffer
 * @param buf_len   maximum size of data buffer in byte
 * @return can_linux_error_code_t
 * @note data and sizeof(data) must be word-aligned. messages longer then buf_len will be truncated
 */
can_linux_error_code_t can_xl_receive_data(volatile CAN_CTRL_t *can,
					   can_xl_frame_info_t *frame,
					   uint32_t *data, uint32_t buf_len);

/**
 * @brief finish the receive frame, switch internally to next receive buffer slot
 *
 * @param can   pointer to CAN core register map
 */
void can_receive_finish(volatile CAN_CTRL_t *can);

/**
 * @brief set acceptance filter settings
 *
 * @param can   pointer to CAN core register map
 * @param idx   index of acceptance filter (0..15)
 * @param code  pointer to acceptance filter code structure, defines compare value for filter
 * @param mask  pointer to acceptance filter mask structure, defines bit mask to compare for filter: 0 - don't care
 * @return can_linux_error_code_t
 *
 * @note require CAN core reset active
 * @note filter idx=0 is default and has all mask bits set to 0 after power-on reset
 */
can_linux_error_code_t can_acceptance_filter_set(volatile CAN_CTRL_t *can,
						 uint8_t idx,
						 can_acceptance_filter_t *code,
						 can_acceptance_filter_t *mask);

/**
 * @brief enable/disable acceptance filter
 *
 * @param can       pointer to CAN core register map
 * @param idx       index of acceptance filter (0..15)
 * @param enable    false - disable, true enable
 * @return can_linux_error_code_t
 *
 * @note require CAN core reset active
 * @note filter idx=0 is default and enabled after power-on reset
 */
can_linux_error_code_t can_acceptance_filter_enable(volatile CAN_CTRL_t *can,
						    uint8_t idx, bool enable);

/**
 * @brief enable/disable transceiver mode switching for CAN XL transceiver
 *
 * @param can       pointer to CAN core register map
 * @param enable    false - disable, true enable
 * @return can_linux_error_code_t
 *
 * @note require CAN core reset active - enable automatically disables error signaling
 */
can_linux_error_code_t
can_transceiver_mode_switching_enable(volatile CAN_CTRL_t *can, bool enable);

/**
 * @brief enable/disable transceiver error signaling for CAN XL transceiver
 *
 * @param can       pointer to CAN core register map
 * @param enable    false - disable, true enable
 * @return can_linux_error_code_t
 *
 * @note require CAN core reset active - will not enable if mode switching enabled
 */
can_linux_error_code_t
can_transceiver_error_signaling_enable(volatile CAN_CTRL_t *can, bool enable);

/*!
 * @brief get the rolling counter for transmitted CAN frames
 *
 * @param can   pointer to CAN core register map
 * @param LLC_Counter pointer to counter value
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_get_HANDLEcnt(volatile CAN_CTRL_t *can,
					 uint8_t *LLC_Counter);

/*!
 * @brief select the next slot in the transmit buffer
 *
 * @param can    pointer to CAN core register map
 * @return can_linux_error_code_t
 */
can_linux_error_code_t can_select_next_tbuf_slot(volatile CAN_CTRL_t *can);

#endif /* CAN_LIB_H */