diff options
Diffstat (limited to 'drivers/net/can/mscan/mscan.c')
| -rw-r--r-- | drivers/net/can/mscan/mscan.c | 160 |
1 files changed, 72 insertions, 88 deletions
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index e6b40954e204..39c7aa2a0b2f 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * CAN bus driver for the alone generic (as possible as) MSCAN controller. * @@ -5,19 +6,6 @@ * Varma Electronics Oy * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> * Copyright (C) 2008-2009 Pengutronix <kernel@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the version 2 of the GNU General Public License - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> @@ -46,12 +34,6 @@ static const struct can_bittiming_const mscan_bittiming_const = { .brp_inc = 1, }; -struct mscan_state { - u8 mode; - u8 canrier; - u8 cantier; -}; - static enum can_state state_map[] = { CAN_STATE_ERROR_ACTIVE, CAN_STATE_ERROR_WARNING, @@ -203,7 +185,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) int i, rtr, buf_id; u32 can_id; - if (can_dropped_invalid_skb(dev, skb)) + if (can_dev_dropped_skb(dev, skb)) return NETDEV_TX_OK; out_8(®s->cantier, 0); @@ -221,6 +203,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) * since buffer with lower id have higher priority (hell..) */ netif_stop_queue(dev); + fallthrough; case 2: if (buf_id < priv->prev_buf_id) { priv->cur_pri++; @@ -261,27 +244,27 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) void __iomem *data = ®s->tx.dsr1_0; u16 *payload = (u16 *)frame->data; - for (i = 0; i < frame->can_dlc / 2; i++) { + for (i = 0; i < frame->len / 2; i++) { out_be16(data, *payload++); data += 2 + _MSCAN_RESERVED_DSR_SIZE; } /* write remaining byte if necessary */ - if (frame->can_dlc & 1) - out_8(data, frame->data[frame->can_dlc - 1]); + if (frame->len & 1) + out_8(data, frame->data[frame->len - 1]); } - out_8(®s->tx.dlr, frame->can_dlc); + out_8(®s->tx.dlr, frame->len); out_8(®s->tx.tbpr, priv->cur_pri); /* Start transmission. */ out_8(®s->cantflg, 1 << buf_id); if (!test_bit(F_TX_PROGRESS, &priv->flags)) - dev->trans_start = jiffies; + netif_trans_update(dev); list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head); - can_put_echo_skb(skb, dev, buf_id); + can_put_echo_skb(skb, dev, buf_id, 0); /* Enable interrupt. */ priv->tx_active |= 1 << buf_id; @@ -290,18 +273,15 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -/* This function returns the old state to see where we came from */ -static enum can_state check_set_state(struct net_device *dev, u8 canrflg) +static enum can_state get_new_state(struct net_device *dev, u8 canrflg) { struct mscan_priv *priv = netdev_priv(dev); - enum can_state state, old_state = priv->can.state; - if (canrflg & MSCAN_CSCIF && old_state <= CAN_STATE_BUS_OFF) { - state = state_map[max(MSCAN_STATE_RX(canrflg), - MSCAN_STATE_TX(canrflg))]; - priv->can.state = state; - } - return old_state; + if (unlikely(canrflg & MSCAN_CSCIF)) + return state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + + return priv->can.state; } static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) @@ -326,19 +306,19 @@ static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) if (can_id & 1) frame->can_id |= CAN_RTR_FLAG; - frame->can_dlc = get_can_dlc(in_8(®s->rx.dlr) & 0xf); + frame->len = can_cc_dlc2len(in_8(®s->rx.dlr) & 0xf); if (!(frame->can_id & CAN_RTR_FLAG)) { void __iomem *data = ®s->rx.dsr1_0; u16 *payload = (u16 *)frame->data; - for (i = 0; i < frame->can_dlc / 2; i++) { + for (i = 0; i < frame->len / 2; i++) { *payload++ = in_be16(data); data += 2 + _MSCAN_RESERVED_DSR_SIZE; } /* read remaining byte if necessary */ - if (frame->can_dlc & 1) - frame->data[frame->can_dlc - 1] = in_8(data); + if (frame->len & 1) + frame->data[frame->len - 1] = in_8(data); } out_8(®s->canrflg, MSCAN_RXF); @@ -350,7 +330,7 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame, struct mscan_priv *priv = netdev_priv(dev); struct mscan_regs __iomem *regs = priv->reg_base; struct net_device_stats *stats = &dev->stats; - enum can_state old_state; + enum can_state new_state; netdev_dbg(dev, "error interrupt (canrflg=%#x)\n", canrflg); frame->can_id = CAN_ERR_FLAG; @@ -364,27 +344,13 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame, frame->data[1] = 0; } - old_state = check_set_state(dev, canrflg); - /* State changed */ - if (old_state != priv->can.state) { - switch (priv->can.state) { - case CAN_STATE_ERROR_WARNING: - frame->can_id |= CAN_ERR_CRTL; - priv->can.can_stats.error_warning++; - if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) < - (canrflg & MSCAN_RSTAT_MSK)) - frame->data[1] |= CAN_ERR_CRTL_RX_WARNING; - if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) < - (canrflg & MSCAN_TSTAT_MSK)) - frame->data[1] |= CAN_ERR_CRTL_TX_WARNING; - break; - case CAN_STATE_ERROR_PASSIVE: - frame->can_id |= CAN_ERR_CRTL; - priv->can.can_stats.error_passive++; - frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; - break; - case CAN_STATE_BUS_OFF: - frame->can_id |= CAN_ERR_BUSOFF; + new_state = get_new_state(dev, canrflg); + if (new_state != priv->can.state) { + can_change_state(dev, frame, + state_map[MSCAN_STATE_TX(canrflg)], + state_map[MSCAN_STATE_RX(canrflg)]); + + if (priv->can.state == CAN_STATE_BUS_OFF) { /* * The MSCAN on the MPC5200 does recover from bus-off * automatically. To avoid that we stop the chip doing @@ -397,13 +363,10 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame, MSCAN_SLPRQ | MSCAN_INITRQ); } can_bus_off(dev); - break; - default: - break; } } priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; - frame->can_dlc = CAN_ERR_DLC; + frame->len = CAN_ERR_DLC; out_8(®s->canrflg, MSCAN_ERR_IF); } @@ -413,13 +376,12 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota) struct net_device *dev = napi->dev; struct mscan_regs __iomem *regs = priv->reg_base; struct net_device_stats *stats = &dev->stats; - int npackets = 0; - int ret = 1; + int work_done = 0; struct sk_buff *skb; struct can_frame *frame; u8 canrflg; - while (npackets < quota) { + while (work_done < quota) { canrflg = in_8(®s->canrflg); if (!(canrflg & (MSCAN_RXF | MSCAN_ERR_IF))) break; @@ -433,25 +395,27 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota) continue; } - if (canrflg & MSCAN_RXF) + if (canrflg & MSCAN_RXF) { mscan_get_rx_frame(dev, frame); - else if (canrflg & MSCAN_ERR_IF) + stats->rx_packets++; + if (!(frame->can_id & CAN_RTR_FLAG)) + stats->rx_bytes += frame->len; + } else if (canrflg & MSCAN_ERR_IF) { mscan_get_err_frame(dev, frame, canrflg); + } - stats->rx_packets++; - stats->rx_bytes += frame->can_dlc; - npackets++; + work_done++; netif_receive_skb(skb); } - if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) { - napi_complete(&priv->napi); - clear_bit(F_RX_PROGRESS, &priv->flags); - if (priv->can.state < CAN_STATE_BUS_OFF) - out_8(®s->canrier, priv->shadow_canrier); - ret = 0; + if (work_done < quota) { + if (likely(napi_complete_done(&priv->napi, work_done))) { + clear_bit(F_RX_PROGRESS, &priv->flags); + if (priv->can.state < CAN_STATE_BUS_OFF) + out_8(®s->canrier, priv->shadow_canrier); + } } - return ret; + return work_done; } static irqreturn_t mscan_isr(int irq, void *dev_id) @@ -478,9 +442,9 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) continue; out_8(®s->cantbsel, mask); - stats->tx_bytes += in_8(®s->tx.dlr); + stats->tx_bytes += can_get_echo_skb(dev, entry->id, + NULL); stats->tx_packets++; - can_get_echo_skb(dev, entry->id); priv->tx_active &= ~mask; list_del(pos); } @@ -490,7 +454,7 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) clear_bit(F_TX_PROGRESS, &priv->flags); priv->cur_pri = 0; } else { - dev->trans_start = jiffies; + netif_trans_update(dev); } if (!test_bit(F_TX_WAIT_ALL, &priv->flags)) @@ -573,10 +537,17 @@ static int mscan_open(struct net_device *dev) struct mscan_priv *priv = netdev_priv(dev); struct mscan_regs __iomem *regs = priv->reg_base; + ret = clk_prepare_enable(priv->clk_ipg); + if (ret) + goto exit_retcode; + ret = clk_prepare_enable(priv->clk_can); + if (ret) + goto exit_dis_ipg_clock; + /* common open */ ret = open_candev(dev); if (ret) - return ret; + goto exit_dis_can_clock; napi_enable(&priv->napi); @@ -604,6 +575,11 @@ exit_free_irq: exit_napi_disable: napi_disable(&priv->napi); close_candev(dev); +exit_dis_can_clock: + clk_disable_unprepare(priv->clk_can); +exit_dis_ipg_clock: + clk_disable_unprepare(priv->clk_ipg); +exit_retcode: return ret; } @@ -621,13 +597,20 @@ static int mscan_close(struct net_device *dev) close_candev(dev); free_irq(dev->irq, dev); + clk_disable_unprepare(priv->clk_can); + clk_disable_unprepare(priv->clk_ipg); + return 0; } static const struct net_device_ops mscan_netdev_ops = { - .ndo_open = mscan_open, - .ndo_stop = mscan_close, - .ndo_start_xmit = mscan_start_xmit, + .ndo_open = mscan_open, + .ndo_stop = mscan_close, + .ndo_start_xmit = mscan_start_xmit, +}; + +static const struct ethtool_ops mscan_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, }; int register_mscandev(struct net_device *dev, int mscan_clksrc) @@ -690,10 +673,11 @@ struct net_device *alloc_mscandev(void) priv = netdev_priv(dev); dev->netdev_ops = &mscan_netdev_ops; + dev->ethtool_ops = &mscan_ethtool_ops; dev->flags |= IFF_ECHO; /* we support local echo */ - netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8); + netif_napi_add_weight(dev, &priv->napi, mscan_rx_poll, 8); priv->can.bittiming_const = &mscan_bittiming_const; priv->can.do_set_bittiming = mscan_do_set_bittiming; |
