build/patch/kernel/sun7i-default/sunxi-can.patch
2018-04-25 16:36:31 +03:00

914 lines
31 KiB
Diff

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index bb709fd..671be9d 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -110,6 +110,13 @@ config PCH_CAN
is an IOH for x86 embedded processor (Intel Atom E6xx series).
This driver can access CAN bus.
+config CAN_SUNXI
+ tristate "Sun7i, Sun4i CAN bus controller"
+ depends on ARCH_SUN4I || ARCH_SUN7I
+ default n
+ ---help---
+ This is the Sun7i, Sun4i Allwinner CAN BUS driver.
+
source "drivers/net/can/mscan/Kconfig"
source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 938be37..02123c4 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -22,5 +22,6 @@ obj-$(CONFIG_CAN_BFIN) += bfin_can.o
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
obj-$(CONFIG_PCH_CAN) += pch_can.o
+obj-$(CONFIG_CAN_SUNXI) += sunxi_can.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/sunxi_can.c b/drivers/net/can/sunxi_can.c
new file mode 100644
index 0000000..57631fa
--- /dev/null
+++ b/drivers/net/can/sunxi_can.c
@@ -0,0 +1,695 @@
+/*
+* sunxi_can.c - CAN bus controller driver for sun7i and sun4i
+*
+* Copyright (c) 2013 Peter Chen
+* - driver for sun7i
+* Copyright (c) 2017 Oleg Strelkov <o.strelkov@gmail.com>
+* - modifications for sun4i
+*
+* Copyright (c) 2013 Inmotion Co,. LTD
+* All right reserved.
+*
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include <plat/sys_config.h>
+#include <mach/irqs.h>
+
+#include "sunxi_can.h"
+
+#define DRV_NAME "sunxi_can"
+
+MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION(DRV_NAME "CAN netdevice driver");
+
+static struct net_device *sunxican_dev;
+static struct can_bittiming_const sunxi_can_bittiming_const = {
+ .name = DRV_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+static void sunxi_can_write_cmdreg(struct sunxi_can_priv *priv, u8 val)
+{
+ unsigned long flags;
+
+ /*
+ * The command register needs some locking and time to settle
+ * the write_reg() operation - especially on SMP systems.
+ */
+ spin_lock_irqsave(&priv->cmdreg_lock, flags);
+ writel(val, CAN_CMD_ADDR);
+ spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
+}
+
+static int sunxi_can_is_absent(struct sunxi_can_priv *priv)
+{
+ return ((readl(CAN_MSEL_ADDR) & 0xFF) == 0xFF);
+}
+
+static int sunxi_can_probe(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+
+ if (sunxi_can_is_absent(priv)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ DRV_NAME, dev->base_addr);
+ return 0;
+ }
+ return -1;
+}
+
+static void set_reset_mode(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ uint32_t status = readl(CAN_MSEL_ADDR);
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if (status & RESET_MODE) {
+ priv->can.state = CAN_STATE_STOPPED;
+ return;
+ }
+
+ writel(readl(CAN_MSEL_ADDR) | RESET_MODE, CAN_MSEL_ADDR); /* select reset mode */
+ //writel(RESET_MODE, CAN_MSEL_ADDR); /* select reset mode */
+ udelay(10);
+ status = readl(CAN_MSEL_ADDR);
+ }
+
+ netdev_err(dev, "setting SUNXI_CAN into reset mode failed!\n");
+}
+
+static void set_normal_mode(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ unsigned char status = readl(CAN_MSEL_ADDR);
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if ((status & RESET_MODE) == 0) {
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ /* enable interrupts */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
+ writel(0xFFFF, CAN_INTEN_ADDR);
+ } else {
+ writel(0xFFFF & ~BERR_IRQ_EN, CAN_INTEN_ADDR);
+ }
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ writel(readl(CAN_MSEL_ADDR) | LOOPBACK_MODE, CAN_MSEL_ADDR);
+ } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
+ /* Put device into listen-only mode */
+ writel(readl(CAN_MSEL_ADDR) | LISTEN_ONLY_MODE, CAN_MSEL_ADDR);
+ }
+ return;
+ }
+
+ /* set chip to normal mode */
+ writel(readl(CAN_MSEL_ADDR) & (~RESET_MODE), CAN_MSEL_ADDR);
+ udelay(10);
+ status = readl(CAN_MSEL_ADDR);
+ }
+
+ netdev_err(dev, "setting SUNXI_CAN into normal mode failed!\n");
+}
+
+
+static void sunxi_can_start(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+
+ /* leave reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(dev);
+
+ /* Clear error counters and error code capture */
+ writel(0x0, CAN_ERRC_ADDR);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+}
+
+static int sunxi_can_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ sunxi_can_start(dev);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sunxi_can_set_bittiming(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u32 cfg;
+
+ cfg = ((bt->brp - 1) & 0x3FF)
+ | (((bt->sjw - 1) & 0x3) << 14)
+ | (((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16)
+ | (((bt->phase_seg2 - 1) & 0x7) << 20);
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ cfg |= 0x800000;
+
+ netdev_info(dev, "setting BITTIMING=0x%08x\n", cfg);
+
+ set_reset_mode(dev); //CAN_BTIME_ADDR only writable in reset mode
+ writel(cfg, CAN_BTIME_ADDR);
+ set_normal_mode(dev);
+
+ return 0;
+}
+
+static int sunxi_can_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ bec->txerr = readl(CAN_ERRC_ADDR) & 0x000F;
+ bec->rxerr = (readl(CAN_ERRC_ADDR) & 0x0F00) >> 16;
+
+ return 0;
+}
+
+/*
+* initialize sunxi_can:
+* - reset chip
+* - set output mode
+* - set baudrate
+* - enable interrupts
+* - start operating mode
+*/
+static void chipset_init(struct net_device *dev)
+{
+ u32 temp_irqen;
+
+ /* config pins
+ * PH20-TX, PH21-RX :4 */
+
+ if (gpio_request_ex("can_para", "can_tx") == 0 || gpio_request_ex("can_para", "can_rx") == 0 ) {
+ pr_info("can request gpio fail!\n");
+ }
+
+ //enable clock
+ writel(readl(0xF1C20000 + 0x6C) | (1 << 4), 0xF1C20000 + 0x6C);
+
+ //set can controller in reset mode
+ set_reset_mode(dev);
+
+ //enable interrupt
+ temp_irqen = BERR_IRQ_EN | ERR_PASSIVE_IRQ_EN
+ | OR_IRQ_EN | RX_IRQ_EN;
+ writel(readl(CAN_INTEN_ADDR) | temp_irqen, CAN_INTEN_ADDR);
+
+ //return to transfer mode
+ set_normal_mode(dev);
+}
+
+/*
+* transmit a CAN message
+* message layout in the sk_buff should be like this:
+* xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+* [ can_id ] [flags] [len] [can data (up to 8 bytes]
+*/
+static netdev_tx_t sunxi_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ uint8_t dlc;
+ canid_t id;
+ uint32_t temp = 0;
+ uint8_t i;
+
+ //wait buff ready
+ while (!(readl(CAN_STA_ADDR) & TBUF_RDY));
+
+ set_reset_mode(dev);
+
+ writel(0xffffffff, CAN_ACPM_ADDR);
+
+ //enter transfer mode
+ set_normal_mode(dev);
+
+ if (can_dropped_invalid_skb(dev, skb))
+ return NETDEV_TX_OK;
+
+ netif_stop_queue(dev);
+
+ dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ temp = ((id >> 30) << 6) | dlc;
+ writel(temp, CAN_BUF0_ADDR);
+ if (id & CAN_EFF_FLAG) {/* extern frame */
+ writel(0xFF & (id >> 21), CAN_BUF1_ADDR); //id28~21
+ writel(0xFF & (id >> 13), CAN_BUF2_ADDR); //id20~13
+ writel(0xFF & (id >> 5), CAN_BUF3_ADDR); //id12~5
+ writel((id & 0x1F) << 3, CAN_BUF4_ADDR); //id4~0
+
+ for (i = 0; i < dlc; i++) {
+ writel(cf->data[i], CAN_BUF5_ADDR + i * 4);
+ }
+ } else { /* standard frame*/
+ writel(0xFF & (id >> 3), CAN_BUF1_ADDR); //id28~21
+ writel((id & 0x7) << 5, CAN_BUF2_ADDR); //id20~13
+
+ for (i = 0; i < dlc; i++) {
+ writel(cf->data[i], CAN_BUF3_ADDR + i * 4);
+ }
+ }
+
+ can_put_echo_skb(skb, dev, 0);
+
+ while (!(readl(CAN_STA_ADDR) & TBUF_RDY));
+ sunxi_can_write_cmdreg(priv, TRANS_REQ);
+
+ return NETDEV_TX_OK;
+}
+
+static void sunxi_can_rx(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ canid_t id;
+ int i;
+
+ /* create zero'ed CAN frame buffer */
+ skb = alloc_can_skb(dev, &cf);
+ if (skb == NULL)
+ return;
+
+ fi = readl(CAN_BUF0_ADDR);
+ cf->can_dlc = get_can_dlc(fi & 0x0F);
+ if (fi >> 7) {
+ /* extended frame format (EFF) */
+ id = (readl(CAN_BUF1_ADDR) << 21) //id28~21
+ | (readl(CAN_BUF2_ADDR) << 13) //id20~13
+ | (readl(CAN_BUF3_ADDR) << 5) //id12~5
+ | ((readl(CAN_BUF4_ADDR) >> 3) & 0x1f); //id4~0
+ id |= CAN_EFF_FLAG;
+
+ if ((fi >> 6) & 0x1) { /* remote transmission request */
+ id |= CAN_RTR_FLAG;
+ } else {
+ for (i = 0; i < cf->can_dlc; i++)
+ cf->data[i] = readl(CAN_BUF5_ADDR + i * 4);
+ }
+ } else {
+ /* standard frame format (SFF) */
+ id = (readl(CAN_BUF1_ADDR) << 3) //id28~21
+ | ((readl(CAN_BUF2_ADDR) >> 5) & 0x7); //id20~18
+
+ if ((fi >> 6) & 0x1) { /* remote transmission request */
+ id |= CAN_RTR_FLAG;
+ } else {
+ for (i = 0; i < cf->can_dlc; i++)
+ cf->data[i] = readl(CAN_BUF3_ADDR + i * 4);
+ }
+ }
+
+ cf->can_id = id;
+
+ /* release receive buffer */
+ sunxi_can_write_cmdreg(priv, RELEASE_RBUF);
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static int sunxi_can_err(struct net_device *dev, uint8_t isrc, uint8_t status)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ enum can_state state = priv->can.state;
+ uint32_t ecc, alc;
+
+ skb = alloc_can_err_skb(dev, &cf);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ if (isrc & DATA_ORUNI) {
+ /* data overrun interrupt */
+ netdev_dbg(dev, "data overrun interrupt\n");
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ sunxi_can_write_cmdreg(priv, CLEAR_DOVERRUN); /* clear bit */
+ }
+
+ if (isrc & ERR_WRN) {
+ /* error warning interrupt */
+ netdev_dbg(dev, "error warning interrupt\n");
+
+ if (status & BUS_OFF) {
+ state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(dev);
+ } else if (status & ERR_STA) {
+ state = CAN_STATE_ERROR_WARNING;
+ } else
+ state = CAN_STATE_ERROR_ACTIVE;
+ }
+ if (isrc & BUS_ERR) {
+ /* bus error interrupt */
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ ecc = readl(CAN_STA_ADDR);
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ if(ecc & BIT_ERR)
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ else if (ecc & FORM_ERR)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ else if (ecc & STUFF_ERR)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ else {
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = (ecc & ERR_SEG_CODE) >> 16;
+ }
+ /* Error occurred during transmission? */
+ if ((ecc & ERR_DIR) == 0)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+ if (isrc & ERR_PASSIVE) {
+ /* error passive interrupt */
+ netdev_dbg(dev, "error passive interrupt\n");
+ if (status & ERR_STA)
+ state = CAN_STATE_ERROR_PASSIVE;
+ else
+ state = CAN_STATE_ERROR_ACTIVE;
+ }
+ if (isrc & ARB_LOST) {
+ /* arbitration lost interrupt */
+ netdev_dbg(dev, "arbitration lost interrupt\n");
+ alc = readl(CAN_STA_ADDR);
+ priv->can.can_stats.arbitration_lost++;
+ stats->tx_errors++;
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = (alc & 0x1f) >> 8;
+ }
+
+ if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
+ state == CAN_STATE_ERROR_PASSIVE)) {
+ uint8_t rxerr = (readl(CAN_ERRC_ADDR) >> 16) & 0xFF;
+ uint8_t txerr = readl(CAN_ERRC_ADDR) & 0xFF;
+ cf->can_id |= CAN_ERR_CRTL;
+ if (state == CAN_STATE_ERROR_WARNING) {
+ priv->can.can_stats.error_warning++;
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ } else {
+ priv->can.can_stats.error_passive++;
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+ }
+
+ priv->can.state = state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+irqreturn_t sunxi_can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ uint8_t isrc, status;
+ int n = 0;
+
+ while ((isrc = readl(CAN_INT_ADDR)) && (n < SUNXI_CAN_MAX_IRQ)) {
+ n++;
+ status = readl(CAN_STA_ADDR);
+ /* check for absent controller due to hw unplug */
+ if (sunxi_can_is_absent(priv))
+ return IRQ_NONE;
+
+ if (isrc & WAKEUP)
+ netdev_warn(dev, "wakeup interrupt\n");
+
+ if (isrc & TBUF_VLD) {
+ pr_debug("sunxicanirq: Tx irq, reg=0x%X\n", isrc);
+ /* transmission complete interrupt */
+ stats->tx_bytes += readl(CAN_RBUF_RBACK_START_ADDR) & 0xf;
+ stats->tx_packets++;
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
+ }
+ if (isrc & RBUF_VLD) {
+ pr_debug("sunxicanirq: Rx irq, reg=0x%X\n", isrc);
+ /* receive interrupt */
+ while (status & RBUF_RDY) { //RX buffer is not empty
+ sunxi_can_rx(dev);
+ status = readl(CAN_STA_ADDR);
+ /* check for absent controller */
+ if (sunxi_can_is_absent(priv))
+ return IRQ_NONE;
+ }
+ }
+ if (isrc & (DATA_ORUNI | ERR_WRN | BUS_ERR | ERR_PASSIVE | ARB_LOST)) {
+ pr_debug("sunxicanirq: error, reg=0x%X\n", isrc);
+ /* error interrupt */
+ if (sunxi_can_err(dev, isrc, status))
+ break;
+ }
+
+ //clear the interrupt
+ writel(isrc, CAN_INT_ADDR);
+ readl(CAN_INT_ADDR);
+ }
+
+ if (n >= SUNXI_CAN_MAX_IRQ)
+ netdev_dbg(dev, "%d messages handled in ISR", n);
+
+ return (n) ? IRQ_HANDLED : IRQ_NONE;
+}
+EXPORT_SYMBOL_GPL(sunxi_can_interrupt);
+
+static int sunxi_can_open(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ writel(0xffffffff, CAN_ACPM_ADDR);
+
+ /* common open */
+ err = open_candev(dev);
+ if (err)
+ return err;
+
+ /* register interrupt handler, if not done by the device driver */
+ if (!(priv->flags & SUNXI_CAN_CUSTOM_IRQ_HANDLER)) {
+ err = request_irq(dev->irq, sunxi_can_interrupt, priv->irq_flags,
+ dev->name, (void *)dev);
+ if (err) {
+ close_candev(dev);
+ pr_info("request_irq err:%d\n", err);
+ return -EAGAIN;
+ }
+ }
+
+ /* init and start chi */
+ sunxi_can_start(dev);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int sunxi_can_close(struct net_device *dev)
+{
+ struct sunxi_can_priv *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ set_reset_mode(dev);
+
+ if (!(priv->flags & SUNXI_CAN_CUSTOM_IRQ_HANDLER))
+ free_irq(dev->irq, (void *)dev);
+
+ close_candev(dev);
+
+ priv->open_time = 0;
+
+ return 0;
+}
+
+struct net_device *alloc_sunxicandev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct sunxi_can_priv *priv;
+
+ dev = alloc_candev(sizeof(struct sunxi_can_priv) + sizeof_priv,
+ SUNXI_CAN_ECHO_SKB_MAX);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ priv->dev = dev;
+ priv->can.bittiming_const = &sunxi_can_bittiming_const;
+ priv->can.do_set_bittiming = sunxi_can_set_bittiming;
+ priv->can.do_set_mode = sunxi_can_set_mode;
+ priv->can.do_get_berr_counter = sunxi_can_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_BERR_REPORTING;
+
+ spin_lock_init(&priv->cmdreg_lock);
+
+ if (sizeof_priv)
+ priv->priv = (void *)priv + sizeof(struct sunxi_can_priv);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_sunxicandev);
+
+void free_sunxicandev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL_GPL(free_sunxicandev);
+
+static const struct net_device_ops sunxican_netdev_ops = {
+ .ndo_open = sunxi_can_open,
+ .ndo_stop = sunxi_can_close,
+ .ndo_start_xmit = sunxi_can_start_xmit,
+};
+
+int register_sunxicandev(struct net_device *dev)
+{
+ if (!sunxi_can_probe(dev))
+ return -ENODEV;
+
+ dev->flags |= IFF_ECHO; /* support local echo */
+ dev->netdev_ops = &sunxican_netdev_ops;
+
+ set_reset_mode(dev);
+
+ return register_candev(dev);
+}
+EXPORT_SYMBOL_GPL(register_sunxicandev);
+
+void unregister_sunxicandev(struct net_device *dev)
+{
+ set_reset_mode(dev);
+ unregister_candev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_sunxicandev);
+
+static __init int sunxi_can_init(void)
+{
+ struct sunxi_can_priv *priv;
+ int err = 0;
+ int ret = 0;
+ int used = 0;
+
+ sunxican_dev = alloc_sunxicandev(0);
+ if(!sunxican_dev) {
+ pr_info("alloc sunxicandev fail\n");
+ }
+
+ ret = script_parser_fetch("can_para", "can_used", &used, sizeof (used));
+ if ( ret || used == 0) {
+ pr_info("[sunxi-can] Cannot setup CANBus driver, maybe not configured in script.bin?");
+ goto exit_free;
+ }
+
+ priv = netdev_priv(sunxican_dev);
+ sunxican_dev->irq = SW_INT_IRQNO_CAN;
+ priv->irq_flags = 0;
+ priv->can.clock.freq = clk_get_rate(clk_get(NULL, "can"));
+ chipset_init(sunxican_dev);
+ err = register_sunxicandev(sunxican_dev);
+ if(err) {
+ dev_err(&sunxican_dev->dev, "registering %s failed (err=%d)\n", DRV_NAME, err);
+ goto exit_free;
+ }
+
+ dev_info(&sunxican_dev->dev, "%s device registered (reg_base=0x%08x, irq=%d)\n",
+ DRV_NAME, CAN_BASE0, sunxican_dev->irq);
+
+ pr_info("%s CAN netdevice driver\n", DRV_NAME);
+
+ return 0;
+
+exit_free:
+ free_sunxicandev(sunxican_dev);
+
+ return err;
+}
+module_init(sunxi_can_init);
+
+static __exit void sunxi_can_exit(void)
+{
+ unregister_sunxicandev(sunxican_dev);
+ free_sunxicandev(sunxican_dev);
+
+ pr_info("%s: driver removed\n", DRV_NAME);
+}
+module_exit(sunxi_can_exit);
diff --git a/drivers/net/can/sunxi_can.h b/drivers/net/can/sunxi_can.h
new file mode 100644
index 0000000..9e84307
--- /dev/null
+++ b/drivers/net/can/sunxi_can.h
@@ -0,0 +1,178 @@
+/*
+* sun7i_can.h - CAN bus controller driver for sun7i
+*
+* Copyright (c) 2013 Peter Chen
+*
+* Copyright (c) 2013 Inmotion Co., Ltd.
+* All right reserved.
+*
+*/
+
+#ifndef SUNXI_CAN_H
+#define SUNXI_CAN_H
+
+#include <linux/irqreturn.h>
+#include <linux/can/dev.h>
+
+#define SUNXI_CAN_ECHO_SKB_MAX 1 /* the SUN7I, SUN4I CAN has one TX buffer object */
+
+/* Registers' address */
+#define CAN_BASE0 0xF1C2BC00
+#define CAN_MSEL_ADDR (CAN_BASE0 + 0x0000) //Can Mode Select Register
+#define CAN_CMD_ADDR (CAN_BASE0 + 0x0004) //Can Command Register
+#define CAN_STA_ADDR (CAN_BASE0 + 0x0008) //Can Status Register
+#define CAN_INT_ADDR (CAN_BASE0 + 0x000c) //Can Interrupt Flag Register
+#define CAN_INTEN_ADDR (CAN_BASE0 + 0x0010) //Can Interrupt Enable Register
+#define CAN_BTIME_ADDR (CAN_BASE0 + 0x0014) //Can Bus Timing 0 Register
+#define CAN_TEWL_ADDR (CAN_BASE0 + 0x0018) //Can Tx Error Warning Limit Register
+#define CAN_ERRC_ADDR (CAN_BASE0 + 0x001c) //Can Error Counter Register
+#define CAN_RMCNT_ADDR (CAN_BASE0 + 0x0020) //Can Receive Message Counter Register
+#define CAN_RBUFSA_ADDR (CAN_BASE0 + 0x0024) //Can Receive Buffer Start Address Register
+#define CAN_BUF0_ADDR (CAN_BASE0 + 0x0040) //Can Tx/Rx Buffer 0 Register
+#define CAN_BUF1_ADDR (CAN_BASE0 + 0x0044) //Can Tx/Rx Buffer 1 Register
+#define CAN_BUF2_ADDR (CAN_BASE0 + 0x0048) //Can Tx/Rx Buffer 2 Register
+#define CAN_BUF3_ADDR (CAN_BASE0 + 0x004c) //Can Tx/Rx Buffer 3 Register
+#define CAN_BUF4_ADDR (CAN_BASE0 + 0x0050) //Can Tx/Rx Buffer 4 Register
+#define CAN_BUF5_ADDR (CAN_BASE0 + 0x0054) //Can Tx/Rx Buffer 5 Register
+#define CAN_BUF6_ADDR (CAN_BASE0 + 0x0058) //Can Tx/Rx Buffer 6 Register
+#define CAN_BUF7_ADDR (CAN_BASE0 + 0x005c) //Can Tx/Rx Buffer 7 Register
+#define CAN_BUF8_ADDR (CAN_BASE0 + 0x0060) //Can Tx/Rx Buffer 8 Register
+#define CAN_BUF9_ADDR (CAN_BASE0 + 0x0064) //Can Tx/Rx Buffer 9 Register
+#define CAN_BUF10_ADDR (CAN_BASE0 + 0x0068) //Can Tx/Rx Buffer 10 Register
+#define CAN_BUF11_ADDR (CAN_BASE0 + 0x006c) //Can Tx/Rx Buffer 11 Register
+#define CAN_BUF12_ADDR (CAN_BASE0 + 0x0070) //Can Tx/Rx Buffer 12 Register
+#define CAN_ACPC_ADDR (CAN_BASE0 + 0x0040) //Can Acceptance Code 0 Register
+#define CAN_ACPM_ADDR (CAN_BASE0 + 0x0044) //Can Acceptance Mask 0 Register
+#define CAN_RBUF_RBACK_START_ADDR (CAN_BASE0 + 0x0180) //CAN transmit buffer for read back register
+#define CAN_RBUF_RBACK_END_ADDR (CAN_BASE0 + 0x01b0) //CAN transmit buffer for read back register
+
+/* Controller Register Description */
+
+/* mode select register (r/w)
+* offset:0x0000 default:0x0000_0001 */
+#define SLEEP_MODE (1<<4) //This bit can only be written in Reset Mode
+#define WAKE_UP (0<<4) //This bit can only be written in Reset Mode
+#define SINGLE_FILTER (1<<3)
+#define DUAL_FILTERS (0<<3)
+#define LOOPBACK_MODE (1<<2)
+#define LISTEN_ONLY_MODE (1<<1)
+#define RESET_MODE (1<<0)
+/* command register (w)
+* offset:0x0004 default:0x0000_0000 */
+#define BUS_OFF_REQ (1<<5)
+#define SELF_RCV_REQ (1<<4)
+#define CLEAR_DOVERRUN (1<<3)
+#define RELEASE_RBUF (1<<2)
+#define ABORT_REQ (1<<1)
+#define TRANS_REQ (1<<0)
+/* status register (r)
+* offset:0x0008 default:0x0000_003c */
+#define BIT_ERR (0<<22)
+#define FORM_ERR (1<<22)
+#define STUFF_ERR (2<<22)
+#define OTHER_ERR (3<<22)
+#define ERR_DIR (1<<21)
+#define ERR_SEG_CODE (0x1f<<16)
+#define START (0x03<<16)
+#define ID28_21 (0x02<<16)
+#define ID20_18 (0x06<<16)
+#define SRTR (0x04<<16)
+#define IDE (0x05<<16)
+#define ID17_13 (0x07<<16)
+#define ID12_5 (0x0f<<16)
+#define ID4_0 (0x0e<<16)
+#define RTR (0x0c<<16)
+#define RB1 (0x0d<<16)
+#define RB0 (0x09<<16)
+#define DLEN (0x0b<<16)
+#define DATA_FIELD (0x0a<<16)
+#define CRC_SEQUENCE (0x08<<16)
+#define CRC_DELIMITER (0x18<<16)
+#define ACK (0x19<<16)
+#define ACK_DELIMITER (0x1b<<16)
+#define END (0x1a<<16)
+#define INTERMISSION (0x12<<16)
+#define ACTIVE_ERROR (0x11<<16)
+#define PASSIVE_ERROR (0x16<<16)
+#define TOLERATE_DOMINANT_BITS (0x13<<16)
+#define ERROR_DELIMITER (0x17<<16)
+#define OVERLOAD (0x1c<<16)
+#define BUS_OFF (1<<7)
+#define ERR_STA (1<<6)
+#define TRANS_BUSY (1<<5)
+#define RCV_BUSY (1<<4)
+#define TRANS_OVER (1<<3)
+#define TBUF_RDY (1<<2)
+#define DATA_ORUN (1<<1)
+#define RBUF_RDY (1<<0)
+/* interrupt register (r)
+* offset:0x000c default:0x0000_0000 */
+#define BUS_ERR (1<<7) //write 1 to clear
+#define ARB_LOST (1<<6) //write 1 to clear
+#define ERR_PASSIVE (1<<5) //write 1 to clear
+#define WAKEUP (1<<4) //write 1 to clear
+#define DATA_ORUNI (1<<3) //write 1 to clear
+#define ERR_WRN (1<<2) //write 1 to clear
+#define TBUF_VLD (1<<1) //write 1 to clear
+#define RBUF_VLD (1<<0) //write 1 to clear
+/* interrupt enable register (r/w)
+* offset:0x0010 default:0x0000_0000 */
+#define BERR_IRQ_EN (1<<7)
+#define BERR_IRQ_DIS (0<<7)
+#define ARB_LOST_IRQ_EN (1<<6)
+#define ARB_LOST_IRQ_DIS (0<<6)
+#define ERR_PASSIVE_IRQ_EN (1<<5)
+#define ERR_PASSIVE_IRQ_DIS (0<<5)
+#define WAKEUP_IRQ_EN (1<<4)
+#define WAKEUP_IRQ_DIS (0<<4)
+#define OR_IRQ_EN (1<<3)
+#define OR_IRQ_DIS (0<<3)
+#define ERR_WRN_IRQ_EN (1<<2)
+#define ERR_WRN_IRQ_DIS (0<<2)
+#define TX_IRQ_EN (1<<1)
+#define TX_IRQ_DIS (0<<1)
+#define RX_IRQ_EN (1<<0)
+#define RX_IRQ_DIS (0<<0)
+/* timing register (r/w)
+* offset:0x0014 default:0x0000_0000 */
+//...
+
+/* output control */
+#define NOR_OMODE (2)
+#define CLK_OMODE (3)
+/* arbitration lost flag*/
+
+/* error code */
+#define ERR_INRCV (1<<5)
+#define ERR_INTRANS (0<<5)
+
+/* filter mode */
+#define FILTER_CLOSE 0
+#define SINGLE_FLTER_MODE 1
+#define DUAL_FILTER_MODE 2
+
+/*
+* Flags for sun7icanpriv.flags
+*/
+#define SUNXI_CAN_CUSTOM_IRQ_HANDLER 0x1
+
+#define SUNXI_CAN_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
+
+/*
+* sun7i_can private data structure
+*/
+struct sunxi_can_priv {
+ struct can_priv can; /* must be the first member */
+ int open_time;
+ struct sk_buff *echo_skb;
+
+ void *priv; /* for board-specific data */
+ struct net_device *dev;
+
+ unsigned long irq_flags; /* for request_irq() */
+ spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */
+
+ u16 flags; /* custom mode flags */
+};
+
+#endif