/* Copyright (C) MOXA Inc. All rights reserved.

   This is free software distributed under the terms of the
   GNU Public License.  See the file COPYING-GPL for details.
*/

#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/version.h>

#include "moxa_can.h"
#include "lookup_table.h"

#define	VERSION_CODE(ver,rel,seq)	((ver << 16) | (rel << 8) | seq)

#define FRAME_HANDLE_COUNTER_MAX 10000
#define CAN_MAX_ERROR_PASSIVE_CALLS 127
#define CAN_MAX_ERROR_CALLS 95
#define CAN_ERROR_PASSIVE_THRESHOLD 128
#define CAN_ERROR_WARNING_THRESHOLD 96

#define DRV_NAME "mxcanctrl"

MODULE_DESCRIPTION("MOXA CAN CTRL driver");
MODULE_LICENSE("GPL v2");

/**
 * @brief CAN bittiming constants
 *
 */
static struct can_bittiming_const moxa_can_bittiming_const = {
	.name = DRV_NAME, /**< Name of the CAN controller hardware  */
	.tseg1_min = 2, /**< Time segment 1 = prop_seg + phase_seg1*/
	.tseg1_max = 512,
	.tseg2_min = 1, /**< Time segment 2 = phase_seg2 */
	.tseg2_max = 128,
	.sjw_max = 128, /**< Synchronisation jump width */
	.brp_min = 1, /**< Bit-rate prescaler */
	.brp_max = 32,
	.brp_inc = 1,
};

/**
 * @brief CAN Data bittiming constants
 *
 */
static struct can_bittiming_const moxa_can_data_bittiming_const = {
	.name = DRV_NAME, /**< Name of the CAN controller hardware  */
	.tseg1_min = 2, /**< Time segment 1 = prop_seg + phase_seg1*/
	.tseg1_max = 256,
	.tseg2_min = 1, /**< Time segment 2 = phase_seg2 */
	.tseg2_max = 128,
	.sjw_max = 128, /**< Synchronisation jump width */
	.brp_min = 1, /**< Bit-rate prescaler */
	.brp_max = 32,
	.brp_inc = 1,
};

static const struct can_bittiming_lookup_table *
moxa_can_find_bittiming(const struct can_bittiming_lookup_table *table,
			size_t table_size, uint32_t bitrate)
{
	size_t i;

	if (!table || table_size == 0 || bitrate == 0)
		return NULL;

	for (i = 0; i < table_size; i++) {
		if (table[i].bittime == bitrate)
			return &table[i];
	}

	return NULL;
}

static uint32_t moxa_can_get_nominal_prescaler(struct moxa_can_priv *priv)
{
	const struct can_bittiming_lookup_table *entry;
	struct can_bittiming *bt = &priv->can.bittiming;

	entry = moxa_can_find_bittiming(can_bitrate_lookup_table,
					CAN_CC_SIZE_OF_LOOKUP_TABLE,
					bt->bitrate);
	if (entry)
		return entry->prescaler;

	return bt->brp;
}

static int moxa_can_change_mtu(struct net_device *ndev, int new_mtu)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);

	if (ndev->flags & IFF_UP)
		return -EBUSY;

	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU)
		return -EINVAL;

	if (new_mtu == CANFD_MTU && !(priv->can.ctrlmode_supported & CAN_CTRLMODE_FD))
		return -EINVAL;
	
	ndev->mtu = new_mtu;
	return 0;
}

static int moxa_can_set_bittiming(struct net_device *ndev)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	struct can_bittiming *bt = &priv->can.bittiming;
	can_bittiming_t cbt = { 0 };
	const struct can_bittiming_lookup_table *entry;
	can_linux_error_code_t err;
	
	/* Standard CAN bittiming */
	entry = moxa_can_find_bittiming(can_bitrate_lookup_table,
					CAN_CC_SIZE_OF_LOOKUP_TABLE,
					bt->bitrate);
	if (entry) {
		bt->tq = entry->tq;
		bt->prop_seg = entry->prop_seg;
		bt->phase_seg1 = entry->phase_seg1;
		bt->phase_seg2 = entry->phase_seg2;
		bt->sjw = entry->sjw;
		bt->brp = entry->prescaler;
		bt->sample_point =
			((1 + entry->prop_seg + entry->phase_seg1) * 1000) /
			entry->tq;

		cbt.phase_seg1 = entry->phase_seg1;
		cbt.phase_seg2 = entry->phase_seg2;
		cbt.prescaler = entry->prescaler;
		cbt.prop_seg = entry->prop_seg;
		cbt.sjw = entry->sjw;
	} else {
		cbt.phase_seg1 = bt->phase_seg1;
		cbt.phase_seg2 = bt->phase_seg2;
		cbt.prescaler = bt->brp;
		cbt.prop_seg = bt->prop_seg;
		cbt.sjw = bt->sjw;
	}
	cbt.cclk = CAN_CTRL_CLOCK;

	err = can_bittiming_set(priv->ctrl, CAN_BITTIMING_AC, &cbt);
	return (err == CAN_NO_ERROR) ? 0 : -EINVAL;
}

static int moxa_can_set_data_bittiming(struct net_device *ndev)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	struct can_bittiming *dbt = &priv->can.data_bittiming;
	can_bittiming_t cbt = { 0 };
	const struct can_bittiming_lookup_table *entry;
	uint32_t nominal_prescaler;
	can_linux_error_code_t err;
	
	/* CAN FD Data bittiming */
	if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
		nominal_prescaler = moxa_can_get_nominal_prescaler(priv);
		entry = moxa_can_find_bittiming(can_fd_dbitrate_lookup_table,
						CAN_FD_SIZE_OF_LOOKUP_TABLE,
						dbt->bitrate);
		if (entry) {
			if (entry->prescaler != nominal_prescaler) {
				netdev_err(ndev,
					   "CAN FD data brp %u must match nominal brp %u\n",
					   dbt->brp, nominal_prescaler);
				return -EINVAL;
			}

			dbt->tq = entry->tq;
			dbt->prop_seg = entry->prop_seg;
			dbt->phase_seg1 = entry->phase_seg1;
			dbt->phase_seg2 = entry->phase_seg2;
			dbt->sjw = entry->sjw;
			dbt->brp = entry->prescaler;
			dbt->sample_point =
				((1 + entry->prop_seg + entry->phase_seg1) * 1000) /
				entry->tq;

			cbt.phase_seg1 = entry->phase_seg1;
			cbt.phase_seg2 = entry->phase_seg2;
			cbt.prescaler = entry->prescaler;
			cbt.prop_seg = entry->prop_seg;
			cbt.sjw = entry->sjw;
			cbt.tdc_enable = true;
			cbt.tdc = entry->tdc;
		} else {
			if (dbt->brp != nominal_prescaler) {
				netdev_err(ndev,
					   "CAN FD data brp %u must match nominal brp %u\n",
					   dbt->brp, nominal_prescaler);
				return -EINVAL;
			}
			cbt.phase_seg1 = dbt->phase_seg1;
			cbt.phase_seg2 = dbt->phase_seg2;
			cbt.prescaler = dbt->brp;
			cbt.prop_seg = dbt->prop_seg;
			cbt.sjw = dbt->sjw;
			cbt.tdc_enable = true;
			cbt.tdc = dbt->prop_seg + dbt->phase_seg1 + 1;
		}
		cbt.cclk = CAN_CTRL_CLOCK;
		
		err = can_bittiming_set(priv->ctrl, CAN_BITTIMING_FD, &cbt);
		return (err == CAN_NO_ERROR) ? 0 : -EINVAL;
	}
	
	return 0;
}

static int moxa_can_get_berr_counter(const struct net_device *ndev, struct can_berr_counter *bec)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);

	bec->rxerr = ioread8((const void *)&priv->ctrl->RECNT);
	bec->txerr = ioread8((const void *)&priv->ctrl->TECNT);

	return 0;
}

static int moxa_can_chip_start(struct net_device *ndev)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	CAN_CTRL_LIMIT_t ctrl_limit;
	CAN_CTRL_RELIM_t ctrl_relim;
	CAN_CTRL_RCTRL_t stat;
	CAN_CTRL_CFG_STAT_t cfg_stat;
	CAN_CTRL_INTE_t inte;
	CAN_CTRL_TCMD_t tcmd;
	CAN_CTRL_TCTRL_t tctrl;

	moxa_can_set_bittiming(ndev);
	moxa_can_set_data_bittiming(ndev);
	
	/* Set limits */
	can_linux_limit_read(priv->ctrl, &ctrl_limit);
	ctrl_limit.AFWL = START_AFWL;
	ctrl_limit.EWL = START_EWL;
	can_linux_limit_write(priv->ctrl, &ctrl_limit);

	can_linux_relim_read(priv->ctrl, &ctrl_relim);
	ctrl_relim.REALIM = 2;
	ctrl_relim.RETLIM = 7;
	can_linux_relim_write(priv->ctrl, &ctrl_relim);

	/* Configure RX control */
		can_linux_rctrl_read(priv->ctrl, &stat);
	stat.ROM = 1; /* Overwrite oldest */
	can_linux_rctrl_write(priv->ctrl, &stat);
	
	/* Enable interrupts */
	can_linux_inte_read(priv->ctrl, &inte);
	inte.RFIE = 1;
	inte.EIE = 1;
	inte.TSIE = 1;
	inte.TPIE = 1;
	inte.ROIE = 1;
	inte.RIE = 1;
	inte.EPIE = 1;
	inte.BEIE = 1;
	can_linux_inte_write(priv->ctrl, &inte);
	
	/* Disable loopback by default */
	can_linux_cfg_stat_read(priv->ctrl, &cfg_stat);
	cfg_stat.LBME = 0;
	cfg_stat.LBMI = 0;
	if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
		cfg_stat.LBMI = 1; /* Internal loopback */
	}
	cfg_stat.BUSOFF = 1;
	can_linux_cfg_stat_write(priv->ctrl, &cfg_stat);

	can_linux_tcmd_read(priv->ctrl, &tcmd);
	tcmd.LOM = !!(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY);
	can_linux_tcmd_write(priv->ctrl, &tcmd);

	can_linux_tctrl_read(priv->ctrl, &tctrl);
	tctrl.FD_ISO = !(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO);
	can_linux_tctrl_write(priv->ctrl, &tctrl);

	priv->can.state = CAN_STATE_ERROR_ACTIVE;
	
	return 0;
}

static void moxa_can_chip_stop(struct net_device *ndev)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	CAN_CTRL_INTE_t inte = { 0 };
	uint32_t isr;

	/* Quiesce the controller before freeing the IRQ so a down interface
	 * no longer participates on the bus or keeps acknowledging frames.
	 */
	can_linux_inte_write(priv->ctrl, &inte);
	if (can_interrupt_get(priv->ctrl, &isr) == CAN_NO_ERROR && isr != 0)
		can_interrupt_clear(priv->ctrl, isr);

	can_reset(priv->ctrl, true);
	priv->can.state = CAN_STATE_STOPPED;
}

static int moxa_can_set_mode(struct net_device *ndev, enum can_mode mode)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	
	switch (mode) {
	case CAN_MODE_START:
		can_reset(priv->ctrl, true);
		moxa_can_chip_start(ndev);
		can_reset(priv->ctrl, false);
		netif_wake_queue(ndev);
		break;
	default:
		return -EOPNOTSUPP;
	}
	return 0;
}

static int moxa_can_open(struct net_device *ndev)
{
    struct moxa_can_priv *private;
    int ret = 0;
    can_linux_error_code_t err = CAN_NO_ERROR;
	
    if (ndev == NULL) {
        return -ENODEV;
	}
	
	private = netdev_priv(ndev);

    /* request irq */
    ret = request_irq(ndev->irq, moxa_can_interrupt, IRQF_SHARED, ndev->name, ndev);
    if (ret != 0) {
        netdev_err(ndev, "irq allocation for CAN failed\n");
        return ret;
    }

	/* Set chip into reset mode */
	err = can_reset(private->ctrl, true);
	if (err != CAN_NO_ERROR) {
		free_irq(ndev->irq, ndev);
		netdev_err(ndev, "mode resetting failed!\n");
		return -ECANCELED;
	}

	ret = open_candev(ndev);
	if (ret != 0) {
		netdev_err(ndev, "opening can device failed!\n");
		free_irq(ndev->irq, ndev);
		return ret;
	}

	err = moxa_can_chip_start(ndev);
	if (err != 0) {
		free_irq(ndev->irq, ndev);
		netdev_err(ndev, "moxa_can_chip_start failed!(err=%d)\n", err);
		return -EPERM;
	}

	napi_enable(&private->napi);
	netif_start_queue(ndev);
	err = can_reset(private->ctrl, false);
	if (err != CAN_NO_ERROR) {
		free_irq(ndev->irq, ndev);
		netdev_err(ndev, "mode resetting failed!\n");
		return -ECANCELED;
	}

	return ret;
}

static int moxa_can_close(struct net_device *ndev)
{
    struct moxa_can_priv *private;

    if (ndev == NULL)
		return -ENODEV;

	private = netdev_priv(ndev);
    netif_stop_queue(ndev);
	napi_disable(&private->napi);
	moxa_can_chip_stop(ndev);
	free_irq(ndev->irq, ndev);
	close_candev(ndev);
    return 0;
}

int moxa_can_suspend(struct net_device *ndev)
{
	struct moxa_can_priv *private;

	if (ndev == NULL)
		return -ENODEV;

	private = netdev_priv(ndev);
	netif_stop_queue(ndev);
	napi_disable(&private->napi);
	moxa_can_chip_stop(ndev);
	free_irq(ndev->irq, ndev);

	return 0;
}
EXPORT_SYMBOL(moxa_can_suspend);

int moxa_can_resume(struct net_device *ndev)
{
	struct moxa_can_priv *private;
	int ret;
	can_linux_error_code_t err;

	if (ndev == NULL)
		return -ENODEV;

	private = netdev_priv(ndev);

	ret = request_irq(ndev->irq, moxa_can_interrupt, IRQF_SHARED,
			  ndev->name, ndev);
	if (ret != 0) {
		netdev_err(ndev, "irq allocation for CAN failed\n");
		return ret;
	}

	err = can_reset(private->ctrl, true);
	if (err != CAN_NO_ERROR) {
		free_irq(ndev->irq, ndev);
		netdev_err(ndev, "mode resetting failed!\n");
		return -ECANCELED;
	}

	ret = moxa_can_chip_start(ndev);
	if (ret != 0) {
		free_irq(ndev->irq, ndev);
		netdev_err(ndev, "moxa_can_chip_start failed!(err=%d)\n", ret);
		return ret;
	}

	napi_enable(&private->napi);
	netif_start_queue(ndev);
	err = can_reset(private->ctrl, false);
	if (err != CAN_NO_ERROR) {
		netif_stop_queue(ndev);
		napi_disable(&private->napi);
		moxa_can_chip_stop(ndev);
		free_irq(ndev->irq, ndev);
		netdev_err(ndev, "mode resetting failed!\n");
		return -ECANCELED;
	}

	return 0;
}
EXPORT_SYMBOL(moxa_can_resume);

static inline bool moxa_can_dev_dropped_skb(struct net_device *dev,
                                       struct sk_buff *skb)
{
    struct can_frame *cf;
    struct canfd_frame *cfd;

    if (skb->len == CAN_MTU) {
        cf = (struct can_frame *)skb->data;
        if (cf->can_dlc > CAN_MAX_DLC)
            goto inval;
    } else if (skb->len == CANFD_MTU) {
        cfd = (struct canfd_frame *)skb->data;
        if (cfd->len > CANFD_MAX_DLEN)
            goto inval;
    } else {
        goto inval;
    }

    return false;

inval:
    dev->stats.tx_dropped++;
    dev_kfree_skb(skb);
    return true;
}

static netdev_tx_t moxa_can_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	struct net_device_stats *stats = &ndev->stats;
	struct can_frame *cf;
	struct canfd_frame *cfd;
	can_frame_info_t frame;
	can_fd_frame_info_t fd_frame;
	can_tbuf_sel_t tbsel;
	can_autoaction_type_t auto_action;
	CAN_CTRL_TCMD_t tcmd;
	can_linux_error_code_t err = CAN_NO_ERROR;


    //printk("Enter %s, %d\n", __FUNCTION__, __LINE__);

	if (moxa_can_dev_dropped_skb(ndev, skb)) {
		stats->tx_dropped++;
		return NETDEV_TX_BUSY;
	}

	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
		stats->tx_dropped++;
		dev_kfree_skb(skb);
		return NETDEV_TX_OK;
	}

	if (priv->transmit_buffer) {
		/* STB_MODE */
		tbsel = (uint8_t)STB_MODE; // STB Mode
		can_transmit_buffer_select(priv->ctrl, tbsel);
	} else {
		/* PTB_MODE */
		tbsel = (uint8_t)PTB_MODE; // PTB Mode
		can_transmit_buffer_select(priv->ctrl, tbsel);
	}

	can_linux_tcmd_read(priv->ctrl, &tcmd);
	auto_action = CAN_AUTOACTION_SECONDARY_NEXT;
	if (tcmd.TBSEL == (uint8_t)0) {
		if (tcmd.TPE != (uint8_t)1)
			auto_action = CAN_AUTOACTION_PRIMARY_SEND;
	}

	/* Stop queue if buffer is full - simple flow control */
	netif_stop_queue(ndev);

	if (can_is_canfd_skb(skb) != false) {
		/* CAN FD */
		cfd = (struct canfd_frame *)skb->data;

		fd_frame.id = cfd->can_id;
		fd_frame.len = cfd->len;

		if(cfd->can_id & CAN_EFF_FLAG) {
			/*extended id length, CAN_EFF_FLAG is set in the msb*/
			fd_frame.use_ext_id = true;
		} else {
			/*standard id*/
			fd_frame.use_ext_id = false;
		}

		fd_frame.flags = 0;
		if (cfd->flags & CANFD_BRS) {
			fd_frame.flags |= CAN_FD_FLAG_BRS;
		}

		err = can_fd_transmit(priv->ctrl, &fd_frame, (uint32_t *)cfd->data, auto_action, (uint32_t)atomic_read(&priv->handle_counter));
	} else {
		/* CAN Classical */
		//err = can_tx(ndev, skb);
		cf = (struct can_frame *)skb->data;

		frame.id = cf->can_id;

#if (LINUX_VERSION_CODE < VERSION_CODE(5,11,0))
		frame.len = cf->can_dlc;
#else
		frame.len = cf->len;
#endif

		frame.is_remote_frame = 0;
		if(cf->can_id & CAN_RTR_FLAG)
			frame.is_remote_frame = 1;
		
		if(cf->can_id & CAN_EFF_FLAG) {
			/*extended id length, CAN_EFF_FLAG is set in the msb*/
			frame.use_ext_id = true;
		} else {
			/*standard id*/
			frame.use_ext_id = false;
		}

		err = can_transmit(priv->ctrl, &frame, (uint32_t *)cf->data, auto_action, (uint32_t)atomic_read(&priv->handle_counter));
	}
	
	if (err == CAN_ERR_TBUF_FULL) {
		netif_stop_queue(ndev);
		stats->tx_dropped++;
		//printk("%s, %d\n", __FUNCTION__, __LINE__);
		return NETDEV_TX_BUSY;
	}else{
		/* Save skb for local echo */
#if (LINUX_VERSION_CODE < VERSION_CODE(5,12,0))
		can_put_echo_skb(skb, ndev, 0);
#else
		can_put_echo_skb(skb, ndev, 0, skb->len);
#endif
		
	}

	can_linux_tcmd_read(priv->ctrl, &tcmd);

	if (tcmd.TBSEL == (uint8_t)1) {
		if (priv->stb_transmit_mode == STB_TRANSMIT_ONE) {
			err = can_transmit_buffer_trigger(priv->ctrl, TBUF_TRIGGER_STB_ONE);
		} else if (priv->stb_transmit_mode == STB_TRANSMIT_ALL) {
			err = can_transmit_buffer_trigger(priv->ctrl, TBUF_TRIGGER_STB_ALL);
		}
		
		if (err != CAN_NO_ERROR) {
			//printk("Unable to trigger TSONE\n");
			stats->tx_dropped++;
			netif_wake_queue(ndev);
			return NETDEV_TX_BUSY;
		}
	}

	if (err == CAN_NO_ERROR) {
		//stats->tx_packets++;
		//stats->tx_bytes += skb->len;
		/* Save skb for local echo */
		//can_put_echo_skb(skb, ndev, 0, skb->len);
	} else {
		stats->tx_dropped++;
		netif_wake_queue(ndev);
		//printk("%s, %d\n", __FUNCTION__, __LINE__);
		return NETDEV_TX_BUSY;
	}

	if ((uint32_t)atomic_read(&priv->handle_counter) >= FRAME_HANDLE_COUNTER_MAX) {
		atomic_set(&priv->handle_counter, 0); // Reset
		//trace_msg("Warning: Counter reset to: 0\n");
    } else {
		atomic_inc(&priv->handle_counter);
    }

	//if (skb != NULL)
	//	kfree_skb(skb); // If you return NETDEV_TX_BUSY from the ndo_start_xmit method, you must not keep any reference to that SKB and you must not attempt to free it up.

	//printk("%s, %d\n", __FUNCTION__, __LINE__);
	return NETDEV_TX_OK;
}

static int moxa_can_rx_poll(struct napi_struct *napi, int quota)
{
	struct net_device *ndev = napi->dev;
	struct moxa_can_priv *priv = netdev_priv(ndev);
	struct net_device_stats *stats = &ndev->stats;
	CAN_CTRL_RCTRL_t stat;
	CAN_CTRL_RBUF_FORMAT_t rbuff_fmt;
	struct sk_buff *skb;
	struct can_frame *cf;
	struct canfd_frame *cfd;
	can_frame_info_t frame;
	can_fd_frame_info_t fd_frame;
	int work_done = 0;
	can_linux_error_code_t err = CAN_NO_ERROR;
	
	can_linux_rctrl_read(priv->ctrl, &stat);

	while ((stat.RSTAT != 0) && work_done < quota) {
		can_linux_rbuf_format_read(priv->ctrl, &rbuff_fmt);

		if (rbuff_fmt.FDF) {
			/* CAN FD */
			skb = alloc_canfd_skb(ndev, &cfd);
			if (!skb) {
				stats->rx_dropped++;
				goto next_frame;
			}

			err = can_fd_receive_data(priv->ctrl, &fd_frame, (uint32_t *)cfd->data, CANFD_MAX_DLEN);
			if (err) {
				kfree_skb(skb);
				stats->rx_dropped++;
				goto next_frame;
			}

			cfd->can_id = fd_frame.id;
			if (fd_frame.use_ext_id)
				cfd->can_id |= CAN_EFF_FLAG;
			cfd->len = fd_frame.len;
			/* Flags handling if needed */
			if (fd_frame.flags & CAN_FD_FLAG_BRS)
				cfd->flags |= CANFD_BRS;
			
			netif_receive_skb(skb);
			stats->rx_packets++;
			stats->rx_bytes += cfd->len;
		} else {
			/* Classic CAN */
			skb = alloc_can_skb(ndev, &cf);
			if (!skb) {
				stats->rx_dropped++;
				goto next_frame;
			}
			
			err = can_receive_data(priv->ctrl, &frame, (uint32_t *)cf->data, CAN_MAX_DLEN);
			if (err) {
				kfree_skb(skb);
				stats->rx_dropped++;
				goto next_frame;
			}
			
			cf->can_id = frame.id;
			if (frame.use_ext_id)
				cf->can_id |= CAN_EFF_FLAG;
			if (frame.is_remote_frame)
				cf->can_id |= CAN_RTR_FLAG;
#if (LINUX_VERSION_CODE < VERSION_CODE(5,11,0))
			cf->can_dlc = frame.len;
#else
			cf->len = frame.len;
#endif
			
			netif_receive_skb(skb);
			stats->rx_packets++;
#if (LINUX_VERSION_CODE < VERSION_CODE(5,11,0))
			stats->rx_bytes += cf->can_dlc;
#else
			stats->rx_bytes += cf->len;
#endif
		}

		work_done++;

next_frame:
		/* Release buffer */
		stat.RREL = 1;
		can_linux_rctrl_write(priv->ctrl, &stat);
		can_linux_rctrl_read(priv->ctrl, &stat);
	}

	if (work_done < quota) {
		if (napi_complete_done(napi, work_done))
			can_interrupt_enable(priv->ctrl, CAN_INTERRUPT_RECV);
	}

	return work_done;
}

static enum can_state moxa_can_state_from_counters(uint8_t rxerr, uint8_t txerr,
						   const CAN_CTRL_CFG_STAT_t *cfg_stat)
{
	if (cfg_stat->BUSOFF)
		return CAN_STATE_BUS_OFF;

	if (rxerr >= CAN_ERROR_PASSIVE_THRESHOLD ||
	    txerr >= CAN_ERROR_PASSIVE_THRESHOLD)
		return CAN_STATE_ERROR_PASSIVE;

	if (rxerr >= CAN_ERROR_WARNING_THRESHOLD ||
	    txerr >= CAN_ERROR_WARNING_THRESHOLD)
		return CAN_STATE_ERROR_WARNING;

	return CAN_STATE_ERROR_ACTIVE;
}

static void moxa_can_refresh_state(struct moxa_can_priv *priv)
{
	CAN_CTRL_CFG_STAT_t cfg_stat;
	uint8_t rxerr = 0, txerr = 0;

	can_linux_cfg_stat_read(priv->ctrl, &cfg_stat);
	can_linux_recnt_read(priv->ctrl, &rxerr);
	can_linux_tecnt_read(priv->ctrl, &txerr);

	priv->can.state = moxa_can_state_from_counters(rxerr, txerr, &cfg_stat);
}

static enum can_state moxa_can_get_chip_status(struct net_device *ndev,
					       struct can_frame *cf,
					       uint8_t rxerr, uint8_t txerr,
						const CAN_CTRL_CFG_STAT_t *cfg_stat)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	enum can_state state;

	state = moxa_can_state_from_counters(rxerr, txerr, cfg_stat);

	if (state == CAN_STATE_BUS_OFF) {
		//printk("%s(%d) bus error\n", __func__, __LINE__);
		priv->can.can_stats.bus_off++;
		cf->can_id |= CAN_ERR_BUSOFF;
		cf->data[6] = txerr;
		cf->data[7] = rxerr;
		can_bus_off(ndev);
		return CAN_STATE_BUS_OFF;
	}

	if (state == CAN_STATE_ERROR_PASSIVE) {
		//printk("%s(%d) error_passive\n", __func__, __LINE__);
		priv->can.can_stats.error_passive++;
		cf->can_id |= CAN_ERR_CRTL;
		cf->data[1] |= (rxerr >= CAN_ERROR_PASSIVE_THRESHOLD) ? CAN_ERR_CRTL_RX_PASSIVE : 0;
		cf->data[1] |= (txerr >= CAN_ERROR_PASSIVE_THRESHOLD) ? CAN_ERR_CRTL_TX_PASSIVE : 0;
		cf->data[6] = txerr;
		cf->data[7] = rxerr;
		return CAN_STATE_ERROR_PASSIVE;
	}

	if (state == CAN_STATE_ERROR_WARNING) {
		//printk("%s(%d) error_warning\n", __func__, __LINE__);
		priv->can.can_stats.error_warning++;
		cf->can_id |= CAN_ERR_CRTL;
		cf->data[1] |= (rxerr >= CAN_ERROR_WARNING_THRESHOLD) ? CAN_ERR_CRTL_RX_WARNING : 0;
		cf->data[1] |= (txerr >= CAN_ERROR_WARNING_THRESHOLD) ? CAN_ERR_CRTL_TX_WARNING : 0;
		cf->data[6] = txerr;
		cf->data[7] = rxerr;
		return CAN_STATE_ERROR_WARNING;
	}

	cf->data[6] = txerr;
	cf->data[7] = rxerr;
	return CAN_STATE_ERROR_ACTIVE;
}

static void moxa_can_error_koer(struct moxa_can_priv *priv,
				struct net_device_stats *stats,
				struct can_frame *cf,
				const CAN_CTRL_EALCAP_t *ealcap)
{
	if (!cf || !ealcap || !stats)
		return;

	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;

	switch (ealcap->KOER) {
	case 0: /* No error */
		break;
	case 1: /* Stuff Error */
		cf->data[2] |= CAN_ERR_PROT_STUFF;
		break;
	case 2: /* Form Error */
		cf->data[2] |= CAN_ERR_PROT_FORM;
		break;
	case 3: /* Ack Error */
		cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
		break;
	case 4: /* Bit1 Error */
		cf->data[2] |= CAN_ERR_PROT_BIT1;
		break;
	case 5: /* Bit0 Error */
		cf->data[2] |= CAN_ERR_PROT_BIT0;
		break;
	case 6: /* CRC Error */
		cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
		break;
	default:
		break;
	}

	stats->rx_errors++;
}

static void moxa_can_handle_bus_events(struct net_device *ndev, uint32_t isr)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	struct net_device_stats *stats = &ndev->stats;
	struct can_frame *cf;
	struct sk_buff *skb;
	CAN_CTRL_EALCAP_t ealcap;
	CAN_CTRL_CFG_STAT_t cfg_stat;
	uint8_t rxerr = 0, txerr = 0;

	skb = alloc_can_err_skb(ndev, &cf);
	if (!skb) {
		stats->rx_dropped++;
		return;
	}

	can_linux_ealcap_read(priv->ctrl, &ealcap);
	can_linux_cfg_stat_read(priv->ctrl, &cfg_stat);
	can_linux_recnt_read(priv->ctrl, &rxerr);
	can_linux_tecnt_read(priv->ctrl, &txerr);
	priv->can.state = moxa_can_get_chip_status(ndev, cf, rxerr, txerr, &cfg_stat);

	if (isr & CAN_INTERRUPT_BUS_ERROR) {
		priv->can.can_stats.bus_error++;
		moxa_can_error_koer(priv, stats, cf, &ealcap);
	}
	if (isr & CAN_INTERRUPT_ARBITRATION_LOST) {
		priv->can.can_stats.arbitration_lost++;
		stats->tx_errors++;
		cf->can_id |= CAN_ERR_LOSTARB;
		cf->data[0] = ealcap.ALC;
	}

	if (isr & CAN_INTERRUPT_TIME_TRIGGER)
		netdev_dbg(ndev, "time trigger interrupt\n");

	stats->rx_packets++;
	stats->rx_bytes += cf->can_dlc;
	netif_rx(skb);
}

irqreturn_t  moxa_can_interrupt(int irq, void *dev_id)
{
    struct net_device *ndev = (struct net_device *)dev_id;
	struct moxa_can_priv *priv = netdev_priv(ndev);
	struct net_device_stats *stats = &ndev->stats;
	uint32_t isr;
    
	if (can_interrupt_get(priv->ctrl, &isr) != CAN_NO_ERROR){
        netdev_err(ndev, "Can't get interrupt vector\n");
		return IRQ_NONE;
	}

	can_interrupt_clear(priv->ctrl, isr);

	if (isr == 0){
        return IRQ_HANDLED;
    }

	if (isr & (CAN_INTERRUPT_TRANS_SECOND | CAN_INTERRUPT_TRANS_PRIMARY)) {
		stats->tx_packets++;
#if (LINUX_VERSION_CODE < VERSION_CODE(5,12,0))
		stats->tx_bytes += can_get_echo_skb(ndev, 0);
#else
		stats->tx_bytes += can_get_echo_skb(ndev, 0, NULL);
#endif

		if (priv->can.state != CAN_STATE_ERROR_ACTIVE)
			moxa_can_refresh_state(priv);
		netif_wake_queue(ndev);
	}

	if (isr & CAN_INTERRUPT_RECV) {
		if (priv->can.state != CAN_STATE_ERROR_ACTIVE)
			moxa_can_refresh_state(priv);
		
		if (napi_schedule_prep(&priv->napi)) {
			can_interrupt_disable(priv->ctrl, CAN_INTERRUPT_RECV);
			__napi_schedule(&priv->napi);
		}
	}

	if (isr & (CAN_INTERRUPT_BUS_ERROR | CAN_INTERRUPT_ERROR_PASSIV |
		   CAN_INTERRUPT_ARBITRATION_LOST | CAN_INTERRUPT_TIME_TRIGGER)) {
		if(priv->can.state != CAN_STATE_BUS_OFF) {
			moxa_can_handle_bus_events(ndev, isr);
		}
	}

    return IRQ_HANDLED;
}

static const struct net_device_ops moxa_can_netdev_ops = {
    .ndo_open = moxa_can_open,
    .ndo_stop = moxa_can_close,
    .ndo_start_xmit = moxa_can_start_xmit,
    .ndo_change_mtu = moxa_can_change_mtu,
};

struct net_device *moxa_can_alloc(struct device *dev)
{
    struct net_device *ndev;
    struct moxa_can_priv *priv;

    ndev = alloc_candev(sizeof(struct moxa_can_priv), 1);
    if(!ndev)
        return NULL;

    priv = netdev_priv(ndev);
    priv->ndev = ndev;
    priv->dev = dev;

	priv->can.bittiming_const = &moxa_can_bittiming_const;
	priv->can.data_bittiming_const = &moxa_can_data_bittiming_const;
	priv->can.clock.freq = CAN_CTRL_CLOCK;
	priv->can.do_set_mode = moxa_can_set_mode;
    priv->can.do_set_bittiming = moxa_can_set_bittiming;
	priv->can.do_set_data_bittiming = moxa_can_set_data_bittiming;
    priv->can.do_get_berr_counter = moxa_can_get_berr_counter;
    priv->can.ctrlmode_supported   |= CAN_CTRLMODE_FD |
									  CAN_CTRLMODE_FD_NON_ISO |
									  CAN_CTRLMODE_LOOPBACK |
                                      CAN_CTRLMODE_LISTENONLY |
                                      CAN_CTRLMODE_BERR_REPORTING;
    
    ndev->min_mtu = CAN_MTU;
    ndev->max_mtu = CANFD_MTU;
	ndev->netdev_ops = &moxa_can_netdev_ops;
    ndev->flags |= IFF_ECHO;

	SET_NETDEV_DEV(ndev, dev);
    MOXA_NETIF_NAPI_ADD(ndev, &priv->napi, moxa_can_rx_poll, NAPI_POLL_WEIGHT);

    return ndev;
}
EXPORT_SYMBOL(moxa_can_alloc);

void moxa_can_free(struct net_device *ndev)
{
    free_candev(ndev);
}
EXPORT_SYMBOL(moxa_can_free);

int moxa_can_register(struct net_device *ndev)
{
	struct moxa_can_priv *priv = netdev_priv(ndev);
	can_linux_error_code_t err;

	err = can_reset(priv->ctrl, true);
	if (err != CAN_NO_ERROR)
		return -ENODEV;

    return register_candev(ndev);
}
EXPORT_SYMBOL(moxa_can_register);

void moxa_can_unregister(struct net_device *ndev)
{
    unregister_candev(ndev);
    //netif_napi_del(&((struct can_local *)netdev_priv(ndev))->napi);
}
EXPORT_SYMBOL(moxa_can_unregister);

static __init int moxa_can_init(void)
{
    printk(KERN_INFO "%s CAN netdevice driver init\n", DRV_NAME);
    
    return 0;
}
module_init(moxa_can_init);

static __exit void moxa_can_exit(void)
{
    printk(KERN_INFO "%s: driver removed\n", DRV_NAME);
}
module_exit(moxa_can_exit);
