/*
 * Copyright (c) 2021 IoT.bzh
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT renesas_rcar_can

#include <zephyr/kernel.h>
#include <errno.h>
#include <zephyr/drivers/can.h>
#include <zephyr/drivers/can/transceiver.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/renesas_cpg_mssr.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>

LOG_MODULE_REGISTER(can_rcar, CONFIG_CAN_LOG_LEVEL);

/* Control Register */
#define RCAR_CAN_CTLR             0x0840
/* Control Register bits */
#define RCAR_CAN_CTLR_BOM         (3 << 11)     /* Bus-Off Recovery Mode Bits */
#define RCAR_CAN_CTLR_BOM_ENT     BIT(11)       /* Automatic halt mode entry at bus-off entry */
#define RCAR_CAN_CTLR_SLPM        BIT(10)
#define RCAR_CAN_CTLR_CANM_HALT   BIT(9)
#define RCAR_CAN_CTLR_CANM_RESET  BIT(8)
#define RCAR_CAN_CTLR_CANM_MASK   (3 << 8)
#define RCAR_CAN_CTLR_MLM         BIT(3)        /* Message Lost Mode Select */
#define RCAR_CAN_CTLR_IDFM        (3 << 1)      /* ID Format Mode Select Bits */
#define RCAR_CAN_CTLR_IDFM_MIXED  BIT(2)        /* Mixed ID mode */
#define RCAR_CAN_CTLR_MBM         BIT(0)        /* Mailbox Mode select */

/* Mask Register */
#define RCAR_CAN_MKR0             0x0430
#define RCAR_CAN_MKR1             0x0434
#define RCAR_CAN_MKR2             0x0400
#define RCAR_CAN_MKR3             0x0404
#define RCAR_CAN_MKR4             0x0408
#define RCAR_CAN_MKR5             0x040C
#define RCAR_CAN_MKR6             0x0410
#define RCAR_CAN_MKR7             0x0414
#define RCAR_CAN_MKR8             0x0418
#define RCAR_CAN_MKR9             0x041C

/* FIFO Received ID Compare Register 0 */
#define RCAR_CAN_FIDCR0           0x0420

/* FIFO Received ID Compare Register 1 */
#define RCAR_CAN_FIDCR1           0x0424

/* FIFO Received ID Compare Registers 0 and 1 bits */
#define RCAR_CAN_FIDCR_IDE        BIT(31)       /* ID Extension Bit */
#define RCAR_CAN_FIDCR_RTR        BIT(30)       /* RTR Bit */

/* Mask Invalid Register 0 */
#define RCAR_CAN_MKIVLR0          0x0438
/* Mask Invalid Register 1 */
#define RCAR_CAN_MKIVLR1          0x0428
/* Mailbox Interrupt Enable Registers*/
#define RCAR_CAN_MIER0            0x043C
#define RCAR_CAN_MIER1            0x042C
#define RCAR_CAN_MIER1_RXFIE      BIT(28)       /* Rx FIFO Interrupt Enable */
#define RCAR_CAN_MIER1_TXFIE      BIT(24)       /* Tx FIFO Interrupt Enable */

#define RCAR_CAN_STR              0x0842        /* Status Register */
#define RCAR_CAN_STR_RSTST        BIT(8)        /* Reset Status Bit */
#define RCAR_CAN_STR_HLTST        BIT(9)        /* Halt Status Bit */
#define RCAR_CAN_STR_SLPST        BIT(10)       /* Sleep Status Bit */
#define MAX_STR_READS             0x100

/* Bit Configuration Register */
#define RCAR_CAN_BCR              0x0844

/* Clock Select Register */
#define RCAR_CAN_CLKR             0x0847
#define RCAR_CAN_CLKR_EXT_CLOCK   0x3         /* External input clock */
#define RCAR_CAN_CLKR_CLKP2       0x1
#define RCAR_CAN_CLKR_CLKP1       0x0

/* Error Interrupt Enable Register */
#define RCAR_CAN_EIER             0x084C

/* Interrupt Enable Register */
#define RCAR_CAN_IER              0x0860
#define RCAR_CAN_IER_ERSIE        BIT(5)        /* Error Interrupt Enable Bit */
#define RCAR_CAN_IER_RXFIE        BIT(4)        /* Rx FIFO Interrupt Enable Bit */
#define RCAR_CAN_IER_TXFIE        BIT(3)        /* Tx FIFO Interrupt Enable Bit */

/* Interrupt Status Register */
#define RCAR_CAN_ISR              0x0861
#define RCAR_CAN_ISR_ERSF         BIT(5)        /* Error (ERS) Interrupt */
#define RCAR_CAN_ISR_RXFF         BIT(4)        /* Reception FIFO Interrupt */
#define RCAR_CAN_ISR_TXFF         BIT(3)        /* Transmission FIFO Interrupt */

/* Receive FIFO Control Register */
#define RCAR_CAN_RFCR             0x0848
#define RCAR_CAN_RFCR_RFE         BIT(0)        /* Receive FIFO Enable */
#define RCAR_CAN_RFCR_RFEST       BIT(7)        /* Receive FIFO Empty Flag */

/* Receive FIFO Pointer Control Register */
#define RCAR_CAN_RFPCR            0x0849

/* Transmit FIFO Control Register */
#define RCAR_CAN_TFCR             0x084A
#define RCAR_CAN_TFCR_TFE         BIT(0)        /* Transmit FIFO Enable */
#define RCAR_CAN_TFCR_TFUST       (7 << 1)      /* Transmit FIFO Unsent Msg Number Status Bits */
#define RCAR_CAN_TFCR_TFUST_SHIFT 1             /* Offset of Tx FIFO Unsent */

/* Transmit FIFO Pointer Control Register */
#define RCAR_CAN_TFPCR            0x084B

/* Error Code Store Register*/
#define RCAR_CAN_ECSR             0x0850        /* Error Code Store Register */
#define RCAR_CAN_ECSR_EDPM        BIT(7)        /* Error Display Mode Select */
#define RCAR_CAN_ECSR_ADEF        BIT(6)        /* ACK Delimiter Error Flag */
#define RCAR_CAN_ECSR_BE0F        BIT(5)        /* Bit Error (dominant) Flag */
#define RCAR_CAN_ECSR_BE1F        BIT(4)        /* Bit Error (recessive) Flag */
#define RCAR_CAN_ECSR_CEF         BIT(3)        /* CRC Error Flag */
#define RCAR_CAN_ECSR_AEF         BIT(2)        /* ACK Error Flag */
#define RCAR_CAN_ECSR_FEF         BIT(1)        /* Form Error Flag */
#define RCAR_CAN_ECSR_SEF         BIT(0)        /* Stuff Error Flag */

/* Test Control Register */
#define RCAR_CAN_TCR              0x0858
#define RCAR_CAN_TCR_TSTE         BIT(0)        /* Test Mode Enable Bit*/
#define RCAR_CAN_TCR_LISTEN_ONLY  BIT(1)
#define RCAR_CAN_TCR_INT_LOOP     (3 << 1)      /* Internal loopback*/

/* Error Interrupt Factor Judge Register bits */
#define RCAR_CAN_EIFR             0x084D
#define RCAR_CAN_EIFR_BLIF        BIT(7)        /* Bus Lock Detect Flag */
#define RCAR_CAN_EIFR_OLIF        BIT(6)        /* Overload Frame Transmission */
#define RCAR_CAN_EIFR_ORIF        BIT(5)        /* Receive Overrun Detect Flag */
#define RCAR_CAN_EIFR_BORIF       BIT(4)        /* Bus-Off Recovery Detect Flag */
#define RCAR_CAN_EIFR_BOEIF       BIT(3)        /* Bus-Off Entry Detect Flag */
#define RCAR_CAN_EIFR_EPIF        BIT(2)        /* Error Passive Detect Flag */
#define RCAR_CAN_EIFR_EWIF        BIT(1)        /* Error Warning Detect Flag */
#define RCAR_CAN_EIFR_BEIF        BIT(0)        /* Bus Error Detect Flag */

/* Receive Error Count Register */
#define RCAR_CAN_RECR             0x084D
/* Transmit Error Count Register */
#define RCAR_CAN_TECR             0x084F

/* Mailbox configuration:
 * mailbox 60 - 63 - Rx FIFO mailboxes
 * mailbox 56 - 59 - Tx FIFO mailboxes
 * non-FIFO mailboxes are not used
 */
#define RCAR_CAN_MB_56            0x0380
#define RCAR_CAN_MB_60            0x03C0
/* DLC must be accessed as a 16 bit register */
#define RCAR_CAN_MB_DLC_OFFSET    0x4           /* Data length code */
#define RCAR_CAN_MB_DATA_OFFSET   0x6           /* Data section */
#define RCAR_CAN_MB_TSH_OFFSET    0x14          /* Timestamp upper byte */
#define RCAR_CAN_MB_TSL_OFFSET    0x15          /* Timestamp lower byte */
#define RCAR_CAN_FIFO_DEPTH       4
#define RCAR_CAN_MB_SID_SHIFT     18
#define RCAR_CAN_MB_RTR           BIT(30)
#define RCAR_CAN_MB_IDE           BIT(31)
#define RCAR_CAN_MB_SID_MASK      0x1FFC0000
#define RCAR_CAN_MB_EID_MASK      0x1FFFFFFF

typedef void (*init_func_t)(const struct device *dev);

struct can_rcar_cfg {
	const struct can_driver_config common;
	uint32_t reg_addr;
	int reg_size;
	init_func_t init_func;
	const struct device *clock_dev;
	struct rcar_cpg_clk mod_clk;
	struct rcar_cpg_clk bus_clk;
	const struct pinctrl_dev_config *pcfg;
};

struct can_rcar_tx_cb {
	can_tx_callback_t cb;
	void *cb_arg;
};

struct can_rcar_data {
	struct can_driver_data common;
	struct k_mutex inst_mutex;
	struct k_sem tx_sem;
	struct can_rcar_tx_cb tx_cb[RCAR_CAN_FIFO_DEPTH];
	uint8_t tx_head;
	uint8_t tx_tail;
	uint8_t tx_unsent;
	struct k_mutex rx_mutex;
	can_rx_callback_t rx_callback[CONFIG_CAN_RCAR_MAX_FILTERS];
	void *rx_callback_arg[CONFIG_CAN_RCAR_MAX_FILTERS];
	struct can_filter filter[CONFIG_CAN_RCAR_MAX_FILTERS];
	enum can_state state;
};

static inline uint16_t can_rcar_read16(const struct can_rcar_cfg *config,
				       uint32_t offs)
{
	return sys_read16(config->reg_addr + offs);
}

static inline void can_rcar_write16(const struct can_rcar_cfg *config,
				    uint32_t offs, uint16_t value)
{
	sys_write16(value, config->reg_addr + offs);
}

static void can_rcar_tx_done(const struct device *dev, uint8_t err)
{
	struct can_rcar_data *data = dev->data;
	struct can_rcar_tx_cb *tx_cb;

	tx_cb = &data->tx_cb[data->tx_tail];
	data->tx_tail++;
	if (data->tx_tail >= RCAR_CAN_FIFO_DEPTH) {
		data->tx_tail = 0;
	}

	data->tx_unsent--;
	tx_cb->cb(dev, err, tx_cb->cb_arg);
	k_sem_give(&data->tx_sem);
}

static void can_rcar_get_error_count(const struct can_rcar_cfg *config,
				     struct can_bus_err_cnt *err_cnt)
{
	err_cnt->tx_err_cnt = sys_read8(config->reg_addr + RCAR_CAN_TECR);
	err_cnt->rx_err_cnt = sys_read8(config->reg_addr + RCAR_CAN_RECR);
}

static void can_rcar_state_change(const struct device *dev, uint32_t newstate)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	const can_state_change_callback_t cb = data->common.state_change_cb;
	void *state_change_cb_data = data->common.state_change_cb_user_data;
	struct can_bus_err_cnt err_cnt;

	if (data->state == newstate) {
		return;
	}

	LOG_DBG("Can state change new: %u old:%u\n", newstate, data->state);

	data->state = newstate;

	if (cb == NULL) {
		return;
	}
	can_rcar_get_error_count(config, &err_cnt);
	cb(dev, newstate, err_cnt, state_change_cb_data);
}

static void can_rcar_error(const struct device *dev)
{
	const struct can_rcar_cfg *config = dev->config;
	uint8_t eifr, ecsr;

	eifr = sys_read8(config->reg_addr + RCAR_CAN_EIFR);

	if (eifr & RCAR_CAN_EIFR_BEIF) {

		ecsr = sys_read8(config->reg_addr + RCAR_CAN_ECSR);
		if (ecsr & RCAR_CAN_ECSR_ADEF) {
			CAN_STATS_ACK_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_ADEF,
				   config->reg_addr + RCAR_CAN_ECSR);
		}
		if (ecsr & RCAR_CAN_ECSR_BE0F) {
			CAN_STATS_BIT0_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_BE0F,
				   config->reg_addr + RCAR_CAN_ECSR);
		}
		if (ecsr & RCAR_CAN_ECSR_BE1F) {
			CAN_STATS_BIT1_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_BE1F,
				   config->reg_addr + RCAR_CAN_ECSR);
		}
		if (ecsr & RCAR_CAN_ECSR_CEF) {
			CAN_STATS_CRC_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_CEF,
				   config->reg_addr + RCAR_CAN_ECSR);
		}
		if (ecsr & RCAR_CAN_ECSR_AEF) {
			CAN_STATS_ACK_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_AEF,
				   config->reg_addr + RCAR_CAN_ECSR);
		}
		if (ecsr & RCAR_CAN_ECSR_FEF) {
			CAN_STATS_FORM_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_FEF,
				   config->reg_addr + RCAR_CAN_ECSR);
		}
		if (ecsr & RCAR_CAN_ECSR_SEF) {
			CAN_STATS_STUFF_ERROR_INC(dev);
			sys_write8((uint8_t)~RCAR_CAN_ECSR_SEF,
				   config->reg_addr + RCAR_CAN_ECSR);
		}

		sys_write8((uint8_t)~RCAR_CAN_EIFR_BEIF,
			   config->reg_addr + RCAR_CAN_EIFR);
	}
	if (eifr & RCAR_CAN_EIFR_EWIF) {
		LOG_DBG("Error warning interrupt\n");
		/* Clear interrupt condition */
		sys_write8((uint8_t)~RCAR_CAN_EIFR_EWIF,
			   config->reg_addr + RCAR_CAN_EIFR);
		can_rcar_state_change(dev, CAN_STATE_ERROR_WARNING);
	}
	if (eifr & RCAR_CAN_EIFR_EPIF) {
		LOG_DBG("Error passive interrupt\n");
		/* Clear interrupt condition */
		sys_write8((uint8_t)~RCAR_CAN_EIFR_EPIF,
			   config->reg_addr + RCAR_CAN_EIFR);
		can_rcar_state_change(dev, CAN_STATE_ERROR_PASSIVE);
	}
	if (eifr & RCAR_CAN_EIFR_BORIF) {
		LOG_DBG("Bus-off recovery interrupt\n");
		sys_write8(RCAR_CAN_IER_ERSIE, config->reg_addr + RCAR_CAN_IER);
		/* Clear interrupt condition */
		sys_write8((uint8_t)~RCAR_CAN_EIFR_BORIF,
			   config->reg_addr + RCAR_CAN_EIFR);
		can_rcar_state_change(dev, CAN_STATE_BUS_OFF);
	}
	if (eifr & RCAR_CAN_EIFR_BOEIF) {
		LOG_DBG("Bus-off entry interrupt\n");
		sys_write8(RCAR_CAN_IER_ERSIE, config->reg_addr + RCAR_CAN_IER);
		/* Clear interrupt condition */
		sys_write8((uint8_t)~RCAR_CAN_EIFR_BOEIF,
			   config->reg_addr + RCAR_CAN_EIFR);
		can_rcar_state_change(dev, CAN_STATE_BUS_OFF);
	}
	if (eifr & RCAR_CAN_EIFR_ORIF) {
		LOG_DBG("Receive overrun error interrupt\n");
		CAN_STATS_RX_OVERRUN_INC(dev);
		sys_write8((uint8_t)~RCAR_CAN_EIFR_ORIF,
			   config->reg_addr + RCAR_CAN_EIFR);
	}
	if (eifr & RCAR_CAN_EIFR_OLIF) {
		LOG_DBG("Overload Frame Transmission error interrupt\n");
		sys_write8((uint8_t)~RCAR_CAN_EIFR_OLIF,
			   config->reg_addr + RCAR_CAN_EIFR);
	}
	if (eifr & RCAR_CAN_EIFR_BLIF) {
		LOG_DBG("Bus lock detected interrupt\n");
		sys_write8((uint8_t)~RCAR_CAN_EIFR_BLIF,
			   config->reg_addr + RCAR_CAN_EIFR);
	}
}

static void can_rcar_rx_filter_isr(const struct device *dev,
				   struct can_rcar_data *data,
				   const struct can_frame *frame)
{
	struct can_frame tmp_frame;
	uint8_t i;

#ifndef CONFIG_CAN_ACCEPT_RTR
	if ((frame->flags & CAN_FRAME_RTR) != 0U) {
		return;
	}
#endif /* !CONFIG_CAN_ACCEPT_RTR */

	for (i = 0; i < CONFIG_CAN_RCAR_MAX_FILTERS; i++) {
		if (data->rx_callback[i] == NULL) {
			continue;
		}

		if (!can_frame_matches_filter(frame, &data->filter[i])) {
			continue; /* filter did not match */
		}
		/* Make a temporary copy in case the user
		 * modifies the message.
		 */
		tmp_frame = *frame;
		data->rx_callback[i](dev, &tmp_frame, data->rx_callback_arg[i]);
	}
}

static void can_rcar_rx_isr(const struct device *dev)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	struct can_frame frame = {0};
	uint32_t val;
	int i;

	val = sys_read32(config->reg_addr + RCAR_CAN_MB_60);
	if (val & RCAR_CAN_MB_IDE) {
		frame.flags |= CAN_FRAME_IDE;
		frame.id = val & RCAR_CAN_MB_EID_MASK;
	} else {
		frame.id = (val & RCAR_CAN_MB_SID_MASK) >> RCAR_CAN_MB_SID_SHIFT;
	}

	frame.dlc = sys_read16(config->reg_addr +
			       RCAR_CAN_MB_60 + RCAR_CAN_MB_DLC_OFFSET) & 0xF;

	/* Be paranoid doc states that any value greater than 8
	 * should be considered as 8 bytes.
	 */
	if (frame.dlc > CAN_MAX_DLC) {
		frame.dlc = CAN_MAX_DLC;
	}

	if (val & RCAR_CAN_MB_RTR) {
		frame.flags |= CAN_FRAME_RTR;
	} else {
		for (i = 0; i < frame.dlc; i++) {
			frame.data[i] = sys_read8(config->reg_addr +
						  RCAR_CAN_MB_60 + RCAR_CAN_MB_DATA_OFFSET + i);
		}
	}
#if defined(CONFIG_CAN_RX_TIMESTAMP)
	/* read upper byte */
	frame.timestamp = sys_read8(config->reg_addr +
				    RCAR_CAN_MB_60 + RCAR_CAN_MB_TSH_OFFSET) << 8;
	/* and then read lower byte */
	frame.timestamp |= sys_read8(config->reg_addr +
				     RCAR_CAN_MB_60 + RCAR_CAN_MB_TSL_OFFSET);
#endif
	/* Increment CPU side pointer */
	sys_write8(0xff, config->reg_addr + RCAR_CAN_RFPCR);

	can_rcar_rx_filter_isr(dev, data, &frame);
}

static void can_rcar_isr(const struct device *dev)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	uint8_t isr, unsent;

	isr = sys_read8(config->reg_addr + RCAR_CAN_ISR);
	if (isr & RCAR_CAN_ISR_ERSF) {
		/* Clear the Error interrupt */
		isr &= ~RCAR_CAN_ISR_ERSF;
		sys_write8(isr, config->reg_addr + RCAR_CAN_ISR);
		can_rcar_error(dev);
	}
	if (isr & RCAR_CAN_ISR_TXFF) {
		/* Check for sent messages */
		while (1) {
			unsent = sys_read8(config->reg_addr + RCAR_CAN_TFCR);
			unsent = (unsent & RCAR_CAN_TFCR_TFUST) >>
				 RCAR_CAN_TFCR_TFUST_SHIFT;
			if (data->tx_unsent <= unsent) {
				break;
			}
			can_rcar_tx_done(dev, 0);
		}

		/* Clear the Tx interrupt */
		isr = sys_read8(config->reg_addr + RCAR_CAN_ISR);
		isr &= ~RCAR_CAN_ISR_TXFF;
		sys_write8(isr, config->reg_addr + RCAR_CAN_ISR);
	}
	if (isr & RCAR_CAN_ISR_RXFF) {
		/* while there is unread messages */
		while (!(sys_read8(config->reg_addr + RCAR_CAN_RFCR)
			 & RCAR_CAN_RFCR_RFEST)) {
			can_rcar_rx_isr(dev);
		}
		/* Clear the Rx interrupt */
		isr = sys_read8(config->reg_addr + RCAR_CAN_ISR);
		isr &= ~RCAR_CAN_ISR_RXFF;
		sys_write8(isr, config->reg_addr + RCAR_CAN_ISR);
	}
}

static int can_rcar_leave_sleep_mode(const struct can_rcar_cfg *config)
{
	uint16_t ctlr, str;

	ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
	ctlr &= ~RCAR_CAN_CTLR_SLPM;
	can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
	for (int i = 0; i < MAX_STR_READS; i++) {
		str = can_rcar_read16(config, RCAR_CAN_STR);
		if (!(str & RCAR_CAN_STR_SLPST)) {
			return 0;
		}
	}
	return -EAGAIN;
}

static int can_rcar_enter_reset_mode(const struct can_rcar_cfg *config, bool force)
{
	uint16_t ctlr;

	ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
	ctlr &= ~RCAR_CAN_CTLR_CANM_MASK;
	ctlr |= RCAR_CAN_CTLR_CANM_RESET;
	if (force) {
		ctlr |= RCAR_CAN_CTLR_CANM_HALT;
	}
	can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
	for (int i = 0; i < MAX_STR_READS; i++) {
		if (can_rcar_read16(config, RCAR_CAN_STR) & RCAR_CAN_STR_RSTST) {
			return 0;
		}
	}
	return -EAGAIN;
}

static int can_rcar_enter_halt_mode(const struct can_rcar_cfg *config)
{
	uint16_t ctlr;

	ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
	ctlr &= ~RCAR_CAN_CTLR_CANM_MASK;
	ctlr |= RCAR_CAN_CTLR_CANM_HALT;
	can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);

	/* Wait for controller to apply high bit timing settings */
	k_usleep(1);

	for (int i = 0; i < MAX_STR_READS; i++) {
		if (can_rcar_read16(config, RCAR_CAN_STR) & RCAR_CAN_STR_HLTST) {
			return 0;
		}
	}

	return -EAGAIN;
}

static int can_rcar_enter_operation_mode(const struct can_rcar_cfg *config)
{
	uint16_t ctlr, str;
	int i;

	ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
	ctlr &= ~RCAR_CAN_CTLR_CANM_MASK;
	can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);

	/* Wait for controller to apply high bit timing settings */
	k_usleep(1);

	for (i = 0; i < MAX_STR_READS; i++) {
		str = can_rcar_read16(config, RCAR_CAN_STR);
		if (!(str & RCAR_CAN_CTLR_CANM_MASK)) {
			break;
		}
	}

	if (i == MAX_STR_READS) {
		return -EAGAIN;
	}

	/* Enable Rx and Tx FIFO */
	sys_write8(RCAR_CAN_RFCR_RFE, config->reg_addr + RCAR_CAN_RFCR);
	sys_write8(RCAR_CAN_TFCR_TFE, config->reg_addr + RCAR_CAN_TFCR);

	return 0;
}

static int can_rcar_get_capabilities(const struct device *dev, can_mode_t *cap)
{
	ARG_UNUSED(dev);

	*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;

	return 0;
}

static int can_rcar_start(const struct device *dev)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	int ret;

	if (data->common.started) {
		return -EALREADY;
	}

	if (config->common.phy != NULL) {
		ret = can_transceiver_enable(config->common.phy, data->common.mode);
		if (ret != 0) {
			LOG_ERR("failed to enable CAN transceiver (err %d)", ret);
			return ret;
		}
	}

	k_mutex_lock(&data->inst_mutex, K_FOREVER);

	CAN_STATS_RESET(dev);

	ret = can_rcar_enter_operation_mode(config);
	if (ret != 0) {
		LOG_ERR("failed to enter operation mode (err %d)", ret);

		if (config->common.phy != NULL) {
			/* Attempt to disable the CAN transceiver in case of error */
			(void)can_transceiver_disable(config->common.phy);
		}
	} else {
		data->common.started = true;
	}

	k_mutex_unlock(&data->inst_mutex);

	return ret;
}

static int can_rcar_stop(const struct device *dev)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	int ret;

	if (!data->common.started) {
		return -EALREADY;
	}

	k_mutex_lock(&data->inst_mutex, K_FOREVER);

	ret = can_rcar_enter_halt_mode(config);
	if (ret != 0) {
		LOG_ERR("failed to enter halt mode (err %d)", ret);
		k_mutex_unlock(&data->inst_mutex);
		return ret;
	}

	data->common.started = false;

	k_mutex_unlock(&data->inst_mutex);

	if (config->common.phy != NULL) {
		ret = can_transceiver_disable(config->common.phy);
		if (ret != 0) {
			LOG_ERR("failed to disable CAN transceiver (err %d)", ret);
			return ret;
		}
	}

	/* Resetting TX FIFO, emptying it */
	sys_write8((uint8_t)~RCAR_CAN_TFCR_TFE, config->reg_addr + RCAR_CAN_TFCR);
	sys_write8(RCAR_CAN_TFCR_TFE, config->reg_addr + RCAR_CAN_TFCR);

	/* Empty TX msgq, returning an error for each message */
	while (data->tx_unsent) {
		can_rcar_tx_done(dev, -ENETDOWN);
	}

	return 0;
}

static int can_rcar_set_mode(const struct device *dev, can_mode_t mode)
{
	can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	uint8_t tcr = 0;
	int ret = 0;

	if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
		supported |= CAN_MODE_MANUAL_RECOVERY;
	}

	if ((mode & ~(supported)) != 0) {
		LOG_ERR("Unsupported mode: 0x%08x", mode);
		return -ENOTSUP;
	}

	if (data->common.started) {
		return -EBUSY;
	}

	k_mutex_lock(&data->inst_mutex, K_FOREVER);

	if ((mode & (CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) ==
	    (CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) {
		LOG_ERR("Combination of loopback and listenonly modes not supported");
		ret = -ENOTSUP;
		goto unlock;
	} else if ((mode & CAN_MODE_LOOPBACK) != 0) {
		/* Loopback mode */
		tcr = RCAR_CAN_TCR_INT_LOOP | RCAR_CAN_TCR_TSTE;
	} else if ((mode & CAN_MODE_LISTENONLY) != 0) {
		/* Listen-only mode */
		tcr = RCAR_CAN_TCR_LISTEN_ONLY | RCAR_CAN_TCR_TSTE;
	} else {
		/* Normal mode */
		tcr = 0;
	}

	sys_write8(tcr, config->reg_addr + RCAR_CAN_TCR);

	if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
		uint16_t ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);

		if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0U) {
			/* Set entry to halt automatically at bus-off */
			ctlr |= RCAR_CAN_CTLR_BOM_ENT;
		} else {
			/* Clear entry to halt automatically at bus-off */
			ctlr &= ~RCAR_CAN_CTLR_BOM_ENT;
		}

		can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
	}

	data->common.mode = mode;

unlock:
	k_mutex_unlock(&data->inst_mutex);

	return ret;
}

/* Bit Configuration Register settings */
#define RCAR_CAN_BCR_TSEG1(x)   (((x) & 0x0f) << 20)
#define RCAR_CAN_BCR_BPR(x)     (((x) & 0x3ff) << 8)
#define RCAR_CAN_BCR_SJW(x)     (((x) & 0x3) << 4)
#define RCAR_CAN_BCR_TSEG2(x)   ((x) & 0x07)

static void can_rcar_set_bittiming(const struct can_rcar_cfg *config,
				   const struct can_timing *timing)
{
	uint32_t bcr;

	bcr = RCAR_CAN_BCR_TSEG1(timing->phase_seg1 + timing->prop_seg - 1) |
	      RCAR_CAN_BCR_BPR(timing->prescaler - 1) |
	      RCAR_CAN_BCR_SJW(timing->sjw - 1) |
	      RCAR_CAN_BCR_TSEG2(timing->phase_seg2 - 1);

	/* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access.
	 * All the registers are big-endian but they get byte-swapped on 32-bit
	 * read/write (but not on 8-bit, contrary to the manuals)...
	 */
	sys_write32((bcr << 8) | RCAR_CAN_CLKR_CLKP2,
		    config->reg_addr + RCAR_CAN_BCR);
}

static int can_rcar_set_timing(const struct device *dev,
			       const struct can_timing *timing)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	int ret = 0;

	struct reg_backup {
		uint32_t address;
		uint8_t value;
	};

	struct reg_backup regs[3] = { { RCAR_CAN_TCR, 0 }, { RCAR_CAN_TFCR, 0 }
				      , { RCAR_CAN_RFCR, 0 } };

	if (data->common.started) {
		return -EBUSY;
	}

	k_mutex_lock(&data->inst_mutex, K_FOREVER);

	/* Changing bittiming should be done in reset mode.
	 * Switching to reset mode is resetting loopback mode (TCR),
	 * transmit and receive FIFOs (TFCR and RFCR).
	 * Storing these reg values to restore them once back in halt mode.
	 */
	for (int i = 0; i < 3; i++) {
		regs[i].value = sys_read8(config->reg_addr + regs[i].address);
	}

	/* Switching to reset mode */
	ret = can_rcar_enter_reset_mode(config, true);
	if (ret != 0) {
		goto unlock;
	}

	/* Setting bit timing */
	can_rcar_set_bittiming(config, timing);

	/* Restoring registers must be done in halt mode */
	ret = can_rcar_enter_halt_mode(config);
	if (ret) {
		goto unlock;
	}

	/* Restoring registers */
	for (int i = 0; i < 3; i++) {
		sys_write8(regs[i].value, config->reg_addr + regs[i].address);
	}

unlock:
	k_mutex_unlock(&data->inst_mutex);
	return ret;
}

static void can_rcar_set_state_change_callback(const struct device *dev,
					       can_state_change_callback_t cb,
					       void *user_data)
{
	struct can_rcar_data *data = dev->data;

	data->common.state_change_cb = cb;
	data->common.state_change_cb_user_data = user_data;
}

static int can_rcar_get_state(const struct device *dev, enum can_state *state,
			      struct can_bus_err_cnt *err_cnt)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;

	if (state != NULL) {
		if (!data->common.started) {
			*state = CAN_STATE_STOPPED;
		} else {
			*state = data->state;
		}
	}

	if (err_cnt != NULL) {
		can_rcar_get_error_count(config, err_cnt);
	}

	return 0;
}

#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static int can_rcar_recover(const struct device *dev, k_timeout_t timeout)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	int64_t start_time;
	int ret = 0;

	if (!data->common.started) {
		return -ENETDOWN;
	}

	if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
		return -ENOTSUP;
	}

	if (data->state != CAN_STATE_BUS_OFF) {
		return 0;
	}

	if (k_mutex_lock(&data->inst_mutex, K_FOREVER)) {
		return -EAGAIN;
	}

	start_time = k_uptime_ticks();
	while (data->state == CAN_STATE_BUS_OFF) {
		ret = can_rcar_enter_operation_mode(config);
		if (ret != 0) {
			goto done;
		}

		if (!K_TIMEOUT_EQ(timeout, K_FOREVER) &&
		    k_uptime_ticks() - start_time >= timeout.ticks) {
			ret = -EAGAIN;
			goto done;
		}
	}

done:
	k_mutex_unlock(&data->inst_mutex);
	return ret;
}
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */

static int can_rcar_send(const struct device *dev, const struct can_frame *frame,
			 k_timeout_t timeout, can_tx_callback_t callback,
			 void *user_data)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	struct can_rcar_tx_cb *tx_cb;
	uint32_t identifier;
	int i;

	LOG_DBG("Sending %d bytes on %s. "
		"Id: 0x%x, "
		"ID type: %s, "
		"Remote Frame: %s"
		, frame->dlc, dev->name
		, frame->id
		, (frame->flags & CAN_FRAME_IDE) != 0 ?
		"extended" : "standard"
		, (frame->flags & CAN_FRAME_RTR) != 0 ? "yes" : "no");

	if (frame->dlc > CAN_MAX_DLC) {
		LOG_ERR("DLC of %d exceeds maximum (%d)",
			frame->dlc, CAN_MAX_DLC);
		return -EINVAL;
	}

	if ((frame->flags & ~(CAN_FRAME_IDE | CAN_FRAME_RTR)) != 0) {
		LOG_ERR("unsupported CAN frame flags 0x%02x", frame->flags);
		return -ENOTSUP;
	}

	if (!data->common.started) {
		return -ENETDOWN;
	}

	/* Wait for a slot into the tx FIFO */
	if (k_sem_take(&data->tx_sem, timeout) != 0) {
		return -EAGAIN;
	}

	k_mutex_lock(&data->inst_mutex, K_FOREVER);
	tx_cb = &data->tx_cb[data->tx_head];
	tx_cb->cb = callback;
	tx_cb->cb_arg = user_data;

	data->tx_head++;
	if (data->tx_head >= RCAR_CAN_FIFO_DEPTH) {
		data->tx_head = 0;
	}

	if ((frame->flags & CAN_FRAME_IDE) != 0) {
		identifier = frame->id | RCAR_CAN_MB_IDE;
	} else {
		identifier = frame->id << RCAR_CAN_MB_SID_SHIFT;
	}

	if ((frame->flags & CAN_FRAME_RTR) != 0) {
		identifier |= RCAR_CAN_MB_RTR;
	}

	sys_write32(identifier, config->reg_addr + RCAR_CAN_MB_56);

	sys_write16(frame->dlc, config->reg_addr
		    + RCAR_CAN_MB_56 + RCAR_CAN_MB_DLC_OFFSET);

	if ((frame->flags & CAN_FRAME_RTR) == 0) {
		for (i = 0; i < frame->dlc; i++) {
			sys_write8(frame->data[i], config->reg_addr
				   + RCAR_CAN_MB_56 + RCAR_CAN_MB_DATA_OFFSET + i);
		}
	}

	compiler_barrier();
	data->tx_unsent++;
	/* Start Tx: increment the CPU-side pointer for the transmit FIFO
	 * to the next mailbox location
	 */
	sys_write8(0xff, config->reg_addr + RCAR_CAN_TFPCR);

	k_mutex_unlock(&data->inst_mutex);

	return 0;
}

static inline int can_rcar_add_rx_filter_unlocked(const struct device *dev,
						  can_rx_callback_t cb,
						  void *cb_arg,
						  const struct can_filter *filter)
{
	struct can_rcar_data *data = dev->data;
	int i;

	for (i = 0; i < CONFIG_CAN_RCAR_MAX_FILTERS; i++) {
		if (data->rx_callback[i] == NULL) {
			data->rx_callback_arg[i] = cb_arg;
			data->filter[i] = *filter;
			compiler_barrier();
			data->rx_callback[i] = cb;
			return i;
		}
	}

	return -ENOSPC;
}

static int can_rcar_add_rx_filter(const struct device *dev, can_rx_callback_t cb,
				  void *cb_arg, const struct can_filter *filter)
{
	struct can_rcar_data *data = dev->data;
	int filter_id;

	if ((filter->flags & ~(CAN_FILTER_IDE)) != 0) {
		LOG_ERR("unsupported CAN filter flags 0x%02x", filter->flags);
		return -ENOTSUP;
	}

	k_mutex_lock(&data->rx_mutex, K_FOREVER);
	filter_id = can_rcar_add_rx_filter_unlocked(dev, cb, cb_arg, filter);
	k_mutex_unlock(&data->rx_mutex);
	return filter_id;
}

static void can_rcar_remove_rx_filter(const struct device *dev, int filter_id)
{
	struct can_rcar_data *data = dev->data;

	if (filter_id < 0 || filter_id >= CONFIG_CAN_RCAR_MAX_FILTERS) {
		LOG_ERR("filter ID %d out of bounds", filter_id);
		return;
	}

	k_mutex_lock(&data->rx_mutex, K_FOREVER);
	compiler_barrier();
	data->rx_callback[filter_id] = NULL;
	k_mutex_unlock(&data->rx_mutex);
}

static int can_rcar_init(const struct device *dev)
{
	const struct can_rcar_cfg *config = dev->config;
	struct can_rcar_data *data = dev->data;
	struct can_timing timing = { 0 };
	int ret;
	uint16_t ctlr;

	k_mutex_init(&data->inst_mutex);
	k_mutex_init(&data->rx_mutex);
	k_sem_init(&data->tx_sem, RCAR_CAN_FIFO_DEPTH, RCAR_CAN_FIFO_DEPTH);

	data->tx_head = 0;
	data->tx_tail = 0;
	data->tx_unsent = 0;

	memset(data->rx_callback, 0, sizeof(data->rx_callback));
	data->state = CAN_STATE_ERROR_ACTIVE;
	data->common.state_change_cb = NULL;
	data->common.state_change_cb_user_data = NULL;

	if (config->common.phy != NULL && !device_is_ready(config->common.phy)) {
		LOG_ERR("CAN transceiver not ready");
		return -ENODEV;
	}

	if (!device_is_ready(config->clock_dev)) {
		LOG_ERR("clock control device not ready");
		return -ENODEV;
	}

	/* Configure dt provided device signals when available */
	ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
	if (ret < 0) {
		return ret;
	}

	/* reset the registers */
	ret = clock_control_off(config->clock_dev,
				(clock_control_subsys_t)&config->mod_clk);
	if (ret < 0) {
		return ret;
	}

	ret = clock_control_on(config->clock_dev,
			       (clock_control_subsys_t)&config->mod_clk);
	if (ret < 0) {
		return ret;
	}

	ret = clock_control_on(config->clock_dev,
			       (clock_control_subsys_t)&config->bus_clk);
	if (ret < 0) {
		return ret;
	}

	ret = can_rcar_enter_reset_mode(config, false);
	__ASSERT(!ret, "Fail to set CAN controller to reset mode");
	if (ret) {
		return ret;
	}

	ret = can_rcar_leave_sleep_mode(config);
	__ASSERT(!ret, "Fail to leave CAN controller from sleep mode");
	if (ret) {
		return ret;
	}

	ret = can_calc_timing(dev, &timing, config->common.bitrate,
			      config->common.sample_point);
	if (ret == -EINVAL) {
		LOG_ERR("Can't find timing for given param");
		return -EIO;
	}

	LOG_DBG("Presc: %d, TS1: %d, TS2: %d",
		timing.prescaler, timing.phase_seg1, timing.phase_seg2);
	LOG_DBG("Sample-point err : %d", ret);

	ret = can_set_timing(dev, &timing);
	if (ret) {
		return ret;
	}

	ret = can_rcar_set_mode(dev, CAN_MODE_NORMAL);
	if (ret) {
		return ret;
	}

	ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
	ctlr |= RCAR_CAN_CTLR_IDFM_MIXED;       /* Select mixed ID mode */
	ctlr &= ~RCAR_CAN_CTLR_BOM_ENT;         /* Clear entry to halt automatically at bus-off */
	ctlr |= RCAR_CAN_CTLR_MBM;              /* Select FIFO mailbox mode */
	ctlr |= RCAR_CAN_CTLR_MLM;              /* Overrun mode */
	ctlr &= ~RCAR_CAN_CTLR_SLPM;            /* Clear CAN Sleep mode */
	can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);

	/* Accept all SID and EID */
	sys_write32(0, config->reg_addr + RCAR_CAN_MKR8);
	sys_write32(0, config->reg_addr + RCAR_CAN_MKR9);
	/* In FIFO mailbox mode, write "0" to bits 24 to 31 */
	sys_write32(0, config->reg_addr + RCAR_CAN_MKIVLR0);
	sys_write32(0, config->reg_addr + RCAR_CAN_MKIVLR1);
	/* Accept standard and extended ID frames, but not
	 * remote frame.
	 */
	sys_write32(0, config->reg_addr + RCAR_CAN_FIDCR0);
	sys_write32(RCAR_CAN_FIDCR_IDE,
		    config->reg_addr + RCAR_CAN_FIDCR1);

	/* Enable and configure FIFO mailbox interrupts Rx and Tx */
	sys_write32(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE,
		    config->reg_addr + RCAR_CAN_MIER1);

	sys_write8(RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE | RCAR_CAN_IER_TXFIE,
		   config->reg_addr + RCAR_CAN_IER);

	/* Accumulate error codes */
	sys_write8(RCAR_CAN_ECSR_EDPM, config->reg_addr + RCAR_CAN_ECSR);

	/* Enable interrupts for all type of errors */
	sys_write8(0xFF, config->reg_addr + RCAR_CAN_EIER);

	config->init_func(dev);

	return 0;
}

static int can_rcar_get_core_clock(const struct device *dev, uint32_t *rate)
{
	const struct can_rcar_cfg *config = dev->config;

	*rate = config->bus_clk.rate;
	return 0;
}

static int can_rcar_get_max_filters(const struct device *dev, bool ide)
{
	ARG_UNUSED(ide);

	return CONFIG_CAN_RCAR_MAX_FILTERS;
}

static DEVICE_API(can, can_rcar_driver_api) = {
	.get_capabilities = can_rcar_get_capabilities,
	.start = can_rcar_start,
	.stop = can_rcar_stop,
	.set_mode = can_rcar_set_mode,
	.set_timing = can_rcar_set_timing,
	.send = can_rcar_send,
	.add_rx_filter = can_rcar_add_rx_filter,
	.remove_rx_filter = can_rcar_remove_rx_filter,
	.get_state = can_rcar_get_state,
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
	.recover = can_rcar_recover,
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
	.set_state_change_callback = can_rcar_set_state_change_callback,
	.get_core_clock = can_rcar_get_core_clock,
	.get_max_filters = can_rcar_get_max_filters,
	.timing_min = {
		.sjw = 0x1,
		.prop_seg = 0x00,
		.phase_seg1 = 0x04,
		.phase_seg2 = 0x02,
		.prescaler = 0x01
	},
	.timing_max = {
		.sjw = 0x4,
		.prop_seg = 0x00,
		.phase_seg1 = 0x10,
		.phase_seg2 = 0x08,
		.prescaler = 0x400
	}
};

/* Device Instantiation */
#define CAN_RCAR_INIT(n)							\
	PINCTRL_DT_INST_DEFINE(n);						\
	static void can_rcar_##n##_init(const struct device *dev);		\
	static const struct can_rcar_cfg can_rcar_cfg_##n = {			\
		.common = CAN_DT_DRIVER_CONFIG_INST_GET(n, 0, 1000000),		\
		.reg_addr = DT_INST_REG_ADDR(n),				\
		.reg_size = DT_INST_REG_SIZE(n),				\
		.init_func = can_rcar_##n##_init,				\
		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),		\
		.mod_clk.module =						\
			DT_INST_CLOCKS_CELL_BY_IDX(n, 0, module),		\
		.mod_clk.domain =						\
			DT_INST_CLOCKS_CELL_BY_IDX(n, 0, domain),		\
		.bus_clk.module =						\
			DT_INST_CLOCKS_CELL_BY_IDX(n, 1, module),		\
		.bus_clk.domain =						\
			DT_INST_CLOCKS_CELL_BY_IDX(n, 1, domain),		\
		.bus_clk.rate = 40000000,					\
		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),			\
	};									\
	static struct can_rcar_data can_rcar_data_##n;				\
										\
	CAN_DEVICE_DT_INST_DEFINE(n, can_rcar_init,				\
				  NULL,						\
				  &can_rcar_data_##n,				\
				  &can_rcar_cfg_##n,				\
				  POST_KERNEL,					\
				  CONFIG_CAN_INIT_PRIORITY,			\
				  &can_rcar_driver_api				\
				  );						\
	static void can_rcar_##n##_init(const struct device *dev)		\
	{									\
		IRQ_CONNECT(DT_INST_IRQN(n),					\
			    0,							\
			    can_rcar_isr,					\
			    DEVICE_DT_INST_GET(n), 0);				\
										\
		irq_enable(DT_INST_IRQN(n));					\
	}

DT_INST_FOREACH_STATUS_OKAY(CAN_RCAR_INIT)
