162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> 362306a36Sopenharmony_ci * Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 PEAK System-Technik GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/can.h> 962306a36Sopenharmony_ci#include <linux/can/dev.h> 1062306a36Sopenharmony_ci#include <linux/ethtool.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "peak_canfd_user.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* internal IP core cache size (used as default echo skbs max number) */ 1562306a36Sopenharmony_ci#define PCANFD_ECHO_SKB_MAX 24 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* bittiming ranges of the PEAK-System PC CAN-FD interfaces */ 1862306a36Sopenharmony_cistatic const struct can_bittiming_const peak_canfd_nominal_const = { 1962306a36Sopenharmony_ci .name = "peak_canfd", 2062306a36Sopenharmony_ci .tseg1_min = 1, 2162306a36Sopenharmony_ci .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), 2262306a36Sopenharmony_ci .tseg2_min = 1, 2362306a36Sopenharmony_ci .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), 2462306a36Sopenharmony_ci .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), 2562306a36Sopenharmony_ci .brp_min = 1, 2662306a36Sopenharmony_ci .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), 2762306a36Sopenharmony_ci .brp_inc = 1, 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const struct can_bittiming_const peak_canfd_data_const = { 3162306a36Sopenharmony_ci .name = "peak_canfd", 3262306a36Sopenharmony_ci .tseg1_min = 1, 3362306a36Sopenharmony_ci .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), 3462306a36Sopenharmony_ci .tseg2_min = 1, 3562306a36Sopenharmony_ci .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), 3662306a36Sopenharmony_ci .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), 3762306a36Sopenharmony_ci .brp_min = 1, 3862306a36Sopenharmony_ci .brp_max = (1 << PUCAN_TFAST_BRP_BITS), 3962306a36Sopenharmony_ci .brp_inc = 1, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct peak_canfd_priv *pucan_init_cmd(struct peak_canfd_priv *priv) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci priv->cmd_len = 0; 4562306a36Sopenharmony_ci return priv; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void *pucan_add_cmd(struct peak_canfd_priv *priv, int cmd_op) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct pucan_command *cmd; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (priv->cmd_len + sizeof(*cmd) > priv->cmd_maxlen) 5362306a36Sopenharmony_ci return NULL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci cmd = priv->cmd_buffer + priv->cmd_len; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* reset all unused bit to default */ 5862306a36Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci cmd->opcode_channel = pucan_cmd_opcode_channel(priv->index, cmd_op); 6162306a36Sopenharmony_ci priv->cmd_len += sizeof(*cmd); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return cmd; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int pucan_write_cmd(struct peak_canfd_priv *priv) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int err; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (priv->pre_cmd) { 7162306a36Sopenharmony_ci err = priv->pre_cmd(priv); 7262306a36Sopenharmony_ci if (err) 7362306a36Sopenharmony_ci return err; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci err = priv->write_cmd(priv); 7762306a36Sopenharmony_ci if (err) 7862306a36Sopenharmony_ci return err; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (priv->post_cmd) 8162306a36Sopenharmony_ci err = priv->post_cmd(priv); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return err; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* uCAN commands interface functions */ 8762306a36Sopenharmony_cistatic int pucan_set_reset_mode(struct peak_canfd_priv *priv) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RESET_MODE); 9062306a36Sopenharmony_ci return pucan_write_cmd(priv); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int pucan_set_normal_mode(struct peak_canfd_priv *priv) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci int err; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_NORMAL_MODE); 9862306a36Sopenharmony_ci err = pucan_write_cmd(priv); 9962306a36Sopenharmony_ci if (!err) 10062306a36Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_ACTIVE; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return err; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int pucan_set_listen_only_mode(struct peak_canfd_priv *priv) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int err; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_LISTEN_ONLY_MODE); 11062306a36Sopenharmony_ci err = pucan_write_cmd(priv); 11162306a36Sopenharmony_ci if (!err) 11262306a36Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_ACTIVE; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return err; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int pucan_set_timing_slow(struct peak_canfd_priv *priv, 11862306a36Sopenharmony_ci const struct can_bittiming *pbt) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct pucan_timing_slow *cmd; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_SLOW); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci cmd->sjw_t = PUCAN_TSLOW_SJW_T(pbt->sjw - 1, 12562306a36Sopenharmony_ci priv->can.ctrlmode & 12662306a36Sopenharmony_ci CAN_CTRLMODE_3_SAMPLES); 12762306a36Sopenharmony_ci cmd->tseg1 = PUCAN_TSLOW_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); 12862306a36Sopenharmony_ci cmd->tseg2 = PUCAN_TSLOW_TSEG2(pbt->phase_seg2 - 1); 12962306a36Sopenharmony_ci cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(pbt->brp - 1)); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci cmd->ewl = 96; /* default */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci netdev_dbg(priv->ndev, 13462306a36Sopenharmony_ci "nominal: brp=%u tseg1=%u tseg2=%u sjw=%u\n", 13562306a36Sopenharmony_ci le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw_t); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return pucan_write_cmd(priv); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int pucan_set_timing_fast(struct peak_canfd_priv *priv, 14162306a36Sopenharmony_ci const struct can_bittiming *pbt) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct pucan_timing_fast *cmd; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_FAST); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci cmd->sjw = PUCAN_TFAST_SJW(pbt->sjw - 1); 14862306a36Sopenharmony_ci cmd->tseg1 = PUCAN_TFAST_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); 14962306a36Sopenharmony_ci cmd->tseg2 = PUCAN_TFAST_TSEG2(pbt->phase_seg2 - 1); 15062306a36Sopenharmony_ci cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(pbt->brp - 1)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci netdev_dbg(priv->ndev, 15362306a36Sopenharmony_ci "data: brp=%u tseg1=%u tseg2=%u sjw=%u\n", 15462306a36Sopenharmony_ci le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return pucan_write_cmd(priv); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int pucan_set_std_filter(struct peak_canfd_priv *priv, u8 row, u32 mask) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct pucan_std_filter *cmd; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_STD_FILTER); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* all the 11-bits CAN ID values are represented by one bit in a 16662306a36Sopenharmony_ci * 64 rows array of 32 bits: the upper 6 bits of the CAN ID select the 16762306a36Sopenharmony_ci * row while the lowest 5 bits select the bit in that row. 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * bit filter 17062306a36Sopenharmony_ci * 1 passed 17162306a36Sopenharmony_ci * 0 discarded 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* select the row */ 17562306a36Sopenharmony_ci cmd->idx = row; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* set/unset bits in the row */ 17862306a36Sopenharmony_ci cmd->mask = cpu_to_le32(mask); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return pucan_write_cmd(priv); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int pucan_tx_abort(struct peak_canfd_priv *priv, u16 flags) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct pucan_tx_abort *cmd; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TX_ABORT); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci cmd->flags = cpu_to_le16(flags); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return pucan_write_cmd(priv); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int pucan_clr_err_counters(struct peak_canfd_priv *priv) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct pucan_wr_err_cnt *cmd; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_WR_ERR_CNT); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci cmd->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE | PUCAN_WRERRCNT_RE); 20162306a36Sopenharmony_ci cmd->tx_counter = 0; 20262306a36Sopenharmony_ci cmd->rx_counter = 0; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return pucan_write_cmd(priv); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int pucan_set_options(struct peak_canfd_priv *priv, u16 opt_mask) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct pucan_options *cmd; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_EN_OPTION); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci cmd->options = cpu_to_le16(opt_mask); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return pucan_write_cmd(priv); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int pucan_clr_options(struct peak_canfd_priv *priv, u16 opt_mask) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct pucan_options *cmd; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_CLR_DIS_OPTION); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci cmd->options = cpu_to_le16(opt_mask); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return pucan_write_cmd(priv); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int pucan_setup_rx_barrier(struct peak_canfd_priv *priv) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RX_BARRIER); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return pucan_write_cmd(priv); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int pucan_netif_rx(struct sk_buff *skb, __le32 ts_low, __le32 ts_high) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); 23962306a36Sopenharmony_ci u64 ts_us; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ts_us = (u64)le32_to_cpu(ts_high) << 32; 24262306a36Sopenharmony_ci ts_us |= le32_to_cpu(ts_low); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* IP core timestamps are µs. */ 24562306a36Sopenharmony_ci hwts->hwtstamp = ns_to_ktime(ts_us * NSEC_PER_USEC); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return netif_rx(skb); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* handle the reception of one CAN frame */ 25162306a36Sopenharmony_cistatic int pucan_handle_can_rx(struct peak_canfd_priv *priv, 25262306a36Sopenharmony_ci struct pucan_rx_msg *msg) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct net_device_stats *stats = &priv->ndev->stats; 25562306a36Sopenharmony_ci struct canfd_frame *cf; 25662306a36Sopenharmony_ci struct sk_buff *skb; 25762306a36Sopenharmony_ci const u16 rx_msg_flags = le16_to_cpu(msg->flags); 25862306a36Sopenharmony_ci u8 cf_len; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) 26162306a36Sopenharmony_ci cf_len = can_fd_dlc2len(pucan_msg_get_dlc(msg)); 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci cf_len = can_cc_dlc2len(pucan_msg_get_dlc(msg)); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* if this frame is an echo, */ 26662306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_LOOPED_BACK) { 26762306a36Sopenharmony_ci unsigned long flags; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci spin_lock_irqsave(&priv->echo_lock, flags); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* count bytes of the echo instead of skb */ 27262306a36Sopenharmony_ci stats->tx_bytes += can_get_echo_skb(priv->ndev, msg->client, NULL); 27362306a36Sopenharmony_ci stats->tx_packets++; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* restart tx queue (a slot is free) */ 27662306a36Sopenharmony_ci netif_wake_queue(priv->ndev); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->echo_lock, flags); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* if this frame is only an echo, stop here. Otherwise, 28162306a36Sopenharmony_ci * continue to push this application self-received frame into 28262306a36Sopenharmony_ci * its own rx queue. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if (!(rx_msg_flags & PUCAN_MSG_SELF_RECEIVE)) 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* otherwise, it should be pushed into rx fifo */ 28962306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { 29062306a36Sopenharmony_ci /* CANFD frame case */ 29162306a36Sopenharmony_ci skb = alloc_canfd_skb(priv->ndev, &cf); 29262306a36Sopenharmony_ci if (!skb) 29362306a36Sopenharmony_ci return -ENOMEM; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) 29662306a36Sopenharmony_ci cf->flags |= CANFD_BRS; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) 29962306a36Sopenharmony_ci cf->flags |= CANFD_ESI; 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci /* CAN 2.0 frame case */ 30262306a36Sopenharmony_ci skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cf); 30362306a36Sopenharmony_ci if (!skb) 30462306a36Sopenharmony_ci return -ENOMEM; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci cf->can_id = le32_to_cpu(msg->can_id); 30862306a36Sopenharmony_ci cf->len = cf_len; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_EXT_ID) 31162306a36Sopenharmony_ci cf->can_id |= CAN_EFF_FLAG; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (rx_msg_flags & PUCAN_MSG_RTR) { 31462306a36Sopenharmony_ci cf->can_id |= CAN_RTR_FLAG; 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci memcpy(cf->data, msg->d, cf->len); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci stats->rx_bytes += cf->len; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci stats->rx_packets++; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci pucan_netif_rx(skb, msg->ts_low, msg->ts_high); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* handle rx/tx error counters notification */ 32862306a36Sopenharmony_cistatic int pucan_handle_error(struct peak_canfd_priv *priv, 32962306a36Sopenharmony_ci struct pucan_error_msg *msg) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci priv->bec.txerr = msg->tx_err_cnt; 33262306a36Sopenharmony_ci priv->bec.rxerr = msg->rx_err_cnt; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* handle status notification */ 33862306a36Sopenharmony_cistatic int pucan_handle_status(struct peak_canfd_priv *priv, 33962306a36Sopenharmony_ci struct pucan_status_msg *msg) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct net_device *ndev = priv->ndev; 34262306a36Sopenharmony_ci struct net_device_stats *stats = &ndev->stats; 34362306a36Sopenharmony_ci struct can_frame *cf; 34462306a36Sopenharmony_ci struct sk_buff *skb; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* this STATUS is the CNF of the RX_BARRIER: Tx path can be setup */ 34762306a36Sopenharmony_ci if (pucan_status_is_rx_barrier(msg)) { 34862306a36Sopenharmony_ci if (priv->enable_tx_path) { 34962306a36Sopenharmony_ci int err = priv->enable_tx_path(priv); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (err) 35262306a36Sopenharmony_ci return err; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* wake network queue up (echo_skb array is empty) */ 35662306a36Sopenharmony_ci netif_wake_queue(ndev); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci skb = alloc_can_err_skb(ndev, &cf); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* test state error bits according to their priority */ 36462306a36Sopenharmony_ci if (pucan_status_is_busoff(msg)) { 36562306a36Sopenharmony_ci netdev_dbg(ndev, "Bus-off entry status\n"); 36662306a36Sopenharmony_ci priv->can.state = CAN_STATE_BUS_OFF; 36762306a36Sopenharmony_ci priv->can.can_stats.bus_off++; 36862306a36Sopenharmony_ci can_bus_off(ndev); 36962306a36Sopenharmony_ci if (skb) 37062306a36Sopenharmony_ci cf->can_id |= CAN_ERR_BUSOFF; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci } else if (pucan_status_is_passive(msg)) { 37362306a36Sopenharmony_ci netdev_dbg(ndev, "Error passive status\n"); 37462306a36Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_PASSIVE; 37562306a36Sopenharmony_ci priv->can.can_stats.error_passive++; 37662306a36Sopenharmony_ci if (skb) { 37762306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; 37862306a36Sopenharmony_ci cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? 37962306a36Sopenharmony_ci CAN_ERR_CRTL_TX_PASSIVE : 38062306a36Sopenharmony_ci CAN_ERR_CRTL_RX_PASSIVE; 38162306a36Sopenharmony_ci cf->data[6] = priv->bec.txerr; 38262306a36Sopenharmony_ci cf->data[7] = priv->bec.rxerr; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci } else if (pucan_status_is_warning(msg)) { 38662306a36Sopenharmony_ci netdev_dbg(ndev, "Error warning status\n"); 38762306a36Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_WARNING; 38862306a36Sopenharmony_ci priv->can.can_stats.error_warning++; 38962306a36Sopenharmony_ci if (skb) { 39062306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; 39162306a36Sopenharmony_ci cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? 39262306a36Sopenharmony_ci CAN_ERR_CRTL_TX_WARNING : 39362306a36Sopenharmony_ci CAN_ERR_CRTL_RX_WARNING; 39462306a36Sopenharmony_ci cf->data[6] = priv->bec.txerr; 39562306a36Sopenharmony_ci cf->data[7] = priv->bec.rxerr; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci } else if (priv->can.state != CAN_STATE_ERROR_ACTIVE) { 39962306a36Sopenharmony_ci /* back to ERROR_ACTIVE */ 40062306a36Sopenharmony_ci netdev_dbg(ndev, "Error active status\n"); 40162306a36Sopenharmony_ci can_change_state(ndev, cf, CAN_STATE_ERROR_ACTIVE, 40262306a36Sopenharmony_ci CAN_STATE_ERROR_ACTIVE); 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci dev_kfree_skb(skb); 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!skb) { 40962306a36Sopenharmony_ci stats->rx_dropped++; 41062306a36Sopenharmony_ci return -ENOMEM; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci pucan_netif_rx(skb, msg->ts_low, msg->ts_high); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* handle uCAN Rx overflow notification */ 41962306a36Sopenharmony_cistatic int pucan_handle_cache_critical(struct peak_canfd_priv *priv) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct net_device_stats *stats = &priv->ndev->stats; 42262306a36Sopenharmony_ci struct can_frame *cf; 42362306a36Sopenharmony_ci struct sk_buff *skb; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci stats->rx_over_errors++; 42662306a36Sopenharmony_ci stats->rx_errors++; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci skb = alloc_can_err_skb(priv->ndev, &cf); 42962306a36Sopenharmony_ci if (!skb) { 43062306a36Sopenharmony_ci stats->rx_dropped++; 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; 43562306a36Sopenharmony_ci cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci cf->data[6] = priv->bec.txerr; 43862306a36Sopenharmony_ci cf->data[7] = priv->bec.rxerr; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci netif_rx(skb); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* handle a single uCAN message */ 44662306a36Sopenharmony_ciint peak_canfd_handle_msg(struct peak_canfd_priv *priv, 44762306a36Sopenharmony_ci struct pucan_rx_msg *msg) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci u16 msg_type = le16_to_cpu(msg->type); 45062306a36Sopenharmony_ci int msg_size = le16_to_cpu(msg->size); 45162306a36Sopenharmony_ci int err; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!msg_size || !msg_type) { 45462306a36Sopenharmony_ci /* null packet found: end of list */ 45562306a36Sopenharmony_ci goto exit; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci switch (msg_type) { 45962306a36Sopenharmony_ci case PUCAN_MSG_CAN_RX: 46062306a36Sopenharmony_ci err = pucan_handle_can_rx(priv, (struct pucan_rx_msg *)msg); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case PUCAN_MSG_ERROR: 46362306a36Sopenharmony_ci err = pucan_handle_error(priv, (struct pucan_error_msg *)msg); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case PUCAN_MSG_STATUS: 46662306a36Sopenharmony_ci err = pucan_handle_status(priv, (struct pucan_status_msg *)msg); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case PUCAN_MSG_CACHE_CRITICAL: 46962306a36Sopenharmony_ci err = pucan_handle_cache_critical(priv); 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci err = 0; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (err < 0) 47662306a36Sopenharmony_ci return err; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciexit: 47962306a36Sopenharmony_ci return msg_size; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* handle a list of rx_count messages from rx_msg memory address */ 48362306a36Sopenharmony_ciint peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, 48462306a36Sopenharmony_ci struct pucan_rx_msg *msg_list, int msg_count) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci void *msg_ptr = msg_list; 48762306a36Sopenharmony_ci int i, msg_size = 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for (i = 0; i < msg_count; i++) { 49062306a36Sopenharmony_ci msg_size = peak_canfd_handle_msg(priv, msg_ptr); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* a null packet can be found at the end of a list */ 49362306a36Sopenharmony_ci if (msg_size <= 0) 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci msg_ptr += ALIGN(msg_size, 4); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (msg_size < 0) 50062306a36Sopenharmony_ci return msg_size; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return i; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int peak_canfd_start(struct peak_canfd_priv *priv) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci int err; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci err = pucan_clr_err_counters(priv); 51062306a36Sopenharmony_ci if (err) 51162306a36Sopenharmony_ci goto err_exit; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci priv->echo_idx = 0; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci priv->bec.txerr = 0; 51662306a36Sopenharmony_ci priv->bec.rxerr = 0; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) 51962306a36Sopenharmony_ci err = pucan_set_listen_only_mode(priv); 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci err = pucan_set_normal_mode(priv); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cierr_exit: 52462306a36Sopenharmony_ci return err; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void peak_canfd_stop(struct peak_canfd_priv *priv) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci int err; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* go back to RESET mode */ 53262306a36Sopenharmony_ci err = pucan_set_reset_mode(priv); 53362306a36Sopenharmony_ci if (err) { 53462306a36Sopenharmony_ci netdev_err(priv->ndev, "channel %u reset failed\n", 53562306a36Sopenharmony_ci priv->index); 53662306a36Sopenharmony_ci } else { 53762306a36Sopenharmony_ci /* abort last Tx (MUST be done in RESET mode only!) */ 53862306a36Sopenharmony_ci pucan_tx_abort(priv, PUCAN_TX_ABORT_FLUSH); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int peak_canfd_set_mode(struct net_device *ndev, enum can_mode mode) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci switch (mode) { 54762306a36Sopenharmony_ci case CAN_MODE_START: 54862306a36Sopenharmony_ci peak_canfd_start(priv); 54962306a36Sopenharmony_ci netif_wake_queue(ndev); 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci default: 55262306a36Sopenharmony_ci return -EOPNOTSUPP; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int peak_canfd_get_berr_counter(const struct net_device *ndev, 55962306a36Sopenharmony_ci struct can_berr_counter *bec) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci *bec = priv->bec; 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int peak_canfd_open(struct net_device *ndev) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 57062306a36Sopenharmony_ci int i, err = 0; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci err = open_candev(ndev); 57362306a36Sopenharmony_ci if (err) { 57462306a36Sopenharmony_ci netdev_err(ndev, "open_candev() failed, error %d\n", err); 57562306a36Sopenharmony_ci goto err_exit; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci err = pucan_set_reset_mode(priv); 57962306a36Sopenharmony_ci if (err) 58062306a36Sopenharmony_ci goto err_close; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { 58362306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) 58462306a36Sopenharmony_ci err = pucan_clr_options(priv, PUCAN_OPTION_CANDFDISO); 58562306a36Sopenharmony_ci else 58662306a36Sopenharmony_ci err = pucan_set_options(priv, PUCAN_OPTION_CANDFDISO); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (err) 58962306a36Sopenharmony_ci goto err_close; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* set option: get rx/tx error counters */ 59362306a36Sopenharmony_ci err = pucan_set_options(priv, PUCAN_OPTION_ERROR); 59462306a36Sopenharmony_ci if (err) 59562306a36Sopenharmony_ci goto err_close; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* accept all standard CAN ID */ 59862306a36Sopenharmony_ci for (i = 0; i <= PUCAN_FLTSTD_ROW_IDX_MAX; i++) 59962306a36Sopenharmony_ci pucan_set_std_filter(priv, i, 0xffffffff); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci err = peak_canfd_start(priv); 60262306a36Sopenharmony_ci if (err) 60362306a36Sopenharmony_ci goto err_close; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* receiving the RB status says when Tx path is ready */ 60662306a36Sopenharmony_ci err = pucan_setup_rx_barrier(priv); 60762306a36Sopenharmony_ci if (!err) 60862306a36Sopenharmony_ci goto err_exit; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cierr_close: 61162306a36Sopenharmony_ci close_candev(ndev); 61262306a36Sopenharmony_cierr_exit: 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int peak_canfd_set_bittiming(struct net_device *ndev) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return pucan_set_timing_slow(priv, &priv->can.bittiming); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int peak_canfd_set_data_bittiming(struct net_device *ndev) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return pucan_set_timing_fast(priv, &priv->can.data_bittiming); 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int peak_canfd_close(struct net_device *ndev) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci netif_stop_queue(ndev); 63562306a36Sopenharmony_ci peak_canfd_stop(priv); 63662306a36Sopenharmony_ci close_candev(ndev); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb, 64262306a36Sopenharmony_ci struct net_device *ndev) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct peak_canfd_priv *priv = netdev_priv(ndev); 64562306a36Sopenharmony_ci struct net_device_stats *stats = &ndev->stats; 64662306a36Sopenharmony_ci struct canfd_frame *cf = (struct canfd_frame *)skb->data; 64762306a36Sopenharmony_ci struct pucan_tx_msg *msg; 64862306a36Sopenharmony_ci u16 msg_size, msg_flags; 64962306a36Sopenharmony_ci unsigned long flags; 65062306a36Sopenharmony_ci bool should_stop_tx_queue; 65162306a36Sopenharmony_ci int room_left; 65262306a36Sopenharmony_ci u8 len; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (can_dev_dropped_skb(ndev, skb)) 65562306a36Sopenharmony_ci return NETDEV_TX_OK; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci msg_size = ALIGN(sizeof(*msg) + cf->len, 4); 65862306a36Sopenharmony_ci msg = priv->alloc_tx_msg(priv, msg_size, &room_left); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* should never happen except under bus-off condition and (auto-)restart 66162306a36Sopenharmony_ci * mechanism 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci if (!msg) { 66462306a36Sopenharmony_ci stats->tx_dropped++; 66562306a36Sopenharmony_ci netif_stop_queue(ndev); 66662306a36Sopenharmony_ci return NETDEV_TX_BUSY; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci msg->size = cpu_to_le16(msg_size); 67062306a36Sopenharmony_ci msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); 67162306a36Sopenharmony_ci msg_flags = 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (cf->can_id & CAN_EFF_FLAG) { 67462306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_EXT_ID; 67562306a36Sopenharmony_ci msg->can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK); 67662306a36Sopenharmony_ci } else { 67762306a36Sopenharmony_ci msg->can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (can_is_canfd_skb(skb)) { 68162306a36Sopenharmony_ci /* CAN FD frame format */ 68262306a36Sopenharmony_ci len = can_fd_len2dlc(cf->len); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_EXT_DATA_LEN; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (cf->flags & CANFD_BRS) 68762306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_BITRATE_SWITCH; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (cf->flags & CANFD_ESI) 69062306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_ERROR_STATE_IND; 69162306a36Sopenharmony_ci } else { 69262306a36Sopenharmony_ci /* CAN 2.0 frame format */ 69362306a36Sopenharmony_ci len = cf->len; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (cf->can_id & CAN_RTR_FLAG) 69662306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_RTR; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* always ask loopback for echo management */ 70062306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_LOOPED_BACK; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* set driver specific bit to differentiate with application loopback */ 70362306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 70462306a36Sopenharmony_ci msg_flags |= PUCAN_MSG_SELF_RECEIVE; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci msg->flags = cpu_to_le16(msg_flags); 70762306a36Sopenharmony_ci msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, len); 70862306a36Sopenharmony_ci memcpy(msg->d, cf->data, cf->len); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* struct msg client field is used as an index in the echo skbs ring */ 71162306a36Sopenharmony_ci msg->client = priv->echo_idx; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci spin_lock_irqsave(&priv->echo_lock, flags); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* prepare and save echo skb in internal slot */ 71662306a36Sopenharmony_ci can_put_echo_skb(skb, ndev, priv->echo_idx, 0); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* move echo index to the next slot */ 71962306a36Sopenharmony_ci priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* if next slot is not free, stop network queue (no slot free in echo 72262306a36Sopenharmony_ci * skb ring means that the controller did not write these frames on 72362306a36Sopenharmony_ci * the bus: no need to continue). 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci should_stop_tx_queue = !!(priv->can.echo_skb[priv->echo_idx]); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* stop network tx queue if not enough room to save one more msg too */ 72862306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_FD) 72962306a36Sopenharmony_ci should_stop_tx_queue |= (room_left < 73062306a36Sopenharmony_ci (sizeof(*msg) + CANFD_MAX_DLEN)); 73162306a36Sopenharmony_ci else 73262306a36Sopenharmony_ci should_stop_tx_queue |= (room_left < 73362306a36Sopenharmony_ci (sizeof(*msg) + CAN_MAX_DLEN)); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (should_stop_tx_queue) 73662306a36Sopenharmony_ci netif_stop_queue(ndev); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->echo_lock, flags); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* write the skb on the interface */ 74162306a36Sopenharmony_ci priv->write_tx_msg(priv, msg); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return NETDEV_TX_OK; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int peak_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct hwtstamp_config hwts_cfg = { 0 }; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci switch (cmd) { 75162306a36Sopenharmony_ci case SIOCSHWTSTAMP: /* set */ 75262306a36Sopenharmony_ci if (copy_from_user(&hwts_cfg, ifr->ifr_data, sizeof(hwts_cfg))) 75362306a36Sopenharmony_ci return -EFAULT; 75462306a36Sopenharmony_ci if (hwts_cfg.tx_type == HWTSTAMP_TX_OFF && 75562306a36Sopenharmony_ci hwts_cfg.rx_filter == HWTSTAMP_FILTER_ALL) 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci return -ERANGE; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci case SIOCGHWTSTAMP: /* get */ 76062306a36Sopenharmony_ci hwts_cfg.tx_type = HWTSTAMP_TX_OFF; 76162306a36Sopenharmony_ci hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL; 76262306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, &hwts_cfg, sizeof(hwts_cfg))) 76362306a36Sopenharmony_ci return -EFAULT; 76462306a36Sopenharmony_ci return 0; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci default: 76762306a36Sopenharmony_ci return -EOPNOTSUPP; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic const struct net_device_ops peak_canfd_netdev_ops = { 77262306a36Sopenharmony_ci .ndo_open = peak_canfd_open, 77362306a36Sopenharmony_ci .ndo_stop = peak_canfd_close, 77462306a36Sopenharmony_ci .ndo_eth_ioctl = peak_eth_ioctl, 77562306a36Sopenharmony_ci .ndo_start_xmit = peak_canfd_start_xmit, 77662306a36Sopenharmony_ci .ndo_change_mtu = can_change_mtu, 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int peak_get_ts_info(struct net_device *dev, 78062306a36Sopenharmony_ci struct ethtool_ts_info *info) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci info->so_timestamping = 78362306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_SOFTWARE | 78462306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 78562306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 78662306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 78762306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 78862306a36Sopenharmony_ci info->phc_index = -1; 78962306a36Sopenharmony_ci info->tx_types = BIT(HWTSTAMP_TX_OFF); 79062306a36Sopenharmony_ci info->rx_filters = BIT(HWTSTAMP_FILTER_ALL); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic const struct ethtool_ops peak_canfd_ethtool_ops = { 79662306a36Sopenharmony_ci .get_ts_info = peak_get_ts_info, 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistruct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, 80062306a36Sopenharmony_ci int echo_skb_max) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct net_device *ndev; 80362306a36Sopenharmony_ci struct peak_canfd_priv *priv; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* we DO support local echo */ 80662306a36Sopenharmony_ci if (echo_skb_max < 0) 80762306a36Sopenharmony_ci echo_skb_max = PCANFD_ECHO_SKB_MAX; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* allocate the candev object */ 81062306a36Sopenharmony_ci ndev = alloc_candev(sizeof_priv, echo_skb_max); 81162306a36Sopenharmony_ci if (!ndev) 81262306a36Sopenharmony_ci return NULL; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci priv = netdev_priv(ndev); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* complete now socket-can initialization side */ 81762306a36Sopenharmony_ci priv->can.state = CAN_STATE_STOPPED; 81862306a36Sopenharmony_ci priv->can.bittiming_const = &peak_canfd_nominal_const; 81962306a36Sopenharmony_ci priv->can.data_bittiming_const = &peak_canfd_data_const; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci priv->can.do_set_mode = peak_canfd_set_mode; 82262306a36Sopenharmony_ci priv->can.do_get_berr_counter = peak_canfd_get_berr_counter; 82362306a36Sopenharmony_ci priv->can.do_set_bittiming = peak_canfd_set_bittiming; 82462306a36Sopenharmony_ci priv->can.do_set_data_bittiming = peak_canfd_set_data_bittiming; 82562306a36Sopenharmony_ci priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | 82662306a36Sopenharmony_ci CAN_CTRLMODE_LISTENONLY | 82762306a36Sopenharmony_ci CAN_CTRLMODE_3_SAMPLES | 82862306a36Sopenharmony_ci CAN_CTRLMODE_FD | 82962306a36Sopenharmony_ci CAN_CTRLMODE_FD_NON_ISO | 83062306a36Sopenharmony_ci CAN_CTRLMODE_BERR_REPORTING; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci priv->ndev = ndev; 83362306a36Sopenharmony_ci priv->index = index; 83462306a36Sopenharmony_ci priv->cmd_len = 0; 83562306a36Sopenharmony_ci spin_lock_init(&priv->echo_lock); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci ndev->flags |= IFF_ECHO; 83862306a36Sopenharmony_ci ndev->netdev_ops = &peak_canfd_netdev_ops; 83962306a36Sopenharmony_ci ndev->ethtool_ops = &peak_canfd_ethtool_ops; 84062306a36Sopenharmony_ci ndev->dev_id = index; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return ndev; 84362306a36Sopenharmony_ci} 844