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 |
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
