162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * CTU CAN FD IP Core
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
762306a36Sopenharmony_ci * Copyright (C) 2018-2021 Ondrej Ille <ondrej.ille@gmail.com> self-funded
862306a36Sopenharmony_ci * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
962306a36Sopenharmony_ci * Copyright (C) 2018-2022 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Project advisors:
1262306a36Sopenharmony_ci *     Jiri Novak <jnovak@fel.cvut.cz>
1362306a36Sopenharmony_ci *     Pavel Pisa <pisa@cmp.felk.cvut.cz>
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Department of Measurement         (http://meas.fel.cvut.cz/)
1662306a36Sopenharmony_ci * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
1762306a36Sopenharmony_ci * Czech Technical University        (http://www.cvut.cz/)
1862306a36Sopenharmony_ci ******************************************************************************/
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/clk.h>
2162306a36Sopenharmony_ci#include <linux/errno.h>
2262306a36Sopenharmony_ci#include <linux/ethtool.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/bitfield.h>
2562306a36Sopenharmony_ci#include <linux/interrupt.h>
2662306a36Sopenharmony_ci#include <linux/io.h>
2762306a36Sopenharmony_ci#include <linux/kernel.h>
2862306a36Sopenharmony_ci#include <linux/module.h>
2962306a36Sopenharmony_ci#include <linux/skbuff.h>
3062306a36Sopenharmony_ci#include <linux/string.h>
3162306a36Sopenharmony_ci#include <linux/types.h>
3262306a36Sopenharmony_ci#include <linux/can/error.h>
3362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "ctucanfd.h"
3662306a36Sopenharmony_ci#include "ctucanfd_kregs.h"
3762306a36Sopenharmony_ci#include "ctucanfd_kframe.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#ifdef DEBUG
4062306a36Sopenharmony_ci#define  ctucan_netdev_dbg(ndev, args...) \
4162306a36Sopenharmony_ci		netdev_dbg(ndev, args)
4262306a36Sopenharmony_ci#else
4362306a36Sopenharmony_ci#define ctucan_netdev_dbg(...) do { } while (0)
4462306a36Sopenharmony_ci#endif
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define CTUCANFD_ID 0xCAFD
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* TX buffer rotation:
4962306a36Sopenharmony_ci * - when a buffer transitions to empty state, rotate order and priorities
5062306a36Sopenharmony_ci * - if more buffers seem to transition at the same time, rotate by the number of buffers
5162306a36Sopenharmony_ci * - it may be assumed that buffers transition to empty state in FIFO order (because we manage
5262306a36Sopenharmony_ci *   priorities that way)
5362306a36Sopenharmony_ci * - at frame filling, do not rotate anything, just increment buffer modulo counter
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define CTUCANFD_FLAG_RX_FFW_BUFFERED	1
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define CTUCAN_STATE_TO_TEXT_ENTRY(st) \
5962306a36Sopenharmony_ci		[st] = #st
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cienum ctucan_txtb_status {
6262306a36Sopenharmony_ci	TXT_NOT_EXIST       = 0x0,
6362306a36Sopenharmony_ci	TXT_RDY             = 0x1,
6462306a36Sopenharmony_ci	TXT_TRAN            = 0x2,
6562306a36Sopenharmony_ci	TXT_ABTP            = 0x3,
6662306a36Sopenharmony_ci	TXT_TOK             = 0x4,
6762306a36Sopenharmony_ci	TXT_ERR             = 0x6,
6862306a36Sopenharmony_ci	TXT_ABT             = 0x7,
6962306a36Sopenharmony_ci	TXT_ETY             = 0x8,
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cienum ctucan_txtb_command {
7362306a36Sopenharmony_ci	TXT_CMD_SET_EMPTY   = 0x01,
7462306a36Sopenharmony_ci	TXT_CMD_SET_READY   = 0x02,
7562306a36Sopenharmony_ci	TXT_CMD_SET_ABORT   = 0x04
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic const struct can_bittiming_const ctu_can_fd_bit_timing_max = {
7962306a36Sopenharmony_ci	.name = "ctu_can_fd",
8062306a36Sopenharmony_ci	.tseg1_min = 2,
8162306a36Sopenharmony_ci	.tseg1_max = 190,
8262306a36Sopenharmony_ci	.tseg2_min = 1,
8362306a36Sopenharmony_ci	.tseg2_max = 63,
8462306a36Sopenharmony_ci	.sjw_max = 31,
8562306a36Sopenharmony_ci	.brp_min = 1,
8662306a36Sopenharmony_ci	.brp_max = 8,
8762306a36Sopenharmony_ci	.brp_inc = 1,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic const struct can_bittiming_const ctu_can_fd_bit_timing_data_max = {
9162306a36Sopenharmony_ci	.name = "ctu_can_fd",
9262306a36Sopenharmony_ci	.tseg1_min = 2,
9362306a36Sopenharmony_ci	.tseg1_max = 94,
9462306a36Sopenharmony_ci	.tseg2_min = 1,
9562306a36Sopenharmony_ci	.tseg2_max = 31,
9662306a36Sopenharmony_ci	.sjw_max = 31,
9762306a36Sopenharmony_ci	.brp_min = 1,
9862306a36Sopenharmony_ci	.brp_max = 2,
9962306a36Sopenharmony_ci	.brp_inc = 1,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic const char * const ctucan_state_strings[CAN_STATE_MAX] = {
10362306a36Sopenharmony_ci	CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_ERROR_ACTIVE),
10462306a36Sopenharmony_ci	CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_ERROR_WARNING),
10562306a36Sopenharmony_ci	CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_ERROR_PASSIVE),
10662306a36Sopenharmony_ci	CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_BUS_OFF),
10762306a36Sopenharmony_ci	CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_STOPPED),
10862306a36Sopenharmony_ci	CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_SLEEPING)
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void ctucan_write32_le(struct ctucan_priv *priv,
11262306a36Sopenharmony_ci			      enum ctu_can_fd_can_registers reg, u32 val)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	iowrite32(val, priv->mem_base + reg);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void ctucan_write32_be(struct ctucan_priv *priv,
11862306a36Sopenharmony_ci			      enum ctu_can_fd_can_registers reg, u32 val)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	iowrite32be(val, priv->mem_base + reg);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic u32 ctucan_read32_le(struct ctucan_priv *priv,
12462306a36Sopenharmony_ci			    enum ctu_can_fd_can_registers reg)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	return ioread32(priv->mem_base + reg);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic u32 ctucan_read32_be(struct ctucan_priv *priv,
13062306a36Sopenharmony_ci			    enum ctu_can_fd_can_registers reg)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return ioread32be(priv->mem_base + reg);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void ctucan_write32(struct ctucan_priv *priv, enum ctu_can_fd_can_registers reg, u32 val)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	priv->write_reg(priv, reg, val);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic u32 ctucan_read32(struct ctucan_priv *priv, enum ctu_can_fd_can_registers reg)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	return priv->read_reg(priv, reg);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void ctucan_write_txt_buf(struct ctucan_priv *priv, enum ctu_can_fd_can_registers buf_base,
14662306a36Sopenharmony_ci				 u32 offset, u32 val)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	priv->write_reg(priv, buf_base + offset, val);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#define CTU_CAN_FD_TXTNF(priv) (!!FIELD_GET(REG_STATUS_TXNF, ctucan_read32(priv, CTUCANFD_STATUS)))
15262306a36Sopenharmony_ci#define CTU_CAN_FD_ENABLED(priv) (!!FIELD_GET(REG_MODE_ENA, ctucan_read32(priv, CTUCANFD_MODE)))
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/**
15562306a36Sopenharmony_ci * ctucan_state_to_str() - Converts CAN controller state code to corresponding text
15662306a36Sopenharmony_ci * @state:	CAN controller state code
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * Return: Pointer to string representation of the error state
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_cistatic const char *ctucan_state_to_str(enum can_state state)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	const char *txt = NULL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (state >= 0 && state < CAN_STATE_MAX)
16562306a36Sopenharmony_ci		txt = ctucan_state_strings[state];
16662306a36Sopenharmony_ci	return txt ? txt : "UNKNOWN";
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/**
17062306a36Sopenharmony_ci * ctucan_reset() - Issues software reset request to CTU CAN FD
17162306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * Return: 0 for success, -%ETIMEDOUT if CAN controller does not leave reset
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_cistatic int ctucan_reset(struct net_device *ndev)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
17862306a36Sopenharmony_ci	int i = 100;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_MODE, REG_MODE_RST);
18162306a36Sopenharmony_ci	clear_bit(CTUCANFD_FLAG_RX_FFW_BUFFERED, &priv->drv_flags);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	do {
18462306a36Sopenharmony_ci		u16 device_id = FIELD_GET(REG_DEVICE_ID_DEVICE_ID,
18562306a36Sopenharmony_ci					  ctucan_read32(priv, CTUCANFD_DEVICE_ID));
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		if (device_id == 0xCAFD)
18862306a36Sopenharmony_ci			return 0;
18962306a36Sopenharmony_ci		if (!i--) {
19062306a36Sopenharmony_ci			netdev_warn(ndev, "device did not leave reset\n");
19162306a36Sopenharmony_ci			return -ETIMEDOUT;
19262306a36Sopenharmony_ci		}
19362306a36Sopenharmony_ci		usleep_range(100, 200);
19462306a36Sopenharmony_ci	} while (1);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * ctucan_set_btr() - Sets CAN bus bit timing in CTU CAN FD
19962306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
20062306a36Sopenharmony_ci * @bt:		Pointer to Bit timing structure
20162306a36Sopenharmony_ci * @nominal:	True - Nominal bit timing, False - Data bit timing
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * Return: 0 - OK, -%EPERM if controller is enabled
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic int ctucan_set_btr(struct net_device *ndev, struct can_bittiming *bt, bool nominal)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
20862306a36Sopenharmony_ci	int max_ph1_len = 31;
20962306a36Sopenharmony_ci	u32 btr = 0;
21062306a36Sopenharmony_ci	u32 prop_seg = bt->prop_seg;
21162306a36Sopenharmony_ci	u32 phase_seg1 = bt->phase_seg1;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (CTU_CAN_FD_ENABLED(priv)) {
21462306a36Sopenharmony_ci		netdev_err(ndev, "BUG! Cannot set bittiming - CAN is enabled\n");
21562306a36Sopenharmony_ci		return -EPERM;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (nominal)
21962306a36Sopenharmony_ci		max_ph1_len = 63;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* The timing calculation functions have only constraints on tseg1, which is prop_seg +
22262306a36Sopenharmony_ci	 * phase1_seg combined. tseg1 is then split in half and stored into prog_seg and phase_seg1.
22362306a36Sopenharmony_ci	 * In CTU CAN FD, PROP is 6/7 bits wide but PH1 only 6/5, so we must re-distribute the
22462306a36Sopenharmony_ci	 * values here.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	if (phase_seg1 > max_ph1_len) {
22762306a36Sopenharmony_ci		prop_seg += phase_seg1 - max_ph1_len;
22862306a36Sopenharmony_ci		phase_seg1 = max_ph1_len;
22962306a36Sopenharmony_ci		bt->prop_seg = prop_seg;
23062306a36Sopenharmony_ci		bt->phase_seg1 = phase_seg1;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (nominal) {
23462306a36Sopenharmony_ci		btr = FIELD_PREP(REG_BTR_PROP, prop_seg);
23562306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_PH1, phase_seg1);
23662306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_PH2, bt->phase_seg2);
23762306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_BRP, bt->brp);
23862306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_SJW, bt->sjw);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		ctucan_write32(priv, CTUCANFD_BTR, btr);
24162306a36Sopenharmony_ci	} else {
24262306a36Sopenharmony_ci		btr = FIELD_PREP(REG_BTR_FD_PROP_FD, prop_seg);
24362306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_FD_PH1_FD, phase_seg1);
24462306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_FD_PH2_FD, bt->phase_seg2);
24562306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_FD_BRP_FD, bt->brp);
24662306a36Sopenharmony_ci		btr |= FIELD_PREP(REG_BTR_FD_SJW_FD, bt->sjw);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		ctucan_write32(priv, CTUCANFD_BTR_FD, btr);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/**
25562306a36Sopenharmony_ci * ctucan_set_bittiming() - CAN set nominal bit timing routine
25662306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * Return: 0 on success, -%EPERM on error
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_cistatic int ctucan_set_bittiming(struct net_device *ndev)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
26362306a36Sopenharmony_ci	struct can_bittiming *bt = &priv->can.bittiming;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Note that bt may be modified here */
26662306a36Sopenharmony_ci	return ctucan_set_btr(ndev, bt, true);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/**
27062306a36Sopenharmony_ci * ctucan_set_data_bittiming() - CAN set data bit timing routine
27162306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * Return: 0 on success, -%EPERM on error
27462306a36Sopenharmony_ci */
27562306a36Sopenharmony_cistatic int ctucan_set_data_bittiming(struct net_device *ndev)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
27862306a36Sopenharmony_ci	struct can_bittiming *dbt = &priv->can.data_bittiming;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Note that dbt may be modified here */
28162306a36Sopenharmony_ci	return ctucan_set_btr(ndev, dbt, false);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci/**
28562306a36Sopenharmony_ci * ctucan_set_secondary_sample_point() - Sets secondary sample point in CTU CAN FD
28662306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Return: 0 on success, -%EPERM if controller is enabled
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic int ctucan_set_secondary_sample_point(struct net_device *ndev)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
29362306a36Sopenharmony_ci	struct can_bittiming *dbt = &priv->can.data_bittiming;
29462306a36Sopenharmony_ci	int ssp_offset = 0;
29562306a36Sopenharmony_ci	u32 ssp_cfg = 0; /* No SSP by default */
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (CTU_CAN_FD_ENABLED(priv)) {
29862306a36Sopenharmony_ci		netdev_err(ndev, "BUG! Cannot set SSP - CAN is enabled\n");
29962306a36Sopenharmony_ci		return -EPERM;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Use SSP for bit-rates above 1 Mbits/s */
30362306a36Sopenharmony_ci	if (dbt->bitrate > 1000000) {
30462306a36Sopenharmony_ci		/* Calculate SSP in minimal time quanta */
30562306a36Sopenharmony_ci		ssp_offset = (priv->can.clock.freq / 1000) * dbt->sample_point / dbt->bitrate;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (ssp_offset > 127) {
30862306a36Sopenharmony_ci			netdev_warn(ndev, "SSP offset saturated to 127\n");
30962306a36Sopenharmony_ci			ssp_offset = 127;
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		ssp_cfg = FIELD_PREP(REG_TRV_DELAY_SSP_OFFSET, ssp_offset);
31362306a36Sopenharmony_ci		ssp_cfg |= FIELD_PREP(REG_TRV_DELAY_SSP_SRC, 0x1);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_TRV_DELAY, ssp_cfg);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/**
32262306a36Sopenharmony_ci * ctucan_set_mode() - Sets CTU CAN FDs mode
32362306a36Sopenharmony_ci * @priv:	Pointer to private data
32462306a36Sopenharmony_ci * @mode:	Pointer to controller modes to be set
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_cistatic void ctucan_set_mode(struct ctucan_priv *priv, const struct can_ctrlmode *mode)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	u32 mode_reg = ctucan_read32(priv, CTUCANFD_MODE);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mode_reg = (mode->flags & CAN_CTRLMODE_LOOPBACK) ?
33162306a36Sopenharmony_ci			(mode_reg | REG_MODE_ILBP) :
33262306a36Sopenharmony_ci			(mode_reg & ~REG_MODE_ILBP);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	mode_reg = (mode->flags & CAN_CTRLMODE_LISTENONLY) ?
33562306a36Sopenharmony_ci			(mode_reg | REG_MODE_BMM) :
33662306a36Sopenharmony_ci			(mode_reg & ~REG_MODE_BMM);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mode_reg = (mode->flags & CAN_CTRLMODE_FD) ?
33962306a36Sopenharmony_ci			(mode_reg | REG_MODE_FDE) :
34062306a36Sopenharmony_ci			(mode_reg & ~REG_MODE_FDE);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	mode_reg = (mode->flags & CAN_CTRLMODE_PRESUME_ACK) ?
34362306a36Sopenharmony_ci			(mode_reg | REG_MODE_ACF) :
34462306a36Sopenharmony_ci			(mode_reg & ~REG_MODE_ACF);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	mode_reg = (mode->flags & CAN_CTRLMODE_FD_NON_ISO) ?
34762306a36Sopenharmony_ci			(mode_reg | REG_MODE_NISOFD) :
34862306a36Sopenharmony_ci			(mode_reg & ~REG_MODE_NISOFD);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* One shot mode supported indirectly via Retransmit limit */
35162306a36Sopenharmony_ci	mode_reg &= ~FIELD_PREP(REG_MODE_RTRTH, 0xF);
35262306a36Sopenharmony_ci	mode_reg = (mode->flags & CAN_CTRLMODE_ONE_SHOT) ?
35362306a36Sopenharmony_ci			(mode_reg | REG_MODE_RTRLE) :
35462306a36Sopenharmony_ci			(mode_reg & ~REG_MODE_RTRLE);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Some bits fixed:
35762306a36Sopenharmony_ci	 *   TSTM  - Off, User shall not be able to change REC/TEC by hand during operation
35862306a36Sopenharmony_ci	 */
35962306a36Sopenharmony_ci	mode_reg &= ~REG_MODE_TSTM;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_MODE, mode_reg);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/**
36562306a36Sopenharmony_ci * ctucan_chip_start() - This routine starts the driver
36662306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
36762306a36Sopenharmony_ci *
36862306a36Sopenharmony_ci * Routine expects that chip is in reset state. It setups initial
36962306a36Sopenharmony_ci * Tx buffers for FIFO priorities, sets bittiming, enables interrupts,
37062306a36Sopenharmony_ci * switches core to operational mode and changes controller
37162306a36Sopenharmony_ci * state to %CAN_STATE_STOPPED.
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * Return: 0 on success and failure value on error
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_cistatic int ctucan_chip_start(struct net_device *ndev)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
37862306a36Sopenharmony_ci	u32 int_ena, int_msk;
37962306a36Sopenharmony_ci	u32 mode_reg;
38062306a36Sopenharmony_ci	int err;
38162306a36Sopenharmony_ci	struct can_ctrlmode mode;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	priv->txb_prio = 0x01234567;
38462306a36Sopenharmony_ci	priv->txb_head = 0;
38562306a36Sopenharmony_ci	priv->txb_tail = 0;
38662306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_TX_PRIORITY, priv->txb_prio);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Configure bit-rates and ssp */
38962306a36Sopenharmony_ci	err = ctucan_set_bittiming(ndev);
39062306a36Sopenharmony_ci	if (err < 0)
39162306a36Sopenharmony_ci		return err;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	err = ctucan_set_data_bittiming(ndev);
39462306a36Sopenharmony_ci	if (err < 0)
39562306a36Sopenharmony_ci		return err;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	err = ctucan_set_secondary_sample_point(ndev);
39862306a36Sopenharmony_ci	if (err < 0)
39962306a36Sopenharmony_ci		return err;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Configure modes */
40262306a36Sopenharmony_ci	mode.flags = priv->can.ctrlmode;
40362306a36Sopenharmony_ci	mode.mask = 0xFFFFFFFF;
40462306a36Sopenharmony_ci	ctucan_set_mode(priv, &mode);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Configure interrupts */
40762306a36Sopenharmony_ci	int_ena = REG_INT_STAT_RBNEI |
40862306a36Sopenharmony_ci		  REG_INT_STAT_TXBHCI |
40962306a36Sopenharmony_ci		  REG_INT_STAT_EWLI |
41062306a36Sopenharmony_ci		  REG_INT_STAT_FCSI;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Bus error reporting -> Allow Error/Arb.lost interrupts */
41362306a36Sopenharmony_ci	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
41462306a36Sopenharmony_ci		int_ena |= REG_INT_STAT_ALI |
41562306a36Sopenharmony_ci			   REG_INT_STAT_BEI;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	int_msk = ~int_ena; /* Mask all disabled interrupts */
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* It's after reset, so there is no need to clear anything */
42162306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_INT_MASK_SET, int_msk);
42262306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_INT_ENA_SET, int_ena);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/* Controller enters ERROR_ACTIVE on initial FCSI */
42562306a36Sopenharmony_ci	priv->can.state = CAN_STATE_STOPPED;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Enable the controller */
42862306a36Sopenharmony_ci	mode_reg = ctucan_read32(priv, CTUCANFD_MODE);
42962306a36Sopenharmony_ci	mode_reg |= REG_MODE_ENA;
43062306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_MODE, mode_reg);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci/**
43662306a36Sopenharmony_ci * ctucan_do_set_mode() - Sets mode of the driver
43762306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
43862306a36Sopenharmony_ci * @mode:	Tells the mode of the driver
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * This check the drivers state and calls the corresponding modes to set.
44162306a36Sopenharmony_ci *
44262306a36Sopenharmony_ci * Return: 0 on success and failure value on error
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic int ctucan_do_set_mode(struct net_device *ndev, enum can_mode mode)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	int ret;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	switch (mode) {
44962306a36Sopenharmony_ci	case CAN_MODE_START:
45062306a36Sopenharmony_ci		ret = ctucan_reset(ndev);
45162306a36Sopenharmony_ci		if (ret < 0)
45262306a36Sopenharmony_ci			return ret;
45362306a36Sopenharmony_ci		ret = ctucan_chip_start(ndev);
45462306a36Sopenharmony_ci		if (ret < 0) {
45562306a36Sopenharmony_ci			netdev_err(ndev, "ctucan_chip_start failed!\n");
45662306a36Sopenharmony_ci			return ret;
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci		netif_wake_queue(ndev);
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	default:
46162306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return ret;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/**
46962306a36Sopenharmony_ci * ctucan_get_tx_status() - Gets status of TXT buffer
47062306a36Sopenharmony_ci * @priv:	Pointer to private data
47162306a36Sopenharmony_ci * @buf:	Buffer index (0-based)
47262306a36Sopenharmony_ci *
47362306a36Sopenharmony_ci * Return: Status of TXT buffer
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_cistatic enum ctucan_txtb_status ctucan_get_tx_status(struct ctucan_priv *priv, u8 buf)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	u32 tx_status = ctucan_read32(priv, CTUCANFD_TX_STATUS);
47862306a36Sopenharmony_ci	enum ctucan_txtb_status status = (tx_status >> (buf * 4)) & 0x7;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return status;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci/**
48462306a36Sopenharmony_ci * ctucan_is_txt_buf_writable() - Checks if frame can be inserted to TXT Buffer
48562306a36Sopenharmony_ci * @priv:	Pointer to private data
48662306a36Sopenharmony_ci * @buf:	Buffer index (0-based)
48762306a36Sopenharmony_ci *
48862306a36Sopenharmony_ci * Return: True - Frame can be inserted to TXT Buffer, False - If attempted, frame will not be
48962306a36Sopenharmony_ci *	   inserted to TXT Buffer
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic bool ctucan_is_txt_buf_writable(struct ctucan_priv *priv, u8 buf)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	enum ctucan_txtb_status buf_status;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	buf_status = ctucan_get_tx_status(priv, buf);
49662306a36Sopenharmony_ci	if (buf_status == TXT_RDY || buf_status == TXT_TRAN || buf_status == TXT_ABTP)
49762306a36Sopenharmony_ci		return false;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return true;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/**
50362306a36Sopenharmony_ci * ctucan_insert_frame() - Inserts frame to TXT buffer
50462306a36Sopenharmony_ci * @priv:	Pointer to private data
50562306a36Sopenharmony_ci * @cf:		Pointer to CAN frame to be inserted
50662306a36Sopenharmony_ci * @buf:	TXT Buffer index to which frame is inserted (0-based)
50762306a36Sopenharmony_ci * @isfdf:	True - CAN FD Frame, False - CAN 2.0 Frame
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * Return: True - Frame inserted successfully
51062306a36Sopenharmony_ci *	   False - Frame was not inserted due to one of:
51162306a36Sopenharmony_ci *			1. TXT Buffer is not writable (it is in wrong state)
51262306a36Sopenharmony_ci *			2. Invalid TXT buffer index
51362306a36Sopenharmony_ci *			3. Invalid frame length
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cistatic bool ctucan_insert_frame(struct ctucan_priv *priv, const struct canfd_frame *cf, u8 buf,
51662306a36Sopenharmony_ci				bool isfdf)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	u32 buf_base;
51962306a36Sopenharmony_ci	u32 ffw = 0;
52062306a36Sopenharmony_ci	u32 idw = 0;
52162306a36Sopenharmony_ci	unsigned int i;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (buf >= priv->ntxbufs)
52462306a36Sopenharmony_ci		return false;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (!ctucan_is_txt_buf_writable(priv, buf))
52762306a36Sopenharmony_ci		return false;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (cf->len > CANFD_MAX_DLEN)
53062306a36Sopenharmony_ci		return false;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Prepare Frame format */
53362306a36Sopenharmony_ci	if (cf->can_id & CAN_RTR_FLAG)
53462306a36Sopenharmony_ci		ffw |= REG_FRAME_FORMAT_W_RTR;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (cf->can_id & CAN_EFF_FLAG)
53762306a36Sopenharmony_ci		ffw |= REG_FRAME_FORMAT_W_IDE;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (isfdf) {
54062306a36Sopenharmony_ci		ffw |= REG_FRAME_FORMAT_W_FDF;
54162306a36Sopenharmony_ci		if (cf->flags & CANFD_BRS)
54262306a36Sopenharmony_ci			ffw |= REG_FRAME_FORMAT_W_BRS;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ffw |= FIELD_PREP(REG_FRAME_FORMAT_W_DLC, can_fd_len2dlc(cf->len));
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* Prepare identifier */
54862306a36Sopenharmony_ci	if (cf->can_id & CAN_EFF_FLAG)
54962306a36Sopenharmony_ci		idw = cf->can_id & CAN_EFF_MASK;
55062306a36Sopenharmony_ci	else
55162306a36Sopenharmony_ci		idw = FIELD_PREP(REG_IDENTIFIER_W_IDENTIFIER_BASE, cf->can_id & CAN_SFF_MASK);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* Write ID, Frame format, Don't write timestamp -> Time triggered transmission disabled */
55462306a36Sopenharmony_ci	buf_base = (buf + 1) * 0x100;
55562306a36Sopenharmony_ci	ctucan_write_txt_buf(priv, buf_base, CTUCANFD_FRAME_FORMAT_W, ffw);
55662306a36Sopenharmony_ci	ctucan_write_txt_buf(priv, buf_base, CTUCANFD_IDENTIFIER_W, idw);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* Write Data payload */
55962306a36Sopenharmony_ci	if (!(cf->can_id & CAN_RTR_FLAG)) {
56062306a36Sopenharmony_ci		for (i = 0; i < cf->len; i += 4) {
56162306a36Sopenharmony_ci			u32 data = le32_to_cpu(*(__le32 *)(cf->data + i));
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci			ctucan_write_txt_buf(priv, buf_base, CTUCANFD_DATA_1_4_W + i, data);
56462306a36Sopenharmony_ci		}
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return true;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci/**
57162306a36Sopenharmony_ci * ctucan_give_txtb_cmd() - Applies command on TXT buffer
57262306a36Sopenharmony_ci * @priv:	Pointer to private data
57362306a36Sopenharmony_ci * @cmd:	Command to give
57462306a36Sopenharmony_ci * @buf:	Buffer index (0-based)
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_cistatic void ctucan_give_txtb_cmd(struct ctucan_priv *priv, enum ctucan_txtb_command cmd, u8 buf)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	u32 tx_cmd = cmd;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	tx_cmd |= 1 << (buf + 8);
58162306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_TX_COMMAND, tx_cmd);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci/**
58562306a36Sopenharmony_ci * ctucan_start_xmit() - Starts the transmission
58662306a36Sopenharmony_ci * @skb:	sk_buff pointer that contains data to be Txed
58762306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
58862306a36Sopenharmony_ci *
58962306a36Sopenharmony_ci * Invoked from upper layers to initiate transmission. Uses the next available free TXT Buffer and
59062306a36Sopenharmony_ci * populates its fields to start the transmission.
59162306a36Sopenharmony_ci *
59262306a36Sopenharmony_ci * Return: %NETDEV_TX_OK on success, %NETDEV_TX_BUSY when no free TXT buffer is available,
59362306a36Sopenharmony_ci *         negative return values reserved for error cases
59462306a36Sopenharmony_ci */
59562306a36Sopenharmony_cistatic netdev_tx_t ctucan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
59862306a36Sopenharmony_ci	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
59962306a36Sopenharmony_ci	u32 txtb_id;
60062306a36Sopenharmony_ci	bool ok;
60162306a36Sopenharmony_ci	unsigned long flags;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (can_dev_dropped_skb(ndev, skb))
60462306a36Sopenharmony_ci		return NETDEV_TX_OK;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (unlikely(!CTU_CAN_FD_TXTNF(priv))) {
60762306a36Sopenharmony_ci		netif_stop_queue(ndev);
60862306a36Sopenharmony_ci		netdev_err(ndev, "BUG!, no TXB free when queue awake!\n");
60962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	txtb_id = priv->txb_head % priv->ntxbufs;
61362306a36Sopenharmony_ci	ctucan_netdev_dbg(ndev, "%s: using TXB#%u\n", __func__, txtb_id);
61462306a36Sopenharmony_ci	ok = ctucan_insert_frame(priv, cf, txtb_id, can_is_canfd_skb(skb));
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (!ok) {
61762306a36Sopenharmony_ci		netdev_err(ndev, "BUG! TXNF set but cannot insert frame into TXTB! HW Bug?");
61862306a36Sopenharmony_ci		kfree_skb(skb);
61962306a36Sopenharmony_ci		ndev->stats.tx_dropped++;
62062306a36Sopenharmony_ci		return NETDEV_TX_OK;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	can_put_echo_skb(skb, ndev, txtb_id, 0);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->tx_lock, flags);
62662306a36Sopenharmony_ci	ctucan_give_txtb_cmd(priv, TXT_CMD_SET_READY, txtb_id);
62762306a36Sopenharmony_ci	priv->txb_head++;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Check if all TX buffers are full */
63062306a36Sopenharmony_ci	if (!CTU_CAN_FD_TXTNF(priv))
63162306a36Sopenharmony_ci		netif_stop_queue(ndev);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->tx_lock, flags);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return NETDEV_TX_OK;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * ctucan_read_rx_frame() - Reads frame from RX FIFO
64062306a36Sopenharmony_ci * @priv:	Pointer to CTU CAN FD's private data
64162306a36Sopenharmony_ci * @cf:		Pointer to CAN frame struct
64262306a36Sopenharmony_ci * @ffw:	Previously read frame format word
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Note: Frame format word must be read separately and provided in 'ffw'.
64562306a36Sopenharmony_ci */
64662306a36Sopenharmony_cistatic void ctucan_read_rx_frame(struct ctucan_priv *priv, struct canfd_frame *cf, u32 ffw)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	u32 idw;
64962306a36Sopenharmony_ci	unsigned int i;
65062306a36Sopenharmony_ci	unsigned int wc;
65162306a36Sopenharmony_ci	unsigned int len;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	idw = ctucan_read32(priv, CTUCANFD_RX_DATA);
65462306a36Sopenharmony_ci	if (FIELD_GET(REG_FRAME_FORMAT_W_IDE, ffw))
65562306a36Sopenharmony_ci		cf->can_id = (idw & CAN_EFF_MASK) | CAN_EFF_FLAG;
65662306a36Sopenharmony_ci	else
65762306a36Sopenharmony_ci		cf->can_id = (idw >> 18) & CAN_SFF_MASK;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* BRS, ESI, RTR Flags */
66062306a36Sopenharmony_ci	if (FIELD_GET(REG_FRAME_FORMAT_W_FDF, ffw)) {
66162306a36Sopenharmony_ci		if (FIELD_GET(REG_FRAME_FORMAT_W_BRS, ffw))
66262306a36Sopenharmony_ci			cf->flags |= CANFD_BRS;
66362306a36Sopenharmony_ci		if (FIELD_GET(REG_FRAME_FORMAT_W_ESI_RSV, ffw))
66462306a36Sopenharmony_ci			cf->flags |= CANFD_ESI;
66562306a36Sopenharmony_ci	} else if (FIELD_GET(REG_FRAME_FORMAT_W_RTR, ffw)) {
66662306a36Sopenharmony_ci		cf->can_id |= CAN_RTR_FLAG;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	wc = FIELD_GET(REG_FRAME_FORMAT_W_RWCNT, ffw) - 3;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* DLC */
67262306a36Sopenharmony_ci	if (FIELD_GET(REG_FRAME_FORMAT_W_DLC, ffw) <= 8) {
67362306a36Sopenharmony_ci		len = FIELD_GET(REG_FRAME_FORMAT_W_DLC, ffw);
67462306a36Sopenharmony_ci	} else {
67562306a36Sopenharmony_ci		if (FIELD_GET(REG_FRAME_FORMAT_W_FDF, ffw))
67662306a36Sopenharmony_ci			len = wc << 2;
67762306a36Sopenharmony_ci		else
67862306a36Sopenharmony_ci			len = 8;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci	cf->len = len;
68162306a36Sopenharmony_ci	if (unlikely(len > wc * 4))
68262306a36Sopenharmony_ci		len = wc * 4;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* Timestamp - Read and throw away */
68562306a36Sopenharmony_ci	ctucan_read32(priv, CTUCANFD_RX_DATA);
68662306a36Sopenharmony_ci	ctucan_read32(priv, CTUCANFD_RX_DATA);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* Data */
68962306a36Sopenharmony_ci	for (i = 0; i < len; i += 4) {
69062306a36Sopenharmony_ci		u32 data = ctucan_read32(priv, CTUCANFD_RX_DATA);
69162306a36Sopenharmony_ci		*(__le32 *)(cf->data + i) = cpu_to_le32(data);
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci	while (unlikely(i < wc * 4)) {
69462306a36Sopenharmony_ci		ctucan_read32(priv, CTUCANFD_RX_DATA);
69562306a36Sopenharmony_ci		i += 4;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/**
70062306a36Sopenharmony_ci * ctucan_rx() -  Called from CAN ISR to complete the received frame processing
70162306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * This function is invoked from the CAN isr(poll) to process the Rx frames. It does minimal
70462306a36Sopenharmony_ci * processing and invokes "netif_receive_skb" to complete further processing.
70562306a36Sopenharmony_ci * Return: 1 when frame is passed to the network layer, 0 when the first frame word is read but
70662306a36Sopenharmony_ci *	   system is out of free SKBs temporally and left code to resolve SKB allocation later,
70762306a36Sopenharmony_ci *         -%EAGAIN in a case of empty Rx FIFO.
70862306a36Sopenharmony_ci */
70962306a36Sopenharmony_cistatic int ctucan_rx(struct net_device *ndev)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
71262306a36Sopenharmony_ci	struct net_device_stats *stats = &ndev->stats;
71362306a36Sopenharmony_ci	struct canfd_frame *cf;
71462306a36Sopenharmony_ci	struct sk_buff *skb;
71562306a36Sopenharmony_ci	u32 ffw;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (test_bit(CTUCANFD_FLAG_RX_FFW_BUFFERED, &priv->drv_flags)) {
71862306a36Sopenharmony_ci		ffw = priv->rxfrm_first_word;
71962306a36Sopenharmony_ci		clear_bit(CTUCANFD_FLAG_RX_FFW_BUFFERED, &priv->drv_flags);
72062306a36Sopenharmony_ci	} else {
72162306a36Sopenharmony_ci		ffw = ctucan_read32(priv, CTUCANFD_RX_DATA);
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (!FIELD_GET(REG_FRAME_FORMAT_W_RWCNT, ffw))
72562306a36Sopenharmony_ci		return -EAGAIN;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (FIELD_GET(REG_FRAME_FORMAT_W_FDF, ffw))
72862306a36Sopenharmony_ci		skb = alloc_canfd_skb(ndev, &cf);
72962306a36Sopenharmony_ci	else
73062306a36Sopenharmony_ci		skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (unlikely(!skb)) {
73362306a36Sopenharmony_ci		priv->rxfrm_first_word = ffw;
73462306a36Sopenharmony_ci		set_bit(CTUCANFD_FLAG_RX_FFW_BUFFERED, &priv->drv_flags);
73562306a36Sopenharmony_ci		return 0;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	ctucan_read_rx_frame(priv, cf, ffw);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	stats->rx_bytes += cf->len;
74162306a36Sopenharmony_ci	stats->rx_packets++;
74262306a36Sopenharmony_ci	netif_receive_skb(skb);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	return 1;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/**
74862306a36Sopenharmony_ci * ctucan_read_fault_state() - Reads CTU CAN FDs fault confinement state.
74962306a36Sopenharmony_ci * @priv:	Pointer to private data
75062306a36Sopenharmony_ci *
75162306a36Sopenharmony_ci * Returns: Fault confinement state of controller
75262306a36Sopenharmony_ci */
75362306a36Sopenharmony_cistatic enum can_state ctucan_read_fault_state(struct ctucan_priv *priv)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	u32 fs;
75662306a36Sopenharmony_ci	u32 rec_tec;
75762306a36Sopenharmony_ci	u32 ewl;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	fs = ctucan_read32(priv, CTUCANFD_EWL);
76062306a36Sopenharmony_ci	rec_tec = ctucan_read32(priv, CTUCANFD_REC);
76162306a36Sopenharmony_ci	ewl = FIELD_GET(REG_EWL_EW_LIMIT, fs);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (FIELD_GET(REG_EWL_ERA, fs)) {
76462306a36Sopenharmony_ci		if (ewl > FIELD_GET(REG_REC_REC_VAL, rec_tec) &&
76562306a36Sopenharmony_ci		    ewl > FIELD_GET(REG_REC_TEC_VAL, rec_tec))
76662306a36Sopenharmony_ci			return CAN_STATE_ERROR_ACTIVE;
76762306a36Sopenharmony_ci		else
76862306a36Sopenharmony_ci			return CAN_STATE_ERROR_WARNING;
76962306a36Sopenharmony_ci	} else if (FIELD_GET(REG_EWL_ERP, fs)) {
77062306a36Sopenharmony_ci		return CAN_STATE_ERROR_PASSIVE;
77162306a36Sopenharmony_ci	} else if (FIELD_GET(REG_EWL_BOF, fs)) {
77262306a36Sopenharmony_ci		return CAN_STATE_BUS_OFF;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	WARN(true, "Invalid error state");
77662306a36Sopenharmony_ci	return CAN_STATE_ERROR_PASSIVE;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/**
78062306a36Sopenharmony_ci * ctucan_get_rec_tec() - Reads REC/TEC counter values from controller
78162306a36Sopenharmony_ci * @priv:	Pointer to private data
78262306a36Sopenharmony_ci * @bec:	Pointer to Error counter structure
78362306a36Sopenharmony_ci */
78462306a36Sopenharmony_cistatic void ctucan_get_rec_tec(struct ctucan_priv *priv, struct can_berr_counter *bec)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	u32 err_ctrs = ctucan_read32(priv, CTUCANFD_REC);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	bec->rxerr = FIELD_GET(REG_REC_REC_VAL, err_ctrs);
78962306a36Sopenharmony_ci	bec->txerr = FIELD_GET(REG_REC_TEC_VAL, err_ctrs);
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci/**
79362306a36Sopenharmony_ci * ctucan_err_interrupt() - Error frame ISR
79462306a36Sopenharmony_ci * @ndev:	net_device pointer
79562306a36Sopenharmony_ci * @isr:	interrupt status register value
79662306a36Sopenharmony_ci *
79762306a36Sopenharmony_ci * This is the CAN error interrupt and it will check the type of error and forward the error
79862306a36Sopenharmony_ci * frame to upper layers.
79962306a36Sopenharmony_ci */
80062306a36Sopenharmony_cistatic void ctucan_err_interrupt(struct net_device *ndev, u32 isr)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
80362306a36Sopenharmony_ci	struct net_device_stats *stats = &ndev->stats;
80462306a36Sopenharmony_ci	struct can_frame *cf;
80562306a36Sopenharmony_ci	struct sk_buff *skb;
80662306a36Sopenharmony_ci	enum can_state state;
80762306a36Sopenharmony_ci	struct can_berr_counter bec;
80862306a36Sopenharmony_ci	u32 err_capt_alc;
80962306a36Sopenharmony_ci	int dologerr = net_ratelimit();
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	ctucan_get_rec_tec(priv, &bec);
81262306a36Sopenharmony_ci	state = ctucan_read_fault_state(priv);
81362306a36Sopenharmony_ci	err_capt_alc = ctucan_read32(priv, CTUCANFD_ERR_CAPT);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	if (dologerr)
81662306a36Sopenharmony_ci		netdev_info(ndev, "%s: ISR = 0x%08x, rxerr %d, txerr %d, error type %lu, pos %lu, ALC id_field %lu, bit %lu\n",
81762306a36Sopenharmony_ci			    __func__, isr, bec.rxerr, bec.txerr,
81862306a36Sopenharmony_ci			    FIELD_GET(REG_ERR_CAPT_ERR_TYPE, err_capt_alc),
81962306a36Sopenharmony_ci			    FIELD_GET(REG_ERR_CAPT_ERR_POS, err_capt_alc),
82062306a36Sopenharmony_ci			    FIELD_GET(REG_ERR_CAPT_ALC_ID_FIELD, err_capt_alc),
82162306a36Sopenharmony_ci			    FIELD_GET(REG_ERR_CAPT_ALC_BIT, err_capt_alc));
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	skb = alloc_can_err_skb(ndev, &cf);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* EWLI: error warning limit condition met
82662306a36Sopenharmony_ci	 * FCSI: fault confinement state changed
82762306a36Sopenharmony_ci	 * ALI:  arbitration lost (just informative)
82862306a36Sopenharmony_ci	 * BEI:  bus error interrupt
82962306a36Sopenharmony_ci	 */
83062306a36Sopenharmony_ci	if (FIELD_GET(REG_INT_STAT_FCSI, isr) || FIELD_GET(REG_INT_STAT_EWLI, isr)) {
83162306a36Sopenharmony_ci		netdev_info(ndev, "state changes from %s to %s\n",
83262306a36Sopenharmony_ci			    ctucan_state_to_str(priv->can.state),
83362306a36Sopenharmony_ci			    ctucan_state_to_str(state));
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		if (priv->can.state == state)
83662306a36Sopenharmony_ci			netdev_warn(ndev,
83762306a36Sopenharmony_ci				    "current and previous state is the same! (missed interrupt?)\n");
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		priv->can.state = state;
84062306a36Sopenharmony_ci		switch (state) {
84162306a36Sopenharmony_ci		case CAN_STATE_BUS_OFF:
84262306a36Sopenharmony_ci			priv->can.can_stats.bus_off++;
84362306a36Sopenharmony_ci			can_bus_off(ndev);
84462306a36Sopenharmony_ci			if (skb)
84562306a36Sopenharmony_ci				cf->can_id |= CAN_ERR_BUSOFF;
84662306a36Sopenharmony_ci			break;
84762306a36Sopenharmony_ci		case CAN_STATE_ERROR_PASSIVE:
84862306a36Sopenharmony_ci			priv->can.can_stats.error_passive++;
84962306a36Sopenharmony_ci			if (skb) {
85062306a36Sopenharmony_ci				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
85162306a36Sopenharmony_ci				cf->data[1] = (bec.rxerr > 127) ?
85262306a36Sopenharmony_ci						CAN_ERR_CRTL_RX_PASSIVE :
85362306a36Sopenharmony_ci						CAN_ERR_CRTL_TX_PASSIVE;
85462306a36Sopenharmony_ci				cf->data[6] = bec.txerr;
85562306a36Sopenharmony_ci				cf->data[7] = bec.rxerr;
85662306a36Sopenharmony_ci			}
85762306a36Sopenharmony_ci			break;
85862306a36Sopenharmony_ci		case CAN_STATE_ERROR_WARNING:
85962306a36Sopenharmony_ci			priv->can.can_stats.error_warning++;
86062306a36Sopenharmony_ci			if (skb) {
86162306a36Sopenharmony_ci				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
86262306a36Sopenharmony_ci				cf->data[1] |= (bec.txerr > bec.rxerr) ?
86362306a36Sopenharmony_ci					CAN_ERR_CRTL_TX_WARNING :
86462306a36Sopenharmony_ci					CAN_ERR_CRTL_RX_WARNING;
86562306a36Sopenharmony_ci				cf->data[6] = bec.txerr;
86662306a36Sopenharmony_ci				cf->data[7] = bec.rxerr;
86762306a36Sopenharmony_ci			}
86862306a36Sopenharmony_ci			break;
86962306a36Sopenharmony_ci		case CAN_STATE_ERROR_ACTIVE:
87062306a36Sopenharmony_ci			cf->can_id |= CAN_ERR_CNT;
87162306a36Sopenharmony_ci			cf->data[1] = CAN_ERR_CRTL_ACTIVE;
87262306a36Sopenharmony_ci			cf->data[6] = bec.txerr;
87362306a36Sopenharmony_ci			cf->data[7] = bec.rxerr;
87462306a36Sopenharmony_ci			break;
87562306a36Sopenharmony_ci		default:
87662306a36Sopenharmony_ci			netdev_warn(ndev, "unhandled error state (%d:%s)!\n",
87762306a36Sopenharmony_ci				    state, ctucan_state_to_str(state));
87862306a36Sopenharmony_ci			break;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Check for Arbitration Lost interrupt */
88362306a36Sopenharmony_ci	if (FIELD_GET(REG_INT_STAT_ALI, isr)) {
88462306a36Sopenharmony_ci		if (dologerr)
88562306a36Sopenharmony_ci			netdev_info(ndev, "arbitration lost\n");
88662306a36Sopenharmony_ci		priv->can.can_stats.arbitration_lost++;
88762306a36Sopenharmony_ci		if (skb) {
88862306a36Sopenharmony_ci			cf->can_id |= CAN_ERR_LOSTARB;
88962306a36Sopenharmony_ci			cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/* Check for Bus Error interrupt */
89462306a36Sopenharmony_ci	if (FIELD_GET(REG_INT_STAT_BEI, isr)) {
89562306a36Sopenharmony_ci		netdev_info(ndev, "bus error\n");
89662306a36Sopenharmony_ci		priv->can.can_stats.bus_error++;
89762306a36Sopenharmony_ci		stats->rx_errors++;
89862306a36Sopenharmony_ci		if (skb) {
89962306a36Sopenharmony_ci			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
90062306a36Sopenharmony_ci			cf->data[2] = CAN_ERR_PROT_UNSPEC;
90162306a36Sopenharmony_ci			cf->data[3] = CAN_ERR_PROT_LOC_UNSPEC;
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (skb) {
90662306a36Sopenharmony_ci		stats->rx_packets++;
90762306a36Sopenharmony_ci		stats->rx_bytes += cf->can_dlc;
90862306a36Sopenharmony_ci		netif_rx(skb);
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci/**
91362306a36Sopenharmony_ci * ctucan_rx_poll() - Poll routine for rx packets (NAPI)
91462306a36Sopenharmony_ci * @napi:	NAPI structure pointer
91562306a36Sopenharmony_ci * @quota:	Max number of rx packets to be processed.
91662306a36Sopenharmony_ci *
91762306a36Sopenharmony_ci * This is the poll routine for rx part. It will process the packets maximux quota value.
91862306a36Sopenharmony_ci *
91962306a36Sopenharmony_ci * Return: Number of packets received
92062306a36Sopenharmony_ci */
92162306a36Sopenharmony_cistatic int ctucan_rx_poll(struct napi_struct *napi, int quota)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct net_device *ndev = napi->dev;
92462306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
92562306a36Sopenharmony_ci	int work_done = 0;
92662306a36Sopenharmony_ci	u32 status;
92762306a36Sopenharmony_ci	u32 framecnt;
92862306a36Sopenharmony_ci	int res = 1;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	framecnt = FIELD_GET(REG_RX_STATUS_RXFRC, ctucan_read32(priv, CTUCANFD_RX_STATUS));
93162306a36Sopenharmony_ci	while (framecnt && work_done < quota && res > 0) {
93262306a36Sopenharmony_ci		res = ctucan_rx(ndev);
93362306a36Sopenharmony_ci		work_done++;
93462306a36Sopenharmony_ci		framecnt = FIELD_GET(REG_RX_STATUS_RXFRC, ctucan_read32(priv, CTUCANFD_RX_STATUS));
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* Check for RX FIFO Overflow */
93862306a36Sopenharmony_ci	status = ctucan_read32(priv, CTUCANFD_STATUS);
93962306a36Sopenharmony_ci	if (FIELD_GET(REG_STATUS_DOR, status)) {
94062306a36Sopenharmony_ci		struct net_device_stats *stats = &ndev->stats;
94162306a36Sopenharmony_ci		struct can_frame *cf;
94262306a36Sopenharmony_ci		struct sk_buff *skb;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		netdev_info(ndev, "rx_poll: rx fifo overflow\n");
94562306a36Sopenharmony_ci		stats->rx_over_errors++;
94662306a36Sopenharmony_ci		stats->rx_errors++;
94762306a36Sopenharmony_ci		skb = alloc_can_err_skb(ndev, &cf);
94862306a36Sopenharmony_ci		if (skb) {
94962306a36Sopenharmony_ci			cf->can_id |= CAN_ERR_CRTL;
95062306a36Sopenharmony_ci			cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
95162306a36Sopenharmony_ci			stats->rx_packets++;
95262306a36Sopenharmony_ci			stats->rx_bytes += cf->can_dlc;
95362306a36Sopenharmony_ci			netif_rx(skb);
95462306a36Sopenharmony_ci		}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci		/* Clear Data Overrun */
95762306a36Sopenharmony_ci		ctucan_write32(priv, CTUCANFD_COMMAND, REG_COMMAND_CDO);
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (!framecnt && res != 0) {
96162306a36Sopenharmony_ci		if (napi_complete_done(napi, work_done)) {
96262306a36Sopenharmony_ci			/* Clear and enable RBNEI. It is level-triggered, so
96362306a36Sopenharmony_ci			 * there is no race condition.
96462306a36Sopenharmony_ci			 */
96562306a36Sopenharmony_ci			ctucan_write32(priv, CTUCANFD_INT_STAT, REG_INT_STAT_RBNEI);
96662306a36Sopenharmony_ci			ctucan_write32(priv, CTUCANFD_INT_MASK_CLR, REG_INT_STAT_RBNEI);
96762306a36Sopenharmony_ci		}
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return work_done;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci/**
97462306a36Sopenharmony_ci * ctucan_rotate_txb_prio() - Rotates priorities of TXT Buffers
97562306a36Sopenharmony_ci * @ndev:	net_device pointer
97662306a36Sopenharmony_ci */
97762306a36Sopenharmony_cistatic void ctucan_rotate_txb_prio(struct net_device *ndev)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
98062306a36Sopenharmony_ci	u32 prio = priv->txb_prio;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	prio = (prio << 4) | ((prio >> ((priv->ntxbufs - 1) * 4)) & 0xF);
98362306a36Sopenharmony_ci	ctucan_netdev_dbg(ndev, "%s: from 0x%08x to 0x%08x\n", __func__, priv->txb_prio, prio);
98462306a36Sopenharmony_ci	priv->txb_prio = prio;
98562306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_TX_PRIORITY, prio);
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci/**
98962306a36Sopenharmony_ci * ctucan_tx_interrupt() - Tx done Isr
99062306a36Sopenharmony_ci * @ndev:	net_device pointer
99162306a36Sopenharmony_ci */
99262306a36Sopenharmony_cistatic void ctucan_tx_interrupt(struct net_device *ndev)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
99562306a36Sopenharmony_ci	struct net_device_stats *stats = &ndev->stats;
99662306a36Sopenharmony_ci	bool first = true;
99762306a36Sopenharmony_ci	bool some_buffers_processed;
99862306a36Sopenharmony_ci	unsigned long flags;
99962306a36Sopenharmony_ci	enum ctucan_txtb_status txtb_status;
100062306a36Sopenharmony_ci	u32 txtb_id;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/*  read tx_status
100362306a36Sopenharmony_ci	 *  if txb[n].finished (bit 2)
100462306a36Sopenharmony_ci	 *	if ok -> echo
100562306a36Sopenharmony_ci	 *	if error / aborted -> ?? (find how to handle oneshot mode)
100662306a36Sopenharmony_ci	 *	txb_tail++
100762306a36Sopenharmony_ci	 */
100862306a36Sopenharmony_ci	do {
100962306a36Sopenharmony_ci		spin_lock_irqsave(&priv->tx_lock, flags);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		some_buffers_processed = false;
101262306a36Sopenharmony_ci		while ((int)(priv->txb_head - priv->txb_tail) > 0) {
101362306a36Sopenharmony_ci			txtb_id = priv->txb_tail % priv->ntxbufs;
101462306a36Sopenharmony_ci			txtb_status = ctucan_get_tx_status(priv, txtb_id);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci			ctucan_netdev_dbg(ndev, "TXI: TXB#%u: status 0x%x\n", txtb_id, txtb_status);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci			switch (txtb_status) {
101962306a36Sopenharmony_ci			case TXT_TOK:
102062306a36Sopenharmony_ci				ctucan_netdev_dbg(ndev, "TXT_OK\n");
102162306a36Sopenharmony_ci				stats->tx_bytes += can_get_echo_skb(ndev, txtb_id, NULL);
102262306a36Sopenharmony_ci				stats->tx_packets++;
102362306a36Sopenharmony_ci				break;
102462306a36Sopenharmony_ci			case TXT_ERR:
102562306a36Sopenharmony_ci				/* This indicated that retransmit limit has been reached. Obviously
102662306a36Sopenharmony_ci				 * we should not echo the frame, but also not indicate any kind of
102762306a36Sopenharmony_ci				 * error. If desired, it was already reported (possible multiple
102862306a36Sopenharmony_ci				 * times) on each arbitration lost.
102962306a36Sopenharmony_ci				 */
103062306a36Sopenharmony_ci				netdev_warn(ndev, "TXB in Error state\n");
103162306a36Sopenharmony_ci				can_free_echo_skb(ndev, txtb_id, NULL);
103262306a36Sopenharmony_ci				stats->tx_dropped++;
103362306a36Sopenharmony_ci				break;
103462306a36Sopenharmony_ci			case TXT_ABT:
103562306a36Sopenharmony_ci				/* Same as for TXT_ERR, only with different cause. We *could*
103662306a36Sopenharmony_ci				 * re-queue the frame, but multiqueue/abort is not supported yet
103762306a36Sopenharmony_ci				 * anyway.
103862306a36Sopenharmony_ci				 */
103962306a36Sopenharmony_ci				netdev_warn(ndev, "TXB in Aborted state\n");
104062306a36Sopenharmony_ci				can_free_echo_skb(ndev, txtb_id, NULL);
104162306a36Sopenharmony_ci				stats->tx_dropped++;
104262306a36Sopenharmony_ci				break;
104362306a36Sopenharmony_ci			default:
104462306a36Sopenharmony_ci				/* Bug only if the first buffer is not finished, otherwise it is
104562306a36Sopenharmony_ci				 * pretty much expected.
104662306a36Sopenharmony_ci				 */
104762306a36Sopenharmony_ci				if (first) {
104862306a36Sopenharmony_ci					netdev_err(ndev,
104962306a36Sopenharmony_ci						   "BUG: TXB#%u not in a finished state (0x%x)!\n",
105062306a36Sopenharmony_ci						   txtb_id, txtb_status);
105162306a36Sopenharmony_ci					spin_unlock_irqrestore(&priv->tx_lock, flags);
105262306a36Sopenharmony_ci					/* do not clear nor wake */
105362306a36Sopenharmony_ci					return;
105462306a36Sopenharmony_ci				}
105562306a36Sopenharmony_ci				goto clear;
105662306a36Sopenharmony_ci			}
105762306a36Sopenharmony_ci			priv->txb_tail++;
105862306a36Sopenharmony_ci			first = false;
105962306a36Sopenharmony_ci			some_buffers_processed = true;
106062306a36Sopenharmony_ci			/* Adjust priorities *before* marking the buffer as empty. */
106162306a36Sopenharmony_ci			ctucan_rotate_txb_prio(ndev);
106262306a36Sopenharmony_ci			ctucan_give_txtb_cmd(priv, TXT_CMD_SET_EMPTY, txtb_id);
106362306a36Sopenharmony_ci		}
106462306a36Sopenharmony_ciclear:
106562306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->tx_lock, flags);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		/* If no buffers were processed this time, we cannot clear - that would introduce
106862306a36Sopenharmony_ci		 * a race condition.
106962306a36Sopenharmony_ci		 */
107062306a36Sopenharmony_ci		if (some_buffers_processed) {
107162306a36Sopenharmony_ci			/* Clear the interrupt again. We do not want to receive again interrupt for
107262306a36Sopenharmony_ci			 * the buffer already handled. If it is the last finished one then it would
107362306a36Sopenharmony_ci			 * cause log of spurious interrupt.
107462306a36Sopenharmony_ci			 */
107562306a36Sopenharmony_ci			ctucan_write32(priv, CTUCANFD_INT_STAT, REG_INT_STAT_TXBHCI);
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci	} while (some_buffers_processed);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->tx_lock, flags);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* Check if at least one TX buffer is free */
108262306a36Sopenharmony_ci	if (CTU_CAN_FD_TXTNF(priv))
108362306a36Sopenharmony_ci		netif_wake_queue(ndev);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->tx_lock, flags);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci/**
108962306a36Sopenharmony_ci * ctucan_interrupt() - CAN Isr
109062306a36Sopenharmony_ci * @irq:	irq number
109162306a36Sopenharmony_ci * @dev_id:	device id pointer
109262306a36Sopenharmony_ci *
109362306a36Sopenharmony_ci * This is the CTU CAN FD ISR. It checks for the type of interrupt
109462306a36Sopenharmony_ci * and invokes the corresponding ISR.
109562306a36Sopenharmony_ci *
109662306a36Sopenharmony_ci * Return:
109762306a36Sopenharmony_ci * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
109862306a36Sopenharmony_ci */
109962306a36Sopenharmony_cistatic irqreturn_t ctucan_interrupt(int irq, void *dev_id)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct net_device *ndev = (struct net_device *)dev_id;
110262306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
110362306a36Sopenharmony_ci	u32 isr, icr;
110462306a36Sopenharmony_ci	u32 imask;
110562306a36Sopenharmony_ci	int irq_loops;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	for (irq_loops = 0; irq_loops < 10000; irq_loops++) {
110862306a36Sopenharmony_ci		/* Get the interrupt status */
110962306a36Sopenharmony_ci		isr = ctucan_read32(priv, CTUCANFD_INT_STAT);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		if (!isr)
111262306a36Sopenharmony_ci			return irq_loops ? IRQ_HANDLED : IRQ_NONE;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		/* Receive Buffer Not Empty Interrupt */
111562306a36Sopenharmony_ci		if (FIELD_GET(REG_INT_STAT_RBNEI, isr)) {
111662306a36Sopenharmony_ci			ctucan_netdev_dbg(ndev, "RXBNEI\n");
111762306a36Sopenharmony_ci			/* Mask RXBNEI the first, then clear interrupt and schedule NAPI. Even if
111862306a36Sopenharmony_ci			 * another IRQ fires, RBNEI will always be 0 (masked).
111962306a36Sopenharmony_ci			 */
112062306a36Sopenharmony_ci			icr = REG_INT_STAT_RBNEI;
112162306a36Sopenharmony_ci			ctucan_write32(priv, CTUCANFD_INT_MASK_SET, icr);
112262306a36Sopenharmony_ci			ctucan_write32(priv, CTUCANFD_INT_STAT, icr);
112362306a36Sopenharmony_ci			napi_schedule(&priv->napi);
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		/* TXT Buffer HW Command Interrupt */
112762306a36Sopenharmony_ci		if (FIELD_GET(REG_INT_STAT_TXBHCI, isr)) {
112862306a36Sopenharmony_ci			ctucan_netdev_dbg(ndev, "TXBHCI\n");
112962306a36Sopenharmony_ci			/* Cleared inside */
113062306a36Sopenharmony_ci			ctucan_tx_interrupt(ndev);
113162306a36Sopenharmony_ci		}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		/* Error interrupts */
113462306a36Sopenharmony_ci		if (FIELD_GET(REG_INT_STAT_EWLI, isr) ||
113562306a36Sopenharmony_ci		    FIELD_GET(REG_INT_STAT_FCSI, isr) ||
113662306a36Sopenharmony_ci		    FIELD_GET(REG_INT_STAT_ALI, isr)) {
113762306a36Sopenharmony_ci			icr = isr & (REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci			ctucan_netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x\n", icr);
114062306a36Sopenharmony_ci			ctucan_write32(priv, CTUCANFD_INT_STAT, icr);
114162306a36Sopenharmony_ci			ctucan_err_interrupt(ndev, isr);
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci		/* Ignore RI, TI, LFI, RFI, BSI */
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	netdev_err(ndev, "%s: stuck interrupt (isr=0x%08x), stopping\n", __func__, isr);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (FIELD_GET(REG_INT_STAT_TXBHCI, isr)) {
114962306a36Sopenharmony_ci		int i;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		netdev_err(ndev, "txb_head=0x%08x txb_tail=0x%08x\n",
115262306a36Sopenharmony_ci			   priv->txb_head, priv->txb_tail);
115362306a36Sopenharmony_ci		for (i = 0; i < priv->ntxbufs; i++) {
115462306a36Sopenharmony_ci			u32 status = ctucan_get_tx_status(priv, i);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci			netdev_err(ndev, "txb[%d] txb status=0x%08x\n", i, status);
115762306a36Sopenharmony_ci		}
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	imask = 0xffffffff;
116162306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_INT_ENA_CLR, imask);
116262306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_INT_MASK_SET, imask);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return IRQ_HANDLED;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci/**
116862306a36Sopenharmony_ci * ctucan_chip_stop() - Driver stop routine
116962306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
117062306a36Sopenharmony_ci *
117162306a36Sopenharmony_ci * This is the drivers stop routine. It will disable the
117262306a36Sopenharmony_ci * interrupts and disable the controller.
117362306a36Sopenharmony_ci */
117462306a36Sopenharmony_cistatic void ctucan_chip_stop(struct net_device *ndev)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
117762306a36Sopenharmony_ci	u32 mask = 0xffffffff;
117862306a36Sopenharmony_ci	u32 mode;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	/* Disable interrupts and disable CAN */
118162306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_INT_ENA_CLR, mask);
118262306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_INT_MASK_SET, mask);
118362306a36Sopenharmony_ci	mode = ctucan_read32(priv, CTUCANFD_MODE);
118462306a36Sopenharmony_ci	mode &= ~REG_MODE_ENA;
118562306a36Sopenharmony_ci	ctucan_write32(priv, CTUCANFD_MODE, mode);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	priv->can.state = CAN_STATE_STOPPED;
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci/**
119162306a36Sopenharmony_ci * ctucan_open() - Driver open routine
119262306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
119362306a36Sopenharmony_ci *
119462306a36Sopenharmony_ci * This is the driver open routine.
119562306a36Sopenharmony_ci * Return: 0 on success and failure value on error
119662306a36Sopenharmony_ci */
119762306a36Sopenharmony_cistatic int ctucan_open(struct net_device *ndev)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
120062306a36Sopenharmony_ci	int ret;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ret = pm_runtime_get_sync(priv->dev);
120362306a36Sopenharmony_ci	if (ret < 0) {
120462306a36Sopenharmony_ci		netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
120562306a36Sopenharmony_ci			   __func__, ret);
120662306a36Sopenharmony_ci		pm_runtime_put_noidle(priv->dev);
120762306a36Sopenharmony_ci		return ret;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	ret = ctucan_reset(ndev);
121162306a36Sopenharmony_ci	if (ret < 0)
121262306a36Sopenharmony_ci		goto err_reset;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/* Common open */
121562306a36Sopenharmony_ci	ret = open_candev(ndev);
121662306a36Sopenharmony_ci	if (ret) {
121762306a36Sopenharmony_ci		netdev_warn(ndev, "open_candev failed!\n");
121862306a36Sopenharmony_ci		goto err_open;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	ret = request_irq(ndev->irq, ctucan_interrupt, priv->irq_flags, ndev->name, ndev);
122262306a36Sopenharmony_ci	if (ret < 0) {
122362306a36Sopenharmony_ci		netdev_err(ndev, "irq allocation for CAN failed\n");
122462306a36Sopenharmony_ci		goto err_irq;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	ret = ctucan_chip_start(ndev);
122862306a36Sopenharmony_ci	if (ret < 0) {
122962306a36Sopenharmony_ci		netdev_err(ndev, "ctucan_chip_start failed!\n");
123062306a36Sopenharmony_ci		goto err_chip_start;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	netdev_info(ndev, "ctu_can_fd device registered\n");
123462306a36Sopenharmony_ci	napi_enable(&priv->napi);
123562306a36Sopenharmony_ci	netif_start_queue(ndev);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cierr_chip_start:
124062306a36Sopenharmony_ci	free_irq(ndev->irq, ndev);
124162306a36Sopenharmony_cierr_irq:
124262306a36Sopenharmony_ci	close_candev(ndev);
124362306a36Sopenharmony_cierr_open:
124462306a36Sopenharmony_cierr_reset:
124562306a36Sopenharmony_ci	pm_runtime_put(priv->dev);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return ret;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/**
125162306a36Sopenharmony_ci * ctucan_close() - Driver close routine
125262306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
125362306a36Sopenharmony_ci *
125462306a36Sopenharmony_ci * Return: 0 always
125562306a36Sopenharmony_ci */
125662306a36Sopenharmony_cistatic int ctucan_close(struct net_device *ndev)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	netif_stop_queue(ndev);
126162306a36Sopenharmony_ci	napi_disable(&priv->napi);
126262306a36Sopenharmony_ci	ctucan_chip_stop(ndev);
126362306a36Sopenharmony_ci	free_irq(ndev->irq, ndev);
126462306a36Sopenharmony_ci	close_candev(ndev);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	pm_runtime_put(priv->dev);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	return 0;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci/**
127262306a36Sopenharmony_ci * ctucan_get_berr_counter() - error counter routine
127362306a36Sopenharmony_ci * @ndev:	Pointer to net_device structure
127462306a36Sopenharmony_ci * @bec:	Pointer to can_berr_counter structure
127562306a36Sopenharmony_ci *
127662306a36Sopenharmony_ci * This is the driver error counter routine.
127762306a36Sopenharmony_ci * Return: 0 on success and failure value on error
127862306a36Sopenharmony_ci */
127962306a36Sopenharmony_cistatic int ctucan_get_berr_counter(const struct net_device *ndev, struct can_berr_counter *bec)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
128262306a36Sopenharmony_ci	int ret;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	ret = pm_runtime_get_sync(priv->dev);
128562306a36Sopenharmony_ci	if (ret < 0) {
128662306a36Sopenharmony_ci		netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", __func__, ret);
128762306a36Sopenharmony_ci		pm_runtime_put_noidle(priv->dev);
128862306a36Sopenharmony_ci		return ret;
128962306a36Sopenharmony_ci	}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	ctucan_get_rec_tec(priv, bec);
129262306a36Sopenharmony_ci	pm_runtime_put(priv->dev);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	return 0;
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_cistatic const struct net_device_ops ctucan_netdev_ops = {
129862306a36Sopenharmony_ci	.ndo_open	= ctucan_open,
129962306a36Sopenharmony_ci	.ndo_stop	= ctucan_close,
130062306a36Sopenharmony_ci	.ndo_start_xmit	= ctucan_start_xmit,
130162306a36Sopenharmony_ci	.ndo_change_mtu	= can_change_mtu,
130262306a36Sopenharmony_ci};
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic const struct ethtool_ops ctucan_ethtool_ops = {
130562306a36Sopenharmony_ci	.get_ts_info = ethtool_op_get_ts_info,
130662306a36Sopenharmony_ci};
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ciint ctucan_suspend(struct device *dev)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
131162306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	if (netif_running(ndev)) {
131462306a36Sopenharmony_ci		netif_stop_queue(ndev);
131562306a36Sopenharmony_ci		netif_device_detach(ndev);
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	priv->can.state = CAN_STATE_SLEEPING;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ciEXPORT_SYMBOL(ctucan_suspend);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ciint ctucan_resume(struct device *dev)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
132762306a36Sopenharmony_ci	struct ctucan_priv *priv = netdev_priv(ndev);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	priv->can.state = CAN_STATE_ERROR_ACTIVE;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	if (netif_running(ndev)) {
133262306a36Sopenharmony_ci		netif_device_attach(ndev);
133362306a36Sopenharmony_ci		netif_start_queue(ndev);
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return 0;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ciEXPORT_SYMBOL(ctucan_resume);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ciint ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigned int ntxbufs,
134162306a36Sopenharmony_ci			unsigned long can_clk_rate, int pm_enable_call,
134262306a36Sopenharmony_ci			void (*set_drvdata_fnc)(struct device *dev, struct net_device *ndev))
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	struct ctucan_priv *priv;
134562306a36Sopenharmony_ci	struct net_device *ndev;
134662306a36Sopenharmony_ci	int ret;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	/* Create a CAN device instance */
134962306a36Sopenharmony_ci	ndev = alloc_candev(sizeof(struct ctucan_priv), ntxbufs);
135062306a36Sopenharmony_ci	if (!ndev)
135162306a36Sopenharmony_ci		return -ENOMEM;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	priv = netdev_priv(ndev);
135462306a36Sopenharmony_ci	spin_lock_init(&priv->tx_lock);
135562306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->peers_on_pdev);
135662306a36Sopenharmony_ci	priv->ntxbufs = ntxbufs;
135762306a36Sopenharmony_ci	priv->dev = dev;
135862306a36Sopenharmony_ci	priv->can.bittiming_const = &ctu_can_fd_bit_timing_max;
135962306a36Sopenharmony_ci	priv->can.data_bittiming_const = &ctu_can_fd_bit_timing_data_max;
136062306a36Sopenharmony_ci	priv->can.do_set_mode = ctucan_do_set_mode;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	/* Needed for timing adjustment to be performed as soon as possible */
136362306a36Sopenharmony_ci	priv->can.do_set_bittiming = ctucan_set_bittiming;
136462306a36Sopenharmony_ci	priv->can.do_set_data_bittiming = ctucan_set_data_bittiming;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	priv->can.do_get_berr_counter = ctucan_get_berr_counter;
136762306a36Sopenharmony_ci	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK
136862306a36Sopenharmony_ci					| CAN_CTRLMODE_LISTENONLY
136962306a36Sopenharmony_ci					| CAN_CTRLMODE_FD
137062306a36Sopenharmony_ci					| CAN_CTRLMODE_PRESUME_ACK
137162306a36Sopenharmony_ci					| CAN_CTRLMODE_BERR_REPORTING
137262306a36Sopenharmony_ci					| CAN_CTRLMODE_FD_NON_ISO
137362306a36Sopenharmony_ci					| CAN_CTRLMODE_ONE_SHOT;
137462306a36Sopenharmony_ci	priv->mem_base = addr;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	/* Get IRQ for the device */
137762306a36Sopenharmony_ci	ndev->irq = irq;
137862306a36Sopenharmony_ci	ndev->flags |= IFF_ECHO;	/* We support local echo */
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	if (set_drvdata_fnc)
138162306a36Sopenharmony_ci		set_drvdata_fnc(dev, ndev);
138262306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, dev);
138362306a36Sopenharmony_ci	ndev->netdev_ops = &ctucan_netdev_ops;
138462306a36Sopenharmony_ci	ndev->ethtool_ops = &ctucan_ethtool_ops;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	/* Getting the can_clk info */
138762306a36Sopenharmony_ci	if (!can_clk_rate) {
138862306a36Sopenharmony_ci		priv->can_clk = devm_clk_get(dev, NULL);
138962306a36Sopenharmony_ci		if (IS_ERR(priv->can_clk)) {
139062306a36Sopenharmony_ci			dev_err(dev, "Device clock not found.\n");
139162306a36Sopenharmony_ci			ret = PTR_ERR(priv->can_clk);
139262306a36Sopenharmony_ci			goto err_free;
139362306a36Sopenharmony_ci		}
139462306a36Sopenharmony_ci		can_clk_rate = clk_get_rate(priv->can_clk);
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	priv->write_reg = ctucan_write32_le;
139862306a36Sopenharmony_ci	priv->read_reg = ctucan_read32_le;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (pm_enable_call)
140162306a36Sopenharmony_ci		pm_runtime_enable(dev);
140262306a36Sopenharmony_ci	ret = pm_runtime_get_sync(dev);
140362306a36Sopenharmony_ci	if (ret < 0) {
140462306a36Sopenharmony_ci		netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
140562306a36Sopenharmony_ci			   __func__, ret);
140662306a36Sopenharmony_ci		pm_runtime_put_noidle(priv->dev);
140762306a36Sopenharmony_ci		goto err_pmdisable;
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	/* Check for big-endianity and set according IO-accessors */
141162306a36Sopenharmony_ci	if ((ctucan_read32(priv, CTUCANFD_DEVICE_ID) & 0xFFFF) != CTUCANFD_ID) {
141262306a36Sopenharmony_ci		priv->write_reg = ctucan_write32_be;
141362306a36Sopenharmony_ci		priv->read_reg = ctucan_read32_be;
141462306a36Sopenharmony_ci		if ((ctucan_read32(priv, CTUCANFD_DEVICE_ID) & 0xFFFF) != CTUCANFD_ID) {
141562306a36Sopenharmony_ci			netdev_err(ndev, "CTU_CAN_FD signature not found\n");
141662306a36Sopenharmony_ci			ret = -ENODEV;
141762306a36Sopenharmony_ci			goto err_deviceoff;
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	ret = ctucan_reset(ndev);
142262306a36Sopenharmony_ci	if (ret < 0)
142362306a36Sopenharmony_ci		goto err_deviceoff;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	priv->can.clock.freq = can_clk_rate;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	netif_napi_add(ndev, &priv->napi, ctucan_rx_poll);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	ret = register_candev(ndev);
143062306a36Sopenharmony_ci	if (ret) {
143162306a36Sopenharmony_ci		dev_err(dev, "fail to register failed (err=%d)\n", ret);
143262306a36Sopenharmony_ci		goto err_deviceoff;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	pm_runtime_put(dev);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	netdev_dbg(ndev, "mem_base=0x%p irq=%d clock=%d, no. of txt buffers:%d\n",
143862306a36Sopenharmony_ci		   priv->mem_base, ndev->irq, priv->can.clock.freq, priv->ntxbufs);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	return 0;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cierr_deviceoff:
144362306a36Sopenharmony_ci	pm_runtime_put(priv->dev);
144462306a36Sopenharmony_cierr_pmdisable:
144562306a36Sopenharmony_ci	if (pm_enable_call)
144662306a36Sopenharmony_ci		pm_runtime_disable(dev);
144762306a36Sopenharmony_cierr_free:
144862306a36Sopenharmony_ci	list_del_init(&priv->peers_on_pdev);
144962306a36Sopenharmony_ci	free_candev(ndev);
145062306a36Sopenharmony_ci	return ret;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ciEXPORT_SYMBOL(ctucan_probe_common);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
145562306a36Sopenharmony_ciMODULE_AUTHOR("Martin Jerabek <martin.jerabek01@gmail.com>");
145662306a36Sopenharmony_ciMODULE_AUTHOR("Pavel Pisa <pisa@cmp.felk.cvut.cz>");
145762306a36Sopenharmony_ciMODULE_AUTHOR("Ondrej Ille <ondrej.ille@gmail.com>");
145862306a36Sopenharmony_ciMODULE_DESCRIPTION("CTU CAN FD interface");
1459