mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 07:33:56 +00:00
35e62ae830
Based on 1 normalized pattern(s): 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 see http www gnu org licenses extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 15 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190530000437.427740574@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
707 lines
17 KiB
C
707 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* CAN bus driver for the alone generic (as possible as) MSCAN controller.
|
|
*
|
|
* Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
|
|
* Varma Electronics Oy
|
|
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
|
* Copyright (C) 2008-2009 Pengutronix <kernel@pengutronix.de>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/list.h>
|
|
#include <linux/can/dev.h>
|
|
#include <linux/can/error.h>
|
|
#include <linux/io.h>
|
|
|
|
#include "mscan.h"
|
|
|
|
static const struct can_bittiming_const mscan_bittiming_const = {
|
|
.name = "mscan",
|
|
.tseg1_min = 4,
|
|
.tseg1_max = 16,
|
|
.tseg2_min = 2,
|
|
.tseg2_max = 8,
|
|
.sjw_max = 4,
|
|
.brp_min = 1,
|
|
.brp_max = 64,
|
|
.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,
|
|
CAN_STATE_ERROR_PASSIVE,
|
|
CAN_STATE_BUS_OFF
|
|
};
|
|
|
|
static int mscan_set_mode(struct net_device *dev, u8 mode)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
int ret = 0;
|
|
int i;
|
|
u8 canctl1;
|
|
|
|
if (mode != MSCAN_NORMAL_MODE) {
|
|
if (priv->tx_active) {
|
|
/* Abort transfers before going to sleep */#
|
|
out_8(®s->cantarq, priv->tx_active);
|
|
/* Suppress TX done interrupts */
|
|
out_8(®s->cantier, 0);
|
|
}
|
|
|
|
canctl1 = in_8(®s->canctl1);
|
|
if ((mode & MSCAN_SLPRQ) && !(canctl1 & MSCAN_SLPAK)) {
|
|
setbits8(®s->canctl0, MSCAN_SLPRQ);
|
|
for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
|
|
if (in_8(®s->canctl1) & MSCAN_SLPAK)
|
|
break;
|
|
udelay(100);
|
|
}
|
|
/*
|
|
* The mscan controller will fail to enter sleep mode,
|
|
* while there are irregular activities on bus, like
|
|
* somebody keeps retransmitting. This behavior is
|
|
* undocumented and seems to differ between mscan built
|
|
* in mpc5200b and mpc5200. We proceed in that case,
|
|
* since otherwise the slprq will be kept set and the
|
|
* controller will get stuck. NOTE: INITRQ or CSWAI
|
|
* will abort all active transmit actions, if still
|
|
* any, at once.
|
|
*/
|
|
if (i >= MSCAN_SET_MODE_RETRIES)
|
|
netdev_dbg(dev,
|
|
"device failed to enter sleep mode. "
|
|
"We proceed anyhow.\n");
|
|
else
|
|
priv->can.state = CAN_STATE_SLEEPING;
|
|
}
|
|
|
|
if ((mode & MSCAN_INITRQ) && !(canctl1 & MSCAN_INITAK)) {
|
|
setbits8(®s->canctl0, MSCAN_INITRQ);
|
|
for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
|
|
if (in_8(®s->canctl1) & MSCAN_INITAK)
|
|
break;
|
|
}
|
|
if (i >= MSCAN_SET_MODE_RETRIES)
|
|
ret = -ENODEV;
|
|
}
|
|
if (!ret)
|
|
priv->can.state = CAN_STATE_STOPPED;
|
|
|
|
if (mode & MSCAN_CSWAI)
|
|
setbits8(®s->canctl0, MSCAN_CSWAI);
|
|
|
|
} else {
|
|
canctl1 = in_8(®s->canctl1);
|
|
if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
|
|
clrbits8(®s->canctl0, MSCAN_SLPRQ | MSCAN_INITRQ);
|
|
for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
|
|
canctl1 = in_8(®s->canctl1);
|
|
if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
|
|
break;
|
|
}
|
|
if (i >= MSCAN_SET_MODE_RETRIES)
|
|
ret = -ENODEV;
|
|
else
|
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int mscan_start(struct net_device *dev)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
u8 canrflg;
|
|
int err;
|
|
|
|
out_8(®s->canrier, 0);
|
|
|
|
INIT_LIST_HEAD(&priv->tx_head);
|
|
priv->prev_buf_id = 0;
|
|
priv->cur_pri = 0;
|
|
priv->tx_active = 0;
|
|
priv->shadow_canrier = 0;
|
|
priv->flags = 0;
|
|
|
|
if (priv->type == MSCAN_TYPE_MPC5121) {
|
|
/* Clear pending bus-off condition */
|
|
if (in_8(®s->canmisc) & MSCAN_BOHOLD)
|
|
out_8(®s->canmisc, MSCAN_BOHOLD);
|
|
}
|
|
|
|
err = mscan_set_mode(dev, MSCAN_NORMAL_MODE);
|
|
if (err)
|
|
return err;
|
|
|
|
canrflg = in_8(®s->canrflg);
|
|
priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
|
|
priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
|
|
MSCAN_STATE_TX(canrflg))];
|
|
out_8(®s->cantier, 0);
|
|
|
|
/* Enable receive interrupts. */
|
|
out_8(®s->canrier, MSCAN_RX_INTS_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mscan_restart(struct net_device *dev)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
|
|
if (priv->type == MSCAN_TYPE_MPC5121) {
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
|
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
|
WARN(!(in_8(®s->canmisc) & MSCAN_BOHOLD),
|
|
"bus-off state expected\n");
|
|
out_8(®s->canmisc, MSCAN_BOHOLD);
|
|
/* Re-enable receive interrupts. */
|
|
out_8(®s->canrier, MSCAN_RX_INTS_ENABLE);
|
|
} else {
|
|
if (priv->can.state <= CAN_STATE_BUS_OFF)
|
|
mscan_set_mode(dev, MSCAN_INIT_MODE);
|
|
return mscan_start(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
struct can_frame *frame = (struct can_frame *)skb->data;
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
int i, rtr, buf_id;
|
|
u32 can_id;
|
|
|
|
if (can_dropped_invalid_skb(dev, skb))
|
|
return NETDEV_TX_OK;
|
|
|
|
out_8(®s->cantier, 0);
|
|
|
|
i = ~priv->tx_active & MSCAN_TXE;
|
|
buf_id = ffs(i) - 1;
|
|
switch (hweight8(i)) {
|
|
case 0:
|
|
netif_stop_queue(dev);
|
|
netdev_err(dev, "Tx Ring full when queue awake!\n");
|
|
return NETDEV_TX_BUSY;
|
|
case 1:
|
|
/*
|
|
* if buf_id < 3, then current frame will be send out of order,
|
|
* since buffer with lower id have higher priority (hell..)
|
|
*/
|
|
netif_stop_queue(dev);
|
|
case 2:
|
|
if (buf_id < priv->prev_buf_id) {
|
|
priv->cur_pri++;
|
|
if (priv->cur_pri == 0xff) {
|
|
set_bit(F_TX_WAIT_ALL, &priv->flags);
|
|
netif_stop_queue(dev);
|
|
}
|
|
}
|
|
set_bit(F_TX_PROGRESS, &priv->flags);
|
|
break;
|
|
}
|
|
priv->prev_buf_id = buf_id;
|
|
out_8(®s->cantbsel, i);
|
|
|
|
rtr = frame->can_id & CAN_RTR_FLAG;
|
|
|
|
/* RTR is always the lowest bit of interest, then IDs follow */
|
|
if (frame->can_id & CAN_EFF_FLAG) {
|
|
can_id = (frame->can_id & CAN_EFF_MASK)
|
|
<< (MSCAN_EFF_RTR_SHIFT + 1);
|
|
if (rtr)
|
|
can_id |= 1 << MSCAN_EFF_RTR_SHIFT;
|
|
out_be16(®s->tx.idr3_2, can_id);
|
|
|
|
can_id >>= 16;
|
|
/* EFF_FLAGS are between the IDs :( */
|
|
can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0)
|
|
| MSCAN_EFF_FLAGS;
|
|
} else {
|
|
can_id = (frame->can_id & CAN_SFF_MASK)
|
|
<< (MSCAN_SFF_RTR_SHIFT + 1);
|
|
if (rtr)
|
|
can_id |= 1 << MSCAN_SFF_RTR_SHIFT;
|
|
}
|
|
out_be16(®s->tx.idr1_0, can_id);
|
|
|
|
if (!rtr) {
|
|
void __iomem *data = ®s->tx.dsr1_0;
|
|
u16 *payload = (u16 *)frame->data;
|
|
|
|
for (i = 0; i < frame->can_dlc / 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]);
|
|
}
|
|
|
|
out_8(®s->tx.dlr, frame->can_dlc);
|
|
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))
|
|
netif_trans_update(dev);
|
|
|
|
list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
|
|
|
|
can_put_echo_skb(skb, dev, buf_id);
|
|
|
|
/* Enable interrupt. */
|
|
priv->tx_active |= 1 << buf_id;
|
|
out_8(®s->cantier, priv->tx_active);
|
|
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
static enum can_state get_new_state(struct net_device *dev, u8 canrflg)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
|
|
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)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
u32 can_id;
|
|
int i;
|
|
|
|
can_id = in_be16(®s->rx.idr1_0);
|
|
if (can_id & (1 << 3)) {
|
|
frame->can_id = CAN_EFF_FLAG;
|
|
can_id = ((can_id << 16) | in_be16(®s->rx.idr3_2));
|
|
can_id = ((can_id & 0xffe00000) |
|
|
((can_id & 0x7ffff) << 2)) >> 2;
|
|
} else {
|
|
can_id >>= 4;
|
|
frame->can_id = 0;
|
|
}
|
|
|
|
frame->can_id |= can_id >> 1;
|
|
if (can_id & 1)
|
|
frame->can_id |= CAN_RTR_FLAG;
|
|
|
|
frame->can_dlc = get_can_dlc(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++) {
|
|
*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);
|
|
}
|
|
|
|
out_8(®s->canrflg, MSCAN_RXF);
|
|
}
|
|
|
|
static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
|
|
u8 canrflg)
|
|
{
|
|
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 new_state;
|
|
|
|
netdev_dbg(dev, "error interrupt (canrflg=%#x)\n", canrflg);
|
|
frame->can_id = CAN_ERR_FLAG;
|
|
|
|
if (canrflg & MSCAN_OVRIF) {
|
|
frame->can_id |= CAN_ERR_CRTL;
|
|
frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
|
stats->rx_over_errors++;
|
|
stats->rx_errors++;
|
|
} else {
|
|
frame->data[1] = 0;
|
|
}
|
|
|
|
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
|
|
* a light-weight stop (we are in irq-context).
|
|
*/
|
|
if (priv->type != MSCAN_TYPE_MPC5121) {
|
|
out_8(®s->cantier, 0);
|
|
out_8(®s->canrier, 0);
|
|
setbits8(®s->canctl0,
|
|
MSCAN_SLPRQ | MSCAN_INITRQ);
|
|
}
|
|
can_bus_off(dev);
|
|
}
|
|
}
|
|
priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
|
|
frame->can_dlc = CAN_ERR_DLC;
|
|
out_8(®s->canrflg, MSCAN_ERR_IF);
|
|
}
|
|
|
|
static int mscan_rx_poll(struct napi_struct *napi, int quota)
|
|
{
|
|
struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
|
|
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;
|
|
struct sk_buff *skb;
|
|
struct can_frame *frame;
|
|
u8 canrflg;
|
|
|
|
while (npackets < quota) {
|
|
canrflg = in_8(®s->canrflg);
|
|
if (!(canrflg & (MSCAN_RXF | MSCAN_ERR_IF)))
|
|
break;
|
|
|
|
skb = alloc_can_skb(dev, &frame);
|
|
if (!skb) {
|
|
if (printk_ratelimit())
|
|
netdev_notice(dev, "packet dropped\n");
|
|
stats->rx_dropped++;
|
|
out_8(®s->canrflg, canrflg);
|
|
continue;
|
|
}
|
|
|
|
if (canrflg & MSCAN_RXF)
|
|
mscan_get_rx_frame(dev, frame);
|
|
else if (canrflg & MSCAN_ERR_IF)
|
|
mscan_get_err_frame(dev, frame, canrflg);
|
|
|
|
stats->rx_packets++;
|
|
stats->rx_bytes += frame->can_dlc;
|
|
npackets++;
|
|
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;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t mscan_isr(int irq, void *dev_id)
|
|
{
|
|
struct net_device *dev = (struct net_device *)dev_id;
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
struct net_device_stats *stats = &dev->stats;
|
|
u8 cantier, cantflg, canrflg;
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
cantier = in_8(®s->cantier) & MSCAN_TXE;
|
|
cantflg = in_8(®s->cantflg) & cantier;
|
|
|
|
if (cantier && cantflg) {
|
|
struct list_head *tmp, *pos;
|
|
|
|
list_for_each_safe(pos, tmp, &priv->tx_head) {
|
|
struct tx_queue_entry *entry =
|
|
list_entry(pos, struct tx_queue_entry, list);
|
|
u8 mask = entry->mask;
|
|
|
|
if (!(cantflg & mask))
|
|
continue;
|
|
|
|
out_8(®s->cantbsel, mask);
|
|
stats->tx_bytes += in_8(®s->tx.dlr);
|
|
stats->tx_packets++;
|
|
can_get_echo_skb(dev, entry->id);
|
|
priv->tx_active &= ~mask;
|
|
list_del(pos);
|
|
}
|
|
|
|
if (list_empty(&priv->tx_head)) {
|
|
clear_bit(F_TX_WAIT_ALL, &priv->flags);
|
|
clear_bit(F_TX_PROGRESS, &priv->flags);
|
|
priv->cur_pri = 0;
|
|
} else {
|
|
netif_trans_update(dev);
|
|
}
|
|
|
|
if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
|
|
netif_wake_queue(dev);
|
|
|
|
out_8(®s->cantier, priv->tx_active);
|
|
ret = IRQ_HANDLED;
|
|
}
|
|
|
|
canrflg = in_8(®s->canrflg);
|
|
if ((canrflg & ~MSCAN_STAT_MSK) &&
|
|
!test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
|
|
if (canrflg & ~MSCAN_STAT_MSK) {
|
|
priv->shadow_canrier = in_8(®s->canrier);
|
|
out_8(®s->canrier, 0);
|
|
napi_schedule(&priv->napi);
|
|
ret = IRQ_HANDLED;
|
|
} else {
|
|
clear_bit(F_RX_PROGRESS, &priv->flags);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (mode) {
|
|
case CAN_MODE_START:
|
|
ret = mscan_restart(dev);
|
|
if (ret)
|
|
break;
|
|
if (netif_queue_stopped(dev))
|
|
netif_wake_queue(dev);
|
|
break;
|
|
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int mscan_do_set_bittiming(struct net_device *dev)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
struct can_bittiming *bt = &priv->can.bittiming;
|
|
u8 btr0, btr1;
|
|
|
|
btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw);
|
|
btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) |
|
|
BTR1_SET_TSEG2(bt->phase_seg2) |
|
|
BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
|
|
|
|
netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
|
|
|
|
out_8(®s->canbtr0, btr0);
|
|
out_8(®s->canbtr1, btr1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mscan_get_berr_counter(const struct net_device *dev,
|
|
struct can_berr_counter *bec)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
|
|
bec->txerr = in_8(®s->cantxerr);
|
|
bec->rxerr = in_8(®s->canrxerr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mscan_open(struct net_device *dev)
|
|
{
|
|
int ret;
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
|
|
if (priv->clk_ipg) {
|
|
ret = clk_prepare_enable(priv->clk_ipg);
|
|
if (ret)
|
|
goto exit_retcode;
|
|
}
|
|
if (priv->clk_can) {
|
|
ret = clk_prepare_enable(priv->clk_can);
|
|
if (ret)
|
|
goto exit_dis_ipg_clock;
|
|
}
|
|
|
|
/* common open */
|
|
ret = open_candev(dev);
|
|
if (ret)
|
|
goto exit_dis_can_clock;
|
|
|
|
napi_enable(&priv->napi);
|
|
|
|
ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
|
|
if (ret < 0) {
|
|
netdev_err(dev, "failed to attach interrupt\n");
|
|
goto exit_napi_disable;
|
|
}
|
|
|
|
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
|
setbits8(®s->canctl1, MSCAN_LISTEN);
|
|
else
|
|
clrbits8(®s->canctl1, MSCAN_LISTEN);
|
|
|
|
ret = mscan_start(dev);
|
|
if (ret)
|
|
goto exit_free_irq;
|
|
|
|
netif_start_queue(dev);
|
|
|
|
return 0;
|
|
|
|
exit_free_irq:
|
|
free_irq(dev->irq, dev);
|
|
exit_napi_disable:
|
|
napi_disable(&priv->napi);
|
|
close_candev(dev);
|
|
exit_dis_can_clock:
|
|
if (priv->clk_can)
|
|
clk_disable_unprepare(priv->clk_can);
|
|
exit_dis_ipg_clock:
|
|
if (priv->clk_ipg)
|
|
clk_disable_unprepare(priv->clk_ipg);
|
|
exit_retcode:
|
|
return ret;
|
|
}
|
|
|
|
static int mscan_close(struct net_device *dev)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
|
|
netif_stop_queue(dev);
|
|
napi_disable(&priv->napi);
|
|
|
|
out_8(®s->cantier, 0);
|
|
out_8(®s->canrier, 0);
|
|
mscan_set_mode(dev, MSCAN_INIT_MODE);
|
|
close_candev(dev);
|
|
free_irq(dev->irq, dev);
|
|
|
|
if (priv->clk_can)
|
|
clk_disable_unprepare(priv->clk_can);
|
|
if (priv->clk_ipg)
|
|
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_change_mtu = can_change_mtu,
|
|
};
|
|
|
|
int register_mscandev(struct net_device *dev, int mscan_clksrc)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
u8 ctl1;
|
|
|
|
ctl1 = in_8(®s->canctl1);
|
|
if (mscan_clksrc)
|
|
ctl1 |= MSCAN_CLKSRC;
|
|
else
|
|
ctl1 &= ~MSCAN_CLKSRC;
|
|
|
|
if (priv->type == MSCAN_TYPE_MPC5121) {
|
|
priv->can.do_get_berr_counter = mscan_get_berr_counter;
|
|
ctl1 |= MSCAN_BORM; /* bus-off recovery upon request */
|
|
}
|
|
|
|
ctl1 |= MSCAN_CANE;
|
|
out_8(®s->canctl1, ctl1);
|
|
udelay(100);
|
|
|
|
/* acceptance mask/acceptance code (accept everything) */
|
|
out_be16(®s->canidar1_0, 0);
|
|
out_be16(®s->canidar3_2, 0);
|
|
out_be16(®s->canidar5_4, 0);
|
|
out_be16(®s->canidar7_6, 0);
|
|
|
|
out_be16(®s->canidmr1_0, 0xffff);
|
|
out_be16(®s->canidmr3_2, 0xffff);
|
|
out_be16(®s->canidmr5_4, 0xffff);
|
|
out_be16(®s->canidmr7_6, 0xffff);
|
|
/* Two 32 bit Acceptance Filters */
|
|
out_8(®s->canidac, MSCAN_AF_32BIT);
|
|
|
|
mscan_set_mode(dev, MSCAN_INIT_MODE);
|
|
|
|
return register_candev(dev);
|
|
}
|
|
|
|
void unregister_mscandev(struct net_device *dev)
|
|
{
|
|
struct mscan_priv *priv = netdev_priv(dev);
|
|
struct mscan_regs __iomem *regs = priv->reg_base;
|
|
mscan_set_mode(dev, MSCAN_INIT_MODE);
|
|
clrbits8(®s->canctl1, MSCAN_CANE);
|
|
unregister_candev(dev);
|
|
}
|
|
|
|
struct net_device *alloc_mscandev(void)
|
|
{
|
|
struct net_device *dev;
|
|
struct mscan_priv *priv;
|
|
int i;
|
|
|
|
dev = alloc_candev(sizeof(struct mscan_priv), MSCAN_ECHO_SKB_MAX);
|
|
if (!dev)
|
|
return NULL;
|
|
priv = netdev_priv(dev);
|
|
|
|
dev->netdev_ops = &mscan_netdev_ops;
|
|
|
|
dev->flags |= IFF_ECHO; /* we support local echo */
|
|
|
|
netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
|
|
|
|
priv->can.bittiming_const = &mscan_bittiming_const;
|
|
priv->can.do_set_bittiming = mscan_do_set_bittiming;
|
|
priv->can.do_set_mode = mscan_do_set_mode;
|
|
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
|
|
CAN_CTRLMODE_LISTENONLY;
|
|
|
|
for (i = 0; i < TX_QUEUE_SIZE; i++) {
|
|
priv->tx_queue[i].id = i;
|
|
priv->tx_queue[i].mask = 1 << i;
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips");
|