[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4F7AF0C7.7020506@grandegger.com>
Date: Tue, 03 Apr 2012 14:44:55 +0200
From: Wolfgang Grandegger <wg@...ndegger.com>
To: AnilKumar Ch <anilkumar@...com>
CC: socketcan@...tkopp.net, m.kleine-budde@...gutronix.de,
linux-can@...r.kernel.org, netdev@...r.kernel.org,
linux-omap@...r.kernel.org, linux-kernel@...r.kernel.org,
anantgole@...com, nsekhar@...com
Subject: Re: [PATCH] ARM: OMAP: AM33XX: CAN: d_can: Add support for Bosch
D_CAN controller
On 04/03/2012 02:32 PM, AnilKumar Ch wrote:
> This patch adds the support for Bosch D_CAN controller.
>
> Bosch D_CAN controller is a full-CAN implementation compliant to
> CAN protocol version 2.0 part A and B. Bosch D_CAN user manual
> can be obtained from: http://www.semiconductors.bosch.de/media/
> en/pdf/ipmodules_1/can/d_can_users_manual_111.pdf
>
> D_CAN device is used on many SoCs like AM335x, DM8148 and DM813x
> EVMs from TI, D_CAN details on AM335x can be accessed from:
> http://www.ti.com/lit/ug/spruh73c/spruh73c.pdf
>
> D_CAN can be configurable for 16, 32, 64 and 128 message objects.
> The driver implementation is based on 64 message objects.
>
> Following are the design choices made while writing the controller
> driver:
> 1. Interface Register set IF0 has be used for receive and IF1 is
> used for transmit message objects.
> 2. Out of the total Message objects available, half of it are kept
> aside for RX purposes and the rest for TX purposes.
> 3. NAPI implementation is such that both the TX and RX paths
> functions in polling mode.
>
> Signed-off-by: AnilKumar Ch <anilkumar@...com>
Please explain why this CAN controller cannot be handled by the existing
C_CAN driver, eventually with some extensions. The register layout seems
almost identical, at least.
Wolfgang.
> ---
> drivers/net/can/Kconfig | 14 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/d_can.c | 1487 ++++++++++++++++++++++++++++++++++++
> include/linux/can/platform/d_can.h | 40 +
> 4 files changed, 1542 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/can/d_can.c
> create mode 100644 include/linux/can/platform/d_can.h
>
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index bb709fd..2529cba 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -68,6 +68,20 @@ config CAN_TI_HECC
> Driver for TI HECC (High End CAN Controller) module found on many
> TI devices. The device specifications are available from www.ti.com
>
> +config CAN_D_CAN
> + tristate "Bosch D_CAN Controller"
> + depends on CAN_DEV
> + ---help---
> + This driver adds support for the D_CAN device found in
> + many SoCs like am335x, dm814x and dm813x boards from TI.
> +
> + The device user guide can be accessed from
> + http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/
> + can/d_can_users_manual_111.pdf
> +
> + To compile this driver as a module, choose M here: the
> + module will be called d_can.
> +
> config CAN_MCP251X
> tristate "Microchip MCP251x SPI CAN controllers"
> depends on CAN_DEV && SPI && HAS_DMA
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index 938be37..4bd3a87 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_CAN_C_CAN) += c_can/
> obj-$(CONFIG_CAN_CC770) += cc770/
> obj-$(CONFIG_CAN_AT91) += at91_can.o
> obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
> +obj-$(CONFIG_CAN_D_CAN) += d_can.o
> obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
> obj-$(CONFIG_CAN_BFIN) += bfin_can.o
> obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
> diff --git a/drivers/net/can/d_can.c b/drivers/net/can/d_can.c
> new file mode 100644
> index 0000000..51e2986
> --- /dev/null
> +++ b/drivers/net/can/d_can.c
> @@ -0,0 +1,1487 @@
> +/*
> + * CAN bus driver for Bosch D_CAN controller
> + *
> + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * Borrowed from C_CAN driver
> + * Copyright (C) 2010 ST Microelectronics
> + * - Bhupesh Sharma <bhupesh.sharma@...com>
> + *
> + * Borrowed heavily from the C_CAN driver originally written by:
> + * Copyright (C) 2007
> + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@...gutronix.de>
> + * - Simon Kallweit, intefo AG <simon.kallweit@...efo.ch>
> + *
> + * Bosch D_CAN controller is compliant to CAN protocol version 2.0 part A and B.
> + * Bosch D_CAN user manual can be obtained from:
> + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/can/
> + * d_can_users_manual_111.pdf
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/netdevice.h>
> +#include <linux/errno.h>
> +#include <linux/skbuff.h>
> +#include <linux/jiffies.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/can/platform/d_can.h>
> +
> +#define D_CAN_DRV_NAME "d_can"
> +#define D_CAN_VERSION "1.0"
> +#define D_CAN_DRV_DESC "CAN bus driver for Bosch D_CAN controller " \
> + D_CAN_VERSION
> +
> +/* TI D_CAN module registers */
> +#define D_CAN_CTL 0x0 /* CAN control register */
> +#define D_CAN_ES 0x4 /* Error and status */
> +#define D_CAN_PARITYERR_EOI 0x4 /* Parity error EOI */
> +#define D_CAN_ERRC 0x8 /* Error counter */
> +#define D_CAN_BTR 0xC /* Bit timing */
> +#define D_CAN_INT 0x10 /* Interrupt register */
> +#define D_CAN_TEST 0x14 /* Test register */
> +#define D_CAN_PERR 0x1C /* Parity Error Code */
> +#define D_CAN_ABOTR 0x80 /* Auto-Bus-On Time */
> +#define D_CAN_TXRQ_X 0x84 /* Transmission Request X */
> +#define D_CAN_TXRQ(n) (0x88 + ((n) * 4)) /* Transmission request */
> +#define D_CAN_NWDAT_X 0x98 /* New data X register */
> +#define D_CAN_NWDAT(n) (0x9C + ((n) * 4)) /* New data */
> +#define D_CAN_INTPND_X 0xAC /* Interrupt Pending X */
> +#define D_CAN_INTPND(n) (0xB0 + ((n) * 4)) /* Interrupt Pending */
> +#define D_CAN_MSGVAL_X 0xC0 /* Message Valid X */
> +#define D_CAN_MSGVAL(n) (0xC4 + ((n) * 4)) /* Message Valid */
> +#define D_CAN_INTMUX(n) (0xD8 + ((n) * 4)) /* Interrupt Multiplexer */
> +#define D_CAN_IFCMD(n) (0x100 + ((n) * 0x20)) /* Command */
> +#define D_CAN_IFMSK(n) (0x104 + ((n) * 0x20)) /* Mask */
> +#define D_CAN_IFARB(n) (0x108 + ((n) * 0x20)) /* Arbitration */
> +#define D_CAN_IFMCTL(n) (0x10c + ((n) * 0x20)) /* Message ctl */
> +#define D_CAN_IFDATA(n) (0x110 + ((n) * 0x20)) /* DATA A */
> +#define D_CAN_IFDATB(n) (0x114 + ((n) * 0x20)) /* DATA B */
> +#define D_CAN_IF3OBS 0x140 /* IF3 Observation */
> +#define D_CAN_IF3UPD(n) (0x160 + ((n) * 4)) /* Update enable */
> +#define D_CAN_TIOC 0x1E0 /* CAN TX IO Control */
> +#define D_CAN_RIOC 0x1E4 /* CAN RX IO Control */
> +
> +/* Control register Bit fields */
> +#define D_CAN_CTL_WUBA BIT(26) /* Automatic wake-up on bus activity */
> +#define D_CAN_CTL_PDR BIT(24) /* Request for local low power mode */
> +#define D_CAN_CTL_DE3 BIT(20) /* Enable DMA request line for IF3 */
> +#define D_CAN_CTL_DE2 BIT(19) /* Enable DMA request line for IF2 */
> +#define D_CAN_CTL_DE1 BIT(18) /* Enable DMA request line for IF1 */
> +#define D_CAN_CTL_IE1 BIT(17) /* Interrupt line 1 enable */
> +#define D_CAN_CTL_INITDBG BIT(16) /* Init state for debug access */
> +#define D_CAN_CTL_SWR BIT(15) /* S/W reset enable */
> +#define D_CAN_CTL_PMD (0xF << 10) /* Parity on/off */
> +#define D_CAN_CTL_ABO BIT(9) /* Auto bus on enable */
> +#define D_CAN_CTL_IDS BIT(8) /* Interruption debug support enable */
> +#define D_CAN_CTL_TEST BIT(7) /* Test mode enable */
> +#define D_CAN_CTL_CCE BIT(6) /* Configuration change enable */
> +#define D_CAN_CTL_DISABLE_AR BIT(5) /* Disable automatic retransmission */
> +#define D_CAN_CTL_EIE BIT(3) /* Error interrupt enable */
> +#define D_CAN_CTL_SIE BIT(2) /* Status change int enable */
> +#define D_CAN_CTL_IE0 BIT(1) /* Interrupt line 0 enable */
> +#define D_CAN_CTL_INIT BIT(0) /* D_CAN initialization mode */
> +
> +/* D_CAN Error and Status and Parity Error EOI reg bit fields */
> +#define D_CAN_ES_PDA BIT(10) /* Local power-down ACK */
> +#define D_CAN_ES_WUP BIT(9) /* Wkae up pending */
> +#define D_CAN_ES_PER BIT(8) /* Parity error detected */
> +#define D_CAN_ES_BOFF BIT(7) /* Bus off state */
> +#define D_CAN_ES_EWARN BIT(6) /* Warning state */
> +#define D_CAN_ES_EPASS BIT(5) /* Error passive state */
> +#define D_CAN_ES_RXOK BIT(4) /* Received a msg successfully */
> +#define D_CAN_ES_TXOK BIT(3) /* Transmitted a msg successfully */
> +#define D_CAN_ES_LEC_MASK 0x7 /* Last error code */
> +
> +/* Error counter reg bit fields */
> +#define D_CAN_ERRC_RP_MASK BIT(15) /* Receive error passive */
> +#define D_CAN_ERRC_REC_SHIFT 8
> +#define D_CAN_ERRC_REC_MASK (0x7F << 8) /* Receive err counter */
> +#define D_CAN_ERRC_TEC_MASK (0xFF << 0) /* Transmit err counter */
> +
> +/* Bit timing reg bit fields */
> +#define D_CAN_BTR_BRPE_SHIFT 16 /* Baud rate prescaler ext */
> +#define D_CAN_BTR_TSEG2_SHIFT 12 /* Time seg after smpl point */
> +#define D_CAN_BTR_TSEG1_SHIFT 8 /* Time seg before smpl point */
> +#define D_CAN_BTR_SJW_SHIFT 6 /* Syncronization jump width */
> +#define D_CAN_BTR_BRP_SHIFT 0 /* Baud rate prescaler */
> +
> +/* D_CAN Test register bit fields */
> +#define D_CAN_TEST_RDA BIT(9) /* RAM direct access enable */
> +#define D_CAN_TEST_EXL BIT(8) /* External loopback mode */
> +#define D_CAN_TEST_RX BIT(7) /* Monitors the reveive pin */
> +#define D_CAN_TEST_TX (0x3 << 5) /* Control of CAN_TX pin */
> +#define D_CAN_TEST_LBACK BIT(4) /* Loopback mode */
> +#define D_CAN_TEST_SILENT BIT(3) /* Silent mdoe */
> +
> +/* D_CAN IF command reg bit fields */
> +#define D_CAN_IF_CMD_WR BIT(23) /* Write/read */
> +#define D_CAN_IF_CMD_MASK BIT(22) /* Access to mask bits */
> +#define D_CAN_IF_CMD_ARB BIT(21) /* Access to arbitration bits */
> +#define D_CAN_IF_CMD_CONTROL BIT(20) /* Acess to control bits */
> +#define D_CAN_IF_CMD_CIP BIT(19) /* Clear int pending */
> +#define D_CAN_IF_CMD_TXRQST BIT(18) /* Access transmission request */
> +#define D_CAN_IF_CMD_DATAA BIT(17) /* Access to Data Bytes 0-3 */
> +#define D_CAN_IF_CMD_DATAB BIT(16) /* Access to Data Bytes 4-7 */
> +#define D_CAN_IF_CMD_BUSY BIT(15) /* Busy flag */
> +#define D_CAN_IF_CMD_DAM BIT(14) /* Activation of DMA */
> +#define D_CAN_IF_CMD_MN_MASK 0xFF /* No. of msg's used for DMA T/F */
> +#define D_CAN_IF_CMD_ALL (D_CAN_IF_CMD_MASK | D_CAN_IF_CMD_ARB | \
> + D_CAN_IF_CMD_CONTROL | D_CAN_IF_CMD_TXRQST | \
> + D_CAN_IF_CMD_DATAA | D_CAN_IF_CMD_DATAB)
> +
> +/* D_CAN IF mask reg bit fields */
> +#define D_CAN_IF_MASK_MX BIT(31) /* Mask Extended Identifier */
> +#define D_CAN_IF_MASK_MD BIT(30) /* Mask Message direction */
> +
> +/* D_CAN IF Arbitration */
> +#define D_CAN_IF_ARB_MSGVAL BIT(31) /* Message Vaild */
> +#define D_CAN_IF_ARB_MSGXTD BIT(30) /* Extended Identifier 0-11 1-29 */
> +#define D_CAN_IF_ARB_DIR_XMIT BIT(29) /* Message direction 0-R 1-T */
> +
> +/* D_CAN IF Message control */
> +#define D_CAN_IF_MCTL_NEWDAT BIT(15) /* New data available */
> +#define D_CAN_IF_MCTL_MSGLST BIT(14) /* Message lost, only for receive */
> +#define D_CAN_IF_MCTL_INTPND BIT(13) /* Interrupt pending */
> +#define D_CAN_IF_MCTL_UMASK BIT(12) /* Use acceptance mask */
> +#define D_CAN_IF_MCTL_TXIE BIT(11) /* Transmit int enable */
> +#define D_CAN_IF_MCTL_RXIE BIT(10) /* Receive int enable */
> +#define D_CAN_IF_MCTL_RMTEN BIT(9) /* Remote enable */
> +#define D_CAN_IF_MCTL_TXRQST BIT(8) /* Transmit request */
> +#define D_CAN_IF_MCTL_EOB BIT(7) /* Data frames */
> +#define D_CAN_IF_MCTL_DLC_MASK 0xF /* Data length code */
> +
> +/* D_CAN IF3 Observation reg bit fields */
> +#define D_CAN_IF3OBS_UP BIT(15) /* Update data status */
> +#define D_CAN_IF3OBS_SDB BIT(12) /* DataB read out status */
> +#define D_CAN_IF3OBS_SDA BIT(11) /* DataA read out status */
> +#define D_CAN_IF3OBS_SC BIT(10) /* Contol bits read out status */
> +#define D_CAN_IF3OBS_SA BIT(9) /* Arbitration read out status */
> +#define D_CAN_IF3OBS_SM BIT(8) /* Mask bits read out status */
> +#define D_CAN_IF3OBS_DB BIT(4) /* Data B read observation */
> +#define D_CAN_IF3OBS_DA BIT(3) /* Data A read observation */
> +#define D_CAN_IF3OBS_CTL BIT(2) /* Control read observation */
> +#define D_CAN_IF3OBS_ARB BIT(1) /* Arbitration data read observation */
> +#define D_CAN_IF3OBS_MASK BIT(0) /* Mask data read observation */
> +
> +/* D_CAN TX I/O reg bit fields */
> +#define D_CAN_TIOC_PU BIT(18) /* CAN_TX pull up/down select */
> +#define D_CAN_TIOC_PD BIT(17) /* CAN_TX pull disable */
> +#define D_CAN_TIOC_OD BIT(16) /* CAN_TX open drain enable */
> +#define D_CAN_TIOC_FUNC BIT(3) /* CAN_TX function */
> +#define D_CAN_TIOC_DIR BIT(2) /* CAN_TX data direction */
> +#define D_CAN_TIOC_OUT BIT(1) /* CAN_TX data out write */
> +#define D_CAN_TIOC_IN BIT(0) /* CAN_TX data in */
> +
> +/* D_CAN RX I/O reg bit fields */
> +#define D_CAN_RIOC_PU BIT(18) /* CAN_RX pull up/down select */
> +#define D_CAN_RIOC_PD BIT(17) /* CAN_RX pull disable */
> +#define D_CAN_RIOC_OD BIT(16) /* CAN_RX open drain enable */
> +#define D_CAN_RIOC_FUNC BIT(3) /* CAN_RX function */
> +#define D_CAN_RIOC_DIR BIT(2) /* CAN_RX data direction */
> +#define D_CAN_RIOC_OUT BIT(1) /* CAN_RX data out write */
> +#define D_CAN_RIOC_IN BIT(0) /* CAN_RX data in */
> +
> +/* IF register masks */
> +#define IFX_WRITE_IDR(x) ((x) & 0x1FFFFFFF)
> +#define IFX_CMD_BITS(x) ((x) & 0xFFFFFF00)
> +#define IFX_CMD_MSG_NUMBER(x) ((x) & 0xFF)
> +
> +/* Message objects split */
> +#define D_CAN_NUM_MSG_OBJECTS 64
> +#define D_CAN_NUM_RX_MSG_OBJECTS 32
> +#define D_CAN_NUM_TX_MSG_OBJECTS 32
> +
> +#define D_CAN_MSG_OBJ_RX_FIRST 1
> +#define D_CAN_MSG_OBJ_RX_LAST (D_CAN_MSG_OBJ_RX_FIRST + \
> + D_CAN_NUM_RX_MSG_OBJECTS - 1)
> +
> +#define D_CAN_MSG_OBJ_TX_FIRST (D_CAN_MSG_OBJ_RX_LAST + 1)
> +#define D_CAN_MSG_OBJ_TX_LAST (D_CAN_MSG_OBJ_TX_FIRST + \
> + D_CAN_NUM_TX_MSG_OBJECTS - 1)
> +
> +#define D_CAN_MSG_OBJ_RX_SPLIT 17
> +#define D_CAN_MSG_OBJ_RX_LOW_LAST (D_CAN_MSG_OBJ_RX_SPLIT - 1)
> +
> +#define D_CAN_NEXT_MSG_OBJ_MASK (D_CAN_NUM_TX_MSG_OBJECTS - 1)
> +
> +/* global interrupt masks */
> +#define D_CAN_ENABLE_ALL_INTERRUPTS 1
> +#define D_CAN_DISABLE_ALL_INTERRUPTS 0
> +
> +/* status interrupt */
> +#define D_CAN_STATUS_INTERRUPT 0x8000
> +
> +#define D_CAN_BUSY_TIMEOUT 14
> +#define D_CAN_TIMEOUT_MS 1000
> +
> +/* Interface register bank number */
> +#define D_CAN_IF_RX_NUM 0
> +#define D_CAN_IF_TX_NUM 1
> +
> +#define D_CAN_GET_XREG_NUM(priv, reg) (__ffs(d_can_read(priv, reg)) >> 2)
> +
> +/* d_can private data structure */
> +struct d_can_priv {
> + struct can_priv can; /* must be the first member */
> + struct napi_struct napi;
> + struct net_device *dev;
> + struct platform_device *pdev;
> + unsigned int tx_next;
> + unsigned int tx_echo;
> + u32 current_status;
> + u32 last_status;
> + u32 irqstatus;
> + bool is_opened;
> + void __iomem *base;
> + void (*ram_init) (unsigned int, unsigned int);
> +};
> +
> +/* CAN Bittiming constants as per D_CAN specs */
> +static struct can_bittiming_const d_can_bittiming_const = {
> + .name = D_CAN_DRV_NAME,
> + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
> + .tseg1_max = 16,
> + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */
> + .tseg2_max = 8,
> + .sjw_max = 4,
> + .brp_min = 1,
> + .brp_max = 1024, /* 6-bit BRP field + 4-bit BRPE field*/
> + .brp_inc = 1,
> +};
> +
> +/* d_can last error code (lec) values */
> +enum d_can_lec_type {
> + LEC_NO_ERROR = 0,
> + LEC_STUFF_ERROR,
> + LEC_FORM_ERROR,
> + LEC_ACK_ERROR,
> + LEC_BIT1_ERROR,
> + LEC_BIT0_ERROR,
> + LEC_CRC_ERROR,
> + LEC_UNUSED,
> +};
> +
> +/* d_can error types */
> +enum d_can_bus_error_types {
> + NO_ERROR = 0,
> + BUS_OFF,
> + ERROR_WARNING,
> + ERROR_PASSIVE,
> +};
> +
> +static inline void d_can_write(struct d_can_priv *priv, u32 reg, u32 val)
> +{
> + writel(val, priv->base + reg);
> +}
> +
> +static inline u32 d_can_read(struct d_can_priv *priv, u32 reg)
> +{
> + return readl(priv->base + reg);
> +}
> +
> +static inline void d_can_set_bit(struct d_can_priv *priv, u32 reg, u32 bitmask)
> +{
> + d_can_write(priv, reg, d_can_read(priv, reg) | bitmask);
> +}
> +
> +static inline u32 d_can_get_bit(struct d_can_priv *priv, u32 reg, u32 bitmask)
> +{
> + return (d_can_read(priv, reg) & bitmask) ? 1 : 0;
> +}
> +
> +static inline void d_can_clear_bit(struct d_can_priv *priv, u32 reg,
> + u32 bitmask)
> +{
> + d_can_write(priv, reg, d_can_read(priv, reg) & ~bitmask);
> +}
> +
> +static inline int get_tx_next_msg_obj(const struct d_can_priv *priv)
> +{
> + return (priv->tx_next & D_CAN_NEXT_MSG_OBJ_MASK) +
> + D_CAN_MSG_OBJ_TX_FIRST;
> +}
> +
> +static inline int get_tx_echo_msg_obj(const struct d_can_priv *priv)
> +{
> + return (priv->tx_echo & D_CAN_NEXT_MSG_OBJ_MASK) +
> + D_CAN_MSG_OBJ_TX_FIRST;
> +}
> +
> +static void d_can_interrupts(struct d_can_priv *priv, unsigned int enable)
> +{
> + unsigned int cntrl_save = d_can_read(priv, D_CAN_CTL);
> +
> + if (enable)
> + cntrl_save |= (D_CAN_CTL_IE1 | D_CAN_CTL_EIE |
> + D_CAN_CTL_SIE | D_CAN_CTL_IE0);
> + else
> + cntrl_save &= ~(D_CAN_CTL_IE1 | D_CAN_CTL_EIE |
> + D_CAN_CTL_SIE | D_CAN_CTL_IE0);
> +
> + d_can_write(priv, D_CAN_CTL, cntrl_save);
> +}
> +
> +static inline int d_can_msg_obj_is_busy(struct d_can_priv *priv, u32 iface)
> +{
> + unsigned long time_out = jiffies + msecs_to_jiffies(D_CAN_BUSY_TIMEOUT);
> +
> + /* Check the status of busy bit */
> + while (d_can_get_bit(priv, D_CAN_IFCMD(iface), D_CAN_IF_CMD_BUSY) &&
> + time_after(time_out, jiffies))
> + cpu_relax();
> +
> + if (time_after(jiffies, time_out))
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static inline void d_can_object_get(struct net_device *dev,
> + u32 iface, u32 objno, u32 mask)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_write(priv, D_CAN_IFCMD(iface), IFX_CMD_BITS(mask) |
> + IFX_CMD_MSG_NUMBER(objno));
> +
> + /*
> + * As per specs, after writing the message object number in the
> + * IF command register the transfer b/w interface register and
> + * message RAM must be complete in 4 - 14 CAN-CLK period.
> + */
> + if (d_can_msg_obj_is_busy(priv, iface))
> + netdev_err(dev, "timed out in object get\n");
> +}
> +
> +static inline void d_can_object_put(struct net_device *dev,
> + u32 iface, u32 objno, u32 mask)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_write(priv, D_CAN_IFCMD(iface), D_CAN_IF_CMD_WR |
> + IFX_CMD_BITS(mask) | IFX_CMD_MSG_NUMBER(objno));
> +
> + /*
> + * As per specs, after writing the message object number in the
> + * IF command register the transfer b/w interface register and
> + * message RAM must be complete in 4 - 14 CAN-CLK period.
> + */
> + if (d_can_msg_obj_is_busy(priv, iface))
> + netdev_err(dev, "timed out in object put\n");
> +}
> +
> +static void d_can_write_msg_object(struct net_device *dev, u32 iface,
> + struct can_frame *frame, u32 objno)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> + unsigned int id;
> + u32 flags = 0;
> +
> + if (!(frame->can_id & CAN_RTR_FLAG))
> + flags |= D_CAN_IF_ARB_DIR_XMIT;
> +
> + if (frame->can_id & CAN_EFF_FLAG) {
> + id = frame->can_id & CAN_EFF_MASK;
> + flags |= D_CAN_IF_ARB_MSGXTD;
> + } else
> + id = ((frame->can_id & CAN_SFF_MASK) << 18);
> +
> + flags |= D_CAN_IF_ARB_MSGVAL;
> + d_can_write(priv, D_CAN_IFARB(iface), IFX_WRITE_IDR(id) | flags);
> +
> + /* Writing lower 4bytes to DATAA IF register */
> + d_can_write(priv, D_CAN_IFDATA(iface),
> + le32_to_cpu(*(u32 *)(frame->data)));
> +
> + /* Writing higher 4bytes to DATAB IF register */
> + if (frame->can_dlc > 4)
> + d_can_write(priv, D_CAN_IFDATB(iface),
> + le32_to_cpu(*(u32 *)(frame->data + 4)));
> +
> + /* enable TX interrupt for this message object */
> + d_can_write(priv, D_CAN_IFMCTL(iface),
> + D_CAN_IF_MCTL_TXIE | D_CAN_IF_MCTL_EOB |
> + D_CAN_IF_MCTL_TXRQST | D_CAN_IF_MCTL_NEWDAT |
> + frame->can_dlc);
> +
> + /* Put message data into message RAM */
> + d_can_object_put(dev, iface, objno, D_CAN_IF_CMD_ALL);
> +}
> +
> +/*
> + * Mark that this particular message object is received and clearing
> + * the interrupt pending register value.
> + */
> +static inline void d_can_mark_rx_msg_obj(struct net_device *dev,
> + u32 iface, u32 ctrl_mask, u32 objno)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_write(priv, D_CAN_IFMCTL(iface), ctrl_mask &
> + ~(D_CAN_IF_MCTL_MSGLST | D_CAN_IF_MCTL_INTPND));
> + d_can_object_put(dev, iface, objno, D_CAN_IF_CMD_CONTROL);
> +}
> +
> +static inline void d_can_activate_all_lower_rx_msg_objs(struct net_device *dev,
> + u32 iface, u32 ctrl_mask)
> +{
> + unsigned int i;
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + for (i = D_CAN_MSG_OBJ_RX_FIRST; i <= D_CAN_MSG_OBJ_RX_LOW_LAST; i++) {
> + d_can_write(priv, D_CAN_IFMCTL(iface), ctrl_mask &
> + ~(D_CAN_IF_MCTL_MSGLST | D_CAN_IF_MCTL_INTPND |
> + D_CAN_IF_MCTL_NEWDAT));
> + d_can_object_put(dev, iface, i, D_CAN_IF_CMD_CONTROL);
> + }
> +}
> +
> +static inline void d_can_activate_rx_msg_obj(struct net_device *dev,
> + u32 iface, u32 ctrl_mask, u32 objno)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_write(priv, D_CAN_IFMCTL(iface), ctrl_mask &
> + ~(D_CAN_IF_MCTL_MSGLST | D_CAN_IF_MCTL_INTPND |
> + D_CAN_IF_MCTL_NEWDAT));
> + d_can_object_put(dev, iface, objno, D_CAN_IF_CMD_CONTROL);
> +}
> +
> +static void d_can_handle_lost_msg_obj(struct net_device *dev,
> + u32 iface, u32 objno)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + struct sk_buff *skb;
> + struct can_frame *frame;
> +
> + netdev_err(dev, "msg lost in buffer %d\n", objno);
> + d_can_object_get(dev, iface, objno, D_CAN_IF_CMD_ALL &
> + ~D_CAN_IF_CMD_TXRQST);
> + d_can_clear_bit(priv, D_CAN_IFMCTL(iface), D_CAN_IF_MCTL_MSGLST);
> + d_can_object_put(dev, iface, objno, D_CAN_IF_CMD_CONTROL);
> +
> + /* create an error msg */
> + skb = alloc_can_err_skb(dev, &frame);
> + if (unlikely(!skb))
> + return;
> +
> + frame->can_id |= CAN_ERR_CRTL;
> + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> + stats->rx_errors++;
> + stats->rx_over_errors++;
> +
> + netif_receive_skb(skb);
> +}
> +
> +static int d_can_read_msg_object(struct net_device *dev, u32 iface, u32 ctrl)
> +{
> + unsigned int arb_val, mctl_val;
> + struct d_can_priv *priv = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + struct can_frame *frame;
> + struct sk_buff *skb;
> + u32 data = 0;
> +
> + skb = alloc_can_skb(dev, &frame);
> + if (!skb) {
> + stats->rx_dropped++;
> + return -ENOMEM;
> + }
> +
> + frame->can_dlc = get_can_dlc(ctrl & 0x0F);
> + arb_val = d_can_read(priv, D_CAN_IFARB(iface));
> + mctl_val = d_can_read(priv, D_CAN_IFMCTL(iface));
> +
> + if (arb_val & D_CAN_IF_ARB_MSGXTD)
> + frame->can_id = (arb_val & CAN_EFF_MASK) | CAN_EFF_FLAG;
> + else
> + frame->can_id = (arb_val >> 18) & CAN_SFF_MASK;
> +
> + if (mctl_val & D_CAN_IF_MCTL_RMTEN)
> + frame->can_id |= CAN_RTR_FLAG;
> + else {
> + data = d_can_read(priv, D_CAN_IFDATA(iface));
> + /* Writing MO lower 4 data bytes to skb */
> + *(u32 *)(frame->data) = cpu_to_le32(data);
> +
> + /* Writing MO higher 4 data bytes to skb */
> + if (frame->can_dlc > 4) {
> + data = d_can_read(priv, D_CAN_IFDATB(iface));
> + *(u32 *)(frame->data + 4) = cpu_to_le32(data);
> + } else
> + *(u32 *)(frame->data + 4) = 0;
> + }
> +
> + netif_receive_skb(skb);
> + stats->rx_packets++;
> + stats->rx_bytes += frame->can_dlc;
> +
> + return 0;
> +}
> +
> +static void d_can_setup_receive_object(struct net_device *dev, u32 iface,
> + u32 objno, u32 mask, u32 id, u32 mcont)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_write(priv, D_CAN_IFMCTL(iface), mcont);
> + d_can_write(priv, D_CAN_IFMSK(iface), IFX_WRITE_IDR(mask));
> + d_can_write(priv, D_CAN_IFARB(iface), IFX_WRITE_IDR(id) |
> + D_CAN_IF_ARB_MSGVAL);
> + d_can_object_put(dev, iface, objno, D_CAN_IF_CMD_ALL &
> + ~D_CAN_IF_CMD_TXRQST);
> +
> + netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, d_can_read(priv,
> + D_CAN_MSGVAL(D_CAN_GET_XREG_NUM(priv, D_CAN_MSGVAL_X))));
> +}
> +
> +static void d_can_inval_msg_object(struct net_device *dev, u32 iface, u32 objno)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_write(priv, D_CAN_IFARB(iface), 0);
> + d_can_write(priv, D_CAN_IFMCTL(iface), 0);
> + d_can_object_put(dev, iface, objno, D_CAN_IF_CMD_ARB |
> + D_CAN_IF_CMD_CONTROL);
> +
> + netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, d_can_read(priv,
> + D_CAN_MSGVAL(D_CAN_GET_XREG_NUM(priv, D_CAN_MSGVAL_X))));
> +}
> +
> +static inline int d_can_is_next_tx_obj_busy(struct d_can_priv *priv, u32 objno)
> +{
> + u32 txrq_x_reg_val = D_CAN_GET_XREG_NUM(priv, D_CAN_TXRQ_X);
> +
> + /* Transmission request register's bit n-1 corresponds to
> + * message object n, we need to handle the same properly */
> + if (d_can_read(priv, D_CAN_TXRQ(txrq_x_reg_val)) &
> + (1 << (objno - D_CAN_MSG_OBJ_TX_FIRST)))
> + return 1;
> +
> + return 0;
> +}
> +
> +static netdev_tx_t d_can_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + u32 msg_obj_no;
> + struct d_can_priv *priv = netdev_priv(dev);
> + struct can_frame *frame = (struct can_frame *)skb->data;
> +
> + if (can_dropped_invalid_skb(dev, skb))
> + return NETDEV_TX_OK;
> +
> + msg_obj_no = get_tx_next_msg_obj(priv);
> +
> + /* prepare message object for transmission */
> + d_can_write_msg_object(dev, D_CAN_IF_TX_NUM, frame, msg_obj_no);
> + can_put_echo_skb(skb, dev, msg_obj_no - D_CAN_MSG_OBJ_TX_FIRST);
> +
> + /*
> + * we have to stop the queue in case of a wrap around or
> + * if the next TX message object is still in use
> + */
> + priv->tx_next++;
> + if (d_can_is_next_tx_obj_busy(priv, get_tx_next_msg_obj(priv)) ||
> + ((priv->tx_next & D_CAN_NEXT_MSG_OBJ_MASK) == 0))
> + netif_stop_queue(dev);
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static void d_can_set_bittiming(struct net_device *dev)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> + const struct can_bittiming *bt = &priv->can.bittiming;
> + u32 can_btc;
> +
> + can_btc = ((bt->phase_seg2 - 1) & 0x7) << D_CAN_BTR_TSEG2_SHIFT;
> + can_btc |= ((bt->phase_seg1 + bt->prop_seg - 1) & 0xF) <<
> + D_CAN_BTR_TSEG1_SHIFT;
> + can_btc |= ((bt->sjw - 1) & 0x3) << D_CAN_BTR_SJW_SHIFT;
> + /* Ten bits contains the BRP, 6 bits for BRP and upper 4 bits for brpe*/
> + can_btc |= ((bt->brp - 1) & 0x3F) << D_CAN_BTR_BRP_SHIFT;
> + can_btc |= ((((bt->brp - 1) >> 6) & 0xF) << D_CAN_BTR_BRPE_SHIFT);
> +
> + d_can_write(priv, D_CAN_BTR, can_btc);
> + netdev_info(dev, "setting CAN BT = %#x\n", can_btc);
> +}
> +
> +/*
> + * Configure D_CAN message objects for Tx and Rx purposes:
> + * D_CAN provides a total of 64 message objects that can be configured
> + * either for Tx or Rx purposes. In this driver first 32 message objects
> + * are used as a reception FIFO and the reception FIFO is signified by the
> + * EoB bit being SET. The remaining 32 message objects are kept aside for
> + * Tx purposes. See user guide document for further details on configuring
> + * message objects.
> + */
> +static void d_can_configure_msg_objects(struct net_device *dev)
> +{
> + unsigned int i;
> +
> + /* first invalidate all message objects */
> + for (i = D_CAN_MSG_OBJ_RX_FIRST; i <= D_CAN_NUM_MSG_OBJECTS; i++)
> + d_can_inval_msg_object(dev, D_CAN_IF_RX_NUM, i);
> +
> + /* setup receive message objects */
> + for (i = D_CAN_MSG_OBJ_RX_FIRST; i < D_CAN_MSG_OBJ_RX_LAST; i++)
> + d_can_setup_receive_object(dev, D_CAN_IF_RX_NUM, i, 0, 0,
> + (D_CAN_IF_MCTL_RXIE | D_CAN_IF_MCTL_UMASK) &
> + ~D_CAN_IF_MCTL_EOB);
> +
> + /* Last object EoB bit should be 1 for terminate */
> + d_can_setup_receive_object(dev, D_CAN_IF_RX_NUM, D_CAN_MSG_OBJ_RX_LAST,
> + 0, 0, D_CAN_IF_MCTL_RXIE | D_CAN_IF_MCTL_UMASK |
> + D_CAN_IF_MCTL_EOB);
> +}
> +
> +static void d_can_test_mode(struct net_device *dev)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + /* Test mode is enabled in this step & the specific TEST bits
> + * are enabled accordingly */
> + d_can_write(priv, D_CAN_CTL, D_CAN_CTL_EIE |
> + D_CAN_CTL_IE1 | D_CAN_CTL_IE0 | D_CAN_CTL_TEST);
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> + /* silent mode : bus-monitoring mode */
> + d_can_write(priv, D_CAN_TEST, D_CAN_TEST_SILENT);
> + else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
> + /* loopback mode : useful for self-test function */
> + d_can_write(priv, D_CAN_TEST, D_CAN_TEST_LBACK);
> + else
> + /* loopback + silent mode : useful for hot self-test */
> + d_can_write(priv, D_CAN_TEST, D_CAN_TEST_LBACK |
> + D_CAN_TEST_SILENT);
> +}
> +
> +/*
> + * Configure D_CAN chip:
> + * - enable/disable auto-retransmission
> + * - set operating mode
> + * - configure message objects
> + */
> +static void d_can_init(struct net_device *dev)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> + unsigned long time_out;
> +
> + netdev_dbg(dev, "resetting d_can ...\n");
> + d_can_set_bit(priv, D_CAN_CTL, D_CAN_CTL_SWR);
> +
> + /* Enter initialization mode by setting the Init bit */
> + d_can_set_bit(priv, D_CAN_CTL, D_CAN_CTL_INIT);
> +
> + /* enable automatic retransmission */
> + d_can_clear_bit(priv, D_CAN_CTL, D_CAN_CTL_DISABLE_AR);
> +
> + /* Set the Configure Change Enable ( CCE) bit */
> + d_can_set_bit(priv, D_CAN_CTL, D_CAN_CTL_CCE);
> +
> + /* Wait for the Init bit to get set */
> + time_out = jiffies + msecs_to_jiffies(D_CAN_TIMEOUT_MS);
> + while (!d_can_get_bit(priv, D_CAN_CTL, D_CAN_CTL_INIT) &&
> + time_after(time_out, jiffies))
> + cpu_relax();
> +
> + if (time_after(jiffies, time_out)) {
> + netdev_err(dev, "timedout, D_CAN INIT bit is not set\n");
> + return;
> + }
> +
> + /* set bittiming params */
> + d_can_set_bittiming(dev);
> +
> + d_can_clear_bit(priv, D_CAN_CTL, D_CAN_CTL_INIT | D_CAN_CTL_CCE);
> +
> + /* Wait for the Init bit to get clear */
> + time_out = jiffies + msecs_to_jiffies(D_CAN_TIMEOUT_MS);
> + while (d_can_get_bit(priv, D_CAN_CTL, D_CAN_CTL_INIT) &&
> + time_after(time_out, jiffies))
> + cpu_relax();
> +
> + if (time_after(jiffies, time_out)) {
> + netdev_err(dev, "timedout, D_CAN INIT bit is not cleared\n");
> + return;
> + }
> +
> + if (priv->can.ctrlmode & (CAN_CTRLMODE_LOOPBACK |
> + CAN_CTRLMODE_LISTENONLY))
> + d_can_test_mode(dev);
> + else
> + d_can_write(priv, D_CAN_CTL, D_CAN_CTL_EIE | D_CAN_CTL_IE1 |
> + D_CAN_CTL_SIE | D_CAN_CTL_IE0);
> +
> + /* Enable TX and RX I/O Control pins */
> + d_can_write(priv, D_CAN_TIOC, D_CAN_TIOC_FUNC);
> + d_can_write(priv, D_CAN_RIOC, D_CAN_RIOC_FUNC);
> +
> + /* configure message objects */
> + d_can_configure_msg_objects(dev);
> +
> + /* set a LEC value so that we can check for updates later */
> + d_can_write(priv, D_CAN_ES, LEC_UNUSED);
> +}
> +
> +static void d_can_start(struct net_device *dev)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + /* basic d_can initialization */
> + d_can_init(dev);
> +
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + /* reset tx helper pointers */
> + priv->tx_next = priv->tx_echo = 0;
> +
> + /* enable status change, error and module interrupts */
> + d_can_interrupts(priv, D_CAN_ENABLE_ALL_INTERRUPTS);
> +}
> +
> +static void d_can_stop(struct net_device *dev)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_interrupts(priv, D_CAN_DISABLE_ALL_INTERRUPTS);
> +
> + /* set the state as STOPPED */
> + priv->can.state = CAN_STATE_STOPPED;
> +}
> +
> +static int d_can_set_mode(struct net_device *dev, enum can_mode mode)
> +{
> + switch (mode) {
> + case CAN_MODE_START:
> + d_can_start(dev);
> + netif_wake_queue(dev);
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int d_can_get_berr_counter(const struct net_device *dev,
> + struct can_berr_counter *bec)
> +{
> + unsigned int err_cnt;
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + err_cnt = d_can_read(priv, D_CAN_ERRC);
> + bec->rxerr = (err_cnt & D_CAN_ERRC_REC_MASK) >> D_CAN_ERRC_REC_SHIFT;
> + bec->txerr = err_cnt & D_CAN_ERRC_TEC_MASK;
> +
> + return 0;
> +}
> +
> +/*
> + * priv->tx_echo holds the number of the oldest can_frame put for
> + * transmission into the hardware, but not yet ACKed by the CAN tx
> + * complete IRQ.
> + * We iterate from priv->tx_echo to priv->tx_next and check if the
> + * packet has been transmitted, echo it back to the CAN framework.
> + * If we discover a not yet transmitted packet, stop looking for more.
> + */
> +static void d_can_do_tx(struct net_device *dev)
> +{
> + u32 msg_obj_no;
> + struct d_can_priv *priv = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + u32 txrq_x_reg_val;
> + u32 txrq_reg_val;
> +
> + for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) {
> + msg_obj_no = get_tx_echo_msg_obj(priv);
> + txrq_x_reg_val = D_CAN_GET_XREG_NUM(priv, D_CAN_TXRQ_X);
> + txrq_reg_val = d_can_read(priv, D_CAN_TXRQ(txrq_x_reg_val));
> + if (!(txrq_reg_val & (1 << (msg_obj_no -
> + D_CAN_MSG_OBJ_TX_FIRST)))) {
> + can_get_echo_skb(dev,
> + msg_obj_no - D_CAN_MSG_OBJ_TX_FIRST);
> + stats->tx_bytes += d_can_read(priv,
> + D_CAN_IFMCTL(D_CAN_IF_TX_NUM)) &
> + D_CAN_IF_MCTL_DLC_MASK;
> + stats->tx_packets++;
> + d_can_inval_msg_object(dev, D_CAN_IF_TX_NUM,
> + msg_obj_no);
> + } else
> + break;
> + }
> +
> + /* restart queue if wrap-up or if queue stalled on last pkt */
> + if (((priv->tx_next & D_CAN_NEXT_MSG_OBJ_MASK) != 0) ||
> + ((priv->tx_echo & D_CAN_NEXT_MSG_OBJ_MASK) == 0))
> + netif_wake_queue(dev);
> +}
> +
> +/*
> + * d_can core saves a received CAN message into the first free message
> + * object it finds free (starting with the lowest). Bits NEWDAT and
> + * INTPND are set for this message object indicating that a new message
> + * has arrived. To work-around this issue, we keep two groups of message
> + * objects whose partitioning is defined by D_CAN_MSG_OBJ_RX_SPLIT.
> + *
> + * To ensure in-order frame reception we use the following
> + * approach while re-activating a message object to receive further
> + * frames:
> + * - if the current message object number is lower than
> + * D_CAN_MSG_RX_LOW_LAST, do not clear the NEWDAT bit while clearing
> + * the INTPND bit.
> + * - if the current message object number is equal to
> + * D_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of all lower
> + * receive message objects.
> + * - if the current message object number is greater than
> + * D_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of
> + * only this message object.
> + */
> +static int d_can_do_rx_poll(struct net_device *dev, int quota)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> + unsigned int msg_obj, mctrl_reg_val;
> + u32 num_rx_pkts = 0;
> + u32 intpnd_x_reg_val;
> + u32 intpnd_reg_val;
> +
> + for (msg_obj = D_CAN_MSG_OBJ_RX_FIRST;
> + msg_obj <= D_CAN_MSG_OBJ_RX_LAST && quota > 0; msg_obj++) {
> +
> + intpnd_x_reg_val = D_CAN_GET_XREG_NUM(priv, D_CAN_INTPND_X);
> + intpnd_reg_val = d_can_read(priv,
> + D_CAN_INTPND(intpnd_x_reg_val));
> +
> + /*
> + * as interrupt pending register's bit n-1 corresponds to
> + * message object n, we need to handle the same properly.
> + */
> + if (intpnd_reg_val & (1 << (msg_obj - 1))) {
> +
> + d_can_object_get(dev, D_CAN_IF_RX_NUM, msg_obj,
> + D_CAN_IF_CMD_ALL &
> + ~D_CAN_IF_CMD_TXRQST);
> +
> + mctrl_reg_val = d_can_read(priv,
> + D_CAN_IFMCTL(D_CAN_IF_RX_NUM));
> +
> + if (!(mctrl_reg_val & D_CAN_IF_MCTL_NEWDAT))
> + continue;
> +
> + /* read the data from the message object */
> + d_can_read_msg_object(dev, D_CAN_IF_RX_NUM,
> + mctrl_reg_val);
> +
> + if (mctrl_reg_val & D_CAN_IF_MCTL_EOB)
> + d_can_setup_receive_object(dev, D_CAN_IF_RX_NUM,
> + D_CAN_MSG_OBJ_RX_LAST, 0, 0,
> + D_CAN_IF_MCTL_RXIE |
> + D_CAN_IF_MCTL_UMASK |
> + D_CAN_IF_MCTL_EOB);
> +
> + if (mctrl_reg_val & D_CAN_IF_MCTL_MSGLST) {
> + d_can_handle_lost_msg_obj(dev, D_CAN_IF_RX_NUM,
> + msg_obj);
> + num_rx_pkts++;
> + quota--;
> + continue;
> + }
> +
> + if (msg_obj < D_CAN_MSG_OBJ_RX_LOW_LAST)
> + d_can_mark_rx_msg_obj(dev, D_CAN_IF_RX_NUM,
> + mctrl_reg_val, msg_obj);
> + else if (msg_obj > D_CAN_MSG_OBJ_RX_LOW_LAST)
> + /* activate this msg obj */
> + d_can_activate_rx_msg_obj(dev, D_CAN_IF_RX_NUM,
> + mctrl_reg_val, msg_obj);
> + else if (msg_obj == D_CAN_MSG_OBJ_RX_LOW_LAST)
> + /* activate all lower message objects */
> + d_can_activate_all_lower_rx_msg_objs(dev,
> + D_CAN_IF_RX_NUM, mctrl_reg_val);
> +
> + num_rx_pkts++;
> + quota--;
> + }
> + }
> + return num_rx_pkts;
> +}
> +
> +static inline int d_can_has_handle_berr(struct d_can_priv *priv)
> +{
> + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
> + (priv->current_status & LEC_UNUSED);
> +}
> +
> +static int d_can_handle_state_change(struct net_device *dev,
> + enum d_can_bus_error_types error_type)
> +{
> + unsigned int err_cnt;
> + unsigned int rx_epass;
> + struct d_can_priv *priv = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + struct can_berr_counter bec;
> +
> + /* propagate the error condition to the CAN stack */
> + skb = alloc_can_err_skb(dev, &cf);
> + if (unlikely(!skb))
> + return 0;
> +
> + d_can_get_berr_counter(dev, &bec);
> + err_cnt = d_can_read(priv, D_CAN_ERRC);
> + rx_epass = err_cnt & D_CAN_ERRC_RP_MASK;
> +
> + switch (error_type) {
> + case ERROR_WARNING:
> + /* error warning state */
> + priv->can.can_stats.error_warning++;
> + priv->can.state = CAN_STATE_ERROR_WARNING;
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] = (bec.txerr > bec.rxerr) ?
> + CAN_ERR_CRTL_TX_WARNING :
> + CAN_ERR_CRTL_RX_WARNING;
> + cf->data[6] = bec.txerr;
> + cf->data[7] = bec.rxerr;
> +
> + break;
> + case ERROR_PASSIVE:
> + /* error passive state */
> + priv->can.can_stats.error_passive++;
> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> + cf->can_id |= CAN_ERR_CRTL;
> + if (rx_epass)
> + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> + if (bec.txerr > 127)
> + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +
> + cf->data[6] = bec.txerr;
> + cf->data[7] = bec.rxerr;
> + break;
> + case BUS_OFF:
> + /* bus-off state */
> + priv->can.state = CAN_STATE_BUS_OFF;
> + cf->can_id |= CAN_ERR_BUSOFF;
> + /*
> + * disable all interrupts in bus-off mode to ensure that
> + * the CPU is not hogged down
> + */
> + d_can_interrupts(priv, D_CAN_DISABLE_ALL_INTERRUPTS);
> + can_bus_off(dev);
> + break;
> + default:
> + break;
> + }
> + netif_receive_skb(skb);
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +
> + return 1;
> +}
> +
> +static int d_can_handle_bus_err(struct net_device *dev,
> + enum d_can_lec_type lec_type)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> +
> + /*
> + * early exit if no lec update or no error.
> + * no lec update means that no CAN bus event has been detected
> + * since CPU wrote 0x7 value to status reg.
> + */
> + if (lec_type == LEC_UNUSED || lec_type == LEC_NO_ERROR)
> + return 0;
> +
> + /* propagate the error condition to the CAN stack */
> + skb = alloc_can_err_skb(dev, &cf);
> + if (unlikely(!skb))
> + return 0;
> +
> + /* common for all type of bus errors */
> + priv->can.can_stats.bus_error++;
> + stats->rx_errors++;
> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> + cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +
> + switch (lec_type) {
> + case LEC_STUFF_ERROR:
> + netdev_dbg(dev, "stuff error\n");
> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> + break;
> + case LEC_FORM_ERROR:
> + netdev_dbg(dev, "form error\n");
> + cf->data[2] |= CAN_ERR_PROT_FORM;
> + break;
> + case LEC_ACK_ERROR:
> + netdev_dbg(dev, "ack error\n");
> + cf->data[2] |= (CAN_ERR_PROT_LOC_ACK |
> + CAN_ERR_PROT_LOC_ACK_DEL);
> + break;
> + case LEC_BIT1_ERROR:
> + netdev_dbg(dev, "bit1 error\n");
> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> + break;
> + case LEC_BIT0_ERROR:
> + netdev_dbg(dev, "bit0 error\n");
> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> + break;
> + case LEC_CRC_ERROR:
> + netdev_dbg(dev, "CRC error\n");
> + cf->data[2] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL);
> + break;
> + default:
> + break;
> + }
> +
> + /* set a LEC value so that we can check for updates later */
> + d_can_write(priv, D_CAN_ES, LEC_UNUSED);
> +
> + netif_receive_skb(skb);
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +
> + return 1;
> +}
> +
> +static int d_can_poll(struct napi_struct *napi, int quota)
> +{
> + int work_done = 0;
> + int lec_type = 0;
> + struct net_device *dev = napi->dev;
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + if (!priv->irqstatus)
> + goto end;
> +
> + /* status events have the highest priority */
> + if (priv->irqstatus == D_CAN_STATUS_INTERRUPT) {
> + priv->current_status = d_can_read(priv, D_CAN_ES);
> +
> + /* handle Tx/Rx events */
> + if (priv->current_status & D_CAN_ES_TXOK)
> + d_can_write(priv, D_CAN_ES,
> + priv->current_status & ~D_CAN_ES_TXOK);
> +
> + if (priv->current_status & D_CAN_ES_RXOK)
> + d_can_write(priv, D_CAN_ES,
> + priv->current_status & ~D_CAN_ES_RXOK);
> +
> + /* handle state changes */
> + if ((priv->current_status & D_CAN_ES_EWARN) &&
> + (!(priv->last_status & D_CAN_ES_EWARN))) {
> + netdev_dbg(dev, "entered error warning state\n");
> + work_done += d_can_handle_state_change(dev,
> + ERROR_WARNING);
> + }
> + if ((priv->current_status & D_CAN_ES_EPASS) &&
> + (!(priv->last_status & D_CAN_ES_EPASS))) {
> + netdev_dbg(dev, "entered error passive state\n");
> + work_done += d_can_handle_state_change(dev,
> + ERROR_PASSIVE);
> + }
> + if ((priv->current_status & D_CAN_ES_BOFF) &&
> + (!(priv->last_status & D_CAN_ES_BOFF))) {
> + netdev_dbg(dev, "entered bus off state\n");
> + work_done += d_can_handle_state_change(dev, BUS_OFF);
> + }
> +
> + /* handle bus recovery events */
> + if ((!(priv->current_status & D_CAN_ES_BOFF)) &&
> + (priv->last_status & D_CAN_ES_BOFF)) {
> + netdev_dbg(dev, "left bus off state\n");
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + }
> + if ((!(priv->current_status & D_CAN_ES_EPASS)) &&
> + (priv->last_status & D_CAN_ES_EPASS)) {
> + netdev_dbg(dev, "left error passive state\n");
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + }
> + priv->last_status = priv->current_status;
> +
> + /* handle lec errors on the bus */
> + lec_type = d_can_has_handle_berr(priv);
> + if (lec_type)
> + work_done += d_can_handle_bus_err(dev, lec_type);
> + } else if ((priv->irqstatus >= D_CAN_MSG_OBJ_RX_FIRST) &&
> + (priv->irqstatus <= D_CAN_MSG_OBJ_RX_LAST)) {
> + /* handle events corresponding to receive message objects */
> + work_done += d_can_do_rx_poll(dev, (quota - work_done));
> + } else if ((priv->irqstatus >= D_CAN_MSG_OBJ_TX_FIRST) &&
> + (priv->irqstatus <= D_CAN_MSG_OBJ_TX_LAST)) {
> + /* handle events corresponding to transmit message objects */
> + d_can_do_tx(dev);
> + }
> +end:
> + if (work_done < quota) {
> + napi_complete(napi);
> + d_can_interrupts(priv, D_CAN_ENABLE_ALL_INTERRUPTS);
> + }
> + return work_done;
> +}
> +
> +static irqreturn_t d_can_isr(int irq, void *dev_id)
> +{
> + struct net_device *dev = (struct net_device *)dev_id;
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + priv->irqstatus = d_can_read(priv, D_CAN_INT);
> + if (!priv->irqstatus)
> + return IRQ_NONE;
> +
> + /* disable all interrupts and schedule the NAPI */
> + d_can_interrupts(priv, D_CAN_DISABLE_ALL_INTERRUPTS);
> + napi_schedule(&priv->napi);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int d_can_open(struct net_device *ndev)
> +{
> + int err;
> + struct d_can_priv *priv = netdev_priv(ndev);
> +
> + /* Open common can device */
> + err = open_candev(ndev);
> + if (err) {
> + netdev_err(ndev, "open_candev() failed %d\n", err);
> + return err;
> + }
> +
> + err = request_irq(ndev->irq, &d_can_isr, IRQF_SHARED, ndev->name,
> + ndev);
> + if (err) {
> + netdev_err(ndev, "failed to request MO_ES interrupt\n");
> + goto exit_close_candev;
> + }
> +
> + /* start the d_can controller */
> + d_can_start(ndev);
> + napi_enable(&priv->napi);
> + netif_start_queue(ndev);
> + priv->is_opened = true;
> +
> + return 0;
> +exit_close_candev:
> + close_candev(ndev);
> + return err;
> +}
> +
> +static int d_can_close(struct net_device *ndev)
> +{
> + struct d_can_priv *priv = netdev_priv(ndev);
> +
> + netif_stop_queue(ndev);
> + napi_disable(&priv->napi);
> + d_can_stop(ndev);
> + free_irq(ndev->irq, ndev);
> + close_candev(ndev);
> + priv->is_opened = false;
> +
> + return 0;
> +}
> +
> +static void d_can_reset_ram(struct d_can_priv *d_can, unsigned int instance,
> + unsigned int enable)
> +{
> + if (d_can->ram_init)
> + d_can->ram_init(instance, enable);
> + /* Give some time delay for DCAN RAM initialization */
> + udelay(1);
> +}
> +
> +static struct net_device *alloc_d_can_dev(unsigned int num_objs)
> +{
> + struct net_device *dev;
> + struct d_can_priv *priv;
> +
> + dev = alloc_candev(sizeof(struct d_can_priv), num_objs/2);
> + if (!dev)
> + return NULL;
> +
> + priv = netdev_priv(dev);
> + netif_napi_add(dev, &priv->napi, d_can_poll, num_objs/2);
> +
> + priv->dev = dev;
> + priv->can.bittiming_const = &d_can_bittiming_const;
> + priv->can.do_set_mode = d_can_set_mode;
> + priv->can.do_get_berr_counter = d_can_get_berr_counter;
> + priv->can.ctrlmode_supported = (CAN_CTRLMODE_LOOPBACK |
> + CAN_CTRLMODE_LISTENONLY |
> + CAN_CTRLMODE_BERR_REPORTING |
> + CAN_CTRLMODE_3_SAMPLES);
> + return dev;
> +}
> +
> +static void free_d_can_dev(struct net_device *dev)
> +{
> + free_candev(dev);
> +}
> +
> +static const struct net_device_ops d_can_netdev_ops = {
> + .ndo_open = d_can_open,
> + .ndo_stop = d_can_close,
> + .ndo_start_xmit = d_can_start_xmit,
> +};
> +
> +static int register_d_can_dev(struct net_device *dev)
> +{
> + /* we support local echo */
> + dev->flags |= IFF_ECHO;
> + dev->netdev_ops = &d_can_netdev_ops;
> + return register_candev(dev);
> +}
> +
> +static void unregister_d_can_dev(struct net_device *dev)
> +{
> + struct d_can_priv *priv = netdev_priv(dev);
> +
> + d_can_interrupts(priv, D_CAN_DISABLE_ALL_INTERRUPTS);
> + unregister_candev(dev);
> +}
> +
> +static int __devinit d_can_plat_probe(struct platform_device *pdev)
> +{
> + int ret = 0;
> + void __iomem *addr;
> + struct net_device *ndev;
> + struct d_can_priv *priv;
> + struct resource *mem, *irq;
> + struct d_can_platform_data *pdata;
> + struct clk *fck;
> +
> + pdata = pdev->dev.platform_data;
> + if (!pdata) {
> + dev_err(&pdev->dev, "No platform data\n");
> + goto exit;
> + }
> +
> + /* get the platform data */
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + ret = -ENODEV;
> + dev_err(&pdev->dev, "No mem resource\n");
> + goto exit;
> + }
> + irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "d_can_ms");
> + if (!irq) {
> + dev_err(&pdev->dev, "No irq resource\n");
> + goto exit;
> + }
> + if (!request_mem_region(mem->start, resource_size(mem),
> + D_CAN_DRV_NAME)) {
> + dev_err(&pdev->dev, "resource unavailable\n");
> + ret = -EBUSY;
> + goto exit;
> + }
> + addr = ioremap(mem->start, resource_size(mem));
> + if (!addr) {
> + dev_err(&pdev->dev, "ioremap failed\n");
> + ret = -ENOMEM;
> + goto exit_release_mem;
> + }
> +
> + /* allocate the d_can device */
> + ndev = alloc_d_can_dev(pdata->num_of_msg_objs);
> + if (!ndev) {
> + dev_err(&pdev->dev, "alloc_d_can_dev failed\n");
> + ret = -ENOMEM;
> + goto exit_iounmap;
> + }
> + fck = clk_get(&pdev->dev, "fck");
> + if (IS_ERR(fck)) {
> + dev_err(&pdev->dev, "fck is not found\n");
> + ret = -ENODEV;
> + goto exit_free_ndev;
> + }
> +
> + priv = netdev_priv(ndev);
> + priv->base = addr;
> + priv->can.clock.freq = clk_get_rate(fck);
> + priv->ram_init = pdata->ram_init;
> + priv->is_opened = false;
> +
> + ndev->irq = irq->start;
> + platform_set_drvdata(pdev, ndev);
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_get_sync(&pdev->dev);
> +
> + ret = register_d_can_dev(ndev);
> + if (ret) {
> + dev_err(&pdev->dev, "registering %s failed (err = %d)\n",
> + D_CAN_DRV_NAME, ret);
> + goto exit_free_device;
> + }
> +
> + /* Initialize DCAN RAM */
> + d_can_reset_ram(priv, pdev->id, 1);
> +
> + dev_info(&pdev->dev, "device registered (irq = %d)\n", ndev->irq);
> + return 0;
> +exit_free_device:
> + platform_set_drvdata(pdev, NULL);
> + pm_runtime_disable(&pdev->dev);
> + clk_put(fck);
> +exit_free_ndev:
> + free_d_can_dev(ndev);
> +exit_iounmap:
> + iounmap(addr);
> +exit_release_mem:
> + release_mem_region(mem->start, resource_size(mem));
> +exit:
> + dev_err(&pdev->dev, "probe failed\n");
> + return ret;
> +}
> +
> +static int __devexit d_can_plat_remove(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct d_can_priv *priv = netdev_priv(ndev);
> + struct resource *mem;
> +
> + d_can_reset_ram(priv, pdev->id, 0);
> +
> + unregister_d_can_dev(ndev);
> + platform_set_drvdata(pdev, NULL);
> +
> + free_d_can_dev(ndev);
> + iounmap(priv->base);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + release_mem_region(mem->start, resource_size(mem));
> +
> + pm_runtime_put_sync(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int d_can_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> + unsigned long time_out;
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct d_can_priv *priv = netdev_priv(ndev);
> +
> + if (netif_running(ndev)) {
> + netif_stop_queue(ndev);
> + netif_device_detach(ndev);
> + }
> +
> + d_can_set_bit(priv, D_CAN_CTL, D_CAN_CTL_PDR);
> +
> + /* PDA bit will become high only when all the pending
> + * transfers are completed */
> + time_out = jiffies + msecs_to_jiffies(D_CAN_TIMEOUT_MS);
> + while (!d_can_get_bit(priv, D_CAN_ES, D_CAN_ES_PDA) &&
> + time_after(time_out, jiffies))
> + cpu_relax();
> +
> + if (time_after(jiffies, time_out)) {
> + netdev_err(ndev, "Not entered power down mode\n");
> + return -ETIMEDOUT;
> + }
> +
> + if (priv->is_opened)
> + d_can_stop(ndev);
> +
> + priv->can.state = CAN_STATE_SLEEPING;
> + /* De-initialize DCAN RAM */
> + d_can_reset_ram(priv, pdev->id, 0);
> + /* Disable the module */
> + pm_runtime_put_sync(&pdev->dev);
> + return 0;
> +}
> +
> +static int d_can_resume(struct platform_device *pdev)
> +{
> + unsigned long time_out;
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct d_can_priv *priv = netdev_priv(ndev);
> +
> + /* Enable the module */
> + pm_runtime_get_sync(&pdev->dev);
> + /* Initialize DCAN RAM */
> + d_can_reset_ram(priv, pdev->id, 1);
> +
> + /* Brought back from power down mode */
> + d_can_clear_bit(priv, D_CAN_CTL, D_CAN_CTL_PDR);
> + d_can_clear_bit(priv, D_CAN_CTL, D_CAN_CTL_INIT);
> +
> + /* Wait for the PDA bit to get clear */
> + time_out = jiffies + msecs_to_jiffies(D_CAN_TIMEOUT_MS);
> + while (d_can_get_bit(priv, D_CAN_ES, D_CAN_ES_PDA) &&
> + time_after(time_out, jiffies))
> + cpu_relax();
> +
> + if (time_after(jiffies, time_out)) {
> + netdev_err(ndev, "Not came out from power down mode\n");
> + return -ETIMEDOUT;
> + }
> +
> + if (priv->is_opened)
> + d_can_start(ndev);
> +
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + if (netif_running(ndev)) {
> + netif_device_attach(ndev);
> + netif_start_queue(ndev);
> + }
> + return 0;
> +}
> +#else
> +#define d_can_suspend NULL
> +#define d_can_resume NULL
> +#endif
> +
> +static struct platform_driver d_can_plat_driver = {
> + .driver = {
> + .name = D_CAN_DRV_NAME,
> + .owner = THIS_MODULE,
> + },
> + .probe = d_can_plat_probe,
> + .remove = __devexit_p(d_can_plat_remove),
> + .suspend = d_can_suspend,
> + .resume = d_can_resume,
> +};
> +
> +static int __init d_can_plat_init(void)
> +{
> + return platform_driver_register(&d_can_plat_driver);
> +}
> +module_init(d_can_plat_init);
> +
> +static void __exit d_can_plat_exit(void)
> +{
> + platform_driver_unregister(&d_can_plat_driver);
> +}
> +module_exit(d_can_plat_exit);
> +
> +MODULE_AUTHOR("AnilKumar Ch <anilkumar@...com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION(D_CAN_VERSION);
> +MODULE_DESCRIPTION(D_CAN_DRV_DESC);
> diff --git a/include/linux/can/platform/d_can.h b/include/linux/can/platform/d_can.h
> new file mode 100644
> index 0000000..b6f2a3f
> --- /dev/null
> +++ b/include/linux/can/platform/d_can.h
> @@ -0,0 +1,40 @@
> +/*
> + * D_CAN controller driver platform header
> + *
> + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * Bosch D_CAN controller is compliant to CAN protocol version 2.0 part A and B.
> + * Bosch D_CAN user manual can be obtained from:
> + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/can/
> + * d_can_users_manual_111.pdf
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __CAN_PLATFORM_TI_D_CAN_H__
> +#define __CAN_PLATFORM_TI_D_CAN_H__
> +
> +/**
> + * struct d_can_platform_data - DCAN Platform Data
> + *
> + * @num_of_msg_objs: Number of message objects
> + * @dma_support: DMA support is required/not
> + * @ram_init: DCAN RAM initialization
> + *
> + * Platform data structure to get all platform specific settings.
> + * this structure also accounts the fact that the IP may have different
> + * RAM and mailbox offsets for different SOC's
> + */
> +struct d_can_platform_data {
> + unsigned int num_of_msg_objs;
> + bool dma_support;
> + void (*ram_init) (unsigned int instance, unsigned int eanble);
> +};
> +#endif
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists