summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuotao Fu <l.fu@pengutronix.de>2009-06-16 16:48:29 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-07-01 10:04:36 +0200
commitfaf5dffcf138816ca7bea7575a2db42d163cc718 (patch)
tree0e3f2157d474c46e11672ada268781854f50f66c
parentd7c6af8628f639120278ba43e7b604fdf9ad3a09 (diff)
downloadlinux-2.6-faf5dffcf138816ca7bea7575a2db42d163cc718.tar.gz
linux-2.6-faf5dffcf138816ca7bea7575a2db42d163cc718.tar.xz
add driver for mcp251x spi can
This one is ripped of latest berlios socketcan tree (r1010) with some minor modifications to get it run with our stack in 2.6.30-rc7-ptx-mxc1 Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
-rw-r--r--drivers/net/can/Kconfig6
-rw-r--r--drivers/net/can/Makefile1
-rw-r--r--drivers/net/can/mcp251x.c1239
-rw-r--r--include/linux/can/mcp251x.h34
4 files changed, 1280 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index d5e18812bf4..d4b737ad58d 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -74,6 +74,12 @@ config CAN_KVASER_PCI
This driver is for the the PCIcanx and PCIcan cards (1, 2 or
4 channel) from Kvaser (http://www.kvaser.com).
+config CAN_MCP251X
+ tristate "Microchip MCP251x SPI CAN controllers"
+ depends on CAN_DEV && SPI
+ ---help---
+ Driver for the Microchip MCP251x SPI CAN controllers.
+
config CAN_DEBUG_DEVICES
bool "CAN devices debugging messages"
depends on CAN
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 523a941b358..7c418115773 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_CAN_DEV) += can-dev.o
can-dev-y := dev.o
obj-$(CONFIG_CAN_SJA1000) += sja1000/
+obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
new file mode 100644
index 00000000000..8de432f4700
--- /dev/null
+++ b/drivers/net/can/mcp251x.c
@@ -0,0 +1,1239 @@
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ * MCP2510 support and bug fixes by Christian Pellegrin
+ * <chripell@evolware.org>
+ *
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Written under contract by:
+ * Chris Elston, Katalix Systems, Ltd.
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ *
+ * Your platform definition file should specify something like:
+ *
+ * static struct mcp251x_platform_data mcp251x_info = {
+ * .oscillator_frequency = 8000000,
+ * .board_specific_setup = &mcp251x_setup,
+ * .model = CAN_MCP251X_MCP2510,
+ * .power_enable = mcp251x_power_enable,
+ * .transceiver_enable = NULL,
+ * };
+ *
+ * static struct spi_board_info spi_board_info[] = {
+ * {
+ * .modalias = "mcp251x",
+ * .platform_data = &mcp251x_info,
+ * .irq = IRQ_EINT13,
+ * .max_speed_hz = 2*1000*1000,
+ * .chip_select = 2,
+ * },
+ * };
+ *
+ * Please see mcp251x.h for a description of the fields in
+ * struct mcp251x_platform_data.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/spi/spi.h>
+#include <linux/can/dev.h>
+#include <linux/can/core.h>
+#include <linux/if_arp.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/can/mcp251x.h>
+
+/* SPI interface instruction set */
+#define INSTRUCTION_WRITE 0x02
+#define INSTRUCTION_READ 0x03
+#define INSTRUCTION_BIT_MODIFY 0x05
+#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
+#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
+#define INSTRUCTION_RESET 0xC0
+
+/* MPC251x registers */
+#define CANSTAT 0x0e
+#define CANCTRL 0x0f
+# define CANCTRL_REQOP_MASK 0xe0
+# define CANCTRL_REQOP_CONF 0x80
+# define CANCTRL_REQOP_LISTEN_ONLY 0x60
+# define CANCTRL_REQOP_LOOPBACK 0x40
+# define CANCTRL_REQOP_SLEEP 0x20
+# define CANCTRL_REQOP_NORMAL 0x00
+# define CANCTRL_OSM 0x08
+# define CANCTRL_ABAT 0x10
+#define TEC 0x1c
+#define REC 0x1d
+#define CNF1 0x2a
+#define CNF2 0x29
+# define CNF2_BTLMODE 0x80
+#define CNF3 0x28
+# define CNF3_SOF 0x08
+# define CNF3_WAKFIL 0x04
+# define CNF3_PHSEG2_MASK 0x07
+#define CANINTE 0x2b
+# define CANINTE_MERRE 0x80
+# define CANINTE_WAKIE 0x40
+# define CANINTE_ERRIE 0x20
+# define CANINTE_TX2IE 0x10
+# define CANINTE_TX1IE 0x08
+# define CANINTE_TX0IE 0x04
+# define CANINTE_RX1IE 0x02
+# define CANINTE_RX0IE 0x01
+#define CANINTF 0x2c
+# define CANINTF_MERRF 0x80
+# define CANINTF_WAKIF 0x40
+# define CANINTF_ERRIF 0x20
+# define CANINTF_TX2IF 0x10
+# define CANINTF_TX1IF 0x08
+# define CANINTF_TX0IF 0x04
+# define CANINTF_RX1IF 0x02
+# define CANINTF_RX0IF 0x01
+#define EFLG 0x2d
+# define EFLG_EWARN 0x01
+# define EFLG_RXWAR 0x02
+# define EFLG_TXWAR 0x04
+# define EFLG_RXEP 0x08
+# define EFLG_TXEP 0x10
+# define EFLG_TXBO 0x20
+# define EFLG_RX0OVR 0x40
+# define EFLG_RX1OVR 0x80
+#define TXBCTRL(n) ((n * 0x10) + 0x30)
+# define TXBCTRL_ABTF 0x40
+# define TXBCTRL_MLOA 0x20
+# define TXBCTRL_TXERR 0x10
+# define TXBCTRL_TXREQ 0x08
+#define RXBCTRL(n) ((n * 0x10) + 0x60)
+# define RXBCTRL_BUKT 0x04
+# define RXBCTRL_RXM0 0x20
+# define RXBCTRL_RXM1 0x40
+
+/* Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame). */
+#define CAN_FRAME_MAX_DATA_LEN 8
+#define SPI_TRANSFER_BUF_LEN (2*(6 + CAN_FRAME_MAX_DATA_LEN))
+#define CAN_FRAME_MAX_BITS 128
+
+#define DEVICE_NAME "mcp251x"
+
+static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+module_param(mcp251x_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+
+static struct can_bittiming_const mcp251x_bittiming_const = {
+ .tseg1_min = 3,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mcp251x_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+
+ struct mutex spi_lock; /* SPI buffer lock */
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct completion awake;
+ int wake;
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+ int restart_tx;
+};
+
+static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ u8 val = 0;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_READ;
+ priv->spi_tx_buf[1] = reg;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ else
+ val = priv->spi_rx_buf[2];
+
+ mutex_unlock(&priv->spi_lock);
+
+ dev_dbg(&spi->dev, "%s: read %02x = %02x\n", __func__, reg, val);
+ return val;
+}
+
+static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
+ u8 mask, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 4,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = mask;
+ priv->spi_tx_buf[3] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static int mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
+ int tx_buf_idx)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ u32 sid, eid, exide, rtr;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */
+ if (exide)
+ sid = (frame->can_id & CAN_EFF_MASK) >> 18;
+ else
+ sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */
+ eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */
+ rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 1, sid >> 3);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 2,
+ ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3));
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 3,
+ (eid >> 8) & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 4, eid & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 5,
+ (rtr << 6) | frame->can_dlc);
+
+ for (i = 0; i < frame->can_dlc ; i++) {
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 6 + i,
+ frame->data[i]);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 6 + CAN_FRAME_MAX_DATA_LEN,
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_LOAD_TXB(tx_buf_idx);
+ tx_buf[1] = sid >> 3;
+ tx_buf[2] = ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3);
+ tx_buf[3] = (eid >> 8) & 0xff;
+ tx_buf[4] = eid & 0xff;
+ tx_buf[5] = (rtr << 6) | frame->can_dlc;
+
+ memcpy(tx_buf + 6, frame->data, frame->can_dlc);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__,
+ ret);
+ return -1;
+ }
+ }
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+ return 0;
+}
+
+static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ dev_dbg(&spi->dev, "%s: out of memory for Rx'd frame\n",
+ __func__);
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = priv->net;
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+ u8 rx_buf[6];
+
+ rx_buf[1] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 1);
+ rx_buf[2] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 2);
+ rx_buf[3] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 3);
+ rx_buf[4] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 4);
+ rx_buf[5] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 5);
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = mcp251x_read_reg(spi,
+ RXBCTRL(buf_idx) +
+ 6 + i);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 14, /* RX buffer: RXBnCTRL to RXBnD7 */
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+ u8 *rx_buf = priv->spi_rx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_READ_RXB(buf_idx);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n",
+ __func__, ret);
+ priv->net->stats.rx_errors++;
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ memcpy(frame->data, rx_buf + 6, CAN_FRAME_MAX_DATA_LEN);
+
+ mutex_unlock(&priv->spi_lock);
+ }
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+}
+
+static void mcp251x_hw_sleep(struct spi_device *spi)
+{
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP);
+}
+
+static void mcp251x_hw_wakeup(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ priv->wake = 1;
+
+ /* Can only wake up by generating a wake-up interrupt. */
+ mcp251x_write_bits(spi, CANINTE, CANINTE_WAKIE, CANINTE_WAKIE);
+ mcp251x_write_bits(spi, CANINTF, CANINTF_WAKIF, CANINTF_WAKIF);
+
+ /* Wait until the device is awake */
+ if (!wait_for_completion_timeout(&priv->awake, HZ))
+ dev_err(&spi->dev, "MCP251x didn't wake-up\n");
+}
+
+static int mcp251x_hard_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ dev_warn(&spi->dev, "hard_xmit called with not null tx_skb\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->len != sizeof(struct can_frame)) {
+ dev_dbg(&spi->dev, "dropping packet - bad length\n");
+ dev_kfree_skb(skb);
+ net->stats.tx_dropped++;
+ return 0;
+ }
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ net->trans_start = jiffies;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s (unimplemented)\n", __func__);
+
+ switch (mode) {
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void mcp251x_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ unsigned long timeout;
+
+ /* Enable interrupts */
+ mcp251x_write_reg(spi, CANINTE,
+ CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE |
+ CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK);
+ } else {
+ /* Put device into normal mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
+
+ /* Wait for the device to enter normal mode */
+ timeout = jiffies + HZ;
+ while (mcp251x_read_reg(spi, CANSTAT) & 0xE0) {
+ udelay(10);
+ if (time_after(jiffies, timeout)) {
+ dev_err(&spi->dev, "MCP251x didn't"
+ " enter in normal mode\n");
+ break;
+ }
+ }
+ }
+}
+
+static int mcp251x_do_set_bittiming(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+ u8 state;
+
+ dev_dbg(&spi->dev, "%s: BRP = %d, PropSeg = %d, PS1 = %d,"
+ " PS2 = %d, SJW = %d\n", __func__, bt->brp,
+ bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+ bt->sjw);
+
+ /* Store original mode and set mode to config */
+ state = mcp251x_read_reg(spi, CANCTRL);
+ state = mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK;
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK,
+ CANCTRL_REQOP_CONF);
+
+ mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << 6) | (bt->brp - 1));
+ mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE |
+ ((bt->phase_seg1 - 1) << 3) |
+ (bt->prop_seg - 1));
+ mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK,
+ (bt->phase_seg2 - 1));
+
+ /* Restore original state */
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, state);
+
+ return 0;
+}
+
+static void mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
+ struct spi_device *spi)
+{
+ int ret;
+
+ /* Set initial baudrate. Make sure that registers are updated
+ always by explicitly calling mcp251x_do_set_bittiming */
+ ret = can_set_bittiming(net);
+ if (ret)
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ else
+ mcp251x_do_set_bittiming(net);
+
+ /* Enable RX0->RX1 buffer roll over and disable filters */
+ mcp251x_write_bits(spi, RXBCTRL(0),
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1);
+ mcp251x_write_bits(spi, RXBCTRL(1),
+ RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_RXM0 | RXBCTRL_RXM1);
+
+ dev_dbg(&spi->dev, "%s RXBCTL 0 and 1: %02x %02x\n", __func__,
+ mcp251x_read_reg(spi, RXBCTRL(0)),
+ mcp251x_read_reg(spi, RXBCTRL(1)));
+}
+
+static void mcp251x_hw_reset(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_RESET;
+
+ ret = spi_write(spi, priv->spi_tx_buf, 1);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ /* wait for reset to finish */
+ mdelay(10);
+}
+
+static int mcp251x_hw_probe(struct spi_device *spi)
+{
+ int st1, st2;
+
+ mcp251x_hw_reset(spi);
+
+ st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
+ st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+
+ dev_dbg(&spi->dev, "%s: 0x%02x - 0x%02x\n", __func__,
+ st1, st2);
+
+ /* check for power up default values */
+ return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+}
+
+static int mcp251x_open(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ enable_irq(spi->irq);
+ mcp251x_hw_wakeup(spi);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ mcp251x_set_normal_mode(spi);
+ netif_wake_queue(net);
+
+ return 0;
+}
+
+static int mcp251x_stop(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ /* Disable and clear pending interrupts */
+ mcp251x_write_reg(spi, CANINTE, 0x00);
+ mcp251x_write_reg(spi, CANINTF, 0x00);
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+ flush_workqueue(priv->wq);
+
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ return 0;
+}
+
+static int mcp251x_do_get_state(struct net_device *net, enum can_state *state)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u8 eflag;
+
+ eflag = mcp251x_read_reg(spi, EFLG);
+
+ if (eflag & EFLG_TXBO)
+ *state = CAN_STATE_BUS_OFF;
+ else if (eflag & (EFLG_RXEP | EFLG_TXEP))
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (eflag & EFLG_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else
+ *state = CAN_STATE_ACTIVE;
+
+ return 0;
+}
+
+static void mcp251x_tx_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ frame = (struct can_frame *)priv->tx_skb->data;
+ if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
+ frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
+ mcp251x_hw_tx(spi, frame, 0);
+ }
+}
+
+static void mcp251x_irq_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ irq_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ u8 intf;
+ u8 txbnctrl;
+
+ if (priv->after_suspend) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ /* clear since we lost tx buffer */
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ netif_wake_queue(net);
+ }
+ mcp251x_set_normal_mode(spi);
+ } else
+ mcp251x_hw_sleep(spi);
+ priv->after_suspend = 0;
+ return;
+ }
+
+ while (!priv->force_quit && !freezing(current)) {
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ dev_warn(&spi->dev,
+ "timeout in txing a packet, restarting\n");
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (priv->wake) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ priv->wake = 0;
+ }
+
+ intf = mcp251x_read_reg(spi, CANINTF);
+ if (intf == 0x00)
+ break;
+ mcp251x_write_bits(spi, CANINTF, intf, 0x00);
+
+ dev_dbg(&spi->dev, "interrupt:%s%s%s%s%s%s%s%s\n",
+ (intf & CANINTF_MERRF) ? " MERR" : "",
+ (intf & CANINTF_WAKIF) ? " WAK" : "",
+ (intf & CANINTF_ERRIF) ? " ERR" : "",
+ (intf & CANINTF_TX2IF) ? " TX2" : "",
+ (intf & CANINTF_TX1IF) ? " TX1" : "",
+ (intf & CANINTF_TX0IF) ? " TX0" : "",
+ (intf & CANINTF_RX1IF) ? " RX1" : "",
+ (intf & CANINTF_RX0IF) ? " RX0" : "");
+
+ if (intf & CANINTF_WAKIF)
+ complete(&priv->awake);
+
+ if (intf & CANINTF_MERRF) {
+ /* if there are no pending Tx buffers, restart queue */
+ txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
+ if (!(txbnctrl & TXBCTRL_TXREQ)) {
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+ }
+
+ if (intf & CANINTF_ERRIF) {
+ struct sk_buff *skb;
+ struct can_frame *frame = NULL;
+ u8 eflag = mcp251x_read_reg(spi, EFLG);
+
+ dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflag);
+
+ /* Create error frame */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame = (struct can_frame *)
+ skb_put(skb, sizeof(struct can_frame));
+ *(unsigned long long *)&frame->data = 0ULL;
+ frame->can_id = CAN_ERR_FLAG;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = net;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* Set error frame flags based on bus state */
+ if (eflag & EFLG_TXBO) {
+ frame->can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & EFLG_TXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & EFLG_RXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ } else if (eflag & EFLG_TXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ } else if (eflag & EFLG_RXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+ }
+ }
+
+ if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) {
+ if (eflag & EFLG_RX0OVR)
+ net->stats.rx_over_errors++;
+ if (eflag & EFLG_RX1OVR)
+ net->stats.rx_over_errors++;
+ if (frame) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] =
+ CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+ mcp251x_write_reg(spi, EFLG, 0x00);
+
+ if (skb)
+ netif_rx(skb);
+ }
+
+ if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) {
+ if (priv->tx_skb) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes +=
+ ((struct can_frame *)
+ (priv->tx_skb->data))->can_dlc;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (intf & CANINTF_RX0IF)
+ mcp251x_hw_rx(spi, 0);
+
+ if (intf & CANINTF_RX1IF)
+ mcp251x_hw_rx(spi, 1);
+
+ }
+
+ mcp251x_read_reg(spi, CANSTAT);
+
+ dev_dbg(&spi->dev, "interrupt ended\n");
+}
+
+static irqreturn_t mcp251x_can_isr(int irq, void *dev_id)
+{
+ struct net_device *net = (struct net_device *)dev_id;
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ dev_dbg(&priv->spi->dev, "%s: irq\n", __func__);
+ /* Schedule bottom half */
+ if (!work_pending(&priv->irq_work))
+ queue_work(priv->wq, &priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mcp251x_tx_timeout(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ priv->restart_tx = 1;
+ queue_work(priv->wq, &priv->irq_work);
+}
+
+static struct net_device *alloc_mcp251x_netdev(int sizeof_priv)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+
+ net = alloc_candev(sizeof_priv);
+ if (!net)
+ return NULL;
+
+ priv = netdev_priv(net);
+
+ net->open = mcp251x_open;
+ net->stop = mcp251x_stop;
+ net->hard_start_xmit = mcp251x_hard_start_xmit;
+ net->tx_timeout = mcp251x_tx_timeout;
+ net->watchdog_timeo = HZ;
+
+ priv->can.bittiming_const = &mcp251x_bittiming_const;
+ priv->can.do_get_state = mcp251x_do_get_state;
+ priv->can.do_set_mode = mcp251x_do_set_mode;
+
+ priv->net = net;
+
+ return net;
+}
+
+static int __devinit mcp251x_can_probe(struct spi_device *spi)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ int ret = -ENODEV;
+
+ if (!pdata) {
+ /* Platform data is required for osc freq */
+ goto error_out;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_mcp251x_netdev(sizeof(struct mcp251x_priv));
+ if (!net) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ priv = netdev_priv(net);
+ dev_set_drvdata(&spi->dev, priv);
+
+ priv->spi = spi;
+ mutex_init(&priv->spi_lock);
+
+ priv->can.bittiming.clock = pdata->oscillator_frequency / 2;
+
+ /* If requested, allocate DMA buffers */
+ if (mcp251x_enable_dma) {
+ spi->dev.coherent_dma_mask = DMA_32BIT_MASK;
+
+ /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ that much and share it between Tx and Rx DMA buffers. */
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE, &priv->spi_tx_dma, GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ mcp251x_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!mcp251x_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(1);
+
+ /* Call out to platform specific setup */
+ if (pdata->board_specific_setup)
+ pdata->board_specific_setup(spi);
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ priv->wq = create_freezeable_workqueue("mcp251x_wq");
+
+ INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
+ INIT_WORK(&priv->irq_work, mcp251x_irq_work_handler);
+
+ init_completion(&priv->awake);
+
+ /* Configure the SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ /* Register IRQ */
+ if (request_irq(spi->irq, mcp251x_can_isr,
+ IRQF_TRIGGER_FALLING, DEVICE_NAME, net) < 0) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ goto error_irq;
+ }
+ disable_irq(spi->irq);
+
+ if (!mcp251x_hw_probe(spi)) {
+ dev_info(&spi->dev, "Probe failed\n");
+ goto error_probe;
+ }
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ ret = register_netdev(net);
+ if (ret >= 0) {
+ dev_info(&spi->dev, "probed\n");
+ return ret;
+ }
+error_probe:
+ free_irq(spi->irq, net);
+error_irq:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ free_candev(net);
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ }
+error_alloc:
+ dev_err(&spi->dev, "probe failed\n");
+error_out:
+ return ret;
+}
+
+static int __devexit mcp251x_can_remove(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ unregister_netdev(net);
+ free_candev(net);
+
+ free_irq(spi->irq, net);
+ priv->force_quit = 1;
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ } else {
+ kfree(priv->spi_tx_buf);
+ kfree(priv->spi_rx_buf);
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp251x_hw_sleep(spi);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+
+ if (pdata->power_enable) {
+ pdata->power_enable(0);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int mcp251x_can_resume(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER) {
+ pdata->power_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else {
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else
+ priv->after_suspend = 0;
+ }
+ return 0;
+}
+#else
+#define mcp251x_can_suspend NULL
+#define mcp251x_can_resume NULL
+#endif
+
+static struct spi_driver mcp251x_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = mcp251x_can_probe,
+ .remove = __devexit_p(mcp251x_can_remove),
+ .suspend = mcp251x_can_suspend,
+ .resume = mcp251x_can_resume,
+};
+
+static int __init mcp251x_can_init(void)
+{
+ return spi_register_driver(&mcp251x_can_driver);
+}
+
+static void __exit mcp251x_can_exit(void)
+{
+ spi_unregister_driver(&mcp251x_can_driver);
+}
+
+module_init(mcp251x_can_init);
+module_exit(mcp251x_can_exit);
+
+MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
+ "Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("Microchip 251x CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/can/mcp251x.h b/include/linux/can/mcp251x.h
new file mode 100644
index 00000000000..d217ffabf77
--- /dev/null
+++ b/include/linux/can/mcp251x.h
@@ -0,0 +1,34 @@
+#ifndef __CAN_PLATFORM_MCP251X_H__
+#define __CAN_PLATFORM_MCP251X_H__
+
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ */
+
+/**
+ * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data
+ * @oscillator_frequency: - oscillator frequency in Hz
+ * @model: - actual type of chip
+ * @board_specific_setup: - called before probing the chip (power,reset)
+ * @transceiver_enable: - called to power on/off the transceiver
+ * @power_enable: - called to power on/off the mcp *and* the
+ * transceiver
+ *
+ * Please note that you should define power_enable or transceiver_enable or
+ * none of them. Defining both of them is no use.
+ *
+ */
+
+struct mcp251x_platform_data {
+ unsigned long oscillator_frequency;
+ int model;
+#define CAN_MCP251X_MCP2510 0
+#define CAN_MCP251X_MCP2515 1
+ int (*board_specific_setup)(struct spi_device *spi);
+ int (*transceiver_enable)(int enable);
+ int (*power_enable) (int enable);
+};
+
+#endif /* __CAN_PLATFORM_MCP251X_H__ */