如何把MCP251X的驱动程序添加到内核

发布于 2022-09-24 02:28:38 字数 39575 浏览 12 评论 0

Index: linux-2.6-working/drivers/can/mcp251x.c
===================================================================
--- /dev/null        1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6-working/drivers/can/mcp251x.c        2006-01-24 11:18:40.000000000 +0000
@@ -0,0 +1,694 @@
+/*
+ * Microchip MCP251x CAN controller driver.
+ *
+ * Copyright (C) 2006 Arcom Control Systems Ltd.
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/can/can.h>
+#include <linux/can/mcp251x.h>
+
+#include <asm/semaphore.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) (0x90 + 2 * (n))
+#define INSTRUCTION_RESET       0xc0
+
+/* MPC251x registers */
+#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 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_RX1OVR   0x80
+#  define EFLG_RX0OVR   0x40
+#define TXBCTRL(n)  ((n * 0x10) + 0x30)
+#  define TXBCTRL_TXREQ  0x08
+
+/* Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame). */
+#define SPI_TRANSFER_BUF_LEN (2*(6 + CAN_FRAME_MAX_DATA_LEN))
+
+struct mcp251x {
+        struct can_device *can;
+        struct semaphore lock;
+        uint8_t *spi_transfer_buf;
+
+        int bit_rate;
+        int reg;
+
+        struct sk_buff *tx_skb;
+
+        struct work_struct tx_work;
+        struct work_struct irq_work;
+};
+
+static uint8_t mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        uint8_t *tx_buf, *rx_buf;
+        uint8_t val;
+        struct spi_transfer t;
+        struct spi_message m;
+        int ret;
+
+        tx_buf = chip->spi_transfer_buf;
+        rx_buf = chip->spi_transfer_buf + 8;
+
+        down(&chip->lock);
+
+        tx_buf[0] = INSTRUCTION_READ;
+        tx_buf[1] = reg;
+
+        t.tx_buf = tx_buf;
+        t.rx_buf = rx_buf;
+        t.len = 3;
+        t.cs_change = 0;
+
+        spi_message_init(&m);
+        spi_message_add_tail(&t, &m);
+
+        ret = spi_sync(spi, &m);
+        if (ret < 0) {
+                dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret);
+                val = 0;
+        } else
+                val = rx_buf[2];
+
+        up(&chip->lock);
+
+        return val;
+}
+
+static void mcp251x_write_reg(struct spi_device *spi, uint8_t reg, uint8_t val)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        int ret;
+
+        down(&chip->lock);
+
+        chip->spi_transfer_buf[0] = INSTRUCTION_WRITE;
+        chip->spi_transfer_buf[1] = reg;
+        chip->spi_transfer_buf[2] = val;
+
+        ret = spi_write(spi, chip->spi_transfer_buf, 3);
+        if (ret < 0)
+                dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret);
+
+        up(&chip->lock);
+}
+
+static void mcp251x_write_bits(struct spi_device *spi, uint8_t reg, uint8_t mask, uint8_t val)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        int ret;
+
+        down(&chip->lock);
+
+        chip->spi_transfer_buf[0] = INSTRUCTION_BIT_MODIFY;
+        chip->spi_transfer_buf[1] = reg;
+        chip->spi_transfer_buf[2] = mask;
+        chip->spi_transfer_buf[3] = val;
+
+        ret = spi_write(spi, chip->spi_transfer_buf, 4);
+        if (ret < 0)
+                dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret);
+
+        up(&chip->lock);
+}
+
+static void hw_reset(struct spi_device *spi)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        int ret;
+
+        down(&chip->lock);
+
+        chip->spi_transfer_buf[0] = INSTRUCTION_RESET;
+
+        ret = spi_write(spi, chip->spi_transfer_buf, 1);
+        if (ret < 0)
+                dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret);
+
+        up(&chip->lock);
+}
+
+
+#ifdef DEBUG
+
+static ssize_t mcp251x_reg_addr_show(struct device *dev, struct device_attribute *attr, char
*buf)
+{
+        struct spi_device *spi = to_spi_device(dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        return sprintf(buf, "0x%02x\n", chip->reg);
+}
+
+static ssize_t mcp251x_reg_addr_store(struct device *dev, struct device_attribute *attr, const
char *buf, size_t count)
+{
+        struct spi_device *spi = to_spi_device(dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        chip->reg = simple_strtoul(buf, NULL, 0);
+
+        return count;
+}
+
+static DEVICE_ATTR(reg_addr, 0600, mcp251x_reg_addr_show, mcp251x_reg_addr_store);
+
+static ssize_t mcp251x_reg_data_show(struct device *dev, struct device_attribute *attr, char
*buf)
+{
+        struct spi_device *spi = to_spi_device(dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        return sprintf(buf, "0x%02x\n", mcp251x_read_reg(spi, chip->reg));
+}
+
+static ssize_t mcp251x_reg_data_store(struct device *dev, struct device_attribute *attr, const
char *buf, size_t count)
+{
+        struct spi_device *spi = to_spi_device(dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        mcp251x_write_reg(spi, chip->reg, simple_strtoul(buf, NULL, 0));
+
+        return count;
+}
+
+static DEVICE_ATTR(reg_data, 0600, mcp251x_reg_data_show, mcp251x_reg_data_store);
+
+#endif /* DEBUG */
+
+
+static void __devinit mcp251x_hw_init(struct spi_device *spi)
+{
+        hw_reset(spi);
+}
+
+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)
+{
+        /* 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);
+}
+
+
+static int mcp251x_set_bit_rate(struct can_device *can, int bit_rate)
+{
+        struct spi_device *spi = to_spi_device(can->cdev.dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+        int tqs; /* tbit/TQ */
+        int brp;
+        int ps1, ps2, propseg, sjw;
+
+        /* Determine the BRP value that gives the requested bit rate. */
+        for(brp = 0; brp < 8; brp++) {
+                tqs = pdata->f_osc / (2 * (brp + 1)) / bit_rate;
+                if (tqs >= 5 && tqs <= 25
+                    && (pdata->f_osc / (2 * (brp + 1)) / tqs) == bit_rate)
+                        break;
+        }
+        if (brp >= 8)
+                return -EINVAL;
+
+        /* The CAN bus bit time (tbit) is determined by:
+         *   tbit = (SyncSeg + PropSeg + PS1 + PS2) * TQ
+         * with:
+         *     SyncSeg = 1
+         *     sample point (between PS1 and PS2) must be at 60%-70% of the bit time
+         *     PropSeg + PS1 >= PS2
+         *     PropSeg + PS1 >= Tdelay
+         *     PS2 > SJW
+         *     1 <= PropSeg <= 8, 1 <= PS1 <=8, 2 <= PS2 <= 8
+         * SJW = 1 is sufficient in most cases.
+         * Tdelay is usually 1 or 2 TQ.
+         */
+
+        propseg = ps1 = ps2 = (tqs - 1) / 3;
+        if (tqs - (1 + propseg + ps1 + ps2) == 2)
+                ps1++;
+        if (tqs - (1 + propseg + ps1 + ps2) == 1)
+                ps2++;
+        sjw = 1;
+
+        dev_dbg(&spi->dev, "bit rate: BRP = %d, Tbit = %d TQ, PropSeg = %d, PS1 = %d, PS2 = %d, SJW =
%d\n",
+                brp, tqs, propseg, ps1, ps2, sjw);
+
+        /* Since we can only change the bit rate when the network device is
+         * down the chip must be in sleep mode. Wake it up and put it into
+         * config mode. */
+        mcp251x_hw_wakeup(spi);
+        mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, CANCTRL_REQOP_CONF);
+
+        mcp251x_write_reg(spi, CNF1, ((sjw-1) << 6) | brp);
+        mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1));
+        mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK, (ps2-1));
+
+        mcp251x_hw_sleep(spi);
+
+        /* Calculate actual bit rate. */
+        chip->bit_rate = pdata->f_osc / (2 * (brp + 1)) / tqs;
+
+        return 0;
+}
+
+static int mcp251x_get_bit_rate(struct can_device *can)
+{
+        struct spi_device *spi = to_spi_device(can->cdev.dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        return chip->bit_rate;
+}
+
+
+static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame, int tx_buf_idx)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        uint8_t *buf = chip->spi_transfer_buf;
+        int ret;
+
+        dev_dbg(&spi->dev, "%s()\n", __FUNCTION__);
+
+        down(&chip->lock);
+
+        buf[0] = INSTRUCTION_LOAD_TXB(tx_buf_idx);
+        buf[1] = frame->header.id >> 3;
+        buf[2] = (frame->header.id << 5) | (frame->header.ide << 3)
+                | frame->header.eid >> 16;
+        buf[3]  = frame->header.eid >> 8;
+        buf[4]  = frame->header.eid;
+        buf[5] = (frame->header.rtr << 6) | frame->header.dlc;
+        memcpy(buf + 6, frame->data, frame->header.dlc);
+
+        ret = spi_write(spi, buf, 6 + CAN_FRAME_MAX_DATA_LEN);
+        if (ret < 0)
+                dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret);
+
+        up(&chip->lock);
+
+        mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+}
+
+static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        uint8_t *buf = chip->spi_transfer_buf;
+        uint8_t *rx_buf;
+        struct spi_transfer t;
+        struct spi_message m;
+        int ret;
+        struct sk_buff *skb;
+        struct can_frame *frame;
+
+        skb = dev_alloc_skb(sizeof(struct can_frame));
+        if (!skb) {
+                dev_dbg(&spi->dev, "%s: out of memory for Rx'd frame\n", __FUNCTION__);
+                chip->can->stats.rx_dropped++;
+                return;
+        }
+        skb->dev = &chip->can->ndev;
+        frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+        down(&chip->lock);
+
+        buf[0] = INSTRUCTION_READ_RXB(buf_idx);
+
+        t.tx_buf = buf;
+        t.rx_buf = rx_buf = buf + (6 + CAN_FRAME_MAX_DATA_LEN);
+        t.len = 14;
+        t.cs_change = 0;
+
+        spi_message_init(&m);
+        spi_message_add_tail(&t, &m);
+
+        ret = spi_sync(spi, &m);
+        if (ret < 0)
+                dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret);
+
+        frame->header.id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+        frame->header.ide = (rx_buf[2] >> 3) & 0x1;
+        frame->header.eid = (rx_buf[2] << 16) | (rx_buf[3] << 8) | rx_buf[4];
+        frame->header.rtr = (rx_buf[5] >> 6) & 0x1;
+        frame->header.dlc = rx_buf[5] & 0x0f;
+        memcpy(frame->data, rx_buf + 6, CAN_FRAME_MAX_DATA_LEN);
+
+        up(&chip->lock);
+
+        chip->can->stats.rx_packets++;
+        chip->can->stats.rx_bytes += frame->header.dlc;
+
+        netif_rx(skb);
+}
+
+
+static void mcp251x_tx_work_handler(void *data)
+{
+        struct spi_device *spi = data;
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        struct can_frame *frame = (struct can_frame *)chip->tx_skb->data;
+
+        dev_dbg(&spi->dev, "%s()\n", __FUNCTION__);
+
+        /* FIXME: move this somewhere more appropriate? */
+        if (frame->header.dlc > CAN_FRAME_MAX_DATA_LEN)
+                frame->header.dlc = CAN_FRAME_MAX_DATA_LEN;
+
+        /* FIXME: use all 3 Tx buffers */
+        mcp251x_hw_tx(spi, frame, 0);
+
+        dev_kfree_skb(chip->tx_skb);
+}
+
+
+static void mcp251x_irq_work_handler(void *data)
+{
+        struct spi_device *spi = data;
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        uint8_t intf;
+
+        for (;;) {
+                intf = mcp251x_read_reg(spi, CANINTF);
+                if (intf == 0x00)
+                        break;
+
+                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_MERRF) {
+                        uint8_t txbnctrl;
+                        /* if there are no pending Tx buffers, restart queue */
+                        txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
+                        if (!(txbnctrl & TXBCTRL_TXREQ))
+                                netif_wake_queue(&chip->can->ndev);
+                }
+                if (intf & CANINTF_ERRIF) {
+                        uint8_t eflg = mcp251x_read_reg(spi, EFLG);
+
+                        dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflg);
+
+                        if (eflg & (EFLG_RX0OVR | EFLG_RX1OVR)) {
+                                if (eflg & EFLG_RX0OVR)
+                                        chip->can->stats.rx_over_errors++;
+                                if (eflg & EFLG_RX1OVR)
+                                        chip->can->stats.rx_over_errors++;
+                                mcp251x_write_reg(spi, EFLG, 0x00);
+                        }
+                }
+                if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) {
+                        chip->can->stats.tx_packets++;
+                        chip->can->stats.tx_bytes += ((struct can_frame *)(chip->tx_skb->data))->header.dlc;
+                        netif_wake_queue(&chip->can->ndev);
+                }
+                if (intf & CANINTF_RX0IF)
+                        mcp251x_hw_rx(spi, 0);
+                if (intf & CANINTF_RX1IF)
+                        mcp251x_hw_rx(spi, 1);
+
+                mcp251x_write_bits(spi, CANINTF, intf, 0x00);
+        }
+}
+
+
+static irqreturn_t mcp251x_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct spi_device *spi = dev_id;
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        /* Can't do anything in interrupt context so fire of the interrupt
+         * handling workqueue. */
+        schedule_work(&chip->irq_work);
+
+        return IRQ_HANDLED;
+}
+
+static int mcp251x_open(struct net_device *netdev)
+{
+        struct can_device *can = netdev->priv;
+        struct spi_device *spi = to_spi_device(can->cdev.dev);
+        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+        if (pdata->transceiver_enable)
+                pdata->transceiver_enable(1);
+
+        mcp251x_hw_wakeup(spi);
+
+        /* Enable interrupts */
+        mcp251x_write_reg(spi, CANINTE,
+                  CANINTE_ERRIE
+                  | CANINTE_TX2IE | CANINTE_TX1IE | CANINTE_TX0IE
+                  | CANINTE_RX1IE | CANINTE_RX0IE);
+
+        /* put device into normal mode */
+        mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
+
+        return 0;
+}
+
+static int mcp251x_stop(struct net_device *netdev)
+{
+        struct can_device *can = netdev->priv;
+        struct spi_device *spi = to_spi_device(can->cdev.dev);
+        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+        /* disable and clear pending interrupts */
+        mcp251x_write_reg(spi, CANINTE, 0x00);
+        mcp251x_write_reg(spi, CANINTF, 0x00);
+
+        mcp251x_hw_sleep(spi);
+
+        if (pdata->transceiver_enable)
+                pdata->transceiver_enable(0);
+
+        return 0;
+}
+
+static int mcp251x_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+        struct can_device *can = netdev->priv;
+        struct spi_device *spi = to_spi_device(can->cdev.dev);
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        struct can_frame *frame;
+
+        if (skb->len != sizeof(struct can_frame)) {
+                dev_dbg(&spi->dev, "dropping packet - bad length\n");
+                dev_kfree_skb(skb);
+                chip->can->stats.tx_dropped++;
+                return 0;
+        }
+
+        netif_stop_queue(netdev);
+
+        frame = (struct can_frame *)skb->data;
+
+        chip->tx_skb = skb;
+        schedule_work(&chip->tx_work);
+
+        return 0;
+}
+
+static int mcp251x_remove(struct spi_device *spi)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+
+        dev_dbg(&spi->dev, "%s: stop\n",  __FUNCTION__);
+
+#ifdef DEBUG
+        device_remove_file(&spi->dev, &dev_attr_reg_addr);
+        device_remove_file(&spi->dev, &dev_attr_reg_data);
+#endif
+
+        can_device_unregister(chip->can);
+        free_irq(spi->irq, spi);
+        kfree(chip->spi_transfer_buf);
+
+        return 0;
+}
+
+static int __devinit mcp251x_probe(struct spi_device *spi)
+{
+        struct can_device *can;
+        struct mcp251x *chip;
+        int ret = 0;
+
+        dev_dbg(&spi->dev, "%s: start\n",  __FUNCTION__);
+
+        can = can_device_alloc(&spi->dev, sizeof(struct mcp251x));
+        if (!can) {
+                ret = -ENOMEM;
+                goto error_alloc;
+        }
+        chip = can_device_get_devdata(can);
+        dev_set_drvdata(&spi->dev, chip);
+        chip->can = can;
+
+        chip->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+        if (!chip->spi_transfer_buf) {
+                ret = -ENOMEM;
+                goto error_buf;
+        }
+        init_MUTEX(&chip->lock);
+
+        ret = request_irq(spi->irq, mcp251x_irq, SA_SAMPLE_RANDOM, "mcp251x", spi);
+        if (ret < 0) {
+                dev_err(&spi->dev, "request irq %d failed (ret = %d)\n", spi->irq, ret);
+                goto error_irq;
+        }
+
+        can->set_bit_rate = mcp251x_set_bit_rate;
+        can->get_bit_rate = mcp251x_get_bit_rate;
+
+        can->ndev.open            = mcp251x_open;
+        can->ndev.stop            = mcp251x_stop;
+        can->ndev.hard_start_xmit = mcp251x_tx;
+
+        ret = can_device_register(can);
+        if (ret < 0) {
+                dev_err(&spi->dev, "register can device failed (ret = %d)\n", ret);
+                goto error_register;
+        }
+
+        INIT_WORK(&chip->tx_work, mcp251x_tx_work_handler, spi);
+        INIT_WORK(&chip->irq_work, mcp251x_irq_work_handler, spi);
+
+#ifdef DEBUG
+        device_create_file(&spi->dev, &dev_attr_reg_addr);
+        device_create_file(&spi->dev, &dev_attr_reg_data);
+#endif
+
+        mcp251x_hw_init(spi);
+        mcp251x_set_bit_rate(can, 125000); /* A reasonable default */
+        mcp251x_hw_sleep(spi);
+
+        return 0;
+
+  error_register:
+        free_irq(spi->irq, spi);
+  error_irq:
+        kfree(chip->spi_transfer_buf);
+  error_buf:
+        class_device_put(&can->cdev);
+  error_alloc:
+        return ret;
+}
+
+static int mcp251x_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+        if (!netif_running(&chip->can->ndev))
+                return 0;
+
+        netif_device_detach(&chip->can->ndev);
+
+        mcp251x_hw_sleep(spi);
+        if (pdata->transceiver_enable)
+                pdata->transceiver_enable(0);
+
+        return 0;
+}
+
+static int mcp251x_resume(struct spi_device *spi)
+{
+        struct mcp251x *chip = dev_get_drvdata(&spi->dev);
+        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+        if (!netif_running(&chip->can->ndev))
+                return 0;
+
+        if (pdata->transceiver_enable)
+                pdata->transceiver_enable(1);
+        mcp251x_hw_wakeup(spi);
+
+        netif_device_attach(&chip->can->ndev);
+
+        return 0;
+}
+
+
+static struct spi_driver mcp251x_driver = {
+        .driver = {
+                .name        = "mcp251x",
+                .bus        = &spi_bus_type,
+                .owner        = THIS_MODULE,
+        },
+        .probe                = mcp251x_probe,
+        .remove                = __devexit_p(mcp251x_remove),
+#ifdef CONFIG_PM
+        .suspend        = mcp251x_suspend,
+        .resume                = mcp251x_resume,
+#endif
+};
+
+static int __init mcp251x_init(void)
+{
+        return spi_register_driver(&mcp251x_driver);
+}
+module_init(mcp251x_init);
+
+static void __exit mcp251x_exit(void)
+{
+        spi_unregister_driver(&mcp251x_driver);
+}
+module_exit(mcp251x_exit);
+
+
+MODULE_DESCRIPTION("MCP251x CAN controller driver");
+MODULE_LICENSE("GPL");

楼下还有。。。。。

[ 本帖最后由 kingreat 于 2008-2-29 16:10 编辑 ]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

过去的过去 2022-10-01 02:28:38

接楼上。。。

Index: linux-2.6-working/include/linux/can/mcp251x.h
===================================================================
--- /dev/null        1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6-working/include/linux/can/mcp251x.h        2006-01-24 11:18:40.000000000 +0000
@@ -0,0 +1,26 @@
+/*
+ * MCP251x CAN controller driver
+ *
+ * Copyright (C) 2006 Arcom Control Systems Ltd.
+ *
+ * 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.
+ */
+#ifndef __LINUX_CAN_MCP251X_H
+#define __LINUX_CAN_MCP251X_H
+
+/**
+ * struct mpc251x - MCP251x CAN controller platform data
+ *
+ * f_osc: input clock frequency in Hz
+ * transceiver_enable: enable/disable CAN bus transceivers.  May be NULL if
+ *     the transceivers are always enabled.
+ */
+struct mcp251x_platform_data {
+        int f_osc;
+        void (*transceiver_enable)(int enable);
+};
+
+#endif /* !__LINUX_CAN_MCP251X_H */
Index: linux-2.6-working/drivers/can/Kconfig
===================================================================
--- linux-2.6-working.orig/drivers/can/Kconfig        2006-01-24 11:17:13.000000000 +0000
+++ linux-2.6-working/drivers/can/Kconfig        2006-01-24 11:18:35.000000000 +0000
@@ -14,4 +14,14 @@
           Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
           sysfs, and debugfs support in CAN controller drivers.

+config CAN_MCP251X
+        tristate "MCP251x CAN controller"
+        depends on CAN
+        depends on SPI
+        help
+          Support for Microchip MCP2510 and MCP2515 CAN controllers.
+
+          To compile this driver as a module, choose M here: the
+          module will be called mcp251x.
+
endmenu
Index: linux-2.6-working/drivers/can/Makefile
===================================================================
--- linux-2.6-working.orig/drivers/can/Makefile        2006-01-24 11:17:13.000000000 +0000
+++ linux-2.6-working/drivers/can/Makefile        2006-01-24 11:18:35.000000000 +0000
@@ -3,3 +3,4 @@
endif

obj-$(CONFIG_CAN)                += can.o
+obj-$(CONFIG_CAN_MCP251X)        += mcp251x.o

这是MCP251x的驱动,我把上边这段代码存为了MCP251X.patch。请问如何把以上代码打到内核上。

[ 本帖最后由 kingreat 于 2008-2-29 16:10 编辑 ]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文