Hey Hans, Hans J. Koch wrote: > This adds a driver for FlexCAN based CAN controllers, > e.g. found in Freescale i.MX35 SoCs. > > The original version of this driver was posted by Sascha Hauer in July 2009: > http://kerneltrap.org/mailarchive/linux-netdev/2009/7/29/6251621 > > I took this version, added NAPI support, and fixed some problems found > during testing. Well, here is the result. Please review.
I got busy, involved in a $CUSTOMER project, but now I have time to review your patch...I'm going to send mine, too. > Thanks, > Hans > > Signed-off-by: Hans J. Koch <[email protected]> > --- > drivers/net/can/Kconfig | 6 + > drivers/net/can/Makefile | 1 + > drivers/net/can/flexcan.c | 828 > +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 835 insertions(+), 0 deletions(-) > create mode 100644 drivers/net/can/flexcan.c > > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig > index 2c5227c..4250c99 100644 > --- a/drivers/net/can/Kconfig > +++ b/drivers/net/can/Kconfig > @@ -73,6 +73,12 @@ config CAN_JANZ_ICAN3 > This driver can also be built as a module. If so, the module will be > called janz-ican3.ko. > > +config CAN_FLEXCAN > + tristate "Support for Freescale FLEXCAN based chips" > + depends on CAN_DEV > + ---help--- > + Driver for Freescale FlexCAN. > + > source "drivers/net/can/mscan/Kconfig" > > source "drivers/net/can/sja1000/Kconfig" > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile > index 9047cd0..0057537 100644 > --- a/drivers/net/can/Makefile > +++ b/drivers/net/can/Makefile > @@ -16,5 +16,6 @@ obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o > obj-$(CONFIG_CAN_MCP251X) += mcp251x.o > obj-$(CONFIG_CAN_BFIN) += bfin_can.o > obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o > +obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o > > ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG > diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c > new file mode 100644 > index 0000000..ab00873 > --- /dev/null > +++ b/drivers/net/can/flexcan.c > @@ -0,0 +1,828 @@ > +/* > + * FLEXCAN CAN controller driver > + * > + * Copyright (C) 2005-2006 Varma Electronics Oy > + * Copyright (C) 2009 Sascha Hauer, Pengutronix > + * Copyright (C) 2010 Hans J. Koch <[email protected]> > + * > + * Based on code originally by Andrey Volkov > + * > + * Licensed under the terms of the GPL v2. > + * > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/if_arp.h> > +#include <linux/if_ether.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/list.h> > +#include <linux/module.h> > +#include <linux/netdevice.h> > +#include <linux/platform_device.h> > + > +#include <linux/can/dev.h> > +#include <linux/can/error.h> > +#include <linux/can/netlink.h> > + > +#define DRIVER_NAME "flexcan" > + > +#define TX_ECHO_SKB_MAX 1 > +#define FLEXCAN_DEF_NAPI_WEIGHT 6 > + > +/* FLEXCAN module configuration register (CANMCR) bits */ > +#define CANMCR_MDIS (1 << 31) > +#define CANMCR_FRZ (1 << 30) > +#define CANMCR_FEN (1 << 29) > +#define CANMCR_HALT (1 << 28) > +#define CANMCR_NOT_RDY (1 << 27) > +#define CANMCR_SOFTRST (1 << 25) > +#define CANMCR_FRZACK (1 << 24) > +#define CANMCR_SUPV (1 << 23) > +#define CANMCR_SRX_DIS (1 << 17) > +#define CANMCR_MAXMB(x) ((x) & 0x0f) > +#define CANMCR_IDAM_A (0 << 8) > +#define CANMCR_IDAM_B (1 << 8) > +#define CANMCR_IDAM_C (2 << 8) > + > +/* FLEXCAN control register (CANCTRL) bits */ > +#define CANCTRL_PRESDIV(x) (((x) & 0xff) << 24) > +#define CANCTRL_RJW(x) (((x) & 0x03) << 22) > +#define CANCTRL_PSEG1(x) (((x) & 0x07) << 19) > +#define CANCTRL_PSEG2(x) (((x) & 0x07) << 16) > +#define CANCTRL_BOFFMSK (1 << 15) > +#define CANCTRL_ERRMSK (1 << 14) > +#define CANCTRL_CLKSRC (1 << 13) > +#define CANCTRL_LPB (1 << 12) > +#define CANCTRL_TWRN_MSK (1 << 11) > +#define CANCTRL_RWRN_MSK (1 << 10) > +#define CANCTRL_SAMP (1 << 7) > +#define CANCTRL_BOFFREC (1 << 6) > +#define CANCTRL_TSYNC (1 << 5) > +#define CANCTRL_LBUF (1 << 4) > +#define CANCTRL_LOM (1 << 3) > +#define CANCTRL_PROPSEG(x) ((x) & 0x07) > + > +/* FLEXCAN error counter register (ERRCNT) bits */ > +#define ERRCNT_REXECTR(x) (((x) & 0xff) << 8) > +#define ERRCNT_TXECTR(x) ((x) & 0xff) > + > +/* FLEXCAN error and status register (ERRSTAT) bits */ > +#define ERRSTAT_TWRNINT (1 << 17) > +#define ERRSTAT_RWRNINT (1 << 16) > +#define ERRSTAT_BIT1ERR (1 << 15) > +#define ERRSTAT_BIT0ERR (1 << 14) > +#define ERRSTAT_ACKERR (1 << 13) > +#define ERRSTAT_CRCERR (1 << 12) > +#define ERRSTAT_FRMERR (1 << 11) > +#define ERRSTAT_STFERR (1 << 10) > +#define ERRSTAT_TXWRN (1 << 9) > +#define ERRSTAT_RXWRN (1 << 8) > +#define ERRSTAT_IDLE (1 << 7) > +#define ERRSTAT_TXRX (1 << 6) > +#define ERRSTAT_FLTCONF_MASK (3 << 4) > +#define ERRSTAT_FLTCONF_ERROR_ACTIVE (0 << 4) > +#define ERRSTAT_FLTCONF_ERROR_PASSIVE (1 << 4) > +#define ERRSTAT_FLTCONF_ERROR_BUS_OFF (2 << 4) > +#define ERRSTAT_BOFFINT (1 << 2) > +#define ERRSTAT_ERRINT (1 << 1) > +#define ERRSTAT_WAKINT (1 << 0) > +#define ERRSTAT_INT (ERRSTAT_BOFFINT | ERRSTAT_ERRINT | ERRSTAT_TWRNINT | \ > + ERRSTAT_RWRNINT) > + > +/* FLEXCAN interrupt flag register (IFLAG) bits */ > +#define IFLAG_BUF(x) (1 << (x)) > +#define IFLAG_RX_FIFO_OVERFLOW (1 << 7) > +#define IFLAG_RX_FIFO_WARN (1 << 6) > +#define IFLAG_RX_FIFO_AVAILABLE (1 << 5) > + > +/* FLEXCAN message buffers */ > +#define MB_CNT_CODE(x) (((x) & 0xf) << 24) > +#define MB_CNT_SRR (1 << 22) > +#define MB_CNT_IDE (1 << 21) > +#define MB_CNT_RTR (1 << 20) > +#define MB_CNT_LENGTH(x) (((x) & 0xf) << 16) > +#define MB_CNT_TIMESTAMP(x) ((x) & 0xffff) > + > +#define MB_ID_STD (0x7ff << 18) > +#define MB_ID_EXT 0x1fffffff > +#define MB_CODE_MASK 0xf0ffffff > + > +#define TX_ECHO_SKB_MAX 1 > + > +/* Structure of the message buffer */ > +struct flexcan_mb { > + u32 can_ctrl; > + u32 can_id; > + u32 data[2]; > +}; > + > +/* Structure of the hardware registers */ > +struct flexcan_regs { > + u32 canmcr; /* 0x00 */ > + u32 canctrl; /* 0x04 */ > + u32 timer; /* 0x08 */ > + u32 reserved1; /* 0x0c */ > + u32 rxgmask; /* 0x10 */ > + u32 rx14mask; /* 0x14 */ > + u32 rx15mask; /* 0x18 */ > + u32 errcnt; /* 0x1c */ > + u32 errstat; /* 0x20 */ > + u32 imask2; /* 0x24 */ > + u32 imask1; /* 0x28 */ > + u32 iflag2; /* 0x2c */ > + u32 iflag1; /* 0x30 */ > + u32 reserved4[19]; > + struct flexcan_mb cantxfg[64]; > +}; > + > +struct flexcan_priv { > + struct can_priv can; > + void __iomem *base; > + > + struct net_device *dev; > + struct napi_struct napi; > + struct clk *clk; > +}; > + > +static struct can_bittiming_const flexcan_bittiming_const = { > + .name = DRIVER_NAME, > + .tseg1_min = 1, > + .tseg1_max = 16, > + .tseg2_min = 2, > + .tseg2_max = 8, > + .sjw_max = 4, > + .brp_min = 1, > + .brp_max = 256, > + .brp_inc = 1, > +}; > + > +/* Mailboxes 0..7 are for RX FIFO, 8 for TX */ > +#define TX_BUF_ID 8 > + > +static void disable_mode_on(struct flexcan_regs __iomem *regs) > +{ > + u32 reg = readl(®s->canmcr); > + > + writel(reg | CANMCR_MDIS, ®s->canmcr); > + udelay(100); > +} > + > +static void disable_mode_off(struct flexcan_regs __iomem *regs) > +{ > + u32 reg = readl(®s->canmcr); > + > + writel(reg & ~CANMCR_MDIS, ®s->canmcr); > + udelay(100); > +} > + > +static int freeze_mode_on(struct flexcan_regs __iomem *regs) > +{ > + u32 reg = readl(®s->canmcr); > + int timeout = 10000; > + > + if (reg & CANMCR_FRZACK) > + return 0; > + > + writel(reg | CANMCR_FRZ, ®s->canmcr); > + writel(reg | CANMCR_HALT, ®s->canmcr); > + while (!(reg & CANMCR_FRZACK)) { > + if (--timeout < 0) > + return -EIO; > + udelay(10); > + reg = readl(®s->canmcr); > + } > + return 0; > +} > + > +static int freeze_mode_off(struct flexcan_regs __iomem *regs) > +{ > + u32 reg = readl(®s->canmcr); > + int timeout = 10000; > + > + if (!(reg & CANMCR_FRZACK)) > + return 0; > + > + writel(reg & ~(CANMCR_FRZ | CANMCR_HALT), ®s->canmcr); > + while (reg & CANMCR_FRZACK) { > + if (--timeout < 0) > + return -EIO; > + udelay(10); > + reg = readl(®s->canmcr); > + } > + return 0; > +} > + > +static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + struct can_frame *frame = (struct can_frame *)skb->data; > + struct flexcan_priv *priv = netdev_priv(dev); > + struct flexcan_regs __iomem *regs = priv->base; > + u32 can_id; > + u32 ctrl = MB_CNT_CODE(0xc) | (frame->can_dlc << 16); > + u32 reg = readl(®s->canctrl); > + check for invalid can frames with can_dropped_invalid_skb > + reg = readl(®s->cantxfg[TX_BUF_ID].can_ctrl); > + > + if (reg != MB_CNT_CODE(0x8)) > + writel(MB_CNT_CODE(0x08), ®s->cantxfg[TX_BUF_ID].can_ctrl); > + > + netif_stop_queue(dev); > + > + if (frame->can_id & CAN_EFF_FLAG) { > + can_id = frame->can_id & CAN_EFF_MASK; > + ctrl |= MB_CNT_IDE | MB_CNT_SRR; > + } else { > + can_id = (frame->can_id & CAN_SFF_MASK) << 18; > + } > + > + if (frame->can_id & CAN_RTR_FLAG) > + ctrl |= MB_CNT_RTR; > + > + if (frame->can_dlc > 0) { > + u32 data; > + data = frame->data[0] << 24; > + data |= frame->data[1] << 16; > + data |= frame->data[2] << 8; > + data |= frame->data[3]; > + writel(data, ®s->cantxfg[TX_BUF_ID].data[0]); we can use cpu_to_be32 here > + } > + if (frame->can_dlc > 3) { > + u32 data; > + data = frame->data[4] << 24; > + data |= frame->data[5] << 16; > + data |= frame->data[6] << 8; > + data |= frame->data[7]; > + writel(data, ®s->cantxfg[TX_BUF_ID].data[1]); > + } > + > + writel(can_id, ®s->cantxfg[TX_BUF_ID].can_id); > + writel(ctrl, ®s->cantxfg[TX_BUF_ID].can_ctrl); > + > + kfree_skb(skb); > + > + return NETDEV_TX_OK; > +} > + > +static void flexcan_rx_frame(struct net_device *ndev, > + struct flexcan_mb __iomem *mb) > +{ > + struct net_device_stats *stats = &ndev->stats; > + struct sk_buff *skb; > + struct can_frame *frame; > + int ctrl, length; > + u32 id; > + > + ctrl = readl(&mb->can_ctrl); > + length = (ctrl >> 16) & 0x0f; > + if (length > 8) { > + stats->rx_dropped++; > + return; > + } do not drop, use get_can_dlc() > + > + skb = dev_alloc_skb(sizeof(struct can_frame)); > + if (!skb) { > + stats->rx_dropped++; > + return; > + } > + > + frame = (struct can_frame *)skb_put(skb, > + sizeof(struct can_frame)); > + > + frame->can_dlc = length; > + id = readl(&mb->can_id); > + > + if (ctrl & MB_CNT_IDE) { > + frame->can_id = id & CAN_EFF_MASK; > + frame->can_id |= CAN_EFF_FLAG; > + } else { > + frame->can_id = (id >> 18) & CAN_SFF_MASK; > + } > + > + if (ctrl & MB_CNT_RTR) > + frame->can_id |= CAN_RTR_FLAG; > + > + if (length > 0) { > + u32 data = readl(&mb->data[0]); > + frame->data[0] = (data >> 24) & 0xff; > + frame->data[1] = (data >> 16) & 0xff; > + frame->data[2] = (data >> 8) & 0xff; > + frame->data[3] = data & 0xff; we can use be32_to_cpu here > + } > + if (length > 3) { > + u32 data = readl(&mb->data[1]); > + frame->data[4] = (data >> 24) & 0xff; > + frame->data[5] = (data >> 16) & 0xff; > + frame->data[6] = (data >> 8) & 0xff; > + frame->data[7] = data & 0xff; > + } > + > + stats->rx_packets++; > + stats->rx_bytes += frame->can_dlc; > + skb->dev = ndev; > + skb->protocol = __constant_htons(ETH_P_CAN); > + skb->ip_summed = CHECKSUM_UNNECESSARY; these are not needed > + > + netif_rx(skb); use netif_receive_skb(skb) in napi > +} > + > +static int flexcan_rx_poll(struct napi_struct *napi, int quota) > +{ > + struct net_device *ndev = napi->dev; > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + u32 iflags, imask; > + int num_pkts = 0; > + > + if (!netif_running(ndev)) > + return 0; > + > + iflags = readl(®s->iflag1); > + > + while ((iflags & IFLAG_RX_FIFO_AVAILABLE) && (num_pkts < quota)) { > + struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; > + > + flexcan_rx_frame(ndev, mb); > + writel(IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); > + readl(®s->timer); > + num_pkts++; > + iflags = readl(®s->iflag1); > + } > + > + if (num_pkts < quota) { > + napi_complete(napi); > + /* Re-enable RX mailbox interrupts */ > + imask = readl(®s->imask1); > + writel(imask | IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); is this read modify write save without locking against the interrupt handler? > + } > + > + return num_pkts; > +} > + > +static void flexcan_error(struct net_device *ndev, u32 stat) > +{ > + struct can_frame *cf; > + struct sk_buff *skb; > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct net_device_stats *stats = &ndev->stats; > + enum can_state state = priv->can.state; > + int error_warning = 0, rx_errors = 0, tx_errors = 0; > + > + skb = dev_alloc_skb(sizeof(struct can_frame)); > + if (!skb) > + return; > + > + skb->dev = ndev; > + skb->protocol = __constant_htons(ETH_P_CAN); > + skb->ip_summed = CHECKSUM_UNNECESSARY; > + > + cf = (struct can_frame *)skb_put(skb, sizeof(*cf)); > + memset(cf, 0, sizeof(*cf)); > + > + cf->can_id = CAN_ERR_FLAG; > + cf->can_dlc = CAN_ERR_DLC; use alloc_can_err_skb instead > + > + if (stat & ERRSTAT_RWRNINT) { > + error_warning = 1; > + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; > + } > + > + if (stat & ERRSTAT_TWRNINT) { > + error_warning = 1; > + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; > + } > + > + switch ((stat >> 4) & 0x3) { > + case 0: > + state = CAN_STATE_ERROR_ACTIVE; > + break; > + case 1: > + state = CAN_STATE_ERROR_PASSIVE; > + break; > + default: > + state = CAN_STATE_BUS_OFF; > + break; > + } > + > + if (stat & ERRSTAT_BOFFINT) { > + cf->can_id |= CAN_ERR_BUSOFF; > + state = CAN_STATE_BUS_OFF; > + } > + > + if (stat & ERRSTAT_BIT1ERR) { > + rx_errors = 1; > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; > + cf->data[2] |= CAN_ERR_PROT_BIT1; > + } > + > + if (stat & ERRSTAT_BIT0ERR) { > + rx_errors = 1; > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; > + cf->data[2] |= CAN_ERR_PROT_BIT0; > + } > + > + if (stat & ERRSTAT_FRMERR) { > + rx_errors = 1; > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; > + cf->data[2] |= CAN_ERR_PROT_FORM; > + } > + > + if (stat & ERRSTAT_STFERR) { > + rx_errors = 1; > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; > + cf->data[2] |= CAN_ERR_PROT_STUFF; > + } > + > + > + if (stat & ERRSTAT_ACKERR) { > + tx_errors = 1; > + cf->can_id |= CAN_ERR_ACK; > + } > + > + if (state == CAN_STATE_BUS_OFF) > + can_bus_off(ndev); > + if (error_warning) > + priv->can.can_stats.error_warning++; > + if (rx_errors) > + stats->rx_errors++; > + if (tx_errors) > + stats->tx_errors++; > + > + priv->can.state = state; > + > + netif_rx(skb); > + > + ndev->last_rx = jiffies; IIRC not needed anymore > + stats->rx_packets++; > + stats->rx_bytes += cf->can_dlc; > +} > + > +static irqreturn_t flexcan_isr(int irq, void *dev_id) > +{ > + struct net_device *ndev = dev_id; > + struct net_device_stats *stats = &ndev->stats; > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + u32 iflags, imask, errstat; > + > + errstat = readl(®s->errstat); > + if (errstat & ERRSTAT_INT) { > + flexcan_error(ndev, errstat); > + writel(errstat & ERRSTAT_INT, ®s->errstat); IMHO the bit errors should be handled in NAPI to avoid IRQ storms. > + } > + > + iflags = readl(®s->iflag1); > + > + if (iflags & IFLAG_RX_FIFO_OVERFLOW) { > + writel(IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); > + stats->rx_over_errors++; > + stats->rx_errors++; > + } > + > + if (iflags & (1 << TX_BUF_ID)) { > + stats->tx_packets++; > + writel((1 << TX_BUF_ID), ®s->iflag1); > + netif_wake_queue(ndev); > + } > + > + if (iflags & IFLAG_RX_FIFO_AVAILABLE) { > + /* disable RX interrupts */ > + imask = readl(®s->imask1); > + writel(imask & ~IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); > + /* Let NAPI poll received packets */ > + napi_schedule(&priv->napi); > + } > + > + return IRQ_HANDLED; > +} > + > +static void init_regs(struct flexcan_regs __iomem *regs) > +{ > + u32 reg; > + int i; > + > + freeze_mode_on(regs); > + > + /* Enable error and bus off interrupt */ > + reg = readl(®s->canctrl); > + reg |= CANCTRL_CLKSRC | CANCTRL_ERRMSK | CANCTRL_BOFFMSK | > + CANCTRL_BOFFREC | CANCTRL_TWRN_MSK | CANCTRL_TWRN_MSK; > + writel(reg, ®s->canctrl); > + > + /* Set lowest buffer transmitted first */ > + reg |= CANCTRL_LBUF; > + writel(reg, ®s->canctrl); > + > + for (i = 0; i < 64; i++) { > + writel(0, ®s->cantxfg[i].can_ctrl); > + writel(0, ®s->cantxfg[i].can_id); > + writel(0, ®s->cantxfg[i].data[0]); > + writel(0, ®s->cantxfg[i].data[1]); > + > + /* Put MB into rx queue */ > + writel(MB_CNT_CODE(0x04), ®s->cantxfg[i].can_ctrl); > + } > + writel(MB_CNT_CODE(0x08), ®s->cantxfg[TX_BUF_ID].can_ctrl); > + > + /* acceptance mask/acceptance code (accept everything) */ > + writel(0x0, ®s->rxgmask); > + writel(0x0, ®s->rx14mask); > + writel(0x0, ®s->rx15mask); > + > + reg = readl(®s->canmcr) & ~0x0f; > + reg |= CANMCR_IDAM_C | CANMCR_FEN | CANMCR_MAXMB(TX_BUF_ID); > + writel(reg, ®s->canmcr); > +} > + > +static int flexcan_set_bittiming(struct net_device *ndev) > +{ > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct can_bittiming *bt = &priv->can.bittiming; > + struct flexcan_regs __iomem *regs = priv->base; > + u32 reg; > + > + clk_enable(priv->clk); > + > + disable_mode_on(regs); > + > + reg = readl(®s->canctrl); > + reg &= ~(CANCTRL_SAMP | CANCTRL_PRESDIV(0xff) | > + CANCTRL_PSEG1(7) | CANCTRL_PSEG2(7) | > + CANCTRL_PROPSEG(7)); > + reg |= CANCTRL_PRESDIV(bt->brp - 1) | > + CANCTRL_PSEG1(bt->phase_seg1 - 1) | > + CANCTRL_PSEG2(bt->phase_seg2 - 1) | > + CANCTRL_RJW(3) | ^ is this intended? > + CANCTRL_PROPSEG(bt->prop_seg - 1); > + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) > + reg |= CANCTRL_SAMP; > + writel(reg, ®s->canctrl); > + > + dev_dbg(&ndev->dev, "flexcan_set_bittiming: canctrl=0x%08x\n", reg); > + > + clk_disable(priv->clk); > + > + return 0; > +} > + > +static int flexcan_open(struct net_device *ndev) > +{ > + int ret; > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + > + clk_enable(priv->clk); > + > + ret = open_candev(ndev); > + if (ret) > + return ret; > + > + disable_mode_off(regs); > + init_regs(regs); > + > + /* Enable flexcan module */ > + freeze_mode_off(regs); > + > + /* Enable interrupts */ > + writel(IFLAG_RX_FIFO_OVERFLOW | IFLAG_RX_FIFO_AVAILABLE | > + IFLAG_BUF(TX_BUF_ID), > + ®s->imask1); > + > + napi_enable(&priv->napi); > + netif_start_queue(ndev); > + > + ret = request_irq(ndev->irq, flexcan_isr, 0, DRIVER_NAME, ndev); > + if (!ret) > + return 0; > + > + close_candev(ndev); > + return ret; > +} > + > +static int flexcan_close(struct net_device *ndev) > +{ > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + > + netif_stop_queue(ndev); > + napi_disable(&priv->napi); > + > + /* Disable all interrupts */ > + writel(0, ®s->imask1); > + free_irq(ndev->irq, ndev); > + > + close_candev(ndev); > + > + /* Disable module */ > + disable_mode_on(regs); > + clk_disable(priv->clk); > + return 0; > +} > + > +static int flexcan_set_mode(struct net_device *ndev, enum can_mode mode) > +{ > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + u32 reg; > + > + switch (mode) { > + case CAN_MODE_START: > + reg = readl(®s->canctrl); > + reg &= ~CANCTRL_BOFFREC; > + writel(reg, ®s->canctrl); > + reg |= CANCTRL_BOFFREC; > + writel(reg, ®s->canctrl); > + priv->can.state = CAN_STATE_ERROR_ACTIVE; > + > + if (netif_queue_stopped(ndev)) > + netif_wake_queue(ndev); > + break; > + > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static const struct net_device_ops flexcan_netdev_ops = { > + .ndo_open = flexcan_open, > + .ndo_stop = flexcan_close, > + .ndo_start_xmit = flexcan_start_xmit, > +}; > + > +static int register_flexcandev(struct net_device *ndev) > +{ > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + u32 reg; > + > + /* Ensure clock is enabled so we can access registers */ > + clk_enable(priv->clk); > + reg = readl(®s->canmcr); > + reg &= ~CANMCR_MDIS; > + reg |= CANMCR_FEN; > + writel(reg, ®s->canmcr); > + init_regs(regs); > + udelay(100); > + > + reg = readl(®s->canmcr); > + clk_disable(priv->clk); > + > + /* Currently we only support newer versions of this core featuring > + * a RX FIFO. Older cores found on some Coldfire derivates are not > + * yet supported. > + */ > + if (!(reg & CANMCR_FEN)) { > + dev_err(&ndev->dev, "Could not enable RX FIFO, unsupported " > + "core"); > + return -ENODEV; > + } > + > + ndev->flags |= IFF_ECHO; /* we support local echo in hardware */ > + ndev->netdev_ops = &flexcan_netdev_ops; > + > + return register_candev(ndev); > +} > + > +static void unregister_flexcandev(struct net_device *ndev) > +{ > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct flexcan_regs __iomem *regs = priv->base; > + u32 reg; > + > + clk_enable(priv->clk); > + reg = readl(®s->canmcr); > + reg |= CANMCR_FRZ | CANMCR_HALT | CANMCR_MDIS; > + writel(reg, ®s->canmcr); > + clk_disable(priv->clk); > + > + unregister_candev(ndev); > +} > + > +static int __devinit flexcan_probe(struct platform_device *pdev) > +{ > + struct resource *mem; > + struct net_device *ndev; > + struct flexcan_priv *priv; > + u32 mem_size; > + int ret; > + > + ndev = alloc_candev(sizeof(struct flexcan_priv), TX_ECHO_SKB_MAX); > + if (!ndev) { > + dev_err(&pdev->dev, "flexcan: alloc_candev failed.\n"); > + return -ENOMEM; > + } > + > + priv = netdev_priv(ndev); > + > + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + > + ndev->irq = platform_get_irq(pdev, 0); > + if (!mem || !ndev->irq) { > + dev_err(&pdev->dev, "flexcan: mem || irq failed.\n"); > + ret = -ENODEV; > + goto failed_req; > + } > + > + mem_size = resource_size(mem); > + > + if (!request_mem_region(mem->start, mem_size, DRIVER_NAME)) { > + dev_err(&pdev->dev, "flexcan: request_mem_region failed.\n"); > + ret = -EBUSY; > + goto failed_req; > + } > + > + SET_NETDEV_DEV(ndev, &pdev->dev); > + > + priv->base = ioremap(mem->start, mem_size); > + if (!priv->base) { > + dev_err(&pdev->dev, "flexcan: ioremap failed.\n"); > + ret = -ENOMEM; > + goto failed_map; > + } > + > + priv->clk = clk_get(&pdev->dev, NULL); > + if (IS_ERR(priv->clk)) { > + dev_err(&pdev->dev, "flexcan: clk_get failed.\n"); > + ret = PTR_ERR(priv->clk); > + goto failed_clock; > + } > + priv->can.clock.freq = clk_get_rate(priv->clk); > + > + platform_set_drvdata(pdev, ndev); > + > + priv->can.do_set_bittiming = flexcan_set_bittiming; using this callback is error prone, with respect to 3-sample and friends, see discussion on socketcan-core. > + priv->can.bittiming_const = &flexcan_bittiming_const; > + priv->can.do_set_mode = flexcan_set_mode; > + priv->can.restart_ms = 500; should this be set in the driver? > + > + netif_napi_add(ndev, &priv->napi, flexcan_rx_poll, > + FLEXCAN_DEF_NAPI_WEIGHT); > + > + ret = register_flexcandev(ndev); > + if (ret) { > + dev_err(&pdev->dev, "flexcan: register_flexcandev failed.\n"); > + goto failed_register; > + } > + > + dev_info(&pdev->dev, "flexcan: probe() succeeded...\n"); > + return 0; > + > +failed_register: > + clk_put(priv->clk); > +failed_clock: > + iounmap(priv->base); > +failed_map: > + release_mem_region(mem->start, mem_size); > +failed_req: > + free_candev(ndev); > + > + return ret; > +} > + > +static int __devexit flexcan_remove(struct platform_device *pdev) > +{ > + struct net_device *ndev = platform_get_drvdata(pdev); > + struct flexcan_priv *priv = netdev_priv(ndev); > + struct resource *mem; > + > + unregister_flexcandev(ndev); > + netif_napi_del(&priv->napi); > + platform_set_drvdata(pdev, NULL); > + iounmap(priv->base); > + clk_put(priv->clk); > + > + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + release_mem_region(mem->start, resource_size(mem)); > + free_candev(ndev); > + > + return 0; > +} > + > +static struct platform_driver flexcan_driver = { > + .driver = { > + .name = DRIVER_NAME, > + }, > + .probe = flexcan_probe, > + .remove = __devexit_p(flexcan_remove), > +}; > + > +static int __init flexcan_init(void) > +{ > + return platform_driver_register(&flexcan_driver); > +} > + > +static void __exit flexcan_exit(void) > +{ > + platform_driver_unregister(&flexcan_driver); > +} > + > +module_init(flexcan_init); > +module_exit(flexcan_exit); > + > +MODULE_AUTHOR("Hans J. Koch <[email protected]>"); > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("SocketCAN driver for FlexCAN based chips"); cheers, Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
