From 5fac0553d5d432ae0a93079680a48c6e6a1ab8e2 Mon Sep 17 00:00:00 2001 From: Stefan Mavrodiev Date: Wed, 25 Apr 2018 15:37:56 +0300 Subject: [PATCH] Add SUNXI-CAN driver --- config/kernel/linux-sun7i-default.config | 27 +- patch/kernel/sun7i-default/sunxi-can.patch | 914 +++++++++++++++++++++ 2 files changed, 940 insertions(+), 1 deletion(-) create mode 100644 patch/kernel/sun7i-default/sunxi-can.patch diff --git a/config/kernel/linux-sun7i-default.config b/config/kernel/linux-sun7i-default.config index 27ac7943a4..455ee6b698 100644 --- a/config/kernel/linux-sun7i-default.config +++ b/config/kernel/linux-sun7i-default.config @@ -1024,7 +1024,32 @@ CONFIG_HAVE_BPF_JIT=y # CONFIG_NET_PKTGEN=m # CONFIG_HAMRADIO is not set -# CONFIG_CAN is not set +CONFIG_CAN=m +# CONFIG_CAN_RAW is not set +# CONFIG_CAN_BCM is not set +# CONFIG_CAN_GW is not set + +# +# CAN Device Drivers +# +# CONFIG_CAN_VCAN is not set +# CONFIG_CAN_SLCAN is not set +CONFIG_CAN_DEV=m +CONFIG_CAN_CALC_BITTIMING=y +# CONFIG_CAN_MCP251X is not set +CONFIG_CAN_SUNXI=m +# CONFIG_CAN_SJA1000 is not set +# CONFIG_CAN_C_CAN is not set +# CONFIG_CAN_CC770 is not set + +# +# CAN USB interfaces +# +# CONFIG_CAN_EMS_USB is not set +# CONFIG_CAN_ESD_USB2 is not set +# CONFIG_CAN_PEAK_USB is not set +# CONFIG_CAN_SOFTING is not set +# CONFIG_CAN_DEBUG_DEVICES is not set CONFIG_IRDA=m # diff --git a/patch/kernel/sun7i-default/sunxi-can.patch b/patch/kernel/sun7i-default/sunxi-can.patch new file mode 100644 index 0000000000..1d2090ee83 --- /dev/null +++ b/patch/kernel/sun7i-default/sunxi-can.patch @@ -0,0 +1,914 @@ +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 ++* - modifications for sun4i ++* ++* Copyright (c) 2013 Inmotion Co,. LTD ++* All right reserved. ++* ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "sunxi_can.h" ++ ++#define DRV_NAME "sunxi_can" ++ ++MODULE_AUTHOR("Peter Chen "); ++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 ++#include ++ ++#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