On 10/18/2010 04:24 PM, Andre B. Oliveira wrote:
> Driver for Microchip MCP2515 SPI CAN controller.
> Uses the spi_async API directly for improved performance.
> 
> Signed-off-by: Andre B. Oliveira <[email protected]>

Please add my S-o-b for my patches that are included here.

Which hardware are you using? I'm currently on a MX35. The imx-spi
driver is currently based on the bitbang spi driver, which uses a
workqueue. If I understand the spi framework correct the completion
function is scheduled in my case in the context of the workqueue.

So we have to use netif_rx_ni(); to push skbs into the network layer. If
I don't do I get the infamous "NOHZ local_softirq_pending 08 warning".

In which context is a DMA based spi driver running? In general the spi
completion function runs from a context that cannot sleep [1]. So my
question is, are we allowed to call netif_rx_ni() in this context, too?

netif_rx_ni() disables preemption and may call do_softirq() [2],  but
do_softirq() will return if it's running within interrupt context [3].

Does this means it's safe to use netif_rx_ni()?

I've some comments inline, though:

[1] http://lxr.linux.no/linux+v2.6.35.7/drivers/spi/spi.c#L678
[2] http://lxr.linux.no/linux+v2.6.35.7/net/core/dev.c#L2530
[3] http://lxr.linux.no/linux+v2.6.35.7/kernel/softirq.c#L253

> ---
>  drivers/net/can/Kconfig   |   10 +
>  drivers/net/can/Makefile  |    1 +
>  drivers/net/can/mcp2515.c |  803 
> +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 814 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/can/mcp2515.c
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 9d9e453..4908bfc 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -48,6 +48,16 @@ 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_MCP2515
> +     tristate "Microchip MCP2515 SPI CAN controller"
> +     depends on SPI
> +     select CAN_DEV

All other drivers have: "depends on CAN_DEV" not a select....see below:

> +     help
> +     Driver for the Microchip MCP2515 SPI CAN controllers.
> +     This is a new driver that uses the asynchronous SPI API directly,
> +     eliminating the overhead of threads, work queues and synchronous SPI
> +     wrappers, thus reducing SPI transactions latency to a minimum.
> +
>  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 0057537..679b103 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_CAN_SJA1000)   += sja1000/
>  obj-$(CONFIG_CAN_MSCAN)              += mscan/
>  obj-$(CONFIG_CAN_AT91)               += at91_can.o
>  obj-$(CONFIG_CAN_TI_HECC)    += ti_hecc.o
> +obj-$(CONFIG_CAN_MCP2515)    += mcp2515.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/mcp2515.c b/drivers/net/can/mcp2515.c
> new file mode 100644
> index 0000000..746171b
> --- /dev/null
> +++ b/drivers/net/can/mcp2515.c
> @@ -0,0 +1,803 @@
> +/*
> + * mcp2515.c: driver for Microchip MCP2515 SPI CAN controller
> + *
> + * Copyright 2010 Andre B. Oliveira
> + *
> + * 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; either version 2
> + * of the License, or (at your option) any later version.

this should match the MODULE_LICENSE below:

> + *
> + * 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/>.
> + */
> +
> +/*
> + * Example of mcp2515 platform spi_board_info definition:
> + *
> + * static struct mcp251x_platform_data mcp251x_info = {
> + *         .oscillator_frequency = 8000000,
> + *         .model = CAN_MCP251X_MCP2515,

I just kicked model from the mcp251x_platform_data, the patches have
already been merged by David Miller.

> + *         .board_specific_setup = mcp251x_setup,
> + *         .power_enable = mcp251x_power_enable,
> + *         .transceiver_enable = NULL,

One of the next steps should be to call there hooks.

> + * };
> + *
> + * static struct spi_board_info spi_board_info[] = {
> + *   {
> + *           .modalias = "mcp2515",
> + *           .bus_num = 2,
> + *           .chip_select = 0,
> + *           .irq = IRQ_GPIO(28),
> + *           .max_speed_hz = 10000000,
> + *           .platform_data = &mcp251x_info,
> + *   },
> + * };
> + */
> +
> +/*
> + * References: Microchip MCP2515 data sheet, DS21801E, 2007.
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/spi/spi.h>
> +#include <linux/spinlock.h>
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/platform/mcp251x.h>
> +
> +MODULE_DESCRIPTION("Driver for Microchip MCP2515 SPI CAN controller");
> +MODULE_AUTHOR("Andre B. Oliveira <[email protected]>");
> +MODULE_LICENSE("GPL");
                  ^^^^^
"GPL v2"?
> +
> +/* Registers */
> +#define CANCTRL              0x0f
> +#define RXB0CTRL     0x60
> +#define RXB1CTRL     0x70
> +
> +/* RXBnCTRL bits */
> +#define RXBCTRL_RXM1 0x40
> +#define RXBCTRL_RXM0 0x20
> +#define RXBCTRL_BUKT 0x04
> +
> +/* RXBnSIDL bits */
> +#define RXBSIDL_SRR  0x10
> +#define RXBSIDL_IDE  0x08
> +
> +/* RXBnDLC bits */
> +#define RXBDLC_RTR   0x40
> +
> +/* CANINTF bits */
> +#define CANINTF_ERRIF        0x20
> +#define CANINTF_TX0IF        0x04
> +#define CANINTF_RX1IF        0x02
> +#define CANINTF_RX0IF        0x01
> +
> +/* EFLG bits */
> +#define EFLG_RX1OVR  0x80
> +#define EFLG_RX0OVR  0x40
> +
> +/* Network device private data */
> +struct mcp2515_priv {
> +     struct can_priv can;    /* must be first for all CAN network devices */
> +     struct spi_device *spi; /* SPI device */
> +
> +     u8 canintf;             /* last read value of CANINTF register */
> +     u8 eflg;                /* last read value of EFLG register */
> +
> +     struct sk_buff *skb;    /* skb to transmit or currently transmitting */
> +
> +     spinlock_t lock;        /* Lock for the following flags: */
> +     unsigned busy:1;        /* set when pending async spi transaction */
> +     unsigned interrupt:1;   /* set when pending interrupt handling */
> +     unsigned transmit:1;    /* set when pending transmission */
> +
> +     /* Message, transfer and buffers for one async spi transaction */
> +     struct spi_message message;
> +     struct spi_transfer transfer;
> +     u8 rx_buf[14] __attribute__((aligned(8)));
> +     u8 tx_buf[14] __attribute__((aligned(8)));
> +};
> +
> +static struct can_bittiming_const mcp2515_bittiming_const = {
> +     .name = "mcp2515",
> +     .tseg1_min = 2,
> +     .tseg1_max = 16,
> +     .tseg2_min = 2,
> +     .tseg2_max = 8,
> +     .sjw_max = 4,
> +     .brp_min = 1,
> +     .brp_max = 64,
> +     .brp_inc = 1,
> +};
> +
> +/*
> + * SPI asynchronous completion callback functions.
> + */
> +static void mcp2515_read_flags_complete(void *context);
> +static void mcp2515_read_rxb0_complete(void *context);
> +static void mcp2515_read_rxb1_complete(void *context);
> +static void mcp2515_clear_canintf_complete(void *context);
> +static void mcp2515_clear_eflg_complete(void *context);
> +static void mcp2515_load_txb0_complete(void *context);
> +static void mcp2515_rts_txb0_complete(void *context);
> +
> +/*
> + * Write VALUE to register at address ADDR.
> + * Synchronous.
> + */
> +static int mcp2515_write(struct spi_device *spi, unsigned addr, unsigned 
> value)
> +{
> +     const u8 buf[3] __attribute__((aligned(8))) = {
> +             [0] = 2,        /* write instruction */
> +             [1] = addr,     /* address */
> +             [2] = value,    /* data */
> +     };

Can we already use "u8 *buf = (u8 *)priv->transfer.tx_buf;" here?

> +
> +     return spi_write(spi, buf, sizeof(buf));
> +}
> +
> +/*
> + * Reset internal registers to default state and enter configuration mode.
> + * Synchronous.
> + */
> +static int mcp2515_reset(struct spi_device *spi)
> +{
> +     const u8 reset = 0xc0;
> +
> +     return spi_write(spi, &reset, sizeof(reset));
> +}
> +
> +/*
> + * Set the bit timing configuration registers, the interrupt enable register
> + * and the receive buffers control registers.
> + * Synchronous.
> + */
> +static int mcp2515_config(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     struct spi_device *spi = priv->spi;
> +     struct can_bittiming *bt = &priv->can.bittiming;
> +     u8 buf[6] __attribute__((aligned(8)));

Can we already use "u8 *buf = (u8 *)priv->transfer.tx_buf;" here?

> +     int err;
> +
> +     buf[0] = 2;     /* write instruction */
> +     buf[1] = 0x28;  /* address of CNF3 */
> +
> +     /* CNF3 */
> +     buf[2] = bt->phase_seg2 - 1;
> +
> +     /* CNF2 */
> +     buf[3] = (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ? 0xc0 : 0x80) |
> +             (bt->phase_seg1 - 1) << 3 | (bt->prop_seg - 1);
> +
> +     /* CNF1 */
> +     buf[4] = (bt->sjw - 1) << 6 | (bt->brp - 1);
> +
> +     /* CANINTE */
> +     buf[5] = ~0;    /* enable all interrupts */
> +
> +     err = spi_write(spi, buf, sizeof(buf));
> +     if (err)
> +             return err;
> +
> +     err = mcp2515_write(spi, RXB0CTRL,
> +                         RXBCTRL_RXM1 | RXBCTRL_RXM0 | RXBCTRL_BUKT);
> +     if (err)
> +             return err;
> +
> +     err = mcp2515_write(spi, RXB1CTRL, RXBCTRL_RXM1 | RXBCTRL_RXM0);
> +     if (err)
> +             return err;
> +
> +     /* Finally, enter normal operation mode. */
> +     err = mcp2515_write(spi, CANCTRL, 0);
> +     if (err)
> +             return err;
> +
> +     netdev_info(dev, "writing CNF: 0x%02x 0x%02x 0x%02x\n",
> +              buf[4], buf[3], buf[2]);
> +
> +     return 0;
> +}
> +
> +/*
> + * Start an asynchronous SPI transaction.
> + */
> +static void mcp2515_spi_async(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     int err;
> +
> +     err = spi_async(priv->spi, &priv->message);
> +     if (err)
> +             netdev_err(dev, "%s failed with err=%d\n", __func__, err);
> +}
> +
> +/*
> + * Read CANINTF and EFLG registers in one shot.
> + * Asynchronous.
> + */
> +static void mcp2515_read_flags(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = (u8 *)priv->transfer.tx_buf;
> +
> +     buf[0] = 3;     /* read instruction */
> +     buf[1] = 0x2c;  /* address of CANINTF */
> +     buf[2] = 0;     /* CANINTF */
> +     buf[3] = 0;     /* EFLG */
> +     priv->transfer.len = 4;
> +     priv->message.complete = mcp2515_read_flags_complete;
> +
> +     mcp2515_spi_async(dev);
> +}
> +
> +/*
> + * Read receive buffer 0 (instruction 0x90) or 1 (instruction 0x94).
> + * Asynchronous.
> + */
> +static void mcp2515_read_rxb(struct net_device *dev, u8 instruction,
> +                          void (*complete)(void *))
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = (u8 *)priv->transfer.tx_buf;
> +
> +     memset(buf, 0, 14);
> +     buf[0] = instruction;
> +     priv->transfer.len = 14; /* instruction + id(4) + dlc + data(8) */
> +     priv->message.complete = complete;
> +
> +     mcp2515_spi_async(dev);
> +}
> +
> +/*
> + * Read receive buffer 0.
> + * Asynchronous.
> + */
> +static void mcp2515_read_rxb0(struct net_device *dev)
> +{
> +     mcp2515_read_rxb(dev, 0x90, mcp2515_read_rxb0_complete);
> +}
> +
> +/*
> + * Read receive buffer 1.
> + * Asynchronous.
> + */
> +static void mcp2515_read_rxb1(struct net_device *dev)
> +{
> +     mcp2515_read_rxb(dev, 0x94, mcp2515_read_rxb1_complete);
> +}
> +
> +/*
> + * Clear CANINTF bits.
> + * Asynchronous.
> + */
> +static void mcp2515_clear_canintf(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = (u8 *)priv->transfer.tx_buf;
> +
> +     buf[0] = 5;     /* bit modify instruction */
> +     buf[1] = 0x2c;  /* address of CANINTF */
> +     buf[2] = priv->canintf & ~(CANINTF_RX0IF | CANINTF_RX1IF); /* mask */
> +     buf[3] = 0;     /* data */
> +     priv->transfer.len = 4;
> +     priv->message.complete = mcp2515_clear_canintf_complete;
> +
> +     mcp2515_spi_async(dev);
> +}
> +
> +/*
> + * Clear EFLG bits.
> + * Asynchronous.
> + */
> +static void mcp2515_clear_eflg(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = (u8 *)priv->transfer.tx_buf;
> +
> +     buf[0] = 5;             /* bit modify instruction */
> +     buf[1] = 0x2d;          /* address of EFLG */
> +     buf[2] = priv->eflg;    /* mask */
> +     buf[3] = 0;             /* data */
> +     priv->transfer.len = 4;
> +     priv->message.complete = mcp2515_clear_eflg_complete;
> +
> +     mcp2515_spi_async(dev);
> +}
> +
> +/*
> + * Set the transmit buffer, starting at TXB0SIDH, for an skb.
> + */
> +static int mcp2515_set_txbuf(u8 *buf, const struct sk_buff *skb)
> +{
> +     struct can_frame *frame = (struct can_frame *)skb->data;
> +
> +     if (frame->can_id & CAN_EFF_FLAG) {
> +             buf[0] = frame->can_id >> 21;
> +             buf[1] = (frame->can_id >> 13 & 0xe0) | 8 |
> +                     (frame->can_id >> 16 & 3);
> +             buf[2] = frame->can_id >> 8;
> +             buf[3] = frame->can_id;
> +     } else {
> +             buf[0] = frame->can_id >> 3;
> +             buf[1] = frame->can_id << 5;
> +             buf[2] = 0;
> +             buf[3] = 0;
> +     }
> +
> +     if (frame->can_id & CAN_RTR_FLAG)
> +             buf[4] = frame->can_dlc | 0x40;
> +     else
> +             buf[4] = frame->can_dlc;
> +
> +     memcpy(buf + 5, frame->data, frame->can_dlc);
> +
> +     return 5 + frame->can_dlc;
> +}
> +
> +/*
> + * Send the "load transmit buffer 0" SPI message.
> + * Asynchronous.
> + */
> +static void mcp2515_load_txb0(struct sk_buff *skb, struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = (u8 *)priv->transfer.tx_buf;
> +
> +     buf[0] = 0x40;  /* load txb0 instruction */
                 ^^^^

Some defines or enums for the instructions would be nice to have.

> +     priv->transfer.len = mcp2515_set_txbuf(buf + 1, skb) + 1;
> +     priv->message.complete = mcp2515_load_txb0_complete;
> +
> +     mcp2515_spi_async(dev);
> +}
> +
> +/*
> + * Send the "request to send transmit buffer 0" SPI message.
> + * Asynchronous.
> + */
> +static void mcp2515_rts_txb0(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = (u8 *)priv->transfer.tx_buf;
> +
> +     buf[0] = 0x81;  /* request to send txb0 instruction */
> +     priv->transfer.len = 1;
> +     priv->message.complete = mcp2515_rts_txb0_complete;
> +
> +     mcp2515_spi_async(dev);
> +}
> +
> +/*
> + * Called when the "read CANINTF and EFLG registers" SPI message completes.
> + */
> +static void mcp2515_read_flags_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     u8 *buf = priv->transfer.rx_buf;
> +     unsigned canintf;
> +     unsigned long flags;
> +
> +     priv->canintf = canintf = buf[2];
> +     priv->eflg = buf[3];
> +
> +     if (canintf & CANINTF_RX0IF)
> +             mcp2515_read_rxb0(dev);
> +     else if (canintf & CANINTF_RX1IF)
> +             mcp2515_read_rxb1(dev);
> +     else if (canintf)
> +             mcp2515_clear_canintf(dev);
> +     else {
> +             spin_lock_irqsave(&priv->lock, flags);
> +             if (priv->transmit) {
> +                     priv->transmit = 0;
> +                     spin_unlock_irqrestore(&priv->lock, flags);
> +                     mcp2515_load_txb0(priv->skb, dev);
> +             } else if (priv->interrupt) {
> +                     priv->interrupt = 0;
> +                     spin_unlock_irqrestore(&priv->lock, flags);
> +                     mcp2515_read_flags(dev);
> +             } else {
> +                     priv->busy = 0;
> +                     spin_unlock_irqrestore(&priv->lock, flags);
> +             }
> +     }
> +}
> +
> +/*
> + * Called when one of the "read receive buffer i" SPI message completes.
> + */
> +static void mcp2515_read_rxb_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     struct sk_buff *skb;
> +     struct can_frame *frame;
> +     u8 *buf = priv->transfer.rx_buf;
> +
> +     skb = alloc_can_skb(dev, &frame);
> +     if (!skb) {
> +             dev->stats.rx_dropped++;
> +             return;
> +     }
> +
> +     if (buf[2] & RXBSIDL_IDE) {
> +             frame->can_id = buf[1] << 21 | (buf[2] & 0xe0) << 13 |
> +                     (buf[2] & 3) << 16 | buf[3] << 8 | buf[4] |
> +                      CAN_EFF_FLAG;
> +             if (buf[5] & RXBDLC_RTR)
> +                     frame->can_id |= CAN_RTR_FLAG;
> +     } else {
> +             frame->can_id = buf[1] << 3 | buf[2] >> 5;
> +             if (buf[2] & RXBSIDL_SRR)
> +                     frame->can_id |= CAN_RTR_FLAG;
> +     }
> +
> +     frame->can_dlc = get_can_dlc(buf[5] & 0xf);
> +
> +     memcpy(frame->data, buf + 6, frame->can_dlc);
> +
> +     dev->stats.rx_packets++;
> +     dev->stats.rx_bytes += frame->can_dlc;
> +
> +     netif_rx(skb);
> +}
> +
> +/*
> + * Transmit a frame if transmission pending, else read and process flags.
> + */
> +static void mcp2515_transmit_or_read_flags(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&priv->lock, flags);
> +     if (priv->transmit) {
> +             priv->transmit = 0;
> +             spin_unlock_irqrestore(&priv->lock, flags);
> +             mcp2515_load_txb0(priv->skb, dev);
> +     } else {
> +             spin_unlock_irqrestore(&priv->lock, flags);
> +             mcp2515_read_flags(dev);
> +     }
> +}
> +
> +/*
> + * Called when the "read receive buffer 0" SPI message completes.
> + */
> +static void mcp2515_read_rxb0_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +
> +     mcp2515_read_rxb_complete(context);
> +
> +     if (priv->canintf & CANINTF_RX1IF)
> +             mcp2515_read_rxb1(dev);
> +     else
> +             mcp2515_transmit_or_read_flags(dev);
> +}
> +
> +/*
> + * Called when the "read receive buffer 1" SPI message completes.
> + */
> +static void mcp2515_read_rxb1_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +
> +     mcp2515_read_rxb_complete(context);
> +
> +     mcp2515_transmit_or_read_flags(dev);
> +}
> +
> +/*
> + * Called when the "clear CANINTF bits" SPI message completes.
> + */
> +static void mcp2515_clear_canintf_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +
> +     if (priv->canintf & CANINTF_TX0IF) {
> +             struct sk_buff *skb = priv->skb;
> +             if (skb) {
                ^^^^^^^^

Can this happen? A tx-complete IRQ without an skb in priv? Please add a
check for this case and test the driver. I'm going to do this, too.

> +                     struct can_frame *f = (struct can_frame *)skb->data;
> +                     dev->stats.tx_bytes += f->can_dlc;
> +                     dev->stats.tx_packets++;
> +                     can_put_echo_skb(skb, dev, 0);

It's better to call the put function (directly) before the CAN frame
gets pushed into the hardware.

> +                     can_get_echo_skb(dev, 0);
> +             }
> +             priv->skb = NULL;
> +             netif_wake_queue(dev);
> +     }
> +
> +     if (priv->eflg)
> +             mcp2515_clear_eflg(dev);
> +     else
> +             mcp2515_read_flags(dev);
> +}
> +
> +/*
> + * Called when the "clear EFLG bits" SPI message completes.
> + */
> +static void mcp2515_clear_eflg_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +
> +     /*
> +      * The receive flow chart (figure 4-3) of the data sheet (DS21801E)
> +      * says that, if RXB0CTRL.BUKT is set (our case), the overflow
> +      * flag that is set is EFLG.RX1OVR, when in fact it is EFLG.RX0OVR
> +      * that is set.  To be safe, we test for any one of them.
> +      */
> +     if (priv->eflg & (EFLG_RX0OVR | EFLG_RX1OVR))
> +             dev->stats.rx_over_errors++;
> +
> +     mcp2515_read_flags(dev);
> +}
> +
> +/*
> + * Called when the "load transmit buffer 0" SPI message completes.
> + */
> +static void mcp2515_load_txb0_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +
> +     mcp2515_rts_txb0(dev);
> +}
> +
> +/*
> + * Called when the "request to send transmit buffer 0" SPI message completes.
> + */
> +static void mcp2515_rts_txb0_complete(void *context)
> +{
> +     struct net_device *dev = context;
> +
> +     mcp2515_read_flags(dev);
> +}
> +
> +/*
> + * Interrupt handler.
> + */
> +static irqreturn_t mcp2515_interrupt(int irq, void *dev_id)
> +{
> +     struct net_device *dev = dev_id;
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +
> +     spin_lock(&priv->lock);
> +     if (priv->busy) {
> +             priv->interrupt = 1;
> +             spin_unlock(&priv->lock);
> +             return IRQ_HANDLED;
> +     }
> +     priv->busy = 1;
> +     spin_unlock(&priv->lock);
> +
> +     mcp2515_read_flags(dev);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +/*
> + * Transmit a frame.
> + */
> +static netdev_tx_t mcp2515_start_xmit(struct sk_buff *skb,
> +                                   struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     unsigned long flags;
> +
> +     if (can_dropped_invalid_skb(dev, skb))
> +             return NETDEV_TX_OK;
> +
> +     netif_stop_queue(dev);
> +     priv->skb = skb;
> +
> +     spin_lock_irqsave(&priv->lock, flags);
> +     if (priv->busy) {
> +             priv->transmit = 1;
> +             spin_unlock_irqrestore(&priv->lock, flags);
> +             return NETDEV_TX_OK;
> +     }
> +     priv->busy = 1;
> +     spin_unlock_irqrestore(&priv->lock, flags);
> +
> +     mcp2515_load_txb0(skb, dev);
> +
> +     return NETDEV_TX_OK;
> +}
> +
> +/*
> + * Called when the network device transitions to the up state.
> + */
> +static int mcp2515_open(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     struct spi_device *spi = priv->spi;
> +     int err;
> +
> +     err = mcp2515_reset(spi);
> +     if (err)
> +             return err;
> +
> +     err = open_candev(dev);
> +     if (err)
> +             return err;
> +
> +     err = request_irq(spi->irq, mcp2515_interrupt,
> +                       IRQF_TRIGGER_FALLING, dev->name, dev);
> +     if (err)
> +             goto err1;
> +
> +     err = mcp2515_config(dev);
> +     if (err)
> +             goto err2;
> +
> +     netif_wake_queue(dev);
> +
> +     return 0;
> +
> +err2:        mcp2515_reset(spi);

the label should be in a seperate line.

> +     free_irq(spi->irq, dev);
> +err1:        close_candev(dev);
> +     return err;
> +}
> +
> +/*
> + * Called when the network device transitions to the down state.
> + */
> +static int mcp2515_stop(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     struct spi_device *spi = priv->spi;
> +
> +     mcp2515_reset(spi);
> +     close_candev(dev);
> +     free_irq(spi->irq, dev);
> +
> +     return 0;
> +}
> +
> +/*
> + * Set up SPI messages.
> + */
> +static void mcp2515_setup_spi_messages(struct net_device *dev)
> +{
> +     struct mcp2515_priv *priv = netdev_priv(dev);
> +     struct device *device;
> +     void *buf;
> +     dma_addr_t dma;
> +
> +     spi_message_init(&priv->message);
> +     priv->message.context = dev;
> +
> +     /* FIXME */
> +     device = &priv->spi->dev;
> +     device->coherent_dma_mask = 0xffffffff;

What needs to be done here?

> +
> +     buf = dma_alloc_coherent(device, 32, &dma, GFP_KERNEL);
> +     if (buf) {
> +             priv->transfer.tx_buf = buf;
> +             priv->transfer.rx_buf = buf + 16;
> +             priv->transfer.tx_dma = dma;
> +             priv->transfer.rx_dma = dma + 16;
> +             priv->message.is_dma_mapped = 1;
> +     } else {
> +             priv->transfer.tx_buf = priv->tx_buf;
> +             priv->transfer.rx_buf = priv->rx_buf;
> +     }
> +
> +     spi_message_add_tail(&priv->transfer, &priv->message);
> +}
> +
> +static int mcp2515_set_mode(struct net_device *dev, enum can_mode mode)
> +{
> +     return 0;
> +}
> +
> +/*
> + * Network device operations.
> + */
> +static const struct net_device_ops mcp2515_netdev_ops = {
> +     .ndo_open = mcp2515_open,
> +     .ndo_stop = mcp2515_stop,
> +     .ndo_start_xmit = mcp2515_start_xmit,
> +};
> +
> +/*
> + * Binds this driver to the spi device.
> + */
> +static int __devinit mcp2515_probe(struct spi_device *spi)
> +{
> +     struct net_device *dev;
> +     struct mcp2515_priv *priv;
> +     struct mcp251x_platform_data *pdata = spi->dev.platform_data;
> +     int err;
> +
> +     if (!pdata)
> +             /* Platform data is required for osc freq */
> +             return -ENODEV;
> +
> +     err = mcp2515_reset(spi);
> +     if (err)
> +             return err;
> +
> +     dev = alloc_candev(sizeof(struct mcp2515_priv), 1);
> +     if (!dev)
> +             return -ENOMEM;
> +
> +     dev_set_drvdata(&spi->dev, dev);
> +     SET_NETDEV_DEV(dev, &spi->dev);
> +
> +     dev->netdev_ops = &mcp2515_netdev_ops;
> +     dev->flags |= IFF_ECHO;
> +
> +     priv = netdev_priv(dev);
> +     priv->can.bittiming_const = &mcp2515_bittiming_const;
> +     priv->can.do_set_mode = mcp2515_set_mode;
> +     priv->can.clock.freq = pdata->oscillator_frequency / 2;
> +     priv->spi = spi;
> +
> +     spin_lock_init(&priv->lock);
> +
> +     mcp2515_setup_spi_messages(dev);
> +
> +     err = register_candev(dev);
> +     if (err) {
> +             free_candev(dev);
> +             return err;
> +     }
> +
> +     netdev_info(dev, "device registered (cs=%u, irq=%d)\n",
> +              spi->chip_select, spi->irq);
> +
> +     return 0;
> +}
> +
> +/*
> + * Unbinds this driver from the spi device.
> + */
> +static int __devexit mcp2515_remove(struct spi_device *spi)
> +{
> +     struct net_device *dev = dev_get_drvdata(&spi->dev);
> +
> +     unregister_candev(dev);
> +     dev_set_drvdata(&spi->dev, NULL);
> +     free_candev(dev);
> +
> +     return 0;
> +}
> +
> +static struct spi_driver mcp2515_spi_driver = {
> +     .driver = {
> +             .name = "mcp2515",
> +             .owner = THIS_MODULE,
> +     },
> +     .probe = mcp2515_probe,
> +     .remove = __devexit_p(mcp2515_remove),
> +};
> +
> +static int __init mcp2515_init(void)
> +{
> +     return spi_register_driver(&mcp2515_spi_driver);
> +}
> +module_init(mcp2515_init);
> +
> +static void __exit mcp2515_exit(void)
> +{
> +     spi_unregister_driver(&mcp2515_spi_driver);
> +}
> +module_exit(mcp2515_exit);

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   |

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to