diff options
Diffstat (limited to 'drivers/net/can')
59 files changed, 27877 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig new file mode 100644 index 00000000..bb709fd6 --- /dev/null +++ b/drivers/net/can/Kconfig @@ -0,0 +1,134 @@ +menu "CAN Device Drivers" + depends on CAN + +config CAN_VCAN + tristate "Virtual Local CAN Interface (vcan)" + depends on CAN + ---help--- + Similar to the network loopback devices, vcan offers a + virtual local CAN interface. + + This driver can also be built as a module. If so, the module + will be called vcan. + +config CAN_SLCAN + tristate "Serial / USB serial CAN Adaptors (slcan)" + depends on CAN + ---help--- + CAN driver for several 'low cost' CAN interfaces that are attached + via serial lines or via USB-to-serial adapters using the LAWICEL + ASCII protocol. The driver implements the tty linediscipline N_SLCAN. + + As only the sending and receiving of CAN frames is implemented, this + driver should work with the (serial/USB) CAN hardware from: + www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de + + Userspace tools to attach the SLCAN line discipline (slcan_attach, + slcand) can be found in the can-utils at the SocketCAN SVN, see + http://developer.berlios.de/projects/socketcan for details. + + The slcan driver supports up to 10 CAN netdevices by default which + can be changed by the 'maxdev=xx' module option. This driver can + also be built as a module. If so, the module will be called slcan. + +config CAN_DEV + tristate "Platform CAN drivers with Netlink support" + depends on CAN + default y + ---help--- + Enables the common framework for platform CAN drivers with Netlink + support. This is the standard library for CAN drivers. + If unsure, say Y. + +config CAN_CALC_BITTIMING + bool "CAN bit-timing calculation" + depends on CAN_DEV + default y + ---help--- + If enabled, CAN bit-timing parameters will be calculated for the + bit-rate specified via Netlink argument "bitrate" when the device + get started. This works fine for the most common CAN controllers + with standard bit-rates but may fail for exotic bit-rates or CAN + source clock frequencies. Disabling saves some space, but then the + bit-timing parameters must be specified directly using the Netlink + arguments "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". + If unsure, say Y. + +config CAN_AT91 + tristate "Atmel AT91 onchip CAN controller" + depends on CAN_DEV && (ARCH_AT91SAM9263 || ARCH_AT91SAM9X5) + ---help--- + This is a driver for the SoC CAN controller in Atmel's AT91SAM9263 + and AT91SAM9X5 processors. + +config CAN_TI_HECC + depends on CAN_DEV && ARCH_OMAP3 + tristate "TI High End CAN Controller" + ---help--- + Driver for TI HECC (High End CAN Controller) module found on many + TI devices. The device specifications are available from www.ti.com + +config CAN_MCP251X + tristate "Microchip MCP251x SPI CAN controllers" + depends on CAN_DEV && SPI && HAS_DMA + ---help--- + Driver for the Microchip MCP251x SPI CAN controllers. + +config CAN_BFIN + depends on CAN_DEV && (BF534 || BF536 || BF537 || BF538 || BF539 || BF54x) + tristate "Analog Devices Blackfin on-chip CAN" + ---help--- + Driver for the Analog Devices Blackfin on-chip CAN controllers + + To compile this driver as a module, choose M here: the + module will be called bfin_can. + +config CAN_JANZ_ICAN3 + tristate "Janz VMOD-ICAN3 Intelligent CAN controller" + depends on CAN_DEV && MFD_JANZ_CMODIO + ---help--- + Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which + connects to a MODULbus carrier board. + + This driver can also be built as a module. If so, the module will be + called janz-ican3.ko. + +config HAVE_CAN_FLEXCAN + bool + +config CAN_FLEXCAN + tristate "Support for Freescale FLEXCAN based chips" + depends on CAN_DEV && HAVE_CAN_FLEXCAN + ---help--- + Say Y here if you want to support for Freescale FlexCAN. + +config PCH_CAN + tristate "Intel EG20T PCH CAN controller" + depends on CAN_DEV && PCI + ---help--- + This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which + is an IOH for x86 embedded processor (Intel Atom E6xx series). + This driver can access CAN bus. + +source "drivers/net/can/mscan/Kconfig" + +source "drivers/net/can/sja1000/Kconfig" + +source "drivers/net/can/c_can/Kconfig" + +source "drivers/net/can/cc770/Kconfig" + +source "drivers/net/can/usb/Kconfig" + +source "drivers/net/can/softing/Kconfig" + +config CAN_DEBUG_DEVICES + bool "CAN devices debugging messages" + depends on CAN + ---help--- + Say Y here if you want the CAN device drivers to produce a bunch of + debug messages to the system log. Select this if you are having + a problem with CAN support and want to see more of what is going + on. + +endmenu diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile new file mode 100644 index 00000000..938be37b --- /dev/null +++ b/drivers/net/can/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for the Linux Controller Area Network drivers. +# + +obj-$(CONFIG_CAN_VCAN) += vcan.o +obj-$(CONFIG_CAN_SLCAN) += slcan.o + +obj-$(CONFIG_CAN_DEV) += can-dev.o +can-dev-y := dev.o + +obj-y += usb/ +obj-y += softing/ + +obj-$(CONFIG_CAN_SJA1000) += sja1000/ +obj-$(CONFIG_CAN_MSCAN) += mscan/ +obj-$(CONFIG_CAN_C_CAN) += c_can/ +obj-$(CONFIG_CAN_CC770) += cc770/ +obj-$(CONFIG_CAN_AT91) += at91_can.o +obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_MCP251X) += mcp251x.o +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 + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c new file mode 100644 index 00000000..6ea905c2 --- /dev/null +++ b/drivers/net/can/at91_can.c @@ -0,0 +1,1390 @@ +/* + * at91_can.c - CAN network driver for AT91 SoC CAN controller + * + * (C) 2007 by Hans J. Koch <hjk@hansjkoch.de> + * (C) 2008, 2009, 2010, 2011 by Marc Kleine-Budde <kernel@pengutronix.de> + * + * This software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2 as distributed in the 'COPYING' + * file from the main directory of the linux kernel source. + * + * + * Your platform definition file should specify something like: + * + * static struct at91_can_data ek_can_data = { + * transceiver_switch = sam9263ek_transceiver_switch, + * }; + * + * at91_add_device_can(&ek_can_data); + * + */ + +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/rtnetlink.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include <mach/board.h> + +#define AT91_MB_MASK(i) ((1 << (i)) - 1) + +/* Common registers */ +enum at91_reg { + AT91_MR = 0x000, + AT91_IER = 0x004, + AT91_IDR = 0x008, + AT91_IMR = 0x00C, + AT91_SR = 0x010, + AT91_BR = 0x014, + AT91_TIM = 0x018, + AT91_TIMESTP = 0x01C, + AT91_ECR = 0x020, + AT91_TCR = 0x024, + AT91_ACR = 0x028, +}; + +/* Mailbox registers (0 <= i <= 15) */ +#define AT91_MMR(i) (enum at91_reg)(0x200 + ((i) * 0x20)) +#define AT91_MAM(i) (enum at91_reg)(0x204 + ((i) * 0x20)) +#define AT91_MID(i) (enum at91_reg)(0x208 + ((i) * 0x20)) +#define AT91_MFID(i) (enum at91_reg)(0x20C + ((i) * 0x20)) +#define AT91_MSR(i) (enum at91_reg)(0x210 + ((i) * 0x20)) +#define AT91_MDL(i) (enum at91_reg)(0x214 + ((i) * 0x20)) +#define AT91_MDH(i) (enum at91_reg)(0x218 + ((i) * 0x20)) +#define AT91_MCR(i) (enum at91_reg)(0x21C + ((i) * 0x20)) + +/* Register bits */ +#define AT91_MR_CANEN BIT(0) +#define AT91_MR_LPM BIT(1) +#define AT91_MR_ABM BIT(2) +#define AT91_MR_OVL BIT(3) +#define AT91_MR_TEOF BIT(4) +#define AT91_MR_TTM BIT(5) +#define AT91_MR_TIMFRZ BIT(6) +#define AT91_MR_DRPT BIT(7) + +#define AT91_SR_RBSY BIT(29) + +#define AT91_MMR_PRIO_SHIFT (16) + +#define AT91_MID_MIDE BIT(29) + +#define AT91_MSR_MRTR BIT(20) +#define AT91_MSR_MABT BIT(22) +#define AT91_MSR_MRDY BIT(23) +#define AT91_MSR_MMI BIT(24) + +#define AT91_MCR_MRTR BIT(20) +#define AT91_MCR_MTCR BIT(23) + +/* Mailbox Modes */ +enum at91_mb_mode { + AT91_MB_MODE_DISABLED = 0, + AT91_MB_MODE_RX = 1, + AT91_MB_MODE_RX_OVRWR = 2, + AT91_MB_MODE_TX = 3, + AT91_MB_MODE_CONSUMER = 4, + AT91_MB_MODE_PRODUCER = 5, +}; + +/* Interrupt mask bits */ +#define AT91_IRQ_ERRA (1 << 16) +#define AT91_IRQ_WARN (1 << 17) +#define AT91_IRQ_ERRP (1 << 18) +#define AT91_IRQ_BOFF (1 << 19) +#define AT91_IRQ_SLEEP (1 << 20) +#define AT91_IRQ_WAKEUP (1 << 21) +#define AT91_IRQ_TOVF (1 << 22) +#define AT91_IRQ_TSTP (1 << 23) +#define AT91_IRQ_CERR (1 << 24) +#define AT91_IRQ_SERR (1 << 25) +#define AT91_IRQ_AERR (1 << 26) +#define AT91_IRQ_FERR (1 << 27) +#define AT91_IRQ_BERR (1 << 28) + +#define AT91_IRQ_ERR_ALL (0x1fff0000) +#define AT91_IRQ_ERR_FRAME (AT91_IRQ_CERR | AT91_IRQ_SERR | \ + AT91_IRQ_AERR | AT91_IRQ_FERR | AT91_IRQ_BERR) +#define AT91_IRQ_ERR_LINE (AT91_IRQ_ERRA | AT91_IRQ_WARN | \ + AT91_IRQ_ERRP | AT91_IRQ_BOFF) + +#define AT91_IRQ_ALL (0x1fffffff) + +enum at91_devtype { + AT91_DEVTYPE_SAM9263, + AT91_DEVTYPE_SAM9X5, +}; + +struct at91_devtype_data { + unsigned int rx_first; + unsigned int rx_split; + unsigned int rx_last; + unsigned int tx_shift; + enum at91_devtype type; +}; + +struct at91_priv { + struct can_priv can; /* must be the first member! */ + struct net_device *dev; + struct napi_struct napi; + + void __iomem *reg_base; + + u32 reg_sr; + unsigned int tx_next; + unsigned int tx_echo; + unsigned int rx_next; + struct at91_devtype_data devtype_data; + + struct clk *clk; + struct at91_can_data *pdata; + + canid_t mb0_id; +}; + +static const struct at91_devtype_data at91_devtype_data[] __devinitconst = { + [AT91_DEVTYPE_SAM9263] = { + .rx_first = 1, + .rx_split = 8, + .rx_last = 11, + .tx_shift = 2, + }, + [AT91_DEVTYPE_SAM9X5] = { + .rx_first = 0, + .rx_split = 4, + .rx_last = 5, + .tx_shift = 1, + }, +}; + +static struct can_bittiming_const at91_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 2, + .brp_max = 128, + .brp_inc = 1, +}; + +#define AT91_IS(_model) \ +static inline int at91_is_sam##_model(const struct at91_priv *priv) \ +{ \ + return priv->devtype_data.type == AT91_DEVTYPE_SAM##_model; \ +} + +AT91_IS(9263); +AT91_IS(9X5); + +static inline unsigned int get_mb_rx_first(const struct at91_priv *priv) +{ + return priv->devtype_data.rx_first; +} + +static inline unsigned int get_mb_rx_last(const struct at91_priv *priv) +{ + return priv->devtype_data.rx_last; +} + +static inline unsigned int get_mb_rx_split(const struct at91_priv *priv) +{ + return priv->devtype_data.rx_split; +} + +static inline unsigned int get_mb_rx_num(const struct at91_priv *priv) +{ + return get_mb_rx_last(priv) - get_mb_rx_first(priv) + 1; +} + +static inline unsigned int get_mb_rx_low_last(const struct at91_priv *priv) +{ + return get_mb_rx_split(priv) - 1; +} + +static inline unsigned int get_mb_rx_low_mask(const struct at91_priv *priv) +{ + return AT91_MB_MASK(get_mb_rx_split(priv)) & + ~AT91_MB_MASK(get_mb_rx_first(priv)); +} + +static inline unsigned int get_mb_tx_shift(const struct at91_priv *priv) +{ + return priv->devtype_data.tx_shift; +} + +static inline unsigned int get_mb_tx_num(const struct at91_priv *priv) +{ + return 1 << get_mb_tx_shift(priv); +} + +static inline unsigned int get_mb_tx_first(const struct at91_priv *priv) +{ + return get_mb_rx_last(priv) + 1; +} + +static inline unsigned int get_mb_tx_last(const struct at91_priv *priv) +{ + return get_mb_tx_first(priv) + get_mb_tx_num(priv) - 1; +} + +static inline unsigned int get_next_prio_shift(const struct at91_priv *priv) +{ + return get_mb_tx_shift(priv); +} + +static inline unsigned int get_next_prio_mask(const struct at91_priv *priv) +{ + return 0xf << get_mb_tx_shift(priv); +} + +static inline unsigned int get_next_mb_mask(const struct at91_priv *priv) +{ + return AT91_MB_MASK(get_mb_tx_shift(priv)); +} + +static inline unsigned int get_next_mask(const struct at91_priv *priv) +{ + return get_next_mb_mask(priv) | get_next_prio_mask(priv); +} + +static inline unsigned int get_irq_mb_rx(const struct at91_priv *priv) +{ + return AT91_MB_MASK(get_mb_rx_last(priv) + 1) & + ~AT91_MB_MASK(get_mb_rx_first(priv)); +} + +static inline unsigned int get_irq_mb_tx(const struct at91_priv *priv) +{ + return AT91_MB_MASK(get_mb_tx_last(priv) + 1) & + ~AT91_MB_MASK(get_mb_tx_first(priv)); +} + +static inline unsigned int get_tx_next_mb(const struct at91_priv *priv) +{ + return (priv->tx_next & get_next_mb_mask(priv)) + get_mb_tx_first(priv); +} + +static inline unsigned int get_tx_next_prio(const struct at91_priv *priv) +{ + return (priv->tx_next >> get_next_prio_shift(priv)) & 0xf; +} + +static inline unsigned int get_tx_echo_mb(const struct at91_priv *priv) +{ + return (priv->tx_echo & get_next_mb_mask(priv)) + get_mb_tx_first(priv); +} + +static inline u32 at91_read(const struct at91_priv *priv, enum at91_reg reg) +{ + return __raw_readl(priv->reg_base + reg); +} + +static inline void at91_write(const struct at91_priv *priv, enum at91_reg reg, + u32 value) +{ + __raw_writel(value, priv->reg_base + reg); +} + +static inline void set_mb_mode_prio(const struct at91_priv *priv, + unsigned int mb, enum at91_mb_mode mode, int prio) +{ + at91_write(priv, AT91_MMR(mb), (mode << 24) | (prio << 16)); +} + +static inline void set_mb_mode(const struct at91_priv *priv, unsigned int mb, + enum at91_mb_mode mode) +{ + set_mb_mode_prio(priv, mb, mode, 0); +} + +static inline u32 at91_can_id_to_reg_mid(canid_t can_id) +{ + u32 reg_mid; + + if (can_id & CAN_EFF_FLAG) + reg_mid = (can_id & CAN_EFF_MASK) | AT91_MID_MIDE; + else + reg_mid = (can_id & CAN_SFF_MASK) << 18; + + return reg_mid; +} + +/* + * Swtich transceiver on or off + */ +static void at91_transceiver_switch(const struct at91_priv *priv, int on) +{ + if (priv->pdata && priv->pdata->transceiver_switch) + priv->pdata->transceiver_switch(on); +} + +static void at91_setup_mailboxes(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + unsigned int i; + u32 reg_mid; + + /* + * Due to a chip bug (errata 50.2.6.3 & 50.3.5.3) the first + * mailbox is disabled. The next 11 mailboxes are used as a + * reception FIFO. The last mailbox is configured with + * overwrite option. The overwrite flag indicates a FIFO + * overflow. + */ + reg_mid = at91_can_id_to_reg_mid(priv->mb0_id); + for (i = 0; i < get_mb_rx_first(priv); i++) { + set_mb_mode(priv, i, AT91_MB_MODE_DISABLED); + at91_write(priv, AT91_MID(i), reg_mid); + at91_write(priv, AT91_MCR(i), 0x0); /* clear dlc */ + } + + for (i = get_mb_rx_first(priv); i < get_mb_rx_last(priv); i++) + set_mb_mode(priv, i, AT91_MB_MODE_RX); + set_mb_mode(priv, get_mb_rx_last(priv), AT91_MB_MODE_RX_OVRWR); + + /* reset acceptance mask and id register */ + for (i = get_mb_rx_first(priv); i <= get_mb_rx_last(priv); i++) { + at91_write(priv, AT91_MAM(i), 0x0); + at91_write(priv, AT91_MID(i), AT91_MID_MIDE); + } + + /* The last 4 mailboxes are used for transmitting. */ + for (i = get_mb_tx_first(priv); i <= get_mb_tx_last(priv); i++) + set_mb_mode_prio(priv, i, AT91_MB_MODE_TX, 0); + + /* Reset tx and rx helper pointers */ + priv->tx_next = priv->tx_echo = 0; + priv->rx_next = get_mb_rx_first(priv); +} + +static int at91_set_bittiming(struct net_device *dev) +{ + const struct at91_priv *priv = netdev_priv(dev); + const struct can_bittiming *bt = &priv->can.bittiming; + u32 reg_br; + + reg_br = ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 << 24 : 0) | + ((bt->brp - 1) << 16) | ((bt->sjw - 1) << 12) | + ((bt->prop_seg - 1) << 8) | ((bt->phase_seg1 - 1) << 4) | + ((bt->phase_seg2 - 1) << 0); + + netdev_info(dev, "writing AT91_BR: 0x%08x\n", reg_br); + + at91_write(priv, AT91_BR, reg_br); + + return 0; +} + +static int at91_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + const struct at91_priv *priv = netdev_priv(dev); + u32 reg_ecr = at91_read(priv, AT91_ECR); + + bec->rxerr = reg_ecr & 0xff; + bec->txerr = reg_ecr >> 16; + + return 0; +} + +static void at91_chip_start(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_mr, reg_ier; + + /* disable interrupts */ + at91_write(priv, AT91_IDR, AT91_IRQ_ALL); + + /* disable chip */ + reg_mr = at91_read(priv, AT91_MR); + at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN); + + at91_set_bittiming(dev); + at91_setup_mailboxes(dev); + at91_transceiver_switch(priv, 1); + + /* enable chip */ + at91_write(priv, AT91_MR, AT91_MR_CANEN); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Enable interrupts */ + reg_ier = get_irq_mb_rx(priv) | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME; + at91_write(priv, AT91_IDR, AT91_IRQ_ALL); + at91_write(priv, AT91_IER, reg_ier); +} + +static void at91_chip_stop(struct net_device *dev, enum can_state state) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_mr; + + /* disable interrupts */ + at91_write(priv, AT91_IDR, AT91_IRQ_ALL); + + reg_mr = at91_read(priv, AT91_MR); + at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN); + + at91_transceiver_switch(priv, 0); + priv->can.state = state; +} + +/* + * theory of operation: + * + * According to the datasheet priority 0 is the highest priority, 15 + * is the lowest. If two mailboxes have the same priority level the + * message of the mailbox with the lowest number is sent first. + * + * We use the first TX mailbox (AT91_MB_TX_FIRST) with prio 0, then + * the next mailbox with prio 0, and so on, until all mailboxes are + * used. Then we start from the beginning with mailbox + * AT91_MB_TX_FIRST, but with prio 1, mailbox AT91_MB_TX_FIRST + 1 + * prio 1. When we reach the last mailbox with prio 15, we have to + * stop sending, waiting for all messages to be delivered, then start + * again with mailbox AT91_MB_TX_FIRST prio 0. + * + * We use the priv->tx_next as counter for the next transmission + * mailbox, but without the offset AT91_MB_TX_FIRST. The lower bits + * encode the mailbox number, the upper 4 bits the mailbox priority: + * + * priv->tx_next = (prio << get_next_prio_shift(priv)) | + * (mb - get_mb_tx_first(priv)); + * + */ +static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + unsigned int mb, prio; + u32 reg_mid, reg_mcr; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + mb = get_tx_next_mb(priv); + prio = get_tx_next_prio(priv); + + if (unlikely(!(at91_read(priv, AT91_MSR(mb)) & AT91_MSR_MRDY))) { + netif_stop_queue(dev); + + netdev_err(dev, "BUG! TX buffer full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + reg_mid = at91_can_id_to_reg_mid(cf->can_id); + reg_mcr = ((cf->can_id & CAN_RTR_FLAG) ? AT91_MCR_MRTR : 0) | + (cf->can_dlc << 16) | AT91_MCR_MTCR; + + /* disable MB while writing ID (see datasheet) */ + set_mb_mode(priv, mb, AT91_MB_MODE_DISABLED); + at91_write(priv, AT91_MID(mb), reg_mid); + set_mb_mode_prio(priv, mb, AT91_MB_MODE_TX, prio); + + at91_write(priv, AT91_MDL(mb), *(u32 *)(cf->data + 0)); + at91_write(priv, AT91_MDH(mb), *(u32 *)(cf->data + 4)); + + /* This triggers transmission */ + at91_write(priv, AT91_MCR(mb), reg_mcr); + + stats->tx_bytes += cf->can_dlc; + + /* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */ + can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv)); + + /* + * we have to stop the queue and deliver all messages in case + * of a prio+mb counter wrap around. This is the case if + * tx_next buffer prio and mailbox equals 0. + * + * also stop the queue if next buffer is still in use + * (== not ready) + */ + priv->tx_next++; + if (!(at91_read(priv, AT91_MSR(get_tx_next_mb(priv))) & + AT91_MSR_MRDY) || + (priv->tx_next & get_next_mask(priv)) == 0) + netif_stop_queue(dev); + + /* Enable interrupt for this mailbox */ + at91_write(priv, AT91_IER, 1 << mb); + + return NETDEV_TX_OK; +} + +/** + * at91_activate_rx_low - activate lower rx mailboxes + * @priv: a91 context + * + * Reenables the lower mailboxes for reception of new CAN messages + */ +static inline void at91_activate_rx_low(const struct at91_priv *priv) +{ + u32 mask = get_mb_rx_low_mask(priv); + at91_write(priv, AT91_TCR, mask); +} + +/** + * at91_activate_rx_mb - reactive single rx mailbox + * @priv: a91 context + * @mb: mailbox to reactivate + * + * Reenables given mailbox for reception of new CAN messages + */ +static inline void at91_activate_rx_mb(const struct at91_priv *priv, + unsigned int mb) +{ + u32 mask = 1 << mb; + at91_write(priv, AT91_TCR, mask); +} + +/** + * at91_rx_overflow_err - send error frame due to rx overflow + * @dev: net device + */ +static void at91_rx_overflow_err(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *cf; + + netdev_dbg(dev, "RX buffer overflow\n"); + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/** + * at91_read_mb - read CAN msg from mailbox (lowlevel impl) + * @dev: net device + * @mb: mailbox number to read from + * @cf: can frame where to store message + * + * Reads a CAN message from the given mailbox and stores data into + * given can frame. "mb" and "cf" must be valid. + */ +static void at91_read_mb(struct net_device *dev, unsigned int mb, + struct can_frame *cf) +{ + const struct at91_priv *priv = netdev_priv(dev); + u32 reg_msr, reg_mid; + + reg_mid = at91_read(priv, AT91_MID(mb)); + if (reg_mid & AT91_MID_MIDE) + cf->can_id = ((reg_mid >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK; + + reg_msr = at91_read(priv, AT91_MSR(mb)); + cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf); + + if (reg_msr & AT91_MSR_MRTR) + cf->can_id |= CAN_RTR_FLAG; + else { + *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb)); + *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb)); + } + + /* allow RX of extended frames */ + at91_write(priv, AT91_MID(mb), AT91_MID_MIDE); + + if (unlikely(mb == get_mb_rx_last(priv) && reg_msr & AT91_MSR_MMI)) + at91_rx_overflow_err(dev); +} + +/** + * at91_read_msg - read CAN message from mailbox + * @dev: net device + * @mb: mail box to read from + * + * Reads a CAN message from given mailbox, and put into linux network + * RX queue, does all housekeeping chores (stats, ...) + */ +static void at91_read_msg(struct net_device *dev, unsigned int mb) +{ + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + skb = alloc_can_skb(dev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return; + } + + at91_read_mb(dev, mb, cf); + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/** + * at91_poll_rx - read multiple CAN messages from mailboxes + * @dev: net device + * @quota: max number of pkgs we're allowed to receive + * + * Theory of Operation: + * + * About 3/4 of the mailboxes (get_mb_rx_first()...get_mb_rx_last()) + * on the chip are reserved for RX. We split them into 2 groups. The + * lower group ranges from get_mb_rx_first() to get_mb_rx_low_last(). + * + * Like it or not, but the chip always saves a received CAN message + * into the first free mailbox it finds (starting with the + * lowest). This makes it very difficult to read the messages in the + * right order from the chip. This is how we work around that problem: + * + * The first message goes into mb nr. 1 and issues an interrupt. All + * rx ints are disabled in the interrupt handler and a napi poll is + * scheduled. We read the mailbox, but do _not_ reenable the mb (to + * receive another message). + * + * lower mbxs upper + * ____^______ __^__ + * / \ / \ + * +-+-+-+-+-+-+-+-++-+-+-+-+ + * | |x|x|x|x|x|x|x|| | | | | + * +-+-+-+-+-+-+-+-++-+-+-+-+ + * 0 0 0 0 0 0 0 0 0 0 1 1 \ mail + * 0 1 2 3 4 5 6 7 8 9 0 1 / box + * ^ + * | + * \ + * unused, due to chip bug + * + * The variable priv->rx_next points to the next mailbox to read a + * message from. As long we're in the lower mailboxes we just read the + * mailbox but not reenable it. + * + * With completion of the last of the lower mailboxes, we reenable the + * whole first group, but continue to look for filled mailboxes in the + * upper mailboxes. Imagine the second group like overflow mailboxes, + * which takes CAN messages if the lower goup is full. While in the + * upper group we reenable the mailbox right after reading it. Giving + * the chip more room to store messages. + * + * After finishing we look again in the lower group if we've still + * quota. + * + */ +static int at91_poll_rx(struct net_device *dev, int quota) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_sr = at91_read(priv, AT91_SR); + const unsigned long *addr = (unsigned long *)®_sr; + unsigned int mb; + int received = 0; + + if (priv->rx_next > get_mb_rx_low_last(priv) && + reg_sr & get_mb_rx_low_mask(priv)) + netdev_info(dev, + "order of incoming frames cannot be guaranteed\n"); + + again: + for (mb = find_next_bit(addr, get_mb_tx_first(priv), priv->rx_next); + mb < get_mb_tx_first(priv) && quota > 0; + reg_sr = at91_read(priv, AT91_SR), + mb = find_next_bit(addr, get_mb_tx_first(priv), ++priv->rx_next)) { + at91_read_msg(dev, mb); + + /* reactivate mailboxes */ + if (mb == get_mb_rx_low_last(priv)) + /* all lower mailboxed, if just finished it */ + at91_activate_rx_low(priv); + else if (mb > get_mb_rx_low_last(priv)) + /* only the mailbox we read */ + at91_activate_rx_mb(priv, mb); + + received++; + quota--; + } + + /* upper group completed, look again in lower */ + if (priv->rx_next > get_mb_rx_low_last(priv) && + quota > 0 && mb > get_mb_rx_last(priv)) { + priv->rx_next = get_mb_rx_first(priv); + goto again; + } + + return received; +} + +static void at91_poll_err_frame(struct net_device *dev, + struct can_frame *cf, u32 reg_sr) +{ + struct at91_priv *priv = netdev_priv(dev); + + /* CRC error */ + if (reg_sr & AT91_IRQ_CERR) { + netdev_dbg(dev, "CERR irq\n"); + dev->stats.rx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + } + + /* Stuffing Error */ + if (reg_sr & AT91_IRQ_SERR) { + netdev_dbg(dev, "SERR irq\n"); + dev->stats.rx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + + /* Acknowledgement Error */ + if (reg_sr & AT91_IRQ_AERR) { + netdev_dbg(dev, "AERR irq\n"); + dev->stats.tx_errors++; + cf->can_id |= CAN_ERR_ACK; + } + + /* Form error */ + if (reg_sr & AT91_IRQ_FERR) { + netdev_dbg(dev, "FERR irq\n"); + dev->stats.rx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_FORM; + } + + /* Bit Error */ + if (reg_sr & AT91_IRQ_BERR) { + netdev_dbg(dev, "BERR irq\n"); + dev->stats.tx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_BIT; + } +} + +static int at91_poll_err(struct net_device *dev, int quota, u32 reg_sr) +{ + struct sk_buff *skb; + struct can_frame *cf; + + if (quota == 0) + return 0; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + at91_poll_err_frame(dev, cf, reg_sr); + netif_receive_skb(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + + return 1; +} + +static int at91_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + const struct at91_priv *priv = netdev_priv(dev); + u32 reg_sr = at91_read(priv, AT91_SR); + int work_done = 0; + + if (reg_sr & get_irq_mb_rx(priv)) + work_done += at91_poll_rx(dev, quota - work_done); + + /* + * The error bits are clear on read, + * so use saved value from irq handler. + */ + reg_sr |= priv->reg_sr; + if (reg_sr & AT91_IRQ_ERR_FRAME) + work_done += at91_poll_err(dev, quota - work_done, reg_sr); + + if (work_done < quota) { + /* enable IRQs for frame errors and all mailboxes >= rx_next */ + u32 reg_ier = AT91_IRQ_ERR_FRAME; + reg_ier |= get_irq_mb_rx(priv) & ~AT91_MB_MASK(priv->rx_next); + + napi_complete(napi); + at91_write(priv, AT91_IER, reg_ier); + } + + return work_done; +} + +/* + * theory of operation: + * + * priv->tx_echo holds the number of the oldest can_frame put for + * transmission into the hardware, but not yet ACKed by the CAN tx + * complete IRQ. + * + * We iterate from priv->tx_echo to priv->tx_next and check if the + * packet has been transmitted, echo it back to the CAN framework. If + * we discover a not yet transmitted package, stop looking for more. + * + */ +static void at91_irq_tx(struct net_device *dev, u32 reg_sr) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_msr; + unsigned int mb; + + /* masking of reg_sr not needed, already done by at91_irq */ + + for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) { + mb = get_tx_echo_mb(priv); + + /* no event in mailbox? */ + if (!(reg_sr & (1 << mb))) + break; + + /* Disable irq for this TX mailbox */ + at91_write(priv, AT91_IDR, 1 << mb); + + /* + * only echo if mailbox signals us a transfer + * complete (MSR_MRDY). Otherwise it's a tansfer + * abort. "can_bus_off()" takes care about the skbs + * parked in the echo queue. + */ + reg_msr = at91_read(priv, AT91_MSR(mb)); + if (likely(reg_msr & AT91_MSR_MRDY && + ~reg_msr & AT91_MSR_MABT)) { + /* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */ + can_get_echo_skb(dev, mb - get_mb_tx_first(priv)); + dev->stats.tx_packets++; + } + } + + /* + * restart queue if we don't have a wrap around but restart if + * we get a TX int for the last can frame directly before a + * wrap around. + */ + if ((priv->tx_next & get_next_mask(priv)) != 0 || + (priv->tx_echo & get_next_mask(priv)) == 0) + netif_wake_queue(dev); +} + +static void at91_irq_err_state(struct net_device *dev, + struct can_frame *cf, enum can_state new_state) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_idr = 0, reg_ier = 0; + struct can_berr_counter bec; + + at91_get_berr_counter(dev, &bec); + + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + /* + * from: ERROR_ACTIVE + * to : ERROR_WARNING, ERROR_PASSIVE, BUS_OFF + * => : there was a warning int + */ + if (new_state >= CAN_STATE_ERROR_WARNING && + new_state <= CAN_STATE_BUS_OFF) { + netdev_dbg(dev, "Error Warning IRQ\n"); + priv->can.can_stats.error_warning++; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + /* + * from: ERROR_ACTIVE, ERROR_WARNING + * to : ERROR_PASSIVE, BUS_OFF + * => : error passive int + */ + if (new_state >= CAN_STATE_ERROR_PASSIVE && + new_state <= CAN_STATE_BUS_OFF) { + netdev_dbg(dev, "Error Passive IRQ\n"); + priv->can.can_stats.error_passive++; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + break; + case CAN_STATE_BUS_OFF: + /* + * from: BUS_OFF + * to : ERROR_ACTIVE, ERROR_WARNING, ERROR_PASSIVE + */ + if (new_state <= CAN_STATE_ERROR_PASSIVE) { + cf->can_id |= CAN_ERR_RESTARTED; + + netdev_dbg(dev, "restarted\n"); + priv->can.can_stats.restarts++; + + netif_carrier_on(dev); + netif_wake_queue(dev); + } + break; + default: + break; + } + + + /* process state changes depending on the new state */ + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* + * actually we want to enable AT91_IRQ_WARN here, but + * it screws up the system under certain + * circumstances. so just enable AT91_IRQ_ERRP, thus + * the "fallthrough" + */ + netdev_dbg(dev, "Error Active\n"); + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_BOFF; + reg_ier = AT91_IRQ_ERRP; + break; + case CAN_STATE_ERROR_PASSIVE: + reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_ERRP; + reg_ier = AT91_IRQ_BOFF; + break; + case CAN_STATE_BUS_OFF: + reg_idr = AT91_IRQ_ERRA | AT91_IRQ_ERRP | + AT91_IRQ_WARN | AT91_IRQ_BOFF; + reg_ier = 0; + + cf->can_id |= CAN_ERR_BUSOFF; + + netdev_dbg(dev, "bus-off\n"); + netif_carrier_off(dev); + priv->can.can_stats.bus_off++; + + /* turn off chip, if restart is disabled */ + if (!priv->can.restart_ms) { + at91_chip_stop(dev, CAN_STATE_BUS_OFF); + return; + } + break; + default: + break; + } + + at91_write(priv, AT91_IDR, reg_idr); + at91_write(priv, AT91_IER, reg_ier); +} + +static int at91_get_state_by_bec(const struct net_device *dev, + enum can_state *state) +{ + struct can_berr_counter bec; + int err; + + err = at91_get_berr_counter(dev, &bec); + if (err) + return err; + + if (bec.txerr < 96 && bec.rxerr < 96) + *state = CAN_STATE_ERROR_ACTIVE; + else if (bec.txerr < 128 && bec.rxerr < 128) + *state = CAN_STATE_ERROR_WARNING; + else if (bec.txerr < 256 && bec.rxerr < 256) + *state = CAN_STATE_ERROR_PASSIVE; + else + *state = CAN_STATE_BUS_OFF; + + return 0; +} + + +static void at91_irq_err(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + struct sk_buff *skb; + struct can_frame *cf; + enum can_state new_state; + u32 reg_sr; + int err; + + if (at91_is_sam9263(priv)) { + reg_sr = at91_read(priv, AT91_SR); + + /* we need to look at the unmasked reg_sr */ + if (unlikely(reg_sr & AT91_IRQ_BOFF)) + new_state = CAN_STATE_BUS_OFF; + else if (unlikely(reg_sr & AT91_IRQ_ERRP)) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (unlikely(reg_sr & AT91_IRQ_WARN)) + new_state = CAN_STATE_ERROR_WARNING; + else if (likely(reg_sr & AT91_IRQ_ERRA)) + new_state = CAN_STATE_ERROR_ACTIVE; + else { + netdev_err(dev, "BUG! hardware in undefined state\n"); + return; + } + } else { + err = at91_get_state_by_bec(dev, &new_state); + if (err) + return; + } + + /* state hasn't changed */ + if (likely(new_state == priv->can.state)) + return; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return; + + at91_irq_err_state(dev, cf, new_state); + netif_rx(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + + priv->can.state = new_state; +} + +/* + * interrupt handler + */ +static irqreturn_t at91_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct at91_priv *priv = netdev_priv(dev); + irqreturn_t handled = IRQ_NONE; + u32 reg_sr, reg_imr; + + reg_sr = at91_read(priv, AT91_SR); + reg_imr = at91_read(priv, AT91_IMR); + + /* Ignore masked interrupts */ + reg_sr &= reg_imr; + if (!reg_sr) + goto exit; + + handled = IRQ_HANDLED; + + /* Receive or error interrupt? -> napi */ + if (reg_sr & (get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME)) { + /* + * The error bits are clear on read, + * save for later use. + */ + priv->reg_sr = reg_sr; + at91_write(priv, AT91_IDR, + get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME); + napi_schedule(&priv->napi); + } + + /* Transmission complete interrupt */ + if (reg_sr & get_irq_mb_tx(priv)) + at91_irq_tx(dev, reg_sr); + + at91_irq_err(dev); + + exit: + return handled; +} + +static int at91_open(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + int err; + + clk_enable(priv->clk); + + /* check or determine and set bittime */ + err = open_candev(dev); + if (err) + goto out; + + /* register interrupt handler */ + if (request_irq(dev->irq, at91_irq, IRQF_SHARED, + dev->name, dev)) { + err = -EAGAIN; + goto out_close; + } + + /* start chip and queuing */ + at91_chip_start(dev); + napi_enable(&priv->napi); + netif_start_queue(dev); + + return 0; + + out_close: + close_candev(dev); + out: + clk_disable(priv->clk); + + return err; +} + +/* + * stop CAN bus activity + */ +static int at91_close(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&priv->napi); + at91_chip_stop(dev, CAN_STATE_STOPPED); + + free_irq(dev->irq, dev); + clk_disable(priv->clk); + + close_candev(dev); + + return 0; +} + +static int at91_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + at91_chip_start(dev); + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct net_device_ops at91_netdev_ops = { + .ndo_open = at91_open, + .ndo_stop = at91_close, + .ndo_start_xmit = at91_start_xmit, +}; + +static ssize_t at91_sysfs_show_mb0_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct at91_priv *priv = netdev_priv(to_net_dev(dev)); + + if (priv->mb0_id & CAN_EFF_FLAG) + return snprintf(buf, PAGE_SIZE, "0x%08x\n", priv->mb0_id); + else + return snprintf(buf, PAGE_SIZE, "0x%03x\n", priv->mb0_id); +} + +static ssize_t at91_sysfs_set_mb0_id(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct net_device *ndev = to_net_dev(dev); + struct at91_priv *priv = netdev_priv(ndev); + unsigned long can_id; + ssize_t ret; + int err; + + rtnl_lock(); + + if (ndev->flags & IFF_UP) { + ret = -EBUSY; + goto out; + } + + err = strict_strtoul(buf, 0, &can_id); + if (err) { + ret = err; + goto out; + } + + if (can_id & CAN_EFF_FLAG) + can_id &= CAN_EFF_MASK | CAN_EFF_FLAG; + else + can_id &= CAN_SFF_MASK; + + priv->mb0_id = can_id; + ret = count; + + out: + rtnl_unlock(); + return ret; +} + +static DEVICE_ATTR(mb0_id, S_IWUSR | S_IRUGO, + at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id); + +static struct attribute *at91_sysfs_attrs[] = { + &dev_attr_mb0_id.attr, + NULL, +}; + +static struct attribute_group at91_sysfs_attr_group = { + .attrs = at91_sysfs_attrs, +}; + +static int __devinit at91_can_probe(struct platform_device *pdev) +{ + const struct at91_devtype_data *devtype_data; + enum at91_devtype devtype; + struct net_device *dev; + struct at91_priv *priv; + struct resource *res; + struct clk *clk; + void __iomem *addr; + int err, irq; + + devtype = pdev->id_entry->driver_data; + devtype_data = &at91_devtype_data[devtype]; + + clk = clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + err = -ENODEV; + goto exit; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || irq <= 0) { + err = -ENODEV; + goto exit_put; + } + + if (!request_mem_region(res->start, + resource_size(res), + pdev->name)) { + err = -EBUSY; + goto exit_put; + } + + addr = ioremap_nocache(res->start, resource_size(res)); + if (!addr) { + err = -ENOMEM; + goto exit_release; + } + + dev = alloc_candev(sizeof(struct at91_priv), + 1 << devtype_data->tx_shift); + if (!dev) { + err = -ENOMEM; + goto exit_iounmap; + } + + dev->netdev_ops = &at91_netdev_ops; + dev->irq = irq; + dev->flags |= IFF_ECHO; + + priv = netdev_priv(dev); + priv->can.clock.freq = clk_get_rate(clk); + priv->can.bittiming_const = &at91_bittiming_const; + priv->can.do_set_mode = at91_set_mode; + priv->can.do_get_berr_counter = at91_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + priv->dev = dev; + priv->reg_base = addr; + priv->devtype_data = *devtype_data; + priv->devtype_data.type = devtype; + priv->clk = clk; + priv->pdata = pdev->dev.platform_data; + priv->mb0_id = 0x7ff; + + netif_napi_add(dev, &priv->napi, at91_poll, get_mb_rx_num(priv)); + + if (at91_is_sam9263(priv)) + dev->sysfs_groups[0] = &at91_sysfs_attr_group; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering netdev failed\n"); + goto exit_free; + } + + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", + priv->reg_base, dev->irq); + + return 0; + + exit_free: + free_candev(dev); + exit_iounmap: + iounmap(addr); + exit_release: + release_mem_region(res->start, resource_size(res)); + exit_put: + clk_put(clk); + exit: + return err; +} + +static int __devexit at91_can_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct at91_priv *priv = netdev_priv(dev); + struct resource *res; + + unregister_netdev(dev); + + platform_set_drvdata(pdev, NULL); + + iounmap(priv->reg_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_put(priv->clk); + + free_candev(dev); + + return 0; +} + +static const struct platform_device_id at91_can_id_table[] = { + { + .name = "at91_can", + .driver_data = AT91_DEVTYPE_SAM9263, + }, { + .name = "at91sam9x5_can", + .driver_data = AT91_DEVTYPE_SAM9X5, + }, { + /* sentinel */ + } +}; + +static struct platform_driver at91_can_driver = { + .probe = at91_can_probe, + .remove = __devexit_p(at91_can_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .id_table = at91_can_id_table, +}; + +module_platform_driver(at91_can_driver); + +MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(KBUILD_MODNAME " CAN netdevice driver"); diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c new file mode 100644 index 00000000..3f884734 --- /dev/null +++ b/drivers/net/can/bfin_can.c @@ -0,0 +1,693 @@ +/* + * Blackfin On-Chip CAN Driver + * + * Copyright 2004-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/platform_device.h> + +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include <asm/bfin_can.h> +#include <asm/portmux.h> + +#define DRV_NAME "bfin_can" +#define BFIN_CAN_TIMEOUT 100 +#define TX_ECHO_SKB_MAX 1 + +/* + * bfin can private data + */ +struct bfin_can_priv { + struct can_priv can; /* must be the first member */ + struct net_device *dev; + void __iomem *membase; + int rx_irq; + int tx_irq; + int err_irq; + unsigned short *pin_list; +}; + +/* + * bfin can timing parameters + */ +static struct can_bittiming_const bfin_can_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + /* + * Although the BRP field can be set to any value, it is recommended + * that the value be greater than or equal to 4, as restrictions + * apply to the bit timing configuration when BRP is less than 4. + */ + .brp_min = 4, + .brp_max = 1024, + .brp_inc = 1, +}; + +static int bfin_can_set_bittiming(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct can_bittiming *bt = &priv->can.bittiming; + u16 clk, timing; + + clk = bt->brp - 1; + timing = ((bt->sjw - 1) << 8) | (bt->prop_seg + bt->phase_seg1 - 1) | + ((bt->phase_seg2 - 1) << 4); + + /* + * If the SAM bit is set, the input signal is oversampled three times + * at the SCLK rate. + */ + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + timing |= SAM; + + bfin_write(®->clock, clk); + bfin_write(®->timing, timing); + + netdev_info(dev, "setting CLOCK=0x%04x TIMING=0x%04x\n", clk, timing); + + return 0; +} + +static void bfin_can_set_reset_mode(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + int timeout = BFIN_CAN_TIMEOUT; + int i; + + /* disable interrupts */ + bfin_write(®->mbim1, 0); + bfin_write(®->mbim2, 0); + bfin_write(®->gim, 0); + + /* reset can and enter configuration mode */ + bfin_write(®->control, SRS | CCR); + SSYNC(); + bfin_write(®->control, CCR); + SSYNC(); + while (!(bfin_read(®->control) & CCA)) { + udelay(10); + if (--timeout == 0) { + netdev_err(dev, "fail to enter configuration mode\n"); + BUG(); + } + } + + /* + * All mailbox configurations are marked as inactive + * by writing to CAN Mailbox Configuration Registers 1 and 2 + * For all bits: 0 - Mailbox disabled, 1 - Mailbox enabled + */ + bfin_write(®->mc1, 0); + bfin_write(®->mc2, 0); + + /* Set Mailbox Direction */ + bfin_write(®->md1, 0xFFFF); /* mailbox 1-16 are RX */ + bfin_write(®->md2, 0); /* mailbox 17-32 are TX */ + + /* RECEIVE_STD_CHL */ + for (i = 0; i < 2; i++) { + bfin_write(®->chl[RECEIVE_STD_CHL + i].id0, 0); + bfin_write(®->chl[RECEIVE_STD_CHL + i].id1, AME); + bfin_write(®->chl[RECEIVE_STD_CHL + i].dlc, 0); + bfin_write(®->msk[RECEIVE_STD_CHL + i].amh, 0x1FFF); + bfin_write(®->msk[RECEIVE_STD_CHL + i].aml, 0xFFFF); + } + + /* RECEIVE_EXT_CHL */ + for (i = 0; i < 2; i++) { + bfin_write(®->chl[RECEIVE_EXT_CHL + i].id0, 0); + bfin_write(®->chl[RECEIVE_EXT_CHL + i].id1, AME | IDE); + bfin_write(®->chl[RECEIVE_EXT_CHL + i].dlc, 0); + bfin_write(®->msk[RECEIVE_EXT_CHL + i].amh, 0x1FFF); + bfin_write(®->msk[RECEIVE_EXT_CHL + i].aml, 0xFFFF); + } + + bfin_write(®->mc2, BIT(TRANSMIT_CHL - 16)); + bfin_write(®->mc1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); + SSYNC(); + + priv->can.state = CAN_STATE_STOPPED; +} + +static void bfin_can_set_normal_mode(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + int timeout = BFIN_CAN_TIMEOUT; + + /* + * leave configuration mode + */ + bfin_write(®->control, bfin_read(®->control) & ~CCR); + + while (bfin_read(®->status) & CCA) { + udelay(10); + if (--timeout == 0) { + netdev_err(dev, "fail to leave configuration mode\n"); + BUG(); + } + } + + /* + * clear _All_ tx and rx interrupts + */ + bfin_write(®->mbtif1, 0xFFFF); + bfin_write(®->mbtif2, 0xFFFF); + bfin_write(®->mbrif1, 0xFFFF); + bfin_write(®->mbrif2, 0xFFFF); + + /* + * clear global interrupt status register + */ + bfin_write(®->gis, 0x7FF); /* overwrites with '1' */ + + /* + * Initialize Interrupts + * - set bits in the mailbox interrupt mask register + * - global interrupt mask + */ + bfin_write(®->mbim1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); + bfin_write(®->mbim2, BIT(TRANSMIT_CHL - 16)); + + bfin_write(®->gim, EPIM | BOIM | RMLIM); + SSYNC(); +} + +static void bfin_can_start(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + + /* enter reset mode */ + if (priv->can.state != CAN_STATE_STOPPED) + bfin_can_set_reset_mode(dev); + + /* leave reset mode */ + bfin_can_set_normal_mode(dev); +} + +static int bfin_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + bfin_can_start(dev); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int bfin_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + + u16 cec = bfin_read(®->cec); + + bec->txerr = cec >> 8; + bec->rxerr = cec; + + return 0; +} + +static int bfin_can_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc = cf->can_dlc; + canid_t id = cf->can_id; + u8 *data = cf->data; + u16 val; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + /* fill id */ + if (id & CAN_EFF_FLAG) { + bfin_write(®->chl[TRANSMIT_CHL].id0, id); + val = ((id & 0x1FFF0000) >> 16) | IDE; + } else + val = (id << 2); + if (id & CAN_RTR_FLAG) + val |= RTR; + bfin_write(®->chl[TRANSMIT_CHL].id1, val | AME); + + /* fill payload */ + for (i = 0; i < 8; i += 2) { + val = ((7 - i) < dlc ? (data[7 - i]) : 0) + + ((6 - i) < dlc ? (data[6 - i] << 8) : 0); + bfin_write(®->chl[TRANSMIT_CHL].data[i], val); + } + + /* fill data length code */ + bfin_write(®->chl[TRANSMIT_CHL].dlc, dlc); + + can_put_echo_skb(skb, dev, 0); + + /* set transmit request */ + bfin_write(®->trs2, BIT(TRANSMIT_CHL - 16)); + + return 0; +} + +static void bfin_can_rx(struct net_device *dev, u16 isrc) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct bfin_can_regs __iomem *reg = priv->membase; + struct can_frame *cf; + struct sk_buff *skb; + int obj; + int i; + u16 val; + + skb = alloc_can_skb(dev, &cf); + if (skb == NULL) + return; + + /* get id */ + if (isrc & BIT(RECEIVE_EXT_CHL)) { + /* extended frame format (EFF) */ + cf->can_id = ((bfin_read(®->chl[RECEIVE_EXT_CHL].id1) + & 0x1FFF) << 16) + + bfin_read(®->chl[RECEIVE_EXT_CHL].id0); + cf->can_id |= CAN_EFF_FLAG; + obj = RECEIVE_EXT_CHL; + } else { + /* standard frame format (SFF) */ + cf->can_id = (bfin_read(®->chl[RECEIVE_STD_CHL].id1) + & 0x1ffc) >> 2; + obj = RECEIVE_STD_CHL; + } + if (bfin_read(®->chl[obj].id1) & RTR) + cf->can_id |= CAN_RTR_FLAG; + + /* get data length code */ + cf->can_dlc = get_can_dlc(bfin_read(®->chl[obj].dlc) & 0xF); + + /* get payload */ + for (i = 0; i < 8; i += 2) { + val = bfin_read(®->chl[obj].data[i]); + cf->data[7 - i] = (7 - i) < cf->can_dlc ? val : 0; + cf->data[6 - i] = (6 - i) < cf->can_dlc ? (val >> 8) : 0; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + enum can_state state = priv->can.state; + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + if (isrc & RMLIS) { + /* 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++; + } + + if (isrc & BOIS) { + netdev_dbg(dev, "bus-off mode interrupt\n"); + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } + + if (isrc & EPIS) { + /* error passive interrupt */ + netdev_dbg(dev, "error passive interrupt\n"); + state = CAN_STATE_ERROR_PASSIVE; + } + + if ((isrc & EWTIS) || (isrc & EWRIS)) { + netdev_dbg(dev, "Error Warning Transmit/Receive Interrupt\n"); + state = CAN_STATE_ERROR_WARNING; + } + + if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING || + state == CAN_STATE_ERROR_PASSIVE)) { + u16 cec = bfin_read(®->cec); + u8 rxerr = cec; + u8 txerr = cec >> 8; + + 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; + } + } + + if (status) { + priv->can.can_stats.bus_error++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + if (status & BEF) + cf->data[2] |= CAN_ERR_PROT_BIT; + else if (status & FER) + cf->data[2] |= CAN_ERR_PROT_FORM; + else if (status & SER) + cf->data[2] |= CAN_ERR_PROT_STUFF; + else + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + } + + priv->can.state = state; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +irqreturn_t bfin_can_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct net_device_stats *stats = &dev->stats; + u16 status, isrc; + + if ((irq == priv->tx_irq) && bfin_read(®->mbtif2)) { + /* transmission complete interrupt */ + bfin_write(®->mbtif2, 0xFFFF); + stats->tx_packets++; + stats->tx_bytes += bfin_read(®->chl[TRANSMIT_CHL].dlc); + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + } else if ((irq == priv->rx_irq) && bfin_read(®->mbrif1)) { + /* receive interrupt */ + isrc = bfin_read(®->mbrif1); + bfin_write(®->mbrif1, 0xFFFF); + bfin_can_rx(dev, isrc); + } else if ((irq == priv->err_irq) && bfin_read(®->gis)) { + /* error interrupt */ + isrc = bfin_read(®->gis); + status = bfin_read(®->esr); + bfin_write(®->gis, 0x7FF); + bfin_can_err(dev, isrc, status); + } else { + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int bfin_can_open(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + int err; + + /* set chip into reset mode */ + bfin_can_set_reset_mode(dev); + + /* common open */ + err = open_candev(dev); + if (err) + goto exit_open; + + /* register interrupt handler */ + err = request_irq(priv->rx_irq, &bfin_can_interrupt, 0, + "bfin-can-rx", dev); + if (err) + goto exit_rx_irq; + err = request_irq(priv->tx_irq, &bfin_can_interrupt, 0, + "bfin-can-tx", dev); + if (err) + goto exit_tx_irq; + err = request_irq(priv->err_irq, &bfin_can_interrupt, 0, + "bfin-can-err", dev); + if (err) + goto exit_err_irq; + + bfin_can_start(dev); + + netif_start_queue(dev); + + return 0; + +exit_err_irq: + free_irq(priv->tx_irq, dev); +exit_tx_irq: + free_irq(priv->rx_irq, dev); +exit_rx_irq: + close_candev(dev); +exit_open: + return err; +} + +static int bfin_can_close(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + bfin_can_set_reset_mode(dev); + + close_candev(dev); + + free_irq(priv->rx_irq, dev); + free_irq(priv->tx_irq, dev); + free_irq(priv->err_irq, dev); + + return 0; +} + +struct net_device *alloc_bfin_candev(void) +{ + struct net_device *dev; + struct bfin_can_priv *priv; + + dev = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + + priv->dev = dev; + priv->can.bittiming_const = &bfin_can_bittiming_const; + priv->can.do_set_bittiming = bfin_can_set_bittiming; + priv->can.do_set_mode = bfin_can_set_mode; + priv->can.do_get_berr_counter = bfin_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + return dev; +} + +static const struct net_device_ops bfin_can_netdev_ops = { + .ndo_open = bfin_can_open, + .ndo_stop = bfin_can_close, + .ndo_start_xmit = bfin_can_start_xmit, +}; + +static int __devinit bfin_can_probe(struct platform_device *pdev) +{ + int err; + struct net_device *dev; + struct bfin_can_priv *priv; + struct resource *res_mem, *rx_irq, *tx_irq, *err_irq; + unsigned short *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + err = -EINVAL; + goto exit; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rx_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + tx_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + err_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res_mem || !rx_irq || !tx_irq || !err_irq) { + err = -EINVAL; + goto exit; + } + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + dev_name(&pdev->dev))) { + err = -EBUSY; + goto exit; + } + + /* request peripheral pins */ + err = peripheral_request_list(pdata, dev_name(&pdev->dev)); + if (err) + goto exit_mem_release; + + dev = alloc_bfin_candev(); + if (!dev) { + err = -ENOMEM; + goto exit_peri_pin_free; + } + + priv = netdev_priv(dev); + priv->membase = (void __iomem *)res_mem->start; + priv->rx_irq = rx_irq->start; + priv->tx_irq = tx_irq->start; + priv->err_irq = err_irq->start; + priv->pin_list = pdata; + priv->can.clock.freq = get_sclk(); + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &bfin_can_netdev_ops; + + bfin_can_set_reset_mode(dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering failed (err=%d)\n", err); + goto exit_candev_free; + } + + dev_info(&pdev->dev, + "%s device registered" + "(®_base=%p, rx_irq=%d, tx_irq=%d, err_irq=%d, sclk=%d)\n", + DRV_NAME, (void *)priv->membase, priv->rx_irq, + priv->tx_irq, priv->err_irq, priv->can.clock.freq); + return 0; + +exit_candev_free: + free_candev(dev); +exit_peri_pin_free: + peripheral_free_list(pdata); +exit_mem_release: + release_mem_region(res_mem->start, resource_size(res_mem)); +exit: + return err; +} + +static int __devexit bfin_can_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct bfin_can_priv *priv = netdev_priv(dev); + struct resource *res; + + bfin_can_set_reset_mode(dev); + + unregister_candev(dev); + + dev_set_drvdata(&pdev->dev, NULL); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + peripheral_free_list(priv->pin_list); + + free_candev(dev); + return 0; +} + +#ifdef CONFIG_PM +static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + int timeout = BFIN_CAN_TIMEOUT; + + if (netif_running(dev)) { + /* enter sleep mode */ + bfin_write(®->control, bfin_read(®->control) | SMR); + SSYNC(); + while (!(bfin_read(®->intr) & SMACK)) { + udelay(10); + if (--timeout == 0) { + netdev_err(dev, "fail to enter sleep mode\n"); + BUG(); + } + } + } + + return 0; +} + +static int bfin_can_resume(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + + if (netif_running(dev)) { + /* leave sleep mode */ + bfin_write(®->intr, 0); + SSYNC(); + } + + return 0; +} +#else +#define bfin_can_suspend NULL +#define bfin_can_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver bfin_can_driver = { + .probe = bfin_can_probe, + .remove = __devexit_p(bfin_can_remove), + .suspend = bfin_can_suspend, + .resume = bfin_can_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bfin_can_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Blackfin on-chip CAN netdevice driver"); diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig new file mode 100644 index 00000000..ffb9773d --- /dev/null +++ b/drivers/net/can/c_can/Kconfig @@ -0,0 +1,15 @@ +menuconfig CAN_C_CAN + tristate "Bosch C_CAN devices" + depends on CAN_DEV && HAS_IOMEM + +if CAN_C_CAN + +config CAN_C_CAN_PLATFORM + tristate "Generic Platform Bus based C_CAN driver" + ---help--- + This driver adds support for the C_CAN chips connected to + the "platform bus" (Linux abstraction for directly to the + processor attached devices) which can be found on various + boards from ST Microelectronics (http://www.st.com) + like the SPEAr1310 and SPEAr320 evaluation boards. +endif diff --git a/drivers/net/can/c_can/Makefile b/drivers/net/can/c_can/Makefile new file mode 100644 index 00000000..9273f6d5 --- /dev/null +++ b/drivers/net/can/c_can/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Bosch C_CAN controller drivers. +# + +obj-$(CONFIG_CAN_C_CAN) += c_can.o +obj-$(CONFIG_CAN_C_CAN_PLATFORM) += c_can_platform.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c new file mode 100644 index 00000000..86cd532c --- /dev/null +++ b/drivers/net/can/c_can/c_can.c @@ -0,0 +1,1152 @@ +/* + * CAN bus driver for Bosch C_CAN controller + * + * Copyright (C) 2010 ST Microelectronics + * Bhupesh Sharma <bhupesh.sharma@st.com> + * + * Borrowed heavily from the C_CAN driver originally written by: + * Copyright (C) 2007 + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de> + * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch> + * + * TX and RX NAPI implementation has been borrowed from at91 CAN driver + * written by: + * Copyright + * (C) 2007 by Hans J. Koch <hjk@hansjkoch.de> + * (C) 2008, 2009 by Marc Kleine-Budde <kernel@pengutronix.de> + * + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B. + * Bosch C_CAN user manual can be obtained from: + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/ + * users_manual_c_can.pdf + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/list.h> +#include <linux/io.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "c_can.h" + +/* control register */ +#define CONTROL_TEST BIT(7) +#define CONTROL_CCE BIT(6) +#define CONTROL_DISABLE_AR BIT(5) +#define CONTROL_ENABLE_AR (0 << 5) +#define CONTROL_EIE BIT(3) +#define CONTROL_SIE BIT(2) +#define CONTROL_IE BIT(1) +#define CONTROL_INIT BIT(0) + +/* test register */ +#define TEST_RX BIT(7) +#define TEST_TX1 BIT(6) +#define TEST_TX2 BIT(5) +#define TEST_LBACK BIT(4) +#define TEST_SILENT BIT(3) +#define TEST_BASIC BIT(2) + +/* status register */ +#define STATUS_BOFF BIT(7) +#define STATUS_EWARN BIT(6) +#define STATUS_EPASS BIT(5) +#define STATUS_RXOK BIT(4) +#define STATUS_TXOK BIT(3) + +/* error counter register */ +#define ERR_CNT_TEC_MASK 0xff +#define ERR_CNT_TEC_SHIFT 0 +#define ERR_CNT_REC_SHIFT 8 +#define ERR_CNT_REC_MASK (0x7f << ERR_CNT_REC_SHIFT) +#define ERR_CNT_RP_SHIFT 15 +#define ERR_CNT_RP_MASK (0x1 << ERR_CNT_RP_SHIFT) + +/* bit-timing register */ +#define BTR_BRP_MASK 0x3f +#define BTR_BRP_SHIFT 0 +#define BTR_SJW_SHIFT 6 +#define BTR_SJW_MASK (0x3 << BTR_SJW_SHIFT) +#define BTR_TSEG1_SHIFT 8 +#define BTR_TSEG1_MASK (0xf << BTR_TSEG1_SHIFT) +#define BTR_TSEG2_SHIFT 12 +#define BTR_TSEG2_MASK (0x7 << BTR_TSEG2_SHIFT) + +/* brp extension register */ +#define BRP_EXT_BRPE_MASK 0x0f +#define BRP_EXT_BRPE_SHIFT 0 + +/* IFx command request */ +#define IF_COMR_BUSY BIT(15) + +/* IFx command mask */ +#define IF_COMM_WR BIT(7) +#define IF_COMM_MASK BIT(6) +#define IF_COMM_ARB BIT(5) +#define IF_COMM_CONTROL BIT(4) +#define IF_COMM_CLR_INT_PND BIT(3) +#define IF_COMM_TXRQST BIT(2) +#define IF_COMM_DATAA BIT(1) +#define IF_COMM_DATAB BIT(0) +#define IF_COMM_ALL (IF_COMM_MASK | IF_COMM_ARB | \ + IF_COMM_CONTROL | IF_COMM_TXRQST | \ + IF_COMM_DATAA | IF_COMM_DATAB) + +/* IFx arbitration */ +#define IF_ARB_MSGVAL BIT(15) +#define IF_ARB_MSGXTD BIT(14) +#define IF_ARB_TRANSMIT BIT(13) + +/* IFx message control */ +#define IF_MCONT_NEWDAT BIT(15) +#define IF_MCONT_MSGLST BIT(14) +#define IF_MCONT_CLR_MSGLST (0 << 14) +#define IF_MCONT_INTPND BIT(13) +#define IF_MCONT_UMASK BIT(12) +#define IF_MCONT_TXIE BIT(11) +#define IF_MCONT_RXIE BIT(10) +#define IF_MCONT_RMTEN BIT(9) +#define IF_MCONT_TXRQST BIT(8) +#define IF_MCONT_EOB BIT(7) +#define IF_MCONT_DLC_MASK 0xf + +/* + * IFx register masks: + * allow easy operation on 16-bit registers when the + * argument is 32-bit instead + */ +#define IFX_WRITE_LOW_16BIT(x) ((x) & 0xFFFF) +#define IFX_WRITE_HIGH_16BIT(x) (((x) & 0xFFFF0000) >> 16) + +/* message object split */ +#define C_CAN_NO_OF_OBJECTS 32 +#define C_CAN_MSG_OBJ_RX_NUM 16 +#define C_CAN_MSG_OBJ_TX_NUM 16 + +#define C_CAN_MSG_OBJ_RX_FIRST 1 +#define C_CAN_MSG_OBJ_RX_LAST (C_CAN_MSG_OBJ_RX_FIRST + \ + C_CAN_MSG_OBJ_RX_NUM - 1) + +#define C_CAN_MSG_OBJ_TX_FIRST (C_CAN_MSG_OBJ_RX_LAST + 1) +#define C_CAN_MSG_OBJ_TX_LAST (C_CAN_MSG_OBJ_TX_FIRST + \ + C_CAN_MSG_OBJ_TX_NUM - 1) + +#define C_CAN_MSG_OBJ_RX_SPLIT 9 +#define C_CAN_MSG_RX_LOW_LAST (C_CAN_MSG_OBJ_RX_SPLIT - 1) + +#define C_CAN_NEXT_MSG_OBJ_MASK (C_CAN_MSG_OBJ_TX_NUM - 1) +#define RECEIVE_OBJECT_BITS 0x0000ffff + +/* status interrupt */ +#define STATUS_INTERRUPT 0x8000 + +/* global interrupt masks */ +#define ENABLE_ALL_INTERRUPTS 1 +#define DISABLE_ALL_INTERRUPTS 0 + +/* minimum timeout for checking BUSY status */ +#define MIN_TIMEOUT_VALUE 6 + +/* napi related */ +#define C_CAN_NAPI_WEIGHT C_CAN_MSG_OBJ_RX_NUM + +/* c_can lec values */ +enum c_can_lec_type { + LEC_NO_ERROR = 0, + LEC_STUFF_ERROR, + LEC_FORM_ERROR, + LEC_ACK_ERROR, + LEC_BIT1_ERROR, + LEC_BIT0_ERROR, + LEC_CRC_ERROR, + LEC_UNUSED, +}; + +/* + * c_can error types: + * Bus errors (BUS_OFF, ERROR_WARNING, ERROR_PASSIVE) are supported + */ +enum c_can_bus_error_types { + C_CAN_NO_ERROR = 0, + C_CAN_BUS_OFF, + C_CAN_ERROR_WARNING, + C_CAN_ERROR_PASSIVE, +}; + +static struct can_bittiming_const c_can_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, /* 6-bit BRP field + 4-bit BRPE field*/ + .brp_inc = 1, +}; + +static inline int get_tx_next_msg_obj(const struct c_can_priv *priv) +{ + return (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) + + C_CAN_MSG_OBJ_TX_FIRST; +} + +static inline int get_tx_echo_msg_obj(const struct c_can_priv *priv) +{ + return (priv->tx_echo & C_CAN_NEXT_MSG_OBJ_MASK) + + C_CAN_MSG_OBJ_TX_FIRST; +} + +static u32 c_can_read_reg32(struct c_can_priv *priv, void *reg) +{ + u32 val = priv->read_reg(priv, reg); + val |= ((u32) priv->read_reg(priv, reg + 2)) << 16; + return val; +} + +static void c_can_enable_all_interrupts(struct c_can_priv *priv, + int enable) +{ + unsigned int cntrl_save = priv->read_reg(priv, + &priv->regs->control); + + if (enable) + cntrl_save |= (CONTROL_SIE | CONTROL_EIE | CONTROL_IE); + else + cntrl_save &= ~(CONTROL_EIE | CONTROL_IE | CONTROL_SIE); + + priv->write_reg(priv, &priv->regs->control, cntrl_save); +} + +static inline int c_can_msg_obj_is_busy(struct c_can_priv *priv, int iface) +{ + int count = MIN_TIMEOUT_VALUE; + + while (count && priv->read_reg(priv, + &priv->regs->ifregs[iface].com_req) & + IF_COMR_BUSY) { + count--; + udelay(1); + } + + if (!count) + return 1; + + return 0; +} + +static inline void c_can_object_get(struct net_device *dev, + int iface, int objno, int mask) +{ + struct c_can_priv *priv = netdev_priv(dev); + + /* + * As per specs, after writting the message object number in the + * IF command request register the transfer b/w interface + * register and message RAM must be complete in 6 CAN-CLK + * period. + */ + priv->write_reg(priv, &priv->regs->ifregs[iface].com_mask, + IFX_WRITE_LOW_16BIT(mask)); + priv->write_reg(priv, &priv->regs->ifregs[iface].com_req, + IFX_WRITE_LOW_16BIT(objno)); + + if (c_can_msg_obj_is_busy(priv, iface)) + netdev_err(dev, "timed out in object get\n"); +} + +static inline void c_can_object_put(struct net_device *dev, + int iface, int objno, int mask) +{ + struct c_can_priv *priv = netdev_priv(dev); + + /* + * As per specs, after writting the message object number in the + * IF command request register the transfer b/w interface + * register and message RAM must be complete in 6 CAN-CLK + * period. + */ + priv->write_reg(priv, &priv->regs->ifregs[iface].com_mask, + (IF_COMM_WR | IFX_WRITE_LOW_16BIT(mask))); + priv->write_reg(priv, &priv->regs->ifregs[iface].com_req, + IFX_WRITE_LOW_16BIT(objno)); + + if (c_can_msg_obj_is_busy(priv, iface)) + netdev_err(dev, "timed out in object put\n"); +} + +static void c_can_write_msg_object(struct net_device *dev, + int iface, struct can_frame *frame, int objno) +{ + int i; + u16 flags = 0; + unsigned int id; + struct c_can_priv *priv = netdev_priv(dev); + + if (!(frame->can_id & CAN_RTR_FLAG)) + flags |= IF_ARB_TRANSMIT; + + if (frame->can_id & CAN_EFF_FLAG) { + id = frame->can_id & CAN_EFF_MASK; + flags |= IF_ARB_MSGXTD; + } else + id = ((frame->can_id & CAN_SFF_MASK) << 18); + + flags |= IF_ARB_MSGVAL; + + priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, + IFX_WRITE_LOW_16BIT(id)); + priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, flags | + IFX_WRITE_HIGH_16BIT(id)); + + for (i = 0; i < frame->can_dlc; i += 2) { + priv->write_reg(priv, &priv->regs->ifregs[iface].data[i / 2], + frame->data[i] | (frame->data[i + 1] << 8)); + } + + /* enable interrupt for this message object */ + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB | + frame->can_dlc); + c_can_object_put(dev, iface, objno, IF_COMM_ALL); +} + +static inline void c_can_mark_rx_msg_obj(struct net_device *dev, + int iface, int ctrl_mask, + int obj) +{ + struct c_can_priv *priv = netdev_priv(dev); + + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND)); + c_can_object_put(dev, iface, obj, IF_COMM_CONTROL); + +} + +static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev, + int iface, + int ctrl_mask) +{ + int i; + struct c_can_priv *priv = netdev_priv(dev); + + for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++) { + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + ctrl_mask & ~(IF_MCONT_MSGLST | + IF_MCONT_INTPND | IF_MCONT_NEWDAT)); + c_can_object_put(dev, iface, i, IF_COMM_CONTROL); + } +} + +static inline void c_can_activate_rx_msg_obj(struct net_device *dev, + int iface, int ctrl_mask, + int obj) +{ + struct c_can_priv *priv = netdev_priv(dev); + + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + ctrl_mask & ~(IF_MCONT_MSGLST | + IF_MCONT_INTPND | IF_MCONT_NEWDAT)); + c_can_object_put(dev, iface, obj, IF_COMM_CONTROL); +} + +static void c_can_handle_lost_msg_obj(struct net_device *dev, + int iface, int objno) +{ + struct c_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(dev, "msg lost in buffer %d\n", objno); + + c_can_object_get(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST); + + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + IF_MCONT_CLR_MSGLST); + + c_can_object_put(dev, 0, objno, IF_COMM_CONTROL); + + /* create an error msg */ + skb = alloc_can_err_skb(dev, &frame); + if (unlikely(!skb)) + return; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_errors++; + stats->rx_over_errors++; + + netif_receive_skb(skb); +} + +static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl) +{ + u16 flags, data; + int i; + unsigned int val; + struct c_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + skb = alloc_can_skb(dev, &frame); + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + frame->can_dlc = get_can_dlc(ctrl & 0x0F); + + flags = priv->read_reg(priv, &priv->regs->ifregs[iface].arb2); + val = priv->read_reg(priv, &priv->regs->ifregs[iface].arb1) | + (flags << 16); + + if (flags & IF_ARB_MSGXTD) + frame->can_id = (val & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + frame->can_id = (val >> 18) & CAN_SFF_MASK; + + if (flags & IF_ARB_TRANSMIT) + frame->can_id |= CAN_RTR_FLAG; + else { + for (i = 0; i < frame->can_dlc; i += 2) { + data = priv->read_reg(priv, + &priv->regs->ifregs[iface].data[i / 2]); + frame->data[i] = data; + frame->data[i + 1] = data >> 8; + } + } + + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += frame->can_dlc; + + return 0; +} + +static void c_can_setup_receive_object(struct net_device *dev, int iface, + int objno, unsigned int mask, + unsigned int id, unsigned int mcont) +{ + struct c_can_priv *priv = netdev_priv(dev); + + priv->write_reg(priv, &priv->regs->ifregs[iface].mask1, + IFX_WRITE_LOW_16BIT(mask)); + priv->write_reg(priv, &priv->regs->ifregs[iface].mask2, + IFX_WRITE_HIGH_16BIT(mask)); + + priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, + IFX_WRITE_LOW_16BIT(id)); + priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, + (IF_ARB_MSGVAL | IFX_WRITE_HIGH_16BIT(id))); + + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, mcont); + c_can_object_put(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST); + + netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, + c_can_read_reg32(priv, &priv->regs->msgval1)); +} + +static void c_can_inval_msg_object(struct net_device *dev, int iface, int objno) +{ + struct c_can_priv *priv = netdev_priv(dev); + + priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, 0); + priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, 0); + priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, 0); + + c_can_object_put(dev, iface, objno, IF_COMM_ARB | IF_COMM_CONTROL); + + netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, + c_can_read_reg32(priv, &priv->regs->msgval1)); +} + +static inline int c_can_is_next_tx_obj_busy(struct c_can_priv *priv, int objno) +{ + int val = c_can_read_reg32(priv, &priv->regs->txrqst1); + + /* + * as transmission request register's bit n-1 corresponds to + * message object n, we need to handle the same properly. + */ + if (val & (1 << (objno - 1))) + return 1; + + return 0; +} + +static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + u32 msg_obj_no; + struct c_can_priv *priv = netdev_priv(dev); + struct can_frame *frame = (struct can_frame *)skb->data; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + msg_obj_no = get_tx_next_msg_obj(priv); + + /* prepare message object for transmission */ + c_can_write_msg_object(dev, 0, frame, msg_obj_no); + can_put_echo_skb(skb, dev, msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST); + + /* + * we have to stop the queue in case of a wrap around or + * if the next TX message object is still in use + */ + priv->tx_next++; + if (c_can_is_next_tx_obj_busy(priv, get_tx_next_msg_obj(priv)) || + (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) == 0) + netif_stop_queue(dev); + + return NETDEV_TX_OK; +} + +static int c_can_set_bittiming(struct net_device *dev) +{ + unsigned int reg_btr, reg_brpe, ctrl_save; + u8 brp, brpe, sjw, tseg1, tseg2; + u32 ten_bit_brp; + struct c_can_priv *priv = netdev_priv(dev); + const struct can_bittiming *bt = &priv->can.bittiming; + + /* c_can provides a 6-bit brp and 4-bit brpe fields */ + ten_bit_brp = bt->brp - 1; + brp = ten_bit_brp & BTR_BRP_MASK; + brpe = ten_bit_brp >> 6; + + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + reg_btr = brp | (sjw << BTR_SJW_SHIFT) | (tseg1 << BTR_TSEG1_SHIFT) | + (tseg2 << BTR_TSEG2_SHIFT); + reg_brpe = brpe & BRP_EXT_BRPE_MASK; + + netdev_info(dev, + "setting BTR=%04x BRPE=%04x\n", reg_btr, reg_brpe); + + ctrl_save = priv->read_reg(priv, &priv->regs->control); + priv->write_reg(priv, &priv->regs->control, + ctrl_save | CONTROL_CCE | CONTROL_INIT); + priv->write_reg(priv, &priv->regs->btr, reg_btr); + priv->write_reg(priv, &priv->regs->brp_ext, reg_brpe); + priv->write_reg(priv, &priv->regs->control, ctrl_save); + + return 0; +} + +/* + * Configure C_CAN message objects for Tx and Rx purposes: + * C_CAN provides a total of 32 message objects that can be configured + * either for Tx or Rx purposes. Here the first 16 message objects are used as + * a reception FIFO. The end of reception FIFO is signified by the EoB bit + * being SET. The remaining 16 message objects are kept aside for Tx purposes. + * See user guide document for further details on configuring message + * objects. + */ +static void c_can_configure_msg_objects(struct net_device *dev) +{ + int i; + + /* first invalidate all message objects */ + for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_NO_OF_OBJECTS; i++) + c_can_inval_msg_object(dev, 0, i); + + /* setup receive message objects */ + for (i = C_CAN_MSG_OBJ_RX_FIRST; i < C_CAN_MSG_OBJ_RX_LAST; i++) + c_can_setup_receive_object(dev, 0, i, 0, 0, + (IF_MCONT_RXIE | IF_MCONT_UMASK) & ~IF_MCONT_EOB); + + c_can_setup_receive_object(dev, 0, C_CAN_MSG_OBJ_RX_LAST, 0, 0, + IF_MCONT_EOB | IF_MCONT_RXIE | IF_MCONT_UMASK); +} + +/* + * Configure C_CAN chip: + * - enable/disable auto-retransmission + * - set operating mode + * - configure message objects + */ +static void c_can_chip_config(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + /* enable automatic retransmission */ + priv->write_reg(priv, &priv->regs->control, + CONTROL_ENABLE_AR); + + if ((priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) && + (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)) { + /* loopback + silent mode : useful for hot self-test */ + priv->write_reg(priv, &priv->regs->control, CONTROL_EIE | + CONTROL_SIE | CONTROL_IE | CONTROL_TEST); + priv->write_reg(priv, &priv->regs->test, + TEST_LBACK | TEST_SILENT); + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + /* loopback mode : useful for self-test function */ + priv->write_reg(priv, &priv->regs->control, CONTROL_EIE | + CONTROL_SIE | CONTROL_IE | CONTROL_TEST); + priv->write_reg(priv, &priv->regs->test, TEST_LBACK); + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + /* silent mode : bus-monitoring mode */ + priv->write_reg(priv, &priv->regs->control, CONTROL_EIE | + CONTROL_SIE | CONTROL_IE | CONTROL_TEST); + priv->write_reg(priv, &priv->regs->test, TEST_SILENT); + } else + /* normal mode*/ + priv->write_reg(priv, &priv->regs->control, + CONTROL_EIE | CONTROL_SIE | CONTROL_IE); + + /* configure message objects */ + c_can_configure_msg_objects(dev); + + /* set a `lec` value so that we can check for updates later */ + priv->write_reg(priv, &priv->regs->status, LEC_UNUSED); + + /* set bittiming params */ + c_can_set_bittiming(dev); +} + +static void c_can_start(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + /* basic c_can configuration */ + c_can_chip_config(dev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* reset tx helper pointers */ + priv->tx_next = priv->tx_echo = 0; + + /* enable status change, error and module interrupts */ + c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS); +} + +static void c_can_stop(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + /* disable all interrupts */ + c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); + + /* set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int c_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + c_can_start(dev); + netif_wake_queue(dev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int c_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + unsigned int reg_err_counter; + struct c_can_priv *priv = netdev_priv(dev); + + reg_err_counter = priv->read_reg(priv, &priv->regs->err_cnt); + bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> + ERR_CNT_REC_SHIFT; + bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; + + return 0; +} + +/* + * theory of operation: + * + * priv->tx_echo holds the number of the oldest can_frame put for + * transmission into the hardware, but not yet ACKed by the CAN tx + * complete IRQ. + * + * We iterate from priv->tx_echo to priv->tx_next and check if the + * packet has been transmitted, echo it back to the CAN framework. + * If we discover a not yet transmitted packet, stop looking for more. + */ +static void c_can_do_tx(struct net_device *dev) +{ + u32 val; + u32 msg_obj_no; + struct c_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + + for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) { + msg_obj_no = get_tx_echo_msg_obj(priv); + val = c_can_read_reg32(priv, &priv->regs->txrqst1); + if (!(val & (1 << (msg_obj_no - 1)))) { + can_get_echo_skb(dev, + msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST); + stats->tx_bytes += priv->read_reg(priv, + &priv->regs->ifregs[0].msg_cntrl) + & IF_MCONT_DLC_MASK; + stats->tx_packets++; + c_can_inval_msg_object(dev, 0, msg_obj_no); + } else { + break; + } + } + + /* restart queue if wrap-up or if queue stalled on last pkt */ + if (((priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) != 0) || + ((priv->tx_echo & C_CAN_NEXT_MSG_OBJ_MASK) == 0)) + netif_wake_queue(dev); +} + +/* + * theory of operation: + * + * c_can core saves a received CAN message into the first free message + * object it finds free (starting with the lowest). Bits NEWDAT and + * INTPND are set for this message object indicating that a new message + * has arrived. To work-around this issue, we keep two groups of message + * objects whose partitioning is defined by C_CAN_MSG_OBJ_RX_SPLIT. + * + * To ensure in-order frame reception we use the following + * approach while re-activating a message object to receive further + * frames: + * - if the current message object number is lower than + * C_CAN_MSG_RX_LOW_LAST, do not clear the NEWDAT bit while clearing + * the INTPND bit. + * - if the current message object number is equal to + * C_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of all lower + * receive message objects. + * - if the current message object number is greater than + * C_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of + * only this message object. + */ +static int c_can_do_rx_poll(struct net_device *dev, int quota) +{ + u32 num_rx_pkts = 0; + unsigned int msg_obj, msg_ctrl_save; + struct c_can_priv *priv = netdev_priv(dev); + u32 val = c_can_read_reg32(priv, &priv->regs->intpnd1); + + for (msg_obj = C_CAN_MSG_OBJ_RX_FIRST; + msg_obj <= C_CAN_MSG_OBJ_RX_LAST && quota > 0; + val = c_can_read_reg32(priv, &priv->regs->intpnd1), + msg_obj++) { + /* + * as interrupt pending register's bit n-1 corresponds to + * message object n, we need to handle the same properly. + */ + if (val & (1 << (msg_obj - 1))) { + c_can_object_get(dev, 0, msg_obj, IF_COMM_ALL & + ~IF_COMM_TXRQST); + msg_ctrl_save = priv->read_reg(priv, + &priv->regs->ifregs[0].msg_cntrl); + + if (msg_ctrl_save & IF_MCONT_EOB) + return num_rx_pkts; + + if (msg_ctrl_save & IF_MCONT_MSGLST) { + c_can_handle_lost_msg_obj(dev, 0, msg_obj); + num_rx_pkts++; + quota--; + continue; + } + + if (!(msg_ctrl_save & IF_MCONT_NEWDAT)) + continue; + + /* read the data from the message object */ + c_can_read_msg_object(dev, 0, msg_ctrl_save); + + if (msg_obj < C_CAN_MSG_RX_LOW_LAST) + c_can_mark_rx_msg_obj(dev, 0, + msg_ctrl_save, msg_obj); + else if (msg_obj > C_CAN_MSG_RX_LOW_LAST) + /* activate this msg obj */ + c_can_activate_rx_msg_obj(dev, 0, + msg_ctrl_save, msg_obj); + else if (msg_obj == C_CAN_MSG_RX_LOW_LAST) + /* activate all lower message objects */ + c_can_activate_all_lower_rx_msg_obj(dev, + 0, msg_ctrl_save); + + num_rx_pkts++; + quota--; + } + } + + return num_rx_pkts; +} + +static inline int c_can_has_and_handle_berr(struct c_can_priv *priv) +{ + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && + (priv->current_status & LEC_UNUSED); +} + +static int c_can_handle_state_change(struct net_device *dev, + enum c_can_bus_error_types error_type) +{ + unsigned int reg_err_counter; + unsigned int rx_err_passive; + struct c_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + c_can_get_berr_counter(dev, &bec); + reg_err_counter = priv->read_reg(priv, &priv->regs->err_cnt); + rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >> + ERR_CNT_RP_SHIFT; + + switch (error_type) { + case C_CAN_ERROR_WARNING: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + + break; + case C_CAN_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + cf->can_id |= CAN_ERR_CRTL; + if (rx_err_passive) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if (bec.txerr > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case C_CAN_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * disable all interrupts in bus-off mode to ensure that + * the CPU is not hogged down + */ + c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); + can_bus_off(dev); + break; + default: + break; + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + +static int c_can_handle_bus_err(struct net_device *dev, + enum c_can_lec_type lec_type) +{ + struct c_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* + * early exit if no lec update or no error. + * no lec update means that no CAN bus event has been detected + * since CPU wrote 0x7 value to status reg. + */ + if (lec_type == LEC_UNUSED || lec_type == LEC_NO_ERROR) + return 0; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + /* + * check for 'last error code' which tells us the + * type of the last error to occur on the CAN bus + */ + + /* common for all type of bus errors */ + priv->can.can_stats.bus_error++; + stats->rx_errors++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + + switch (lec_type) { + case LEC_STUFF_ERROR: + netdev_dbg(dev, "stuff error\n"); + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case LEC_FORM_ERROR: + netdev_dbg(dev, "form error\n"); + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case LEC_ACK_ERROR: + netdev_dbg(dev, "ack error\n"); + cf->data[2] |= (CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL); + break; + case LEC_BIT1_ERROR: + netdev_dbg(dev, "bit1 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT1; + break; + case LEC_BIT0_ERROR: + netdev_dbg(dev, "bit0 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT0; + break; + case LEC_CRC_ERROR: + netdev_dbg(dev, "CRC error\n"); + cf->data[2] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + break; + default: + break; + } + + /* set a `lec` value so that we can check for updates later */ + priv->write_reg(priv, &priv->regs->status, LEC_UNUSED); + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + +static int c_can_poll(struct napi_struct *napi, int quota) +{ + u16 irqstatus; + int lec_type = 0; + int work_done = 0; + struct net_device *dev = napi->dev; + struct c_can_priv *priv = netdev_priv(dev); + + irqstatus = priv->irqstatus; + if (!irqstatus) + goto end; + + /* status events have the highest priority */ + if (irqstatus == STATUS_INTERRUPT) { + priv->current_status = priv->read_reg(priv, + &priv->regs->status); + + /* handle Tx/Rx events */ + if (priv->current_status & STATUS_TXOK) + priv->write_reg(priv, &priv->regs->status, + priv->current_status & ~STATUS_TXOK); + + if (priv->current_status & STATUS_RXOK) + priv->write_reg(priv, &priv->regs->status, + priv->current_status & ~STATUS_RXOK); + + /* handle state changes */ + if ((priv->current_status & STATUS_EWARN) && + (!(priv->last_status & STATUS_EWARN))) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += c_can_handle_state_change(dev, + C_CAN_ERROR_WARNING); + } + if ((priv->current_status & STATUS_EPASS) && + (!(priv->last_status & STATUS_EPASS))) { + netdev_dbg(dev, "entered error passive state\n"); + work_done += c_can_handle_state_change(dev, + C_CAN_ERROR_PASSIVE); + } + if ((priv->current_status & STATUS_BOFF) && + (!(priv->last_status & STATUS_BOFF))) { + netdev_dbg(dev, "entered bus off state\n"); + work_done += c_can_handle_state_change(dev, + C_CAN_BUS_OFF); + } + + /* handle bus recovery events */ + if ((!(priv->current_status & STATUS_BOFF)) && + (priv->last_status & STATUS_BOFF)) { + netdev_dbg(dev, "left bus off state\n"); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + if ((!(priv->current_status & STATUS_EPASS)) && + (priv->last_status & STATUS_EPASS)) { + netdev_dbg(dev, "left error passive state\n"); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + priv->last_status = priv->current_status; + + /* handle lec errors on the bus */ + lec_type = c_can_has_and_handle_berr(priv); + if (lec_type) + work_done += c_can_handle_bus_err(dev, lec_type); + } else if ((irqstatus >= C_CAN_MSG_OBJ_RX_FIRST) && + (irqstatus <= C_CAN_MSG_OBJ_RX_LAST)) { + /* handle events corresponding to receive message objects */ + work_done += c_can_do_rx_poll(dev, (quota - work_done)); + } else if ((irqstatus >= C_CAN_MSG_OBJ_TX_FIRST) && + (irqstatus <= C_CAN_MSG_OBJ_TX_LAST)) { + /* handle events corresponding to transmit message objects */ + c_can_do_tx(dev); + } + +end: + if (work_done < quota) { + napi_complete(napi); + /* enable all IRQs */ + c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS); + } + + return work_done; +} + +static irqreturn_t c_can_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct c_can_priv *priv = netdev_priv(dev); + + priv->irqstatus = priv->read_reg(priv, &priv->regs->interrupt); + if (!priv->irqstatus) + return IRQ_NONE; + + /* disable all interrupts and schedule the NAPI */ + c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); + napi_schedule(&priv->napi); + + return IRQ_HANDLED; +} + +static int c_can_open(struct net_device *dev) +{ + int err; + struct c_can_priv *priv = netdev_priv(dev); + + /* open the can device */ + err = open_candev(dev); + if (err) { + netdev_err(dev, "failed to open can device\n"); + return err; + } + + /* register interrupt handler */ + err = request_irq(dev->irq, &c_can_isr, IRQF_SHARED, dev->name, + dev); + if (err < 0) { + netdev_err(dev, "failed to request interrupt\n"); + goto exit_irq_fail; + } + + napi_enable(&priv->napi); + + /* start the c_can controller */ + c_can_start(dev); + + netif_start_queue(dev); + + return 0; + +exit_irq_fail: + close_candev(dev); + return err; +} + +static int c_can_close(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&priv->napi); + c_can_stop(dev); + free_irq(dev->irq, dev); + close_candev(dev); + + return 0; +} + +struct net_device *alloc_c_can_dev(void) +{ + struct net_device *dev; + struct c_can_priv *priv; + + dev = alloc_candev(sizeof(struct c_can_priv), C_CAN_MSG_OBJ_TX_NUM); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + netif_napi_add(dev, &priv->napi, c_can_poll, C_CAN_NAPI_WEIGHT); + + priv->dev = dev; + priv->can.bittiming_const = &c_can_bittiming_const; + priv->can.do_set_mode = c_can_set_mode; + priv->can.do_get_berr_counter = c_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING; + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_c_can_dev); + +void free_c_can_dev(struct net_device *dev) +{ + free_candev(dev); +} +EXPORT_SYMBOL_GPL(free_c_can_dev); + +static const struct net_device_ops c_can_netdev_ops = { + .ndo_open = c_can_open, + .ndo_stop = c_can_close, + .ndo_start_xmit = c_can_start_xmit, +}; + +int register_c_can_dev(struct net_device *dev) +{ + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &c_can_netdev_ops; + + return register_candev(dev); +} +EXPORT_SYMBOL_GPL(register_c_can_dev); + +void unregister_c_can_dev(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + /* disable all interrupts */ + c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); + + unregister_candev(dev); +} +EXPORT_SYMBOL_GPL(unregister_c_can_dev); + +MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for Bosch C_CAN controller"); diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h new file mode 100644 index 00000000..5f32d34a --- /dev/null +++ b/drivers/net/can/c_can/c_can.h @@ -0,0 +1,87 @@ +/* + * CAN bus driver for Bosch C_CAN controller + * + * Copyright (C) 2010 ST Microelectronics + * Bhupesh Sharma <bhupesh.sharma@st.com> + * + * Borrowed heavily from the C_CAN driver originally written by: + * Copyright (C) 2007 + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de> + * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch> + * + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B. + * Bosch C_CAN user manual can be obtained from: + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/ + * users_manual_c_can.pdf + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef C_CAN_H +#define C_CAN_H + +/* c_can IF registers */ +struct c_can_if_regs { + u16 com_req; + u16 com_mask; + u16 mask1; + u16 mask2; + u16 arb1; + u16 arb2; + u16 msg_cntrl; + u16 data[4]; + u16 _reserved[13]; +}; + +/* c_can hardware registers */ +struct c_can_regs { + u16 control; + u16 status; + u16 err_cnt; + u16 btr; + u16 interrupt; + u16 test; + u16 brp_ext; + u16 _reserved1; + struct c_can_if_regs ifregs[2]; /* [0] = IF1 and [1] = IF2 */ + u16 _reserved2[8]; + u16 txrqst1; + u16 txrqst2; + u16 _reserved3[6]; + u16 newdat1; + u16 newdat2; + u16 _reserved4[6]; + u16 intpnd1; + u16 intpnd2; + u16 _reserved5[6]; + u16 msgval1; + u16 msgval2; + u16 _reserved6[6]; +}; + +/* c_can private data structure */ +struct c_can_priv { + struct can_priv can; /* must be the first member */ + struct napi_struct napi; + struct net_device *dev; + int tx_object; + int current_status; + int last_status; + u16 (*read_reg) (struct c_can_priv *priv, void *reg); + void (*write_reg) (struct c_can_priv *priv, void *reg, u16 val); + struct c_can_regs __iomem *regs; + unsigned long irq_flags; /* for request_irq() */ + unsigned int tx_next; + unsigned int tx_echo; + void *priv; /* for board-specific data */ + u16 irqstatus; +}; + +struct net_device *alloc_c_can_dev(void); +void free_c_can_dev(struct net_device *dev); +int register_c_can_dev(struct net_device *dev); +void unregister_c_can_dev(struct net_device *dev); + +#endif /* C_CAN_H */ diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c new file mode 100644 index 00000000..5e1a5ff6 --- /dev/null +++ b/drivers/net/can/c_can/c_can_platform.c @@ -0,0 +1,204 @@ +/* + * Platform CAN bus driver for Bosch C_CAN controller + * + * Copyright (C) 2010 ST Microelectronics + * Bhupesh Sharma <bhupesh.sharma@st.com> + * + * Borrowed heavily from the C_CAN driver originally written by: + * Copyright (C) 2007 + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de> + * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch> + * + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B. + * Bosch C_CAN user manual can be obtained from: + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/ + * users_manual_c_can.pdf + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <linux/can/dev.h> + +#include "c_can.h" + +/* + * 16-bit c_can registers can be arranged differently in the memory + * architecture of different implementations. For example: 16-bit + * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. + * Handle the same by providing a common read/write interface. + */ +static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv, + void *reg) +{ + return readw(reg); +} + +static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv, + void *reg, u16 val) +{ + writew(val, reg); +} + +static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv, + void *reg) +{ + return readw(reg + (long)reg - (long)priv->regs); +} + +static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv, + void *reg, u16 val) +{ + writew(val, reg + (long)reg - (long)priv->regs); +} + +static int __devinit c_can_plat_probe(struct platform_device *pdev) +{ + int ret; + void __iomem *addr; + struct net_device *dev; + struct c_can_priv *priv; + struct resource *mem; + int irq; +#ifdef CONFIG_HAVE_CLK + struct clk *clk; + + /* get the appropriate clk */ + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + ret = -ENODEV; + goto exit; + } +#endif + + /* get the platform data */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!mem || irq <= 0) { + ret = -ENODEV; + goto exit_free_clk; + } + + if (!request_mem_region(mem->start, resource_size(mem), + KBUILD_MODNAME)) { + dev_err(&pdev->dev, "resource unavailable\n"); + ret = -ENODEV; + goto exit_free_clk; + } + + addr = ioremap(mem->start, resource_size(mem)); + if (!addr) { + dev_err(&pdev->dev, "failed to map can port\n"); + ret = -ENOMEM; + goto exit_release_mem; + } + + /* allocate the c_can device */ + dev = alloc_c_can_dev(); + if (!dev) { + ret = -ENOMEM; + goto exit_iounmap; + } + + priv = netdev_priv(dev); + + dev->irq = irq; + priv->regs = addr; +#ifdef CONFIG_HAVE_CLK + priv->can.clock.freq = clk_get_rate(clk); + priv->priv = clk; +#endif + + switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) { + case IORESOURCE_MEM_32BIT: + priv->read_reg = c_can_plat_read_reg_aligned_to_32bit; + priv->write_reg = c_can_plat_write_reg_aligned_to_32bit; + break; + case IORESOURCE_MEM_16BIT: + default: + priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; + priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + break; + } + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + ret = register_c_can_dev(dev); + if (ret) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + KBUILD_MODNAME, ret); + goto exit_free_device; + } + + dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", + KBUILD_MODNAME, priv->regs, dev->irq); + return 0; + +exit_free_device: + platform_set_drvdata(pdev, NULL); + free_c_can_dev(dev); +exit_iounmap: + iounmap(addr); +exit_release_mem: + release_mem_region(mem->start, resource_size(mem)); +exit_free_clk: +#ifdef CONFIG_HAVE_CLK + clk_put(clk); +exit: +#endif + dev_err(&pdev->dev, "probe failed\n"); + + return ret; +} + +static int __devexit c_can_plat_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct c_can_priv *priv = netdev_priv(dev); + struct resource *mem; + + unregister_c_can_dev(dev); + platform_set_drvdata(pdev, NULL); + + free_c_can_dev(dev); + iounmap(priv->regs); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + +#ifdef CONFIG_HAVE_CLK + clk_put(priv->priv); +#endif + + return 0; +} + +static struct platform_driver c_can_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .probe = c_can_plat_probe, + .remove = __devexit_p(c_can_plat_remove), +}; + +module_platform_driver(c_can_plat_driver); + +MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Platform CAN bus driver for Bosch C_CAN controller"); diff --git a/drivers/net/can/cc770/Kconfig b/drivers/net/can/cc770/Kconfig new file mode 100644 index 00000000..22c07a8c --- /dev/null +++ b/drivers/net/can/cc770/Kconfig @@ -0,0 +1,21 @@ +menuconfig CAN_CC770 + tristate "Bosch CC770 and Intel AN82527 devices" + depends on CAN_DEV && HAS_IOMEM + +if CAN_CC770 + +config CAN_CC770_ISA + tristate "ISA Bus based legacy CC770 driver" + ---help--- + This driver adds legacy support for CC770 and AN82527 chips + connected to the ISA bus using I/O port, memory mapped or + indirect access. + +config CAN_CC770_PLATFORM + tristate "Generic Platform Bus based CC770 driver" + ---help--- + This driver adds support for the CC770 and AN82527 chips + connected to the "platform bus" (Linux abstraction for directly + to the processor attached devices). + +endif diff --git a/drivers/net/can/cc770/Makefile b/drivers/net/can/cc770/Makefile new file mode 100644 index 00000000..9fb8321b --- /dev/null +++ b/drivers/net/can/cc770/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Bosch CC770 CAN controller drivers. +# + +obj-$(CONFIG_CAN_CC770) += cc770.o +obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o +obj-$(CONFIG_CAN_CC770_PLATFORM) += cc770_platform.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c new file mode 100644 index 00000000..d42a6a73 --- /dev/null +++ b/drivers/net/can/cc770/cc770.c @@ -0,0 +1,882 @@ +/* + * Core driver for the CC770 and AN82527 CAN controllers + * + * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#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/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/platform/cc770.h> + +#include "cc770.h" + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver"); + +/* + * The CC770 is a CAN controller from Bosch, which is 100% compatible + * with the AN82527 from Intel, but with "bugs" being fixed and some + * additional functionality, mainly: + * + * 1. RX and TX error counters are readable. + * 2. Support of silent (listen-only) mode. + * 3. Message object 15 can receive all types of frames, also RTR and EFF. + * + * Details are available from Bosch's "CC770_Product_Info_2007-01.pdf", + * which explains in detail the compatibility between the CC770 and the + * 82527. This driver use the additional functionality 3. on real CC770 + * devices. Unfortunately, the CC770 does still not store the message + * identifier of received remote transmission request frames and + * therefore it's set to 0. + * + * The message objects 1..14 can be used for TX and RX while the message + * objects 15 is optimized for RX. It has a shadow register for reliable + * data receiption under heavy bus load. Therefore it makes sense to use + * this message object for the needed use case. The frame type (EFF/SFF) + * for the message object 15 can be defined via kernel module parameter + * "msgobj15_eff". If not equal 0, it will receive 29-bit EFF frames, + * otherwise 11 bit SFF messages. + */ +static int msgobj15_eff; +module_param(msgobj15_eff, int, S_IRUGO); +MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 " + "(default: 11-bit standard frames)"); + +static int i82527_compat; +module_param(i82527_compat, int, S_IRUGO); +MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode " + "without using additional functions"); + +/* + * This driver uses the last 5 message objects 11..15. The definitions + * and structure below allows to configure and assign them to the real + * message object. + */ +static unsigned char cc770_obj_flags[CC770_OBJ_MAX] = { + [CC770_OBJ_RX0] = CC770_OBJ_FLAG_RX, + [CC770_OBJ_RX1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_EFF, + [CC770_OBJ_RX_RTR0] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR, + [CC770_OBJ_RX_RTR1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR | + CC770_OBJ_FLAG_EFF, + [CC770_OBJ_TX] = 0, +}; + +static struct can_bittiming_const cc770_bittiming_const = { + .name = KBUILD_MODNAME, + .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 inline int intid2obj(unsigned int intid) +{ + if (intid == 2) + return 0; + else + return MSGOBJ_LAST + 2 - intid; +} + +static void enable_all_objs(const struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + u8 msgcfg; + unsigned char obj_flags; + unsigned int o, mo; + + for (o = 0; o < ARRAY_SIZE(priv->obj_flags); o++) { + obj_flags = priv->obj_flags[o]; + mo = obj2msgobj(o); + + if (obj_flags & CC770_OBJ_FLAG_RX) { + /* + * We don't need extra objects for RTR and EFF if + * the additional CC770 functions are enabled. + */ + if (priv->control_normal_mode & CTRL_EAF) { + if (o > 0) + continue; + netdev_dbg(dev, "Message object %d for " + "RX data, RTR, SFF and EFF\n", mo); + } else { + netdev_dbg(dev, + "Message object %d for RX %s %s\n", + mo, obj_flags & CC770_OBJ_FLAG_RTR ? + "RTR" : "data", + obj_flags & CC770_OBJ_FLAG_EFF ? + "EFF" : "SFF"); + } + + if (obj_flags & CC770_OBJ_FLAG_EFF) + msgcfg = MSGCFG_XTD; + else + msgcfg = 0; + if (obj_flags & CC770_OBJ_FLAG_RTR) + msgcfg |= MSGCFG_DIR; + + cc770_write_reg(priv, msgobj[mo].config, msgcfg); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_RES | + RXIE_SET | INTPND_RES); + + if (obj_flags & CC770_OBJ_FLAG_RTR) + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | CPUUPD_SET | + TXRQST_RES | RMTPND_RES); + else + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_RES | RMTPND_RES); + } else { + netdev_dbg(dev, "Message object %d for " + "TX data, RTR, SFF and EFF\n", mo); + + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_RES | + CPUUPD_RES | NEWDAT_RES); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | + RXIE_RES | INTPND_RES); + } + } +} + +static void disable_all_objs(const struct cc770_priv *priv) +{ + int o, mo; + + for (o = 0; o < ARRAY_SIZE(priv->obj_flags); o++) { + mo = obj2msgobj(o); + + if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX) { + if (o > 0 && priv->control_normal_mode & CTRL_EAF) + continue; + + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_RES | RMTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | + RXIE_RES | INTPND_RES); + } else { + /* Clear message object for send */ + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_RES | + CPUUPD_RES | NEWDAT_RES); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | + RXIE_RES | INTPND_RES); + } + } +} + +static void set_reset_mode(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + + /* Enable configuration and puts chip in bus-off, disable interrupts */ + cc770_write_reg(priv, control, CTRL_CCE | CTRL_INI); + + priv->can.state = CAN_STATE_STOPPED; + + /* Clear interrupts */ + cc770_read_reg(priv, interrupt); + + /* Clear status register */ + cc770_write_reg(priv, status, 0); + + /* Disable all used message objects */ + disable_all_objs(priv); +} + +static void set_normal_mode(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + + /* Clear interrupts */ + cc770_read_reg(priv, interrupt); + + /* Clear status register and pre-set last error code */ + cc770_write_reg(priv, status, STAT_LEC_MASK); + + /* Enable all used message objects*/ + enable_all_objs(dev); + + /* + * Clear bus-off, interrupts only for errors, + * not for status change + */ + cc770_write_reg(priv, control, priv->control_normal_mode); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static void chipset_init(struct cc770_priv *priv) +{ + int mo, id, data; + + /* Enable configuration and put chip in bus-off, disable interrupts */ + cc770_write_reg(priv, control, (CTRL_CCE | CTRL_INI)); + + /* Set CLKOUT divider and slew rates */ + cc770_write_reg(priv, clkout, priv->clkout); + + /* Configure CPU interface / CLKOUT enable */ + cc770_write_reg(priv, cpu_interface, priv->cpu_interface); + + /* Set bus configuration */ + cc770_write_reg(priv, bus_config, priv->bus_config); + + /* Clear interrupts */ + cc770_read_reg(priv, interrupt); + + /* Clear status register */ + cc770_write_reg(priv, status, 0); + + /* Clear and invalidate message objects */ + for (mo = MSGOBJ_FIRST; mo <= MSGOBJ_LAST; mo++) { + cc770_write_reg(priv, msgobj[mo].ctrl0, + INTPND_UNC | RXIE_RES | + TXIE_RES | MSGVAL_RES); + cc770_write_reg(priv, msgobj[mo].ctrl0, + INTPND_RES | RXIE_RES | + TXIE_RES | MSGVAL_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_RES | RMTPND_RES); + for (data = 0; data < 8; data++) + cc770_write_reg(priv, msgobj[mo].data[data], 0); + for (id = 0; id < 4; id++) + cc770_write_reg(priv, msgobj[mo].id[id], 0); + cc770_write_reg(priv, msgobj[mo].config, 0); + } + + /* Set all global ID masks to "don't care" */ + cc770_write_reg(priv, global_mask_std[0], 0); + cc770_write_reg(priv, global_mask_std[1], 0); + cc770_write_reg(priv, global_mask_ext[0], 0); + cc770_write_reg(priv, global_mask_ext[1], 0); + cc770_write_reg(priv, global_mask_ext[2], 0); + cc770_write_reg(priv, global_mask_ext[3], 0); + +} + +static int cc770_probe_chip(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + + /* Enable configuration, put chip in bus-off, disable ints */ + cc770_write_reg(priv, control, CTRL_CCE | CTRL_EAF | CTRL_INI); + /* Configure cpu interface / CLKOUT disable */ + cc770_write_reg(priv, cpu_interface, priv->cpu_interface); + + /* + * Check if hardware reset is still inactive or maybe there + * is no chip in this address space + */ + if (cc770_read_reg(priv, cpu_interface) & CPUIF_RST) { + netdev_info(dev, "probing @0x%p failed (reset)\n", + priv->reg_base); + return -ENODEV; + } + + /* Write and read back test pattern (some arbitrary values) */ + cc770_write_reg(priv, msgobj[1].data[1], 0x25); + cc770_write_reg(priv, msgobj[2].data[3], 0x52); + cc770_write_reg(priv, msgobj[10].data[6], 0xc3); + if ((cc770_read_reg(priv, msgobj[1].data[1]) != 0x25) || + (cc770_read_reg(priv, msgobj[2].data[3]) != 0x52) || + (cc770_read_reg(priv, msgobj[10].data[6]) != 0xc3)) { + netdev_info(dev, "probing @0x%p failed (pattern)\n", + priv->reg_base); + return -ENODEV; + } + + /* Check if this chip is a CC770 supporting additional functions */ + if (cc770_read_reg(priv, control) & CTRL_EAF) + priv->control_normal_mode |= CTRL_EAF; + + return 0; +} + +static void cc770_start(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + + /* leave reset mode */ + if (priv->can.state != CAN_STATE_STOPPED) + set_reset_mode(dev); + + /* leave reset mode */ + set_normal_mode(dev); +} + +static int cc770_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + cc770_start(dev); + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int cc770_set_bittiming(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + cc770_write_reg(priv, bit_timing_0, btr0); + cc770_write_reg(priv, bit_timing_1, btr1); + + return 0; +} + +static int cc770_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct cc770_priv *priv = netdev_priv(dev); + + bec->txerr = cc770_read_reg(priv, tx_error_counter); + bec->rxerr = cc770_read_reg(priv, rx_error_counter); + + return 0; +} + +static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + unsigned int mo = obj2msgobj(CC770_OBJ_TX); + u8 dlc, rtr; + u32 id; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + if ((cc770_read_reg(priv, + msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) { + netdev_err(dev, "TX register is still occupied!\n"); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(dev); + + dlc = cf->can_dlc; + id = cf->can_id; + if (cf->can_id & CAN_RTR_FLAG) + rtr = 0; + else + rtr = MSGCFG_DIR; + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES); + if (id & CAN_EFF_FLAG) { + id &= CAN_EFF_MASK; + cc770_write_reg(priv, msgobj[mo].config, + (dlc << 4) | rtr | MSGCFG_XTD); + cc770_write_reg(priv, msgobj[mo].id[3], id << 3); + cc770_write_reg(priv, msgobj[mo].id[2], id >> 5); + cc770_write_reg(priv, msgobj[mo].id[1], id >> 13); + cc770_write_reg(priv, msgobj[mo].id[0], id >> 21); + } else { + id &= CAN_SFF_MASK; + cc770_write_reg(priv, msgobj[mo].config, (dlc << 4) | rtr); + cc770_write_reg(priv, msgobj[mo].id[0], id >> 3); + cc770_write_reg(priv, msgobj[mo].id[1], id << 5); + } + + for (i = 0; i < dlc; i++) + cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); + + /* Store echo skb before starting the transfer */ + can_put_echo_skb(skb, dev, 0); + + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); + + stats->tx_bytes += dlc; + + + /* + * HM: We had some cases of repeated IRQs so make sure the + * INT is acknowledged I know it's already further up, but + * doing again fixed the issue + */ + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); + + return NETDEV_TX_OK; +} + +static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 config; + u32 id; + int i; + + skb = alloc_can_skb(dev, &cf); + if (!skb) + return; + + config = cc770_read_reg(priv, msgobj[mo].config); + + if (ctrl1 & RMTPND_SET) { + /* + * Unfortunately, the chip does not store the real message + * identifier of the received remote transmission request + * frame. Therefore we set it to 0. + */ + cf->can_id = CAN_RTR_FLAG; + if (config & MSGCFG_XTD) + cf->can_id |= CAN_EFF_FLAG; + cf->can_dlc = 0; + } else { + if (config & MSGCFG_XTD) { + id = cc770_read_reg(priv, msgobj[mo].id[3]); + id |= cc770_read_reg(priv, msgobj[mo].id[2]) << 8; + id |= cc770_read_reg(priv, msgobj[mo].id[1]) << 16; + id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 24; + id >>= 3; + id |= CAN_EFF_FLAG; + } else { + id = cc770_read_reg(priv, msgobj[mo].id[1]); + id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 8; + id >>= 5; + } + + cf->can_id = id; + cf->can_dlc = get_can_dlc((config & 0xf0) >> 4); + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]); + } + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static int cc770_err(struct net_device *dev, u8 status) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 lec; + + netdev_dbg(dev, "status interrupt (%#x)\n", status); + + skb = alloc_can_err_skb(dev, &cf); + if (!skb) + return -ENOMEM; + + /* Use extended functions of the CC770 */ + if (priv->control_normal_mode & CTRL_EAF) { + cf->data[6] = cc770_read_reg(priv, tx_error_counter); + cf->data[7] = cc770_read_reg(priv, rx_error_counter); + } + + if (status & STAT_BOFF) { + /* Disable interrupts */ + cc770_write_reg(priv, control, CTRL_INI); + cf->can_id |= CAN_ERR_BUSOFF; + priv->can.state = CAN_STATE_BUS_OFF; + can_bus_off(dev); + } else if (status & STAT_WARN) { + cf->can_id |= CAN_ERR_CRTL; + /* Only the CC770 does show error passive */ + if (cf->data[7] > 127) { + cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE | + CAN_ERR_CRTL_TX_PASSIVE; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + } else { + cf->data[1] = CAN_ERR_CRTL_RX_WARNING | + CAN_ERR_CRTL_TX_WARNING; + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + } + } else { + /* Back to error avtive */ + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + lec = status & STAT_LEC_MASK; + if (lec < 7 && lec > 0) { + if (lec == STAT_LEC_ACK) { + cf->can_id |= CAN_ERR_ACK; + } else { + cf->can_id |= CAN_ERR_PROT; + switch (lec) { + case STAT_LEC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case STAT_LEC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case STAT_LEC_BIT1: + cf->data[2] |= CAN_ERR_PROT_BIT1; + break; + case STAT_LEC_BIT0: + cf->data[2] |= CAN_ERR_PROT_BIT0; + break; + case STAT_LEC_CRC: + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + break; + } + } + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +static int cc770_status_interrupt(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + u8 status; + + status = cc770_read_reg(priv, status); + /* Reset the status register including RXOK and TXOK */ + cc770_write_reg(priv, status, STAT_LEC_MASK); + + if (status & (STAT_WARN | STAT_BOFF) || + (status & STAT_LEC_MASK) != STAT_LEC_MASK) { + cc770_err(dev, status); + return status & STAT_BOFF; + } + + return 0; +} + +static void cc770_rx_interrupt(struct net_device *dev, unsigned int o) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned int mo = obj2msgobj(o); + u8 ctrl1; + int n = CC770_MAX_MSG; + + while (n--) { + ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); + + if (!(ctrl1 & NEWDAT_SET)) { + /* Check for RTR if additional functions are enabled */ + if (priv->control_normal_mode & CTRL_EAF) { + if (!(cc770_read_reg(priv, msgobj[mo].ctrl0) & + INTPND_SET)) + break; + } else { + break; + } + } + + if (ctrl1 & MSGLST_SET) { + stats->rx_over_errors++; + stats->rx_errors++; + } + if (mo < MSGOBJ_LAST) + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_UNC | RMTPND_UNC); + cc770_rx(dev, mo, ctrl1); + + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_RES | + RXIE_SET | INTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_RES | RMTPND_RES); + } +} + +static void cc770_rtr_interrupt(struct net_device *dev, unsigned int o) +{ + struct cc770_priv *priv = netdev_priv(dev); + unsigned int mo = obj2msgobj(o); + u8 ctrl0, ctrl1; + int n = CC770_MAX_MSG; + + while (n--) { + ctrl0 = cc770_read_reg(priv, msgobj[mo].ctrl0); + if (!(ctrl0 & INTPND_SET)) + break; + + ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); + cc770_rx(dev, mo, ctrl1); + + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_RES | + RXIE_SET | INTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | CPUUPD_SET | + TXRQST_RES | RMTPND_RES); + } +} + +static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned int mo = obj2msgobj(o); + + /* Nothing more to send, switch off interrupts */ + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); + /* + * We had some cases of repeated IRQ so make sure the + * INT is acknowledged + */ + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); + + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); +} + +irqreturn_t cc770_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct cc770_priv *priv = netdev_priv(dev); + u8 intid; + int o, n = 0; + + /* Shared interrupts and IRQ off? */ + if (priv->can.state == CAN_STATE_STOPPED) + return IRQ_NONE; + + if (priv->pre_irq) + priv->pre_irq(priv); + + while (n < CC770_MAX_IRQ) { + /* Read the highest pending interrupt request */ + intid = cc770_read_reg(priv, interrupt); + if (!intid) + break; + n++; + + if (intid == 1) { + /* Exit in case of bus-off */ + if (cc770_status_interrupt(dev)) + break; + } else { + o = intid2obj(intid); + + if (o >= CC770_OBJ_MAX) { + netdev_err(dev, "Unexpected interrupt id %d\n", + intid); + continue; + } + + if (priv->obj_flags[o] & CC770_OBJ_FLAG_RTR) + cc770_rtr_interrupt(dev, o); + else if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX) + cc770_rx_interrupt(dev, o); + else + cc770_tx_interrupt(dev, o); + } + } + + if (priv->post_irq) + priv->post_irq(priv); + + if (n >= CC770_MAX_IRQ) + netdev_dbg(dev, "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} + +static int cc770_open(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + int err; + + /* set chip into reset mode */ + set_reset_mode(dev); + + /* common open */ + err = open_candev(dev); + if (err) + return err; + + err = request_irq(dev->irq, &cc770_interrupt, priv->irq_flags, + dev->name, dev); + if (err) { + close_candev(dev); + return -EAGAIN; + } + + /* init and start chip */ + cc770_start(dev); + + netif_start_queue(dev); + + return 0; +} + +static int cc770_close(struct net_device *dev) +{ + netif_stop_queue(dev); + set_reset_mode(dev); + + free_irq(dev->irq, dev); + close_candev(dev); + + return 0; +} + +struct net_device *alloc_cc770dev(int sizeof_priv) +{ + struct net_device *dev; + struct cc770_priv *priv; + + dev = alloc_candev(sizeof(struct cc770_priv) + sizeof_priv, + CC770_ECHO_SKB_MAX); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + + priv->dev = dev; + priv->can.bittiming_const = &cc770_bittiming_const; + priv->can.do_set_bittiming = cc770_set_bittiming; + priv->can.do_set_mode = cc770_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags)); + + if (sizeof_priv) + priv->priv = (void *)priv + sizeof(struct cc770_priv); + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_cc770dev); + +void free_cc770dev(struct net_device *dev) +{ + free_candev(dev); +} +EXPORT_SYMBOL_GPL(free_cc770dev); + +static const struct net_device_ops cc770_netdev_ops = { + .ndo_open = cc770_open, + .ndo_stop = cc770_close, + .ndo_start_xmit = cc770_start_xmit, +}; + +int register_cc770dev(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + int err; + + err = cc770_probe_chip(dev); + if (err) + return err; + + dev->netdev_ops = &cc770_netdev_ops; + + dev->flags |= IFF_ECHO; /* we support local echo */ + + /* Should we use additional functions? */ + if (!i82527_compat && priv->control_normal_mode & CTRL_EAF) { + priv->can.do_get_berr_counter = cc770_get_berr_counter; + priv->control_normal_mode = CTRL_IE | CTRL_EAF | CTRL_EIE; + netdev_dbg(dev, "i82527 mode with additional functions\n"); + } else { + priv->control_normal_mode = CTRL_IE | CTRL_EIE; + netdev_dbg(dev, "strict i82527 compatibility mode\n"); + } + + chipset_init(priv); + set_reset_mode(dev); + + return register_candev(dev); +} +EXPORT_SYMBOL_GPL(register_cc770dev); + +void unregister_cc770dev(struct net_device *dev) +{ + set_reset_mode(dev); + unregister_candev(dev); +} +EXPORT_SYMBOL_GPL(unregister_cc770dev); + +static __init int cc770_init(void) +{ + if (msgobj15_eff) { + cc770_obj_flags[CC770_OBJ_RX0] |= CC770_OBJ_FLAG_EFF; + cc770_obj_flags[CC770_OBJ_RX1] &= ~CC770_OBJ_FLAG_EFF; + } + + pr_info("CAN netdevice driver\n"); + + return 0; +} +module_init(cc770_init); + +static __exit void cc770_exit(void) +{ + pr_info("driver removed\n"); +} +module_exit(cc770_exit); diff --git a/drivers/net/can/cc770/cc770.h b/drivers/net/can/cc770/cc770.h new file mode 100644 index 00000000..a1739db9 --- /dev/null +++ b/drivers/net/can/cc770/cc770.h @@ -0,0 +1,203 @@ +/* + * Core driver for the CC770 and AN82527 CAN controllers + * + * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> + * + * 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. + */ + +#ifndef CC770_DEV_H +#define CC770_DEV_H + +#include <linux/can/dev.h> + +struct cc770_msgobj { + u8 ctrl0; + u8 ctrl1; + u8 id[4]; + u8 config; + u8 data[8]; + u8 dontuse; /* padding */ +} __packed; + +struct cc770_regs { + union { + struct cc770_msgobj msgobj[16]; /* Message object 1..15 */ + struct { + u8 control; /* Control Register */ + u8 status; /* Status Register */ + u8 cpu_interface; /* CPU Interface Register */ + u8 dontuse1; + u8 high_speed_read[2]; /* High Speed Read */ + u8 global_mask_std[2]; /* Standard Global Mask */ + u8 global_mask_ext[4]; /* Extended Global Mask */ + u8 msg15_mask[4]; /* Message 15 Mask */ + u8 dontuse2[15]; + u8 clkout; /* Clock Out Register */ + u8 dontuse3[15]; + u8 bus_config; /* Bus Configuration Register */ + u8 dontuse4[15]; + u8 bit_timing_0; /* Bit Timing Register byte 0 */ + u8 dontuse5[15]; + u8 bit_timing_1; /* Bit Timing Register byte 1 */ + u8 dontuse6[15]; + u8 interrupt; /* Interrupt Register */ + u8 dontuse7[15]; + u8 rx_error_counter; /* Receive Error Counter */ + u8 dontuse8[15]; + u8 tx_error_counter; /* Transmit Error Counter */ + u8 dontuse9[31]; + u8 p1_conf; + u8 dontuse10[15]; + u8 p2_conf; + u8 dontuse11[15]; + u8 p1_in; + u8 dontuse12[15]; + u8 p2_in; + u8 dontuse13[15]; + u8 p1_out; + u8 dontuse14[15]; + u8 p2_out; + u8 dontuse15[15]; + u8 serial_reset_addr; + }; + }; +} __packed; + +/* Control Register (0x00) */ +#define CTRL_INI 0x01 /* Initialization */ +#define CTRL_IE 0x02 /* Interrupt Enable */ +#define CTRL_SIE 0x04 /* Status Interrupt Enable */ +#define CTRL_EIE 0x08 /* Error Interrupt Enable */ +#define CTRL_EAF 0x20 /* Enable additional functions */ +#define CTRL_CCE 0x40 /* Change Configuration Enable */ + +/* Status Register (0x01) */ +#define STAT_LEC_STUFF 0x01 /* Stuff error */ +#define STAT_LEC_FORM 0x02 /* Form error */ +#define STAT_LEC_ACK 0x03 /* Acknowledgement error */ +#define STAT_LEC_BIT1 0x04 /* Bit1 error */ +#define STAT_LEC_BIT0 0x05 /* Bit0 error */ +#define STAT_LEC_CRC 0x06 /* CRC error */ +#define STAT_LEC_MASK 0x07 /* Last Error Code mask */ +#define STAT_TXOK 0x08 /* Transmit Message Successfully */ +#define STAT_RXOK 0x10 /* Receive Message Successfully */ +#define STAT_WAKE 0x20 /* Wake Up Status */ +#define STAT_WARN 0x40 /* Warning Status */ +#define STAT_BOFF 0x80 /* Bus Off Status */ + +/* + * CPU Interface Register (0x02) + * Clock Out Register (0x1f) + * Bus Configuration Register (0x2f) + * + * see include/linux/can/platform/cc770.h + */ + +/* Message Control Register 0 (Base Address + 0x0) */ +#define INTPND_RES 0x01 /* No Interrupt pending */ +#define INTPND_SET 0x02 /* Interrupt pending */ +#define INTPND_UNC 0x03 +#define RXIE_RES 0x04 /* Receive Interrupt Disable */ +#define RXIE_SET 0x08 /* Receive Interrupt Enable */ +#define RXIE_UNC 0x0c +#define TXIE_RES 0x10 /* Transmit Interrupt Disable */ +#define TXIE_SET 0x20 /* Transmit Interrupt Enable */ +#define TXIE_UNC 0x30 +#define MSGVAL_RES 0x40 /* Message Invalid */ +#define MSGVAL_SET 0x80 /* Message Valid */ +#define MSGVAL_UNC 0xc0 + +/* Message Control Register 1 (Base Address + 0x01) */ +#define NEWDAT_RES 0x01 /* No New Data */ +#define NEWDAT_SET 0x02 /* New Data */ +#define NEWDAT_UNC 0x03 +#define MSGLST_RES 0x04 /* No Message Lost */ +#define MSGLST_SET 0x08 /* Message Lost */ +#define MSGLST_UNC 0x0c +#define CPUUPD_RES 0x04 /* No CPU Updating */ +#define CPUUPD_SET 0x08 /* CPU Updating */ +#define CPUUPD_UNC 0x0c +#define TXRQST_RES 0x10 /* No Transmission Request */ +#define TXRQST_SET 0x20 /* Transmission Request */ +#define TXRQST_UNC 0x30 +#define RMTPND_RES 0x40 /* No Remote Request Pending */ +#define RMTPND_SET 0x80 /* Remote Request Pending */ +#define RMTPND_UNC 0xc0 + +/* Message Configuration Register (Base Address + 0x06) */ +#define MSGCFG_XTD 0x04 /* Extended Identifier */ +#define MSGCFG_DIR 0x08 /* Direction is Transmit */ + +#define MSGOBJ_FIRST 1 +#define MSGOBJ_LAST 15 + +#define CC770_IO_SIZE 0x100 +#define CC770_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ +#define CC770_MAX_MSG 4 /* max. number of messages handled in ISR */ + +#define CC770_ECHO_SKB_MAX 1 + +#define cc770_read_reg(priv, member) \ + priv->read_reg(priv, offsetof(struct cc770_regs, member)) + +#define cc770_write_reg(priv, member, value) \ + priv->write_reg(priv, offsetof(struct cc770_regs, member), value) + +/* + * Message objects and flags used by this driver + */ +#define CC770_OBJ_FLAG_RX 0x01 +#define CC770_OBJ_FLAG_RTR 0x02 +#define CC770_OBJ_FLAG_EFF 0x04 + +enum { + CC770_OBJ_RX0 = 0, /* for receiving normal messages */ + CC770_OBJ_RX1, /* for receiving normal messages */ + CC770_OBJ_RX_RTR0, /* for receiving remote transmission requests */ + CC770_OBJ_RX_RTR1, /* for receiving remote transmission requests */ + CC770_OBJ_TX, /* for sending messages */ + CC770_OBJ_MAX +}; + +#define obj2msgobj(o) (MSGOBJ_LAST - (o)) /* message object 11..15 */ + +/* + * CC770 private data structure + */ +struct cc770_priv { + struct can_priv can; /* must be the first member */ + struct sk_buff *echo_skb; + + /* the lower-layer is responsible for appropriate locking */ + u8 (*read_reg)(const struct cc770_priv *priv, int reg); + void (*write_reg)(const struct cc770_priv *priv, int reg, u8 val); + void (*pre_irq)(const struct cc770_priv *priv); + void (*post_irq)(const struct cc770_priv *priv); + + void *priv; /* for board-specific data */ + struct net_device *dev; + + void __iomem *reg_base; /* ioremap'ed address to registers */ + unsigned long irq_flags; /* for request_irq() */ + + unsigned char obj_flags[CC770_OBJ_MAX]; + u8 control_normal_mode; /* Control register for normal mode */ + u8 cpu_interface; /* CPU interface register */ + u8 clkout; /* Clock out register */ + u8 bus_config; /* Bus conffiguration register */ +}; + +struct net_device *alloc_cc770dev(int sizeof_priv); +void free_cc770dev(struct net_device *dev); +int register_cc770dev(struct net_device *dev); +void unregister_cc770dev(struct net_device *dev); + +#endif /* CC770_DEV_H */ diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c new file mode 100644 index 00000000..9f3a25cc --- /dev/null +++ b/drivers/net/can/cc770/cc770_isa.c @@ -0,0 +1,381 @@ +/* + * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus + * + * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> + * + * 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. + */ + +/* + * Bosch CC770 and Intel AN82527 CAN controllers on the ISA or PC-104 bus. + * The I/O port or memory address and the IRQ number must be specified via + * module parameters: + * + * insmod cc770_isa.ko port=0x310,0x380 irq=7,11 + * + * for ISA devices using I/O ports or: + * + * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 + * + * for memory mapped ISA devices. + * + * Indirect access via address and data port is supported as well: + * + * insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,11 + * + * Furthermore, the following mode parameter can be defined: + * + * clk: External oscillator clock frequency (default=16000000 [16 MHz]) + * cir: CPU interface register (default=0x40 [DSC]) + * bcr: Bus configuration register (default=0x40 [CBY]) + * cor: Clockout register (default=0x00) + * + * Note: for clk, cir, bcr and cor, the first argument re-defines the + * default for all other devices, e.g.: + * + * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000 + * + * is equivalent to + * + * insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000 + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/platform/cc770.h> + +#include "cc770.h" + +#define MAXDEV 8 + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus"); +MODULE_LICENSE("GPL v2"); + +#define CLK_DEFAULT 16000000 /* 16 MHz */ +#define COR_DEFAULT 0x00 +#define BCR_DEFAULT BUSCFG_CBY + +static unsigned long port[MAXDEV]; +static unsigned long mem[MAXDEV]; +static int __devinitdata irq[MAXDEV]; +static int __devinitdata clk[MAXDEV]; +static u8 __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static u8 __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static u8 __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; + +module_param_array(port, ulong, NULL, S_IRUGO); +MODULE_PARM_DESC(port, "I/O port number"); + +module_param_array(mem, ulong, NULL, S_IRUGO); +MODULE_PARM_DESC(mem, "I/O memory address"); + +module_param_array(indirect, int, NULL, S_IRUGO); +MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); + +module_param_array(irq, int, NULL, S_IRUGO); +MODULE_PARM_DESC(irq, "IRQ number"); + +module_param_array(clk, int, NULL, S_IRUGO); +MODULE_PARM_DESC(clk, "External oscillator clock frequency " + "(default=16000000 [16 MHz])"); + +module_param_array(cir, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])"); + +module_param_array(cor, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(cor, "Clockout register (default=0x00)"); + +module_param_array(bcr, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])"); + +#define CC770_IOSIZE 0x20 +#define CC770_IOSIZE_INDIRECT 0x02 + +/* Spinlock for cc770_isa_port_write_reg_indirect + * and cc770_isa_port_read_reg_indirect + */ +static DEFINE_SPINLOCK(cc770_isa_port_lock); + +static struct platform_device *cc770_isa_devs[MAXDEV]; + +static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg) +{ + return readb(priv->reg_base + reg); +} + +static void cc770_isa_mem_write_reg(const struct cc770_priv *priv, + int reg, u8 val) +{ + writeb(val, priv->reg_base + reg); +} + +static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg) +{ + return inb((unsigned long)priv->reg_base + reg); +} + +static void cc770_isa_port_write_reg(const struct cc770_priv *priv, + int reg, u8 val) +{ + outb(val, (unsigned long)priv->reg_base + reg); +} + +static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv, + int reg) +{ + unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags; + u8 val; + + spin_lock_irqsave(&cc770_isa_port_lock, flags); + outb(reg, base); + val = inb(base + 1); + spin_unlock_irqrestore(&cc770_isa_port_lock, flags); + + return val; +} + +static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv, + int reg, u8 val) +{ + unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags; + + spin_lock_irqsave(&cc770_isa_port_lock, flags); + outb(reg, base); + outb(val, base + 1); + spin_unlock_irqrestore(&cc770_isa_port_lock, flags); +} + +static int __devinit cc770_isa_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct cc770_priv *priv; + void __iomem *base = NULL; + int iosize = CC770_IOSIZE; + int idx = pdev->id; + int err; + u32 clktmp; + + dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n", + idx, port[idx], mem[idx], irq[idx]); + if (mem[idx]) { + if (!request_mem_region(mem[idx], iosize, KBUILD_MODNAME)) { + err = -EBUSY; + goto exit; + } + base = ioremap_nocache(mem[idx], iosize); + if (!base) { + err = -ENOMEM; + goto exit_release; + } + } else { + if (indirect[idx] > 0 || + (indirect[idx] == -1 && indirect[0] > 0)) + iosize = CC770_IOSIZE_INDIRECT; + if (!request_region(port[idx], iosize, KBUILD_MODNAME)) { + err = -EBUSY; + goto exit; + } + } + + dev = alloc_cc770dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_unmap; + } + priv = netdev_priv(dev); + + dev->irq = irq[idx]; + priv->irq_flags = IRQF_SHARED; + if (mem[idx]) { + priv->reg_base = base; + dev->base_addr = mem[idx]; + priv->read_reg = cc770_isa_mem_read_reg; + priv->write_reg = cc770_isa_mem_write_reg; + } else { + priv->reg_base = (void __iomem *)port[idx]; + dev->base_addr = port[idx]; + + if (iosize == CC770_IOSIZE_INDIRECT) { + priv->read_reg = cc770_isa_port_read_reg_indirect; + priv->write_reg = cc770_isa_port_write_reg_indirect; + } else { + priv->read_reg = cc770_isa_port_read_reg; + priv->write_reg = cc770_isa_port_write_reg; + } + } + + if (clk[idx]) + clktmp = clk[idx]; + else if (clk[0]) + clktmp = clk[0]; + else + clktmp = CLK_DEFAULT; + priv->can.clock.freq = clktmp; + + if (cir[idx] != 0xff) { + priv->cpu_interface = cir[idx]; + } else if (cir[0] != 0xff) { + priv->cpu_interface = cir[0]; + } else { + /* The system clock may not exceed 10 MHz */ + if (clktmp > 10000000) { + priv->cpu_interface |= CPUIF_DSC; + clktmp /= 2; + } + /* The memory clock may not exceed 8 MHz */ + if (clktmp > 8000000) + priv->cpu_interface |= CPUIF_DMC; + } + + if (priv->cpu_interface & CPUIF_DSC) + priv->can.clock.freq /= 2; + + if (bcr[idx] != 0xff) + priv->bus_config = bcr[idx]; + else if (bcr[0] != 0xff) + priv->bus_config = bcr[0]; + else + priv->bus_config = BCR_DEFAULT; + + if (cor[idx] != 0xff) + priv->clkout = cor[idx]; + else if (cor[0] != 0xff) + priv->clkout = cor[0]; + else + priv->clkout = COR_DEFAULT; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_cc770dev(dev); + if (err) { + dev_err(&pdev->dev, + "couldn't register device (err=%d)\n", err); + goto exit_unmap; + } + + dev_info(&pdev->dev, "device registered (reg_base=0x%p, irq=%d)\n", + priv->reg_base, dev->irq); + return 0; + + exit_unmap: + if (mem[idx]) + iounmap(base); + exit_release: + if (mem[idx]) + release_mem_region(mem[idx], iosize); + else + release_region(port[idx], iosize); + exit: + return err; +} + +static int __devexit cc770_isa_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct cc770_priv *priv = netdev_priv(dev); + int idx = pdev->id; + + unregister_cc770dev(dev); + dev_set_drvdata(&pdev->dev, NULL); + + if (mem[idx]) { + iounmap(priv->reg_base); + release_mem_region(mem[idx], CC770_IOSIZE); + } else { + if (priv->read_reg == cc770_isa_port_read_reg_indirect) + release_region(port[idx], CC770_IOSIZE_INDIRECT); + else + release_region(port[idx], CC770_IOSIZE); + } + free_cc770dev(dev); + + return 0; +} + +static struct platform_driver cc770_isa_driver = { + .probe = cc770_isa_probe, + .remove = __devexit_p(cc770_isa_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init cc770_isa_init(void) +{ + int idx, err; + + for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) { + if ((port[idx] || mem[idx]) && irq[idx]) { + cc770_isa_devs[idx] = + platform_device_alloc(KBUILD_MODNAME, idx); + if (!cc770_isa_devs[idx]) { + err = -ENOMEM; + goto exit_free_devices; + } + err = platform_device_add(cc770_isa_devs[idx]); + if (err) { + platform_device_put(cc770_isa_devs[idx]); + goto exit_free_devices; + } + pr_debug("platform device %d: port=%#lx, mem=%#lx, " + "irq=%d\n", + idx, port[idx], mem[idx], irq[idx]); + } else if (idx == 0 || port[idx] || mem[idx]) { + pr_err("insufficient parameters supplied\n"); + err = -EINVAL; + goto exit_free_devices; + } + } + + err = platform_driver_register(&cc770_isa_driver); + if (err) + goto exit_free_devices; + + pr_info("driver for max. %d devices registered\n", MAXDEV); + + return 0; + +exit_free_devices: + while (--idx >= 0) { + if (cc770_isa_devs[idx]) + platform_device_unregister(cc770_isa_devs[idx]); + } + + return err; +} +module_init(cc770_isa_init); + +static void __exit cc770_isa_exit(void) +{ + int idx; + + platform_driver_unregister(&cc770_isa_driver); + for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) { + if (cc770_isa_devs[idx]) + platform_device_unregister(cc770_isa_devs[idx]); + } +} +module_exit(cc770_isa_exit); diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c new file mode 100644 index 00000000..53115eee --- /dev/null +++ b/drivers/net/can/cc770/cc770_platform.c @@ -0,0 +1,272 @@ +/* + * Driver for CC770 and AN82527 CAN controllers on the platform bus + * + * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> + * + * 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. + */ + +/* + * If platform data are used you should have similar definitions + * in your board-specific code: + * + * static struct cc770_platform_data myboard_cc770_pdata = { + * .osc_freq = 16000000, + * .cir = 0x41, + * .cor = 0x20, + * .bcr = 0x40, + * }; + * + * Please see include/linux/can/platform/cc770.h for description of + * above fields. + * + * If the device tree is used, you need a CAN node definition in your + * DTS file similar to: + * + * can@3,100 { + * compatible = "bosch,cc770"; + * reg = <3 0x100 0x80>; + * interrupts = <2 0>; + * interrupt-parent = <&mpic>; + * bosch,external-clock-frequency = <16000000>; + * }; + * + * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further + * information. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/platform/cc770.h> + +#include "cc770.h" + +#define DRV_NAME "cc770_platform" + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus"); +MODULE_LICENSE("GPL v2"); + +#define CC770_PLATFORM_CAN_CLOCK 16000000 + +static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg) +{ + return ioread8(priv->reg_base + reg); +} + +static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg, + u8 val) +{ + iowrite8(val, priv->reg_base + reg); +} + +static int __devinit cc770_get_of_node_data(struct platform_device *pdev, + struct cc770_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + const u32 *prop; + int prop_size; + u32 clkext; + + prop = of_get_property(np, "bosch,external-clock-frequency", + &prop_size); + if (prop && (prop_size == sizeof(u32))) + clkext = *prop; + else + clkext = CC770_PLATFORM_CAN_CLOCK; /* default */ + priv->can.clock.freq = clkext; + + /* The system clock may not exceed 10 MHz */ + if (priv->can.clock.freq > 10000000) { + priv->cpu_interface |= CPUIF_DSC; + priv->can.clock.freq /= 2; + } + + /* The memory clock may not exceed 8 MHz */ + if (priv->can.clock.freq > 8000000) + priv->cpu_interface |= CPUIF_DMC; + + if (of_get_property(np, "bosch,divide-memory-clock", NULL)) + priv->cpu_interface |= CPUIF_DMC; + if (of_get_property(np, "bosch,iso-low-speed-mux", NULL)) + priv->cpu_interface |= CPUIF_MUX; + + if (!of_get_property(np, "bosch,no-comperator-bypass", NULL)) + priv->bus_config |= BUSCFG_CBY; + if (of_get_property(np, "bosch,disconnect-rx0-input", NULL)) + priv->bus_config |= BUSCFG_DR0; + if (of_get_property(np, "bosch,disconnect-rx1-input", NULL)) + priv->bus_config |= BUSCFG_DR1; + if (of_get_property(np, "bosch,disconnect-tx1-output", NULL)) + priv->bus_config |= BUSCFG_DT1; + if (of_get_property(np, "bosch,polarity-dominant", NULL)) + priv->bus_config |= BUSCFG_POL; + + prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size); + if (prop && (prop_size == sizeof(u32)) && *prop > 0) { + u32 cdv = clkext / *prop; + int slew; + + if (cdv > 0 && cdv < 16) { + priv->cpu_interface |= CPUIF_CEN; + priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK; + + prop = of_get_property(np, "bosch,slew-rate", + &prop_size); + if (prop && (prop_size == sizeof(u32))) { + slew = *prop; + } else { + /* Determine default slew rate */ + slew = (CLKOUT_SL_MASK >> + CLKOUT_SL_SHIFT) - + ((cdv * clkext - 1) / 8000000); + if (slew < 0) + slew = 0; + } + priv->clkout |= (slew << CLKOUT_SL_SHIFT) & + CLKOUT_SL_MASK; + } else { + dev_dbg(&pdev->dev, "invalid clock-out-frequency\n"); + } + } + + return 0; +} + +static int __devinit cc770_get_platform_data(struct platform_device *pdev, + struct cc770_priv *priv) +{ + + struct cc770_platform_data *pdata = pdev->dev.platform_data; + + priv->can.clock.freq = pdata->osc_freq; + if (priv->cpu_interface | CPUIF_DSC) + priv->can.clock.freq /= 2; + priv->clkout = pdata->cor; + priv->bus_config = pdata->bcr; + priv->cpu_interface = pdata->cir; + + return 0; +} + +static int __devinit cc770_platform_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct cc770_priv *priv; + struct resource *mem; + resource_size_t mem_size; + void __iomem *base; + int err, irq; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!mem || irq <= 0) + return -ENODEV; + + mem_size = resource_size(mem); + if (!request_mem_region(mem->start, mem_size, pdev->name)) + return -EBUSY; + + base = ioremap(mem->start, mem_size); + if (!base) { + err = -ENOMEM; + goto exit_release_mem; + } + + dev = alloc_cc770dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_unmap_mem; + } + + dev->irq = irq; + priv = netdev_priv(dev); + priv->read_reg = cc770_platform_read_reg; + priv->write_reg = cc770_platform_write_reg; + priv->irq_flags = IRQF_SHARED; + priv->reg_base = base; + + if (pdev->dev.of_node) + err = cc770_get_of_node_data(pdev, priv); + else if (pdev->dev.platform_data) + err = cc770_get_platform_data(pdev, priv); + else + err = -ENODEV; + if (err) + goto exit_free_cc770; + + dev_dbg(&pdev->dev, + "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x " + "bus_config=0x%02x clkout=0x%02x\n", + priv->reg_base, dev->irq, priv->can.clock.freq, + priv->cpu_interface, priv->bus_config, priv->clkout); + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_cc770dev(dev); + if (err) { + dev_err(&pdev->dev, + "couldn't register CC700 device (err=%d)\n", err); + goto exit_free_cc770; + } + + return 0; + +exit_free_cc770: + free_cc770dev(dev); +exit_unmap_mem: + iounmap(base); +exit_release_mem: + release_mem_region(mem->start, mem_size); + + return err; +} + +static int __devexit cc770_platform_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct cc770_priv *priv = netdev_priv(dev); + struct resource *mem; + + unregister_cc770dev(dev); + iounmap(priv->reg_base); + free_cc770dev(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + + return 0; +} + +static struct of_device_id __devinitdata cc770_platform_table[] = { + {.compatible = "bosch,cc770"}, /* CC770 from Bosch */ + {.compatible = "intc,82527"}, /* AN82527 from Intel CP */ + {}, +}; + +static struct platform_driver cc770_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = cc770_platform_table, + }, + .probe = cc770_platform_probe, + .remove = __devexit_p(cc770_platform_remove), +}; + +module_platform_driver(cc770_platform_driver); diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c new file mode 100644 index 00000000..c5fe3a3d --- /dev/null +++ b/drivers/net/can/dev.c @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix + * Copyright (C) 2006 Andrey Volkov, Varma Electronics + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * + * 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 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/netlink.h> +#include <net/rtnetlink.h> + +#define MOD_DESC "CAN device driver interface" + +MODULE_DESCRIPTION(MOD_DESC); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); + +#ifdef CONFIG_CAN_CALC_BITTIMING +#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ + +/* + * Bit-timing calculation derived from: + * + * Code based on LinCAN sources and H8S2638 project + * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz + * Copyright 2005 Stanislav Marek + * email: pisa@cmp.felk.cvut.cz + * + * Calculates proper bit-timing parameters for a specified bit-rate + * and sample-point, which can then be used to set the bit-timing + * registers of the CAN controller. You can find more information + * in the header file linux/can/netlink.h. + */ +static int can_update_spt(const struct can_bittiming_const *btc, + int sampl_pt, int tseg, int *tseg1, int *tseg2) +{ + *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000; + if (*tseg2 < btc->tseg2_min) + *tseg2 = btc->tseg2_min; + if (*tseg2 > btc->tseg2_max) + *tseg2 = btc->tseg2_max; + *tseg1 = tseg - *tseg2; + if (*tseg1 > btc->tseg1_max) { + *tseg1 = btc->tseg1_max; + *tseg2 = tseg - *tseg1; + } + return 1000 * (tseg + 1 - *tseg2) / (tseg + 1); +} + +static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) +{ + struct can_priv *priv = netdev_priv(dev); + const struct can_bittiming_const *btc = priv->bittiming_const; + long rate, best_rate = 0; + long best_error = 1000000000, error = 0; + int best_tseg = 0, best_brp = 0, brp = 0; + int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0; + int spt_error = 1000, spt = 0, sampl_pt; + u64 v64; + + if (!priv->bittiming_const) + return -ENOTSUPP; + + /* Use CIA recommended sample points */ + if (bt->sample_point) { + sampl_pt = bt->sample_point; + } else { + if (bt->bitrate > 800000) + sampl_pt = 750; + else if (bt->bitrate > 500000) + sampl_pt = 800; + else + sampl_pt = 875; + } + + /* tseg even = round down, odd = round up */ + for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; + tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) { + tsegall = 1 + tseg / 2; + /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ + brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2; + /* chose brp step which is possible in system */ + brp = (brp / btc->brp_inc) * btc->brp_inc; + if ((brp < btc->brp_min) || (brp > btc->brp_max)) + continue; + rate = priv->clock.freq / (brp * tsegall); + error = bt->bitrate - rate; + /* tseg brp biterror */ + if (error < 0) + error = -error; + if (error > best_error) + continue; + best_error = error; + if (error == 0) { + spt = can_update_spt(btc, sampl_pt, tseg / 2, + &tseg1, &tseg2); + error = sampl_pt - spt; + if (error < 0) + error = -error; + if (error > spt_error) + continue; + spt_error = error; + } + best_tseg = tseg / 2; + best_brp = brp; + best_rate = rate; + if (error == 0) + break; + } + + if (best_error) { + /* Error in one-tenth of a percent */ + error = (best_error * 1000) / bt->bitrate; + if (error > CAN_CALC_MAX_ERROR) { + netdev_err(dev, + "bitrate error %ld.%ld%% too high\n", + error / 10, error % 10); + return -EDOM; + } else { + netdev_warn(dev, "bitrate error %ld.%ld%%\n", + error / 10, error % 10); + } + } + + /* real sample point */ + bt->sample_point = can_update_spt(btc, sampl_pt, best_tseg, + &tseg1, &tseg2); + + v64 = (u64)best_brp * 1000000000UL; + do_div(v64, priv->clock.freq); + bt->tq = (u32)v64; + bt->prop_seg = tseg1 / 2; + bt->phase_seg1 = tseg1 - bt->prop_seg; + bt->phase_seg2 = tseg2; + + /* check for sjw user settings */ + if (!bt->sjw || !btc->sjw_max) + bt->sjw = 1; + else { + /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */ + if (bt->sjw > btc->sjw_max) + bt->sjw = btc->sjw_max; + /* bt->sjw must not be higher than tseg2 */ + if (tseg2 < bt->sjw) + bt->sjw = tseg2; + } + + bt->brp = best_brp; + /* real bit-rate */ + bt->bitrate = priv->clock.freq / (bt->brp * (tseg1 + tseg2 + 1)); + + return 0; +} +#else /* !CONFIG_CAN_CALC_BITTIMING */ +static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) +{ + netdev_err(dev, "bit-timing calculation not available\n"); + return -EINVAL; +} +#endif /* CONFIG_CAN_CALC_BITTIMING */ + +/* + * Checks the validity of the specified bit-timing parameters prop_seg, + * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate + * prescaler value brp. You can find more information in the header + * file linux/can/netlink.h. + */ +static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt) +{ + struct can_priv *priv = netdev_priv(dev); + const struct can_bittiming_const *btc = priv->bittiming_const; + int tseg1, alltseg; + u64 brp64; + + if (!priv->bittiming_const) + return -ENOTSUPP; + + tseg1 = bt->prop_seg + bt->phase_seg1; + if (!bt->sjw) + bt->sjw = 1; + if (bt->sjw > btc->sjw_max || + tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max || + bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max) + return -ERANGE; + + brp64 = (u64)priv->clock.freq * (u64)bt->tq; + if (btc->brp_inc > 1) + do_div(brp64, btc->brp_inc); + brp64 += 500000000UL - 1; + do_div(brp64, 1000000000UL); /* the practicable BRP */ + if (btc->brp_inc > 1) + brp64 *= btc->brp_inc; + bt->brp = (u32)brp64; + + if (bt->brp < btc->brp_min || bt->brp > btc->brp_max) + return -EINVAL; + + alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1; + bt->bitrate = priv->clock.freq / (bt->brp * alltseg); + bt->sample_point = ((tseg1 + 1) * 1000) / alltseg; + + return 0; +} + +static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt) +{ + struct can_priv *priv = netdev_priv(dev); + int err; + + /* Check if the CAN device has bit-timing parameters */ + if (priv->bittiming_const) { + + /* Non-expert mode? Check if the bitrate has been pre-defined */ + if (!bt->tq) + /* Determine bit-timing parameters */ + err = can_calc_bittiming(dev, bt); + else + /* Check bit-timing params and calculate proper brp */ + err = can_fixup_bittiming(dev, bt); + if (err) + return err; + } + + return 0; +} + +/* + * Local echo of CAN messages + * + * CAN network devices *should* support a local echo functionality + * (see Documentation/networking/can.txt). To test the handling of CAN + * interfaces that do not support the local echo both driver types are + * implemented. In the case that the driver does not support the echo + * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core + * to perform the echo as a fallback solution. + */ +static void can_flush_echo_skb(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + int i; + + for (i = 0; i < priv->echo_skb_max; i++) { + if (priv->echo_skb[i]) { + kfree_skb(priv->echo_skb[i]); + priv->echo_skb[i] = NULL; + stats->tx_dropped++; + stats->tx_aborted_errors++; + } + } +} + +/* + * Put the skb on the stack to be looped backed locally lateron + * + * The function is typically called in the start_xmit function + * of the device driver. The driver must protect access to + * priv->echo_skb, if necessary. + */ +void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, + unsigned int idx) +{ + struct can_priv *priv = netdev_priv(dev); + + BUG_ON(idx >= priv->echo_skb_max); + + /* check flag whether this packet has to be looped back */ + if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) { + kfree_skb(skb); + return; + } + + if (!priv->echo_skb[idx]) { + struct sock *srcsk = skb->sk; + + if (atomic_read(&skb->users) != 1) { + struct sk_buff *old_skb = skb; + + skb = skb_clone(old_skb, GFP_ATOMIC); + kfree_skb(old_skb); + if (!skb) + return; + } else + skb_orphan(skb); + + skb->sk = srcsk; + + /* make settings for echo to reduce code in irq context */ + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->dev = dev; + + /* save this skb for tx interrupt echo handling */ + priv->echo_skb[idx] = skb; + } else { + /* locking problem with netif_stop_queue() ?? */ + netdev_err(dev, "%s: BUG! echo_skb is occupied!\n", __func__); + kfree_skb(skb); + } +} +EXPORT_SYMBOL_GPL(can_put_echo_skb); + +/* + * Get the skb from the stack and loop it back locally + * + * The function is typically called when the TX done interrupt + * is handled in the device driver. The driver must protect + * access to priv->echo_skb, if necessary. + */ +unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx) +{ + struct can_priv *priv = netdev_priv(dev); + + BUG_ON(idx >= priv->echo_skb_max); + + if (priv->echo_skb[idx]) { + struct sk_buff *skb = priv->echo_skb[idx]; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc = cf->can_dlc; + + netif_rx(priv->echo_skb[idx]); + priv->echo_skb[idx] = NULL; + + return dlc; + } + + return 0; +} +EXPORT_SYMBOL_GPL(can_get_echo_skb); + +/* + * Remove the skb from the stack and free it. + * + * The function is typically called when TX failed. + */ +void can_free_echo_skb(struct net_device *dev, unsigned int idx) +{ + struct can_priv *priv = netdev_priv(dev); + + BUG_ON(idx >= priv->echo_skb_max); + + if (priv->echo_skb[idx]) { + kfree_skb(priv->echo_skb[idx]); + priv->echo_skb[idx] = NULL; + } +} +EXPORT_SYMBOL_GPL(can_free_echo_skb); + +/* + * CAN device restart for bus-off recovery + */ +void can_restart(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *cf; + int err; + + BUG_ON(netif_carrier_ok(dev)); + + /* + * No synchronization needed because the device is bus-off and + * no messages can come in or go out. + */ + can_flush_echo_skb(dev); + + /* send restart message upstream */ + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) { + err = -ENOMEM; + goto restart; + } + cf->can_id |= CAN_ERR_RESTARTED; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + +restart: + netdev_dbg(dev, "restarted\n"); + priv->can_stats.restarts++; + + /* Now restart the device */ + err = priv->do_set_mode(dev, CAN_MODE_START); + + netif_carrier_on(dev); + if (err) + netdev_err(dev, "Error %d during restart", err); +} + +int can_restart_now(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + /* + * A manual restart is only permitted if automatic restart is + * disabled and the device is in the bus-off state + */ + if (priv->restart_ms) + return -EINVAL; + if (priv->state != CAN_STATE_BUS_OFF) + return -EBUSY; + + /* Runs as soon as possible in the timer context */ + mod_timer(&priv->restart_timer, jiffies); + + return 0; +} + +/* + * CAN bus-off + * + * This functions should be called when the device goes bus-off to + * tell the netif layer that no more packets can be sent or received. + * If enabled, a timer is started to trigger bus-off recovery. + */ +void can_bus_off(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + netdev_dbg(dev, "bus-off\n"); + + netif_carrier_off(dev); + priv->can_stats.bus_off++; + + if (priv->restart_ms) + mod_timer(&priv->restart_timer, + jiffies + (priv->restart_ms * HZ) / 1000); +} +EXPORT_SYMBOL_GPL(can_bus_off); + +static void can_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = sizeof(struct can_frame); + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; + + /* New-style flags. */ + dev->flags = IFF_NOARP; + dev->features = NETIF_F_HW_CSUM; +} + +struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); + if (unlikely(!skb)) + return NULL; + + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(*cf, 0, sizeof(struct can_frame)); + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_skb); + +struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = alloc_can_skb(dev, cf); + if (unlikely(!skb)) + return NULL; + + (*cf)->can_id = CAN_ERR_FLAG; + (*cf)->can_dlc = CAN_ERR_DLC; + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_err_skb); + +/* + * Allocate and setup space for the CAN network device + */ +struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max) +{ + struct net_device *dev; + struct can_priv *priv; + int size; + + if (echo_skb_max) + size = ALIGN(sizeof_priv, sizeof(struct sk_buff *)) + + echo_skb_max * sizeof(struct sk_buff *); + else + size = sizeof_priv; + + dev = alloc_netdev(size, "can%d", can_setup); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + + if (echo_skb_max) { + priv->echo_skb_max = echo_skb_max; + priv->echo_skb = (void *)priv + + ALIGN(sizeof_priv, sizeof(struct sk_buff *)); + } + + priv->state = CAN_STATE_STOPPED; + + init_timer(&priv->restart_timer); + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_candev); + +/* + * Free space of the CAN network device + */ +void free_candev(struct net_device *dev) +{ + free_netdev(dev); +} +EXPORT_SYMBOL_GPL(free_candev); + +/* + * Common open function when the device gets opened. + * + * This function should be called in the open function of the device + * driver. + */ +int open_candev(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + if (!priv->bittiming.tq && !priv->bittiming.bitrate) { + netdev_err(dev, "bit-timing not yet defined\n"); + return -EINVAL; + } + + /* Switch carrier on if device was stopped while in bus-off state */ + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + + setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev); + + return 0; +} +EXPORT_SYMBOL_GPL(open_candev); + +/* + * Common close function for cleanup before the device gets closed. + * + * This function should be called in the close function of the device + * driver. + */ +void close_candev(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + if (del_timer_sync(&priv->restart_timer)) + dev_put(dev); + can_flush_echo_skb(dev); +} +EXPORT_SYMBOL_GPL(close_candev); + +/* + * CAN netlink interface + */ +static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { + [IFLA_CAN_STATE] = { .type = NLA_U32 }, + [IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) }, + [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, + [IFLA_CAN_RESTART] = { .type = NLA_U32 }, + [IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) }, + [IFLA_CAN_BITTIMING_CONST] + = { .len = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) }, + [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) }, +}; + +static int can_changelink(struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct can_priv *priv = netdev_priv(dev); + int err; + + /* We need synchronization with dev->stop() */ + ASSERT_RTNL(); + + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm; + + /* Do not allow changing controller mode while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + cm = nla_data(data[IFLA_CAN_CTRLMODE]); + if (cm->flags & ~priv->ctrlmode_supported) + return -EOPNOTSUPP; + priv->ctrlmode &= ~cm->mask; + priv->ctrlmode |= cm->flags; + } + + if (data[IFLA_CAN_BITTIMING]) { + struct can_bittiming bt; + + /* Do not allow changing bittiming while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); + if ((!bt.bitrate && !bt.tq) || (bt.bitrate && bt.tq)) + return -EINVAL; + err = can_get_bittiming(dev, &bt); + if (err) + return err; + memcpy(&priv->bittiming, &bt, sizeof(bt)); + + if (priv->do_set_bittiming) { + /* Finally, set the bit-timing registers */ + err = priv->do_set_bittiming(dev); + if (err) + return err; + } + } + + if (data[IFLA_CAN_RESTART_MS]) { + /* Do not allow changing restart delay while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]); + } + + if (data[IFLA_CAN_RESTART]) { + /* Do not allow a restart while not running */ + if (!(dev->flags & IFF_UP)) + return -EINVAL; + err = can_restart_now(dev); + if (err) + return err; + } + + return 0; +} + +static size_t can_get_size(const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + size_t size; + + size = nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ + size += sizeof(struct can_ctrlmode); /* IFLA_CAN_CTRLMODE */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ + size += sizeof(struct can_bittiming); /* IFLA_CAN_BITTIMING */ + size += sizeof(struct can_clock); /* IFLA_CAN_CLOCK */ + if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ + size += sizeof(struct can_berr_counter); + if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ + size += sizeof(struct can_bittiming_const); + + return size; +} + +static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct can_ctrlmode cm = {.flags = priv->ctrlmode}; + struct can_berr_counter bec; + enum can_state state = priv->state; + + if (priv->do_get_state) + priv->do_get_state(dev, &state); + NLA_PUT_U32(skb, IFLA_CAN_STATE, state); + NLA_PUT(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm); + NLA_PUT_U32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms); + NLA_PUT(skb, IFLA_CAN_BITTIMING, + sizeof(priv->bittiming), &priv->bittiming); + NLA_PUT(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock); + if (priv->do_get_berr_counter && !priv->do_get_berr_counter(dev, &bec)) + NLA_PUT(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec); + if (priv->bittiming_const) + NLA_PUT(skb, IFLA_CAN_BITTIMING_CONST, + sizeof(*priv->bittiming_const), priv->bittiming_const); + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static size_t can_get_xstats_size(const struct net_device *dev) +{ + return sizeof(struct can_device_stats); +} + +static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + NLA_PUT(skb, IFLA_INFO_XSTATS, + sizeof(priv->can_stats), &priv->can_stats); + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int can_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + return -EOPNOTSUPP; +} + +static struct rtnl_link_ops can_link_ops __read_mostly = { + .kind = "can", + .maxtype = IFLA_CAN_MAX, + .policy = can_policy, + .setup = can_setup, + .newlink = can_newlink, + .changelink = can_changelink, + .get_size = can_get_size, + .fill_info = can_fill_info, + .get_xstats_size = can_get_xstats_size, + .fill_xstats = can_fill_xstats, +}; + +/* + * Register the CAN network device + */ +int register_candev(struct net_device *dev) +{ + dev->rtnl_link_ops = &can_link_ops; + return register_netdev(dev); +} +EXPORT_SYMBOL_GPL(register_candev); + +/* + * Unregister the CAN network device + */ +void unregister_candev(struct net_device *dev) +{ + unregister_netdev(dev); +} +EXPORT_SYMBOL_GPL(unregister_candev); + +static __init int can_dev_init(void) +{ + int err; + + err = rtnl_link_register(&can_link_ops); + if (!err) + printk(KERN_INFO MOD_DESC "\n"); + + return err; +} +module_init(can_dev_init); + +static __exit void can_dev_exit(void) +{ + rtnl_link_unregister(&can_link_ops); +} +module_exit(can_dev_exit); + +MODULE_ALIAS_RTNL_LINK("can"); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c new file mode 100644 index 00000000..00baa7e0 --- /dev/null +++ b/drivers/net/can/flexcan.c @@ -0,0 +1,1068 @@ +/* + * flexcan.c - FLEXCAN CAN controller driver + * + * Copyright (c) 2005-2006 Varma Electronics Oy + * Copyright (c) 2009 Sascha Hauer, Pengutronix + * Copyright (c) 2010 Marc Kleine-Budde, Pengutronix + * + * Based on code originally by Andrey Volkov <avolkov@varma-el.com> + * + * LICENCE: + * 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 version 2. + * + * 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. + * + */ + +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/platform/flexcan.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#define DRV_NAME "flexcan" + +/* 8 for RX fifo and 2 error handling */ +#define FLEXCAN_NAPI_WEIGHT (8 + 2) + +/* FLEXCAN module configuration register (CANMCR) bits */ +#define FLEXCAN_MCR_MDIS BIT(31) +#define FLEXCAN_MCR_FRZ BIT(30) +#define FLEXCAN_MCR_FEN BIT(29) +#define FLEXCAN_MCR_HALT BIT(28) +#define FLEXCAN_MCR_NOT_RDY BIT(27) +#define FLEXCAN_MCR_WAK_MSK BIT(26) +#define FLEXCAN_MCR_SOFTRST BIT(25) +#define FLEXCAN_MCR_FRZ_ACK BIT(24) +#define FLEXCAN_MCR_SUPV BIT(23) +#define FLEXCAN_MCR_SLF_WAK BIT(22) +#define FLEXCAN_MCR_WRN_EN BIT(21) +#define FLEXCAN_MCR_LPM_ACK BIT(20) +#define FLEXCAN_MCR_WAK_SRC BIT(19) +#define FLEXCAN_MCR_DOZE BIT(18) +#define FLEXCAN_MCR_SRX_DIS BIT(17) +#define FLEXCAN_MCR_BCC BIT(16) +#define FLEXCAN_MCR_LPRIO_EN BIT(13) +#define FLEXCAN_MCR_AEN BIT(12) +#define FLEXCAN_MCR_MAXMB(x) ((x) & 0xf) +#define FLEXCAN_MCR_IDAM_A (0 << 8) +#define FLEXCAN_MCR_IDAM_B (1 << 8) +#define FLEXCAN_MCR_IDAM_C (2 << 8) +#define FLEXCAN_MCR_IDAM_D (3 << 8) + +/* FLEXCAN control register (CANCTRL) bits */ +#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xff) << 24) +#define FLEXCAN_CTRL_RJW(x) (((x) & 0x03) << 22) +#define FLEXCAN_CTRL_PSEG1(x) (((x) & 0x07) << 19) +#define FLEXCAN_CTRL_PSEG2(x) (((x) & 0x07) << 16) +#define FLEXCAN_CTRL_BOFF_MSK BIT(15) +#define FLEXCAN_CTRL_ERR_MSK BIT(14) +#define FLEXCAN_CTRL_CLK_SRC BIT(13) +#define FLEXCAN_CTRL_LPB BIT(12) +#define FLEXCAN_CTRL_TWRN_MSK BIT(11) +#define FLEXCAN_CTRL_RWRN_MSK BIT(10) +#define FLEXCAN_CTRL_SMP BIT(7) +#define FLEXCAN_CTRL_BOFF_REC BIT(6) +#define FLEXCAN_CTRL_TSYN BIT(5) +#define FLEXCAN_CTRL_LBUF BIT(4) +#define FLEXCAN_CTRL_LOM BIT(3) +#define FLEXCAN_CTRL_PROPSEG(x) ((x) & 0x07) +#define FLEXCAN_CTRL_ERR_BUS (FLEXCAN_CTRL_ERR_MSK) +#define FLEXCAN_CTRL_ERR_STATE \ + (FLEXCAN_CTRL_TWRN_MSK | FLEXCAN_CTRL_RWRN_MSK | \ + FLEXCAN_CTRL_BOFF_MSK) +#define FLEXCAN_CTRL_ERR_ALL \ + (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE) + +/* FLEXCAN error and status register (ESR) bits */ +#define FLEXCAN_ESR_TWRN_INT BIT(17) +#define FLEXCAN_ESR_RWRN_INT BIT(16) +#define FLEXCAN_ESR_BIT1_ERR BIT(15) +#define FLEXCAN_ESR_BIT0_ERR BIT(14) +#define FLEXCAN_ESR_ACK_ERR BIT(13) +#define FLEXCAN_ESR_CRC_ERR BIT(12) +#define FLEXCAN_ESR_FRM_ERR BIT(11) +#define FLEXCAN_ESR_STF_ERR BIT(10) +#define FLEXCAN_ESR_TX_WRN BIT(9) +#define FLEXCAN_ESR_RX_WRN BIT(8) +#define FLEXCAN_ESR_IDLE BIT(7) +#define FLEXCAN_ESR_TXRX BIT(6) +#define FLEXCAN_EST_FLT_CONF_SHIFT (4) +#define FLEXCAN_ESR_FLT_CONF_MASK (0x3 << FLEXCAN_EST_FLT_CONF_SHIFT) +#define FLEXCAN_ESR_FLT_CONF_ACTIVE (0x0 << FLEXCAN_EST_FLT_CONF_SHIFT) +#define FLEXCAN_ESR_FLT_CONF_PASSIVE (0x1 << FLEXCAN_EST_FLT_CONF_SHIFT) +#define FLEXCAN_ESR_BOFF_INT BIT(2) +#define FLEXCAN_ESR_ERR_INT BIT(1) +#define FLEXCAN_ESR_WAK_INT BIT(0) +#define FLEXCAN_ESR_ERR_BUS \ + (FLEXCAN_ESR_BIT1_ERR | FLEXCAN_ESR_BIT0_ERR | \ + FLEXCAN_ESR_ACK_ERR | FLEXCAN_ESR_CRC_ERR | \ + FLEXCAN_ESR_FRM_ERR | FLEXCAN_ESR_STF_ERR) +#define FLEXCAN_ESR_ERR_STATE \ + (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | FLEXCAN_ESR_BOFF_INT) +#define FLEXCAN_ESR_ERR_ALL \ + (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) +#define FLEXCAN_ESR_ALL_INT \ + (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ + FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) + +/* FLEXCAN interrupt flag register (IFLAG) bits */ +#define FLEXCAN_TX_BUF_ID 8 +#define FLEXCAN_IFLAG_BUF(x) BIT(x) +#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7) +#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6) +#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5) +#define FLEXCAN_IFLAG_DEFAULT \ + (FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | \ + FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID)) + +/* FLEXCAN message buffers */ +#define FLEXCAN_MB_CNT_CODE(x) (((x) & 0xf) << 24) +#define FLEXCAN_MB_CNT_SRR BIT(22) +#define FLEXCAN_MB_CNT_IDE BIT(21) +#define FLEXCAN_MB_CNT_RTR BIT(20) +#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xf) << 16) +#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xffff) + +#define FLEXCAN_MB_CODE_MASK (0xf0ffffff) + +/* Structure of the message buffer */ +struct flexcan_mb { + u32 can_ctrl; + u32 can_id; + u32 data[2]; +}; + +/* Structure of the hardware registers */ +struct flexcan_regs { + u32 mcr; /* 0x00 */ + u32 ctrl; /* 0x04 */ + u32 timer; /* 0x08 */ + u32 _reserved1; /* 0x0c */ + u32 rxgmask; /* 0x10 */ + u32 rx14mask; /* 0x14 */ + u32 rx15mask; /* 0x18 */ + u32 ecr; /* 0x1c */ + u32 esr; /* 0x20 */ + u32 imask2; /* 0x24 */ + u32 imask1; /* 0x28 */ + u32 iflag2; /* 0x2c */ + u32 iflag1; /* 0x30 */ + u32 _reserved2[19]; + struct flexcan_mb cantxfg[64]; +}; + +struct flexcan_priv { + struct can_priv can; + struct net_device *dev; + struct napi_struct napi; + + void __iomem *base; + u32 reg_esr; + u32 reg_ctrl_default; + + struct clk *clk; + struct flexcan_platform_data *pdata; +}; + +static struct can_bittiming_const flexcan_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +/* + * Abstract off the read/write for arm versus ppc. + */ +#if defined(__BIG_ENDIAN) +static inline u32 flexcan_read(void __iomem *addr) +{ + return in_be32(addr); +} + +static inline void flexcan_write(u32 val, void __iomem *addr) +{ + out_be32(addr, val); +} +#else +static inline u32 flexcan_read(void __iomem *addr) +{ + return readl(addr); +} + +static inline void flexcan_write(u32 val, void __iomem *addr) +{ + writel(val, addr); +} +#endif + +/* + * Swtich transceiver on or off + */ +static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on) +{ + if (priv->pdata && priv->pdata->transceiver_switch) + priv->pdata->transceiver_switch(on); +} + +static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, + u32 reg_esr) +{ + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && + (reg_esr & FLEXCAN_ESR_ERR_BUS); +} + +static inline void flexcan_chip_enable(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + u32 reg; + + reg = flexcan_read(®s->mcr); + reg &= ~FLEXCAN_MCR_MDIS; + flexcan_write(reg, ®s->mcr); + + udelay(10); +} + +static inline void flexcan_chip_disable(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + u32 reg; + + reg = flexcan_read(®s->mcr); + reg |= FLEXCAN_MCR_MDIS; + flexcan_write(reg, ®s->mcr); +} + +static int flexcan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + u32 reg = flexcan_read(®s->ecr); + + bec->txerr = (reg >> 0) & 0xff; + bec->rxerr = (reg >> 8) & 0xff; + + return 0; +} + +static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 can_id; + u32 ctrl = FLEXCAN_MB_CNT_CODE(0xc) | (cf->can_dlc << 16); + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + if (cf->can_id & CAN_EFF_FLAG) { + can_id = cf->can_id & CAN_EFF_MASK; + ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR; + } else { + can_id = (cf->can_id & CAN_SFF_MASK) << 18; + } + + if (cf->can_id & CAN_RTR_FLAG) + ctrl |= FLEXCAN_MB_CNT_RTR; + + if (cf->can_dlc > 0) { + u32 data = be32_to_cpup((__be32 *)&cf->data[0]); + flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); + } + if (cf->can_dlc > 3) { + u32 data = be32_to_cpup((__be32 *)&cf->data[4]); + flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); + } + + can_put_echo_skb(skb, dev, 0); + + flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); + flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + + return NETDEV_TX_OK; +} + +static void do_bus_err(struct net_device *dev, + struct can_frame *cf, u32 reg_esr) +{ + struct flexcan_priv *priv = netdev_priv(dev); + int rx_errors = 0, tx_errors = 0; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + if (reg_esr & FLEXCAN_ESR_BIT1_ERR) { + netdev_dbg(dev, "BIT1_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_BIT1; + tx_errors = 1; + } + if (reg_esr & FLEXCAN_ESR_BIT0_ERR) { + netdev_dbg(dev, "BIT0_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_BIT0; + tx_errors = 1; + } + if (reg_esr & FLEXCAN_ESR_ACK_ERR) { + netdev_dbg(dev, "ACK_ERR irq\n"); + cf->can_id |= CAN_ERR_ACK; + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + tx_errors = 1; + } + if (reg_esr & FLEXCAN_ESR_CRC_ERR) { + netdev_dbg(dev, "CRC_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_BIT; + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + rx_errors = 1; + } + if (reg_esr & FLEXCAN_ESR_FRM_ERR) { + netdev_dbg(dev, "FRM_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_FORM; + rx_errors = 1; + } + if (reg_esr & FLEXCAN_ESR_STF_ERR) { + netdev_dbg(dev, "STF_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_STUFF; + rx_errors = 1; + } + + priv->can.can_stats.bus_error++; + if (rx_errors) + dev->stats.rx_errors++; + if (tx_errors) + dev->stats.tx_errors++; +} + +static int flexcan_poll_bus_err(struct net_device *dev, u32 reg_esr) +{ + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + do_bus_err(dev, cf, reg_esr); + netif_receive_skb(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + + return 1; +} + +static void do_state(struct net_device *dev, + struct can_frame *cf, enum can_state new_state) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct can_berr_counter bec; + + flexcan_get_berr_counter(dev, &bec); + + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + /* + * from: ERROR_ACTIVE + * to : ERROR_WARNING, ERROR_PASSIVE, BUS_OFF + * => : there was a warning int + */ + if (new_state >= CAN_STATE_ERROR_WARNING && + new_state <= CAN_STATE_BUS_OFF) { + netdev_dbg(dev, "Error Warning IRQ\n"); + priv->can.can_stats.error_warning++; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + /* + * from: ERROR_ACTIVE, ERROR_WARNING + * to : ERROR_PASSIVE, BUS_OFF + * => : error passive int + */ + if (new_state >= CAN_STATE_ERROR_PASSIVE && + new_state <= CAN_STATE_BUS_OFF) { + netdev_dbg(dev, "Error Passive IRQ\n"); + priv->can.can_stats.error_passive++; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + break; + case CAN_STATE_BUS_OFF: + netdev_err(dev, "BUG! " + "hardware recovered automatically from BUS_OFF\n"); + break; + default: + break; + } + + /* process state changes depending on the new state */ + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + netdev_dbg(dev, "Error Active\n"); + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + break; + case CAN_STATE_BUS_OFF: + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + break; + default: + break; + } +} + +static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct sk_buff *skb; + struct can_frame *cf; + enum can_state new_state; + int flt; + + flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK; + if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) { + if (likely(!(reg_esr & (FLEXCAN_ESR_TX_WRN | + FLEXCAN_ESR_RX_WRN)))) + new_state = CAN_STATE_ERROR_ACTIVE; + else + new_state = CAN_STATE_ERROR_WARNING; + } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; + else + new_state = CAN_STATE_BUS_OFF; + + /* state hasn't changed */ + if (likely(new_state == priv->can.state)) + return 0; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + do_state(dev, cf, new_state); + priv->can.state = new_state; + netif_receive_skb(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + + return 1; +} + +static void flexcan_read_fifo(const struct net_device *dev, + struct can_frame *cf) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; + u32 reg_ctrl, reg_id; + + reg_ctrl = flexcan_read(&mb->can_ctrl); + reg_id = flexcan_read(&mb->can_id); + if (reg_ctrl & FLEXCAN_MB_CNT_IDE) + cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (reg_id >> 18) & CAN_SFF_MASK; + + if (reg_ctrl & FLEXCAN_MB_CNT_RTR) + cf->can_id |= CAN_RTR_FLAG; + cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); + + *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0])); + *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1])); + + /* mark as read */ + flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); + flexcan_read(®s->timer); +} + +static int flexcan_read_frame(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + skb = alloc_can_skb(dev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + flexcan_read_fifo(dev, cf); + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + +static int flexcan_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + u32 reg_iflag1, reg_esr; + int work_done = 0; + + /* + * The error bits are cleared on read, + * use saved value from irq handler. + */ + reg_esr = flexcan_read(®s->esr) | priv->reg_esr; + + /* handle state changes */ + work_done += flexcan_poll_state(dev, reg_esr); + + /* handle RX-FIFO */ + reg_iflag1 = flexcan_read(®s->iflag1); + while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE && + work_done < quota) { + work_done += flexcan_read_frame(dev); + reg_iflag1 = flexcan_read(®s->iflag1); + } + + /* report bus errors */ + if (flexcan_has_and_handle_berr(priv, reg_esr) && work_done < quota) + work_done += flexcan_poll_bus_err(dev, reg_esr); + + if (work_done < quota) { + napi_complete(napi); + /* enable IRQs */ + flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + flexcan_write(priv->reg_ctrl_default, ®s->ctrl); + } + + return work_done; +} + +static irqreturn_t flexcan_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct net_device_stats *stats = &dev->stats; + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + u32 reg_iflag1, reg_esr; + + reg_iflag1 = flexcan_read(®s->iflag1); + reg_esr = flexcan_read(®s->esr); + /* ACK all bus error and state change IRQ sources */ + if (reg_esr & FLEXCAN_ESR_ALL_INT) + flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); + + /* + * schedule NAPI in case of: + * - rx IRQ + * - state change IRQ + * - bus error IRQ and bus error reporting is activated + */ + if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) || + (reg_esr & FLEXCAN_ESR_ERR_STATE) || + flexcan_has_and_handle_berr(priv, reg_esr)) { + /* + * The error bits are cleared on read, + * save them for later use. + */ + priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; + flexcan_write(FLEXCAN_IFLAG_DEFAULT & + ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); + flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + ®s->ctrl); + napi_schedule(&priv->napi); + } + + /* FIFO overflow */ + if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { + flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); + dev->stats.rx_over_errors++; + dev->stats.rx_errors++; + } + + /* transmission complete interrupt */ + if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) { + stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_packets++; + flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); + netif_wake_queue(dev); + } + + return IRQ_HANDLED; +} + +static void flexcan_set_bittiming(struct net_device *dev) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + const struct can_bittiming *bt = &priv->can.bittiming; + struct flexcan_regs __iomem *regs = priv->base; + u32 reg; + + reg = flexcan_read(®s->ctrl); + reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) | + FLEXCAN_CTRL_RJW(0x3) | + FLEXCAN_CTRL_PSEG1(0x7) | + FLEXCAN_CTRL_PSEG2(0x7) | + FLEXCAN_CTRL_PROPSEG(0x7) | + FLEXCAN_CTRL_LPB | + FLEXCAN_CTRL_SMP | + FLEXCAN_CTRL_LOM); + + reg |= FLEXCAN_CTRL_PRESDIV(bt->brp - 1) | + FLEXCAN_CTRL_PSEG1(bt->phase_seg1 - 1) | + FLEXCAN_CTRL_PSEG2(bt->phase_seg2 - 1) | + FLEXCAN_CTRL_RJW(bt->sjw - 1) | + FLEXCAN_CTRL_PROPSEG(bt->prop_seg - 1); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg |= FLEXCAN_CTRL_LPB; + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg |= FLEXCAN_CTRL_LOM; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + reg |= FLEXCAN_CTRL_SMP; + + netdev_info(dev, "writing ctrl=0x%08x\n", reg); + flexcan_write(reg, ®s->ctrl); + + /* print chip status */ + netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, + flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); +} + +/* + * flexcan_chip_start + * + * this functions is entered with clocks enabled + * + */ +static int flexcan_chip_start(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + unsigned int i; + int err; + u32 reg_mcr, reg_ctrl; + + /* enable module */ + flexcan_chip_enable(priv); + + /* soft reset */ + flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); + udelay(10); + + reg_mcr = flexcan_read(®s->mcr); + if (reg_mcr & FLEXCAN_MCR_SOFTRST) { + netdev_err(dev, "Failed to softreset can module (mcr=0x%08x)\n", + reg_mcr); + err = -ENODEV; + goto out; + } + + flexcan_set_bittiming(dev); + + /* + * MCR + * + * enable freeze + * enable fifo + * halt now + * only supervisor access + * enable warning int + * choose format C + * disable local echo + * + */ + reg_mcr = flexcan_read(®s->mcr); + reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | + FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS; + netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); + flexcan_write(reg_mcr, ®s->mcr); + + /* + * CTRL + * + * disable timer sync feature + * + * disable auto busoff recovery + * transmit lowest buffer first + * + * enable tx and rx warning interrupt + * enable bus off interrupt + * (== FLEXCAN_CTRL_ERR_STATE) + * + * _note_: we enable the "error interrupt" + * (FLEXCAN_CTRL_ERR_MSK), too. Otherwise we don't get any + * warning or bus passive interrupts. + */ + reg_ctrl = flexcan_read(®s->ctrl); + reg_ctrl &= ~FLEXCAN_CTRL_TSYN; + reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | + FLEXCAN_CTRL_ERR_STATE | FLEXCAN_CTRL_ERR_MSK; + + /* save for later use */ + priv->reg_ctrl_default = reg_ctrl; + netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); + flexcan_write(reg_ctrl, ®s->ctrl); + + for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) { + flexcan_write(0, ®s->cantxfg[i].can_ctrl); + flexcan_write(0, ®s->cantxfg[i].can_id); + flexcan_write(0, ®s->cantxfg[i].data[0]); + flexcan_write(0, ®s->cantxfg[i].data[1]); + + /* put MB into rx queue */ + flexcan_write(FLEXCAN_MB_CNT_CODE(0x4), + ®s->cantxfg[i].can_ctrl); + } + + /* acceptance mask/acceptance code (accept everything) */ + flexcan_write(0x0, ®s->rxgmask); + flexcan_write(0x0, ®s->rx14mask); + flexcan_write(0x0, ®s->rx15mask); + + flexcan_transceiver_switch(priv, 1); + + /* synchronize with the can bus */ + reg_mcr = flexcan_read(®s->mcr); + reg_mcr &= ~FLEXCAN_MCR_HALT; + flexcan_write(reg_mcr, ®s->mcr); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* enable FIFO interrupts */ + flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + + /* print chip status */ + netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, + flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); + + return 0; + + out: + flexcan_chip_disable(priv); + return err; +} + +/* + * flexcan_chip_stop + * + * this functions is entered with clocks enabled + * + */ +static void flexcan_chip_stop(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + u32 reg; + + /* Disable all interrupts */ + flexcan_write(0, ®s->imask1); + + /* Disable + halt module */ + reg = flexcan_read(®s->mcr); + reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT; + flexcan_write(reg, ®s->mcr); + + flexcan_transceiver_switch(priv, 0); + priv->can.state = CAN_STATE_STOPPED; + + return; +} + +static int flexcan_open(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + int err; + + clk_prepare_enable(priv->clk); + + err = open_candev(dev); + if (err) + goto out; + + err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev); + if (err) + goto out_close; + + /* start chip and queuing */ + err = flexcan_chip_start(dev); + if (err) + goto out_close; + napi_enable(&priv->napi); + netif_start_queue(dev); + + return 0; + + out_close: + close_candev(dev); + out: + clk_disable_unprepare(priv->clk); + + return err; +} + +static int flexcan_close(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&priv->napi); + flexcan_chip_stop(dev); + + free_irq(dev->irq, dev); + clk_disable_unprepare(priv->clk); + + close_candev(dev); + + return 0; +} + +static int flexcan_set_mode(struct net_device *dev, enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = flexcan_chip_start(dev); + if (err) + return err; + + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct net_device_ops flexcan_netdev_ops = { + .ndo_open = flexcan_open, + .ndo_stop = flexcan_close, + .ndo_start_xmit = flexcan_start_xmit, +}; + +static int __devinit register_flexcandev(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + u32 reg, err; + + clk_prepare_enable(priv->clk); + + /* select "bus clock", chip must be disabled */ + flexcan_chip_disable(priv); + reg = flexcan_read(®s->ctrl); + reg |= FLEXCAN_CTRL_CLK_SRC; + flexcan_write(reg, ®s->ctrl); + + flexcan_chip_enable(priv); + + /* set freeze, halt and activate FIFO, restrict register access */ + reg = flexcan_read(®s->mcr); + reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | + FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; + flexcan_write(reg, ®s->mcr); + + /* + * Currently we only support newer versions of this core + * featuring a RX FIFO. Older cores found on some Coldfire + * derivates are not yet supported. + */ + reg = flexcan_read(®s->mcr); + if (!(reg & FLEXCAN_MCR_FEN)) { + netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); + err = -ENODEV; + goto out; + } + + err = register_candev(dev); + + out: + /* disable core and turn off clocks */ + flexcan_chip_disable(priv); + clk_disable_unprepare(priv->clk); + + return err; +} + +static void __devexit unregister_flexcandev(struct net_device *dev) +{ + unregister_candev(dev); +} + +static int __devinit flexcan_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct flexcan_priv *priv; + struct resource *mem; + struct clk *clk = NULL; + void __iomem *base; + resource_size_t mem_size; + int err, irq; + u32 clock_freq = 0; + + if (pdev->dev.of_node) { + const __be32 *clock_freq_p; + + clock_freq_p = of_get_property(pdev->dev.of_node, + "clock-frequency", NULL); + if (clock_freq_p) + clock_freq = be32_to_cpup(clock_freq_p); + } + + if (!clock_freq) { + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + err = PTR_ERR(clk); + goto failed_clock; + } + clock_freq = clk_get_rate(clk); + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!mem || irq <= 0) { + err = -ENODEV; + goto failed_get; + } + + mem_size = resource_size(mem); + if (!request_mem_region(mem->start, mem_size, pdev->name)) { + err = -EBUSY; + goto failed_get; + } + + base = ioremap(mem->start, mem_size); + if (!base) { + err = -ENOMEM; + goto failed_map; + } + + dev = alloc_candev(sizeof(struct flexcan_priv), 1); + if (!dev) { + err = -ENOMEM; + goto failed_alloc; + } + + dev->netdev_ops = &flexcan_netdev_ops; + dev->irq = irq; + dev->flags |= IFF_ECHO; + + priv = netdev_priv(dev); + priv->can.clock.freq = clock_freq; + priv->can.bittiming_const = &flexcan_bittiming_const; + priv->can.do_set_mode = flexcan_set_mode; + priv->can.do_get_berr_counter = flexcan_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_BERR_REPORTING; + priv->base = base; + priv->dev = dev; + priv->clk = clk; + priv->pdata = pdev->dev.platform_data; + + netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_flexcandev(dev); + if (err) { + dev_err(&pdev->dev, "registering netdev failed\n"); + goto failed_register; + } + + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", + priv->base, dev->irq); + + return 0; + + failed_register: + free_candev(dev); + failed_alloc: + iounmap(base); + failed_map: + release_mem_region(mem->start, mem_size); + failed_get: + if (clk) + clk_put(clk); + failed_clock: + return err; +} + +static int __devexit flexcan_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv = netdev_priv(dev); + struct resource *mem; + + unregister_flexcandev(dev); + platform_set_drvdata(pdev, NULL); + iounmap(priv->base); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + + if (priv->clk) + clk_put(priv->clk); + + free_candev(dev); + + return 0; +} + +static struct of_device_id flexcan_of_match[] = { + { + .compatible = "fsl,p1010-flexcan", + }, + {}, +}; + +static struct platform_driver flexcan_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = flexcan_of_match, + }, + .probe = flexcan_probe, + .remove = __devexit_p(flexcan_remove), +}; + +module_platform_driver(flexcan_driver); + +MODULE_AUTHOR("Sascha Hauer <kernel@pengutronix.de>, " + "Marc Kleine-Budde <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN port driver for flexcan based chip"); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c new file mode 100644 index 00000000..08c893cb --- /dev/null +++ b/drivers/net/can/janz-ican3.c @@ -0,0 +1,1811 @@ +/* + * Janz MODULbus VMOD-ICAN3 CAN Interface Driver + * + * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include <linux/mfd/janz.h> +#include <asm/io.h> + +/* the DPM has 64k of memory, organized into 256x 256 byte pages */ +#define DPM_NUM_PAGES 256 +#define DPM_PAGE_SIZE 256 +#define DPM_PAGE_ADDR(p) ((p) * DPM_PAGE_SIZE) + +/* JANZ ICAN3 "old-style" host interface queue page numbers */ +#define QUEUE_OLD_CONTROL 0 +#define QUEUE_OLD_RB0 1 +#define QUEUE_OLD_RB1 2 +#define QUEUE_OLD_WB0 3 +#define QUEUE_OLD_WB1 4 + +/* Janz ICAN3 "old-style" host interface control registers */ +#define MSYNC_PEER 0x00 /* ICAN only */ +#define MSYNC_LOCL 0x01 /* host only */ +#define TARGET_RUNNING 0x02 + +#define MSYNC_RB0 0x01 +#define MSYNC_RB1 0x02 +#define MSYNC_RBLW 0x04 +#define MSYNC_RB_MASK (MSYNC_RB0 | MSYNC_RB1) + +#define MSYNC_WB0 0x10 +#define MSYNC_WB1 0x20 +#define MSYNC_WBLW 0x40 +#define MSYNC_WB_MASK (MSYNC_WB0 | MSYNC_WB1) + +/* Janz ICAN3 "new-style" host interface queue page numbers */ +#define QUEUE_TOHOST 5 +#define QUEUE_FROMHOST_MID 6 +#define QUEUE_FROMHOST_HIGH 7 +#define QUEUE_FROMHOST_LOW 8 + +/* The first free page in the DPM is #9 */ +#define DPM_FREE_START 9 + +/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */ +#define DESC_VALID 0x80 +#define DESC_WRAP 0x40 +#define DESC_INTERRUPT 0x20 +#define DESC_IVALID 0x10 +#define DESC_LEN(len) (len) + +/* Janz ICAN3 Firmware Messages */ +#define MSG_CONNECTI 0x02 +#define MSG_DISCONNECT 0x03 +#define MSG_IDVERS 0x04 +#define MSG_MSGLOST 0x05 +#define MSG_NEWHOSTIF 0x08 +#define MSG_INQUIRY 0x0a +#define MSG_SETAFILMASK 0x10 +#define MSG_INITFDPMQUEUE 0x11 +#define MSG_HWCONF 0x12 +#define MSG_FMSGLOST 0x15 +#define MSG_CEVTIND 0x37 +#define MSG_CBTRREQ 0x41 +#define MSG_COFFREQ 0x42 +#define MSG_CONREQ 0x43 +#define MSG_CCONFREQ 0x47 + +/* + * Janz ICAN3 CAN Inquiry Message Types + * + * NOTE: there appears to be a firmware bug here. You must send + * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED + * NOTE: response. The controller never responds to a message with + * NOTE: the INQUIRY_EXTENDED subspec :( + */ +#define INQUIRY_STATUS 0x00 +#define INQUIRY_TERMINATION 0x01 +#define INQUIRY_EXTENDED 0x04 + +/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */ +#define SETAFILMASK_REJECT 0x00 +#define SETAFILMASK_FASTIF 0x02 + +/* Janz ICAN3 CAN Hardware Configuration Message Types */ +#define HWCONF_TERMINATE_ON 0x01 +#define HWCONF_TERMINATE_OFF 0x00 + +/* Janz ICAN3 CAN Event Indication Message Types */ +#define CEVTIND_EI 0x01 +#define CEVTIND_DOI 0x02 +#define CEVTIND_LOST 0x04 +#define CEVTIND_FULL 0x08 +#define CEVTIND_BEI 0x10 + +#define CEVTIND_CHIP_SJA1000 0x02 + +#define ICAN3_BUSERR_QUOTA_MAX 255 + +/* Janz ICAN3 CAN Frame Conversion */ +#define ICAN3_ECHO 0x10 +#define ICAN3_EFF_RTR 0x40 +#define ICAN3_SFF_RTR 0x10 +#define ICAN3_EFF 0x80 + +#define ICAN3_CAN_TYPE_MASK 0x0f +#define ICAN3_CAN_TYPE_SFF 0x00 +#define ICAN3_CAN_TYPE_EFF 0x01 + +#define ICAN3_CAN_DLC_MASK 0x0f + +/* + * SJA1000 Status and Error Register Definitions + * + * Copied from drivers/net/can/sja1000/sja1000.h + */ + +/* status register content */ +#define SR_BS 0x80 +#define SR_ES 0x40 +#define SR_TS 0x20 +#define SR_RS 0x10 +#define SR_TCS 0x08 +#define SR_TBS 0x04 +#define SR_DOS 0x02 +#define SR_RBS 0x01 + +#define SR_CRIT (SR_BS|SR_ES) + +/* ECC register */ +#define ECC_SEG 0x1F +#define ECC_DIR 0x20 +#define ECC_ERR 6 +#define ECC_BIT 0x00 +#define ECC_FORM 0x40 +#define ECC_STUFF 0x80 +#define ECC_MASK 0xc0 + +/* Number of buffers for use in the "new-style" host interface */ +#define ICAN3_NEW_BUFFERS 16 + +/* Number of buffers for use in the "fast" host interface */ +#define ICAN3_TX_BUFFERS 512 +#define ICAN3_RX_BUFFERS 1024 + +/* SJA1000 Clock Input */ +#define ICAN3_CAN_CLOCK 8000000 + +/* Driver Name */ +#define DRV_NAME "janz-ican3" + +/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */ +struct ican3_dpm_control { + /* window address register */ + u8 window_address; + u8 unused1; + + /* + * Read access: clear interrupt from microcontroller + * Write access: send interrupt to microcontroller + */ + u8 interrupt; + u8 unused2; + + /* write-only: reset all hardware on the module */ + u8 hwreset; + u8 unused3; + + /* write-only: generate an interrupt to the TPU */ + u8 tpuinterrupt; +}; + +struct ican3_dev { + + /* must be the first member */ + struct can_priv can; + + /* CAN network device */ + struct net_device *ndev; + struct napi_struct napi; + + /* Device for printing */ + struct device *dev; + + /* module number */ + unsigned int num; + + /* base address of registers and IRQ */ + struct janz_cmodio_onboard_regs __iomem *ctrl; + struct ican3_dpm_control __iomem *dpmctrl; + void __iomem *dpm; + int irq; + + /* CAN bus termination status */ + struct completion termination_comp; + bool termination_enabled; + + /* CAN bus error status registers */ + struct completion buserror_comp; + struct can_berr_counter bec; + + /* old and new style host interface */ + unsigned int iftype; + + /* + * Any function which changes the current DPM page must hold this + * lock while it is performing data accesses. This ensures that the + * function will not be preempted and end up reading data from a + * different DPM page than it expects. + */ + spinlock_t lock; + + /* new host interface */ + unsigned int rx_int; + unsigned int rx_num; + unsigned int tx_num; + + /* fast host interface */ + unsigned int fastrx_start; + unsigned int fastrx_int; + unsigned int fastrx_num; + unsigned int fasttx_start; + unsigned int fasttx_num; + + /* first free DPM page */ + unsigned int free_page; +}; + +struct ican3_msg { + u8 control; + u8 spec; + __le16 len; + u8 data[252]; +}; + +struct ican3_new_desc { + u8 control; + u8 pointer; +}; + +struct ican3_fast_desc { + u8 control; + u8 command; + u8 data[14]; +}; + +/* write to the window basic address register */ +static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page) +{ + BUG_ON(page >= DPM_NUM_PAGES); + iowrite8(page, &mod->dpmctrl->window_address); +} + +/* + * ICAN3 "old-style" host interface + */ + +/* + * Receive a message from the ICAN3 "old-style" firmware interface + * + * LOCKING: must hold mod->lock + * + * returns 0 on success, -ENOMEM when no message exists + */ +static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned int mbox, mbox_page; + u8 locl, peer, xord; + + /* get the MSYNC registers */ + ican3_set_page(mod, QUEUE_OLD_CONTROL); + peer = ioread8(mod->dpm + MSYNC_PEER); + locl = ioread8(mod->dpm + MSYNC_LOCL); + xord = locl ^ peer; + + if ((xord & MSYNC_RB_MASK) == 0x00) { + dev_dbg(mod->dev, "no mbox for reading\n"); + return -ENOMEM; + } + + /* find the first free mbox to read */ + if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK) + mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1; + else + mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1; + + /* copy the message */ + mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1; + ican3_set_page(mod, mbox_page); + memcpy_fromio(msg, mod->dpm, sizeof(*msg)); + + /* + * notify the firmware that the read buffer is available + * for it to fill again + */ + locl ^= mbox; + + ican3_set_page(mod, QUEUE_OLD_CONTROL); + iowrite8(locl, mod->dpm + MSYNC_LOCL); + return 0; +} + +/* + * Send a message through the "old-style" firmware interface + * + * LOCKING: must hold mod->lock + * + * returns 0 on success, -ENOMEM when no free space exists + */ +static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned int mbox, mbox_page; + u8 locl, peer, xord; + + /* get the MSYNC registers */ + ican3_set_page(mod, QUEUE_OLD_CONTROL); + peer = ioread8(mod->dpm + MSYNC_PEER); + locl = ioread8(mod->dpm + MSYNC_LOCL); + xord = locl ^ peer; + + if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) { + dev_err(mod->dev, "no mbox for writing\n"); + return -ENOMEM; + } + + /* calculate a free mbox to use */ + mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0; + + /* copy the message to the DPM */ + mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1; + ican3_set_page(mod, mbox_page); + memcpy_toio(mod->dpm, msg, sizeof(*msg)); + + locl ^= mbox; + if (mbox == MSYNC_WB1) + locl |= MSYNC_WBLW; + + ican3_set_page(mod, QUEUE_OLD_CONTROL); + iowrite8(locl, mod->dpm + MSYNC_LOCL); + return 0; +} + +/* + * ICAN3 "new-style" Host Interface Setup + */ + +static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod) +{ + struct ican3_new_desc desc; + unsigned long flags; + void __iomem *dst; + int i; + + spin_lock_irqsave(&mod->lock, flags); + + /* setup the internal datastructures for RX */ + mod->rx_num = 0; + mod->rx_int = 0; + + /* tohost queue descriptors are in page 5 */ + ican3_set_page(mod, QUEUE_TOHOST); + dst = mod->dpm; + + /* initialize the tohost (rx) queue descriptors: pages 9-24 */ + for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { + desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */ + desc.pointer = mod->free_page; + + /* set wrap flag on last buffer */ + if (i == ICAN3_NEW_BUFFERS - 1) + desc.control |= DESC_WRAP; + + memcpy_toio(dst, &desc, sizeof(desc)); + dst += sizeof(desc); + mod->free_page++; + } + + /* fromhost (tx) mid queue descriptors are in page 6 */ + ican3_set_page(mod, QUEUE_FROMHOST_MID); + dst = mod->dpm; + + /* setup the internal datastructures for TX */ + mod->tx_num = 0; + + /* initialize the fromhost mid queue descriptors: pages 25-40 */ + for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { + desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */ + desc.pointer = mod->free_page; + + /* set wrap flag on last buffer */ + if (i == ICAN3_NEW_BUFFERS - 1) + desc.control |= DESC_WRAP; + + memcpy_toio(dst, &desc, sizeof(desc)); + dst += sizeof(desc); + mod->free_page++; + } + + /* fromhost hi queue descriptors are in page 7 */ + ican3_set_page(mod, QUEUE_FROMHOST_HIGH); + dst = mod->dpm; + + /* initialize only a single buffer in the fromhost hi queue (unused) */ + desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ + desc.pointer = mod->free_page; + memcpy_toio(dst, &desc, sizeof(desc)); + mod->free_page++; + + /* fromhost low queue descriptors are in page 8 */ + ican3_set_page(mod, QUEUE_FROMHOST_LOW); + dst = mod->dpm; + + /* initialize only a single buffer in the fromhost low queue (unused) */ + desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ + desc.pointer = mod->free_page; + memcpy_toio(dst, &desc, sizeof(desc)); + mod->free_page++; + + spin_unlock_irqrestore(&mod->lock, flags); +} + +/* + * ICAN3 Fast Host Interface Setup + */ + +static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod) +{ + struct ican3_fast_desc desc; + unsigned long flags; + unsigned int addr; + void __iomem *dst; + int i; + + spin_lock_irqsave(&mod->lock, flags); + + /* save the start recv page */ + mod->fastrx_start = mod->free_page; + mod->fastrx_num = 0; + mod->fastrx_int = 0; + + /* build a single fast tohost queue descriptor */ + memset(&desc, 0, sizeof(desc)); + desc.control = 0x00; + desc.command = 1; + + /* build the tohost queue descriptor ring in memory */ + addr = 0; + for (i = 0; i < ICAN3_RX_BUFFERS; i++) { + + /* set the wrap bit on the last buffer */ + if (i == ICAN3_RX_BUFFERS - 1) + desc.control |= DESC_WRAP; + + /* switch to the correct page */ + ican3_set_page(mod, mod->free_page); + + /* copy the descriptor to the DPM */ + dst = mod->dpm + addr; + memcpy_toio(dst, &desc, sizeof(desc)); + addr += sizeof(desc); + + /* move to the next page if necessary */ + if (addr >= DPM_PAGE_SIZE) { + addr = 0; + mod->free_page++; + } + } + + /* make sure we page-align the next queue */ + if (addr != 0) + mod->free_page++; + + /* save the start xmit page */ + mod->fasttx_start = mod->free_page; + mod->fasttx_num = 0; + + /* build a single fast fromhost queue descriptor */ + memset(&desc, 0, sizeof(desc)); + desc.control = DESC_VALID; + desc.command = 1; + + /* build the fromhost queue descriptor ring in memory */ + addr = 0; + for (i = 0; i < ICAN3_TX_BUFFERS; i++) { + + /* set the wrap bit on the last buffer */ + if (i == ICAN3_TX_BUFFERS - 1) + desc.control |= DESC_WRAP; + + /* switch to the correct page */ + ican3_set_page(mod, mod->free_page); + + /* copy the descriptor to the DPM */ + dst = mod->dpm + addr; + memcpy_toio(dst, &desc, sizeof(desc)); + addr += sizeof(desc); + + /* move to the next page if necessary */ + if (addr >= DPM_PAGE_SIZE) { + addr = 0; + mod->free_page++; + } + } + + spin_unlock_irqrestore(&mod->lock, flags); +} + +/* + * ICAN3 "new-style" Host Interface Message Helpers + */ + +/* + * LOCKING: must hold mod->lock + */ +static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct ican3_new_desc desc; + void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc)); + + /* switch to the fromhost mid queue, and read the buffer descriptor */ + ican3_set_page(mod, QUEUE_FROMHOST_MID); + memcpy_fromio(&desc, desc_addr, sizeof(desc)); + + if (!(desc.control & DESC_VALID)) { + dev_dbg(mod->dev, "%s: no free buffers\n", __func__); + return -ENOMEM; + } + + /* switch to the data page, copy the data */ + ican3_set_page(mod, desc.pointer); + memcpy_toio(mod->dpm, msg, sizeof(*msg)); + + /* switch back to the descriptor, set the valid bit, write it back */ + ican3_set_page(mod, QUEUE_FROMHOST_MID); + desc.control ^= DESC_VALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* update the tx number */ + mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1); + return 0; +} + +/* + * LOCKING: must hold mod->lock + */ +static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct ican3_new_desc desc; + void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc)); + + /* switch to the tohost queue, and read the buffer descriptor */ + ican3_set_page(mod, QUEUE_TOHOST); + memcpy_fromio(&desc, desc_addr, sizeof(desc)); + + if (!(desc.control & DESC_VALID)) { + dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__); + return -ENOMEM; + } + + /* switch to the data page, copy the data */ + ican3_set_page(mod, desc.pointer); + memcpy_fromio(msg, mod->dpm, sizeof(*msg)); + + /* switch back to the descriptor, toggle the valid bit, write it back */ + ican3_set_page(mod, QUEUE_TOHOST); + desc.control ^= DESC_VALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* update the rx number */ + mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1); + return 0; +} + +/* + * Message Send / Recv Helpers + */ + +static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&mod->lock, flags); + + if (mod->iftype == 0) + ret = ican3_old_send_msg(mod, msg); + else + ret = ican3_new_send_msg(mod, msg); + + spin_unlock_irqrestore(&mod->lock, flags); + return ret; +} + +static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&mod->lock, flags); + + if (mod->iftype == 0) + ret = ican3_old_recv_msg(mod, msg); + else + ret = ican3_new_recv_msg(mod, msg); + + spin_unlock_irqrestore(&mod->lock, flags); + return ret; +} + +/* + * Quick Pre-constructed Messages + */ + +static int __devinit ican3_msg_connect(struct ican3_dev *mod) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CONNECTI; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); +} + +static int __devexit ican3_msg_disconnect(struct ican3_dev *mod) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_DISCONNECT; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); +} + +static int __devinit ican3_msg_newhostif(struct ican3_dev *mod) +{ + struct ican3_msg msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_NEWHOSTIF; + msg.len = cpu_to_le16(0); + + /* If we're not using the old interface, switching seems bogus */ + WARN_ON(mod->iftype != 0); + + ret = ican3_send_msg(mod, &msg); + if (ret) + return ret; + + /* mark the module as using the new host interface */ + mod->iftype = 1; + return 0; +} + +static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod) +{ + struct ican3_msg msg; + unsigned int addr; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_INITFDPMQUEUE; + msg.len = cpu_to_le16(8); + + /* write the tohost queue start address */ + addr = DPM_PAGE_ADDR(mod->fastrx_start); + msg.data[0] = addr & 0xff; + msg.data[1] = (addr >> 8) & 0xff; + msg.data[2] = (addr >> 16) & 0xff; + msg.data[3] = (addr >> 24) & 0xff; + + /* write the fromhost queue start address */ + addr = DPM_PAGE_ADDR(mod->fasttx_start); + msg.data[4] = addr & 0xff; + msg.data[5] = (addr >> 8) & 0xff; + msg.data[6] = (addr >> 16) & 0xff; + msg.data[7] = (addr >> 24) & 0xff; + + /* If we're not using the new interface yet, we cannot do this */ + WARN_ON(mod->iftype != 1); + + return ican3_send_msg(mod, &msg); +} + +/* + * Setup the CAN filter to either accept or reject all + * messages from the CAN bus. + */ +static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept) +{ + struct ican3_msg msg; + int ret; + + /* Standard Frame Format */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_SETAFILMASK; + msg.len = cpu_to_le16(5); + msg.data[0] = 0x00; /* IDLo LSB */ + msg.data[1] = 0x00; /* IDLo MSB */ + msg.data[2] = 0xff; /* IDHi LSB */ + msg.data[3] = 0x07; /* IDHi MSB */ + + /* accept all frames for fast host if, or reject all frames */ + msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; + + ret = ican3_send_msg(mod, &msg); + if (ret) + return ret; + + /* Extended Frame Format */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_SETAFILMASK; + msg.len = cpu_to_le16(13); + msg.data[0] = 0; /* MUX = 0 */ + msg.data[1] = 0x00; /* IDLo LSB */ + msg.data[2] = 0x00; + msg.data[3] = 0x00; + msg.data[4] = 0x20; /* IDLo MSB */ + msg.data[5] = 0xff; /* IDHi LSB */ + msg.data[6] = 0xff; + msg.data[7] = 0xff; + msg.data[8] = 0x3f; /* IDHi MSB */ + + /* accept all frames for fast host if, or reject all frames */ + msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; + + return ican3_send_msg(mod, &msg); +} + +/* + * Bring the CAN bus online or offline + */ +static int ican3_set_bus_state(struct ican3_dev *mod, bool on) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = on ? MSG_CONREQ : MSG_COFFREQ; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); +} + +static int ican3_set_termination(struct ican3_dev *mod, bool on) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_HWCONF; + msg.len = cpu_to_le16(2); + msg.data[0] = 0x00; + msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF; + + return ican3_send_msg(mod, &msg); +} + +static int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_INQUIRY; + msg.len = cpu_to_le16(2); + msg.data[0] = subspec; + msg.data[1] = 0x00; + + return ican3_send_msg(mod, &msg); +} + +static int ican3_set_buserror(struct ican3_dev *mod, u8 quota) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CCONFREQ; + msg.len = cpu_to_le16(2); + msg.data[0] = 0x00; + msg.data[1] = quota; + + return ican3_send_msg(mod, &msg); +} + +/* + * ICAN3 to Linux CAN Frame Conversion + */ + +static void ican3_to_can_frame(struct ican3_dev *mod, + struct ican3_fast_desc *desc, + struct can_frame *cf) +{ + if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) { + if (desc->data[1] & ICAN3_SFF_RTR) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_id |= desc->data[0] << 3; + cf->can_id |= (desc->data[1] & 0xe0) >> 5; + cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK; + memcpy(cf->data, &desc->data[2], sizeof(cf->data)); + } else { + cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK; + if (desc->data[0] & ICAN3_EFF_RTR) + cf->can_id |= CAN_RTR_FLAG; + + if (desc->data[0] & ICAN3_EFF) { + cf->can_id |= CAN_EFF_FLAG; + cf->can_id |= desc->data[2] << 21; /* 28-21 */ + cf->can_id |= desc->data[3] << 13; /* 20-13 */ + cf->can_id |= desc->data[4] << 5; /* 12-5 */ + cf->can_id |= (desc->data[5] & 0xf8) >> 3; + } else { + cf->can_id |= desc->data[2] << 3; /* 10-3 */ + cf->can_id |= desc->data[3] >> 5; /* 2-0 */ + } + + memcpy(cf->data, &desc->data[6], sizeof(cf->data)); + } +} + +static void can_frame_to_ican3(struct ican3_dev *mod, + struct can_frame *cf, + struct ican3_fast_desc *desc) +{ + /* clear out any stale data in the descriptor */ + memset(desc->data, 0, sizeof(desc->data)); + + /* we always use the extended format, with the ECHO flag set */ + desc->command = ICAN3_CAN_TYPE_EFF; + desc->data[0] |= cf->can_dlc; + desc->data[1] |= ICAN3_ECHO; + + if (cf->can_id & CAN_RTR_FLAG) + desc->data[0] |= ICAN3_EFF_RTR; + + /* pack the id into the correct places */ + if (cf->can_id & CAN_EFF_FLAG) { + desc->data[0] |= ICAN3_EFF; + desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */ + desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */ + desc->data[4] = (cf->can_id & 0x00001fe0) >> 5; /* 12-5 */ + desc->data[5] = (cf->can_id & 0x0000001f) << 3; /* 4-0 */ + } else { + desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */ + desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0 */ + } + + /* copy the data bits into the descriptor */ + memcpy(&desc->data[6], cf->data, sizeof(cf->data)); +} + +/* + * Interrupt Handling + */ + +/* + * Handle an ID + Version message response from the firmware. We never generate + * this message in production code, but it is very useful when debugging to be + * able to display this message. + */ +static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg) +{ + dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data); +} + +static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct net_device *dev = mod->ndev; + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* + * Report that communication messages with the microcontroller firmware + * are being lost. These are never CAN frames, so we do not generate an + * error frame for userspace + */ + if (msg->spec == MSG_MSGLOST) { + dev_err(mod->dev, "lost %d control messages\n", msg->data[0]); + return; + } + + /* + * Oops, this indicates that we have lost messages in the fast queue, + * which are exclusively CAN messages. Our driver isn't reading CAN + * frames fast enough. + * + * We'll pretend that the SJA1000 told us that it ran out of buffer + * space, because there is not a better message for this. + */ + skb = alloc_can_err_skb(dev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_errors++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +/* + * Handle CAN Event Indication Messages from the firmware + * + * The ICAN3 firmware provides the values of some SJA1000 registers when it + * generates this message. The code below is largely copied from the + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary + */ +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct net_device *dev = mod->ndev; + struct net_device_stats *stats = &dev->stats; + enum can_state state = mod->can.state; + u8 status, isrc, rxerr, txerr; + struct can_frame *cf; + struct sk_buff *skb; + + /* we can only handle the SJA1000 part */ + if (msg->data[1] != CEVTIND_CHIP_SJA1000) { + dev_err(mod->dev, "unable to handle errors on non-SJA1000\n"); + return -ENODEV; + } + + /* check the message length for sanity */ + if (le16_to_cpu(msg->len) < 6) { + dev_err(mod->dev, "error message too short\n"); + return -EINVAL; + } + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + isrc = msg->data[0]; + status = msg->data[3]; + rxerr = msg->data[4]; + txerr = msg->data[5]; + + /* data overrun interrupt */ + if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) { + dev_dbg(mod->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++; + } + + /* error warning + passive interrupt */ + if (isrc == CEVTIND_EI) { + dev_dbg(mod->dev, "error warning + passive interrupt\n"); + if (status & SR_BS) { + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } else if (status & SR_ES) { + if (rxerr >= 128 || txerr >= 128) + state = CAN_STATE_ERROR_PASSIVE; + else + state = CAN_STATE_ERROR_WARNING; + } else { + state = CAN_STATE_ERROR_ACTIVE; + } + } + + /* bus error interrupt */ + if (isrc == CEVTIND_BEI) { + u8 ecc = msg->data[2]; + + dev_dbg(mod->dev, "bus error interrupt\n"); + mod->can.can_stats.bus_error++; + stats->rx_errors++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & ECC_MASK) { + case ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & ECC_SEG; + break; + } + + if ((ecc & ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING || + state == CAN_STATE_ERROR_PASSIVE)) { + cf->can_id |= CAN_ERR_CRTL; + if (state == CAN_STATE_ERROR_WARNING) { + mod->can.can_stats.error_warning++; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } else { + mod->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; + } + + mod->can.state = state; + stats->rx_errors++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + return 0; +} + +static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) +{ + switch (msg->data[0]) { + case INQUIRY_STATUS: + case INQUIRY_EXTENDED: + mod->bec.rxerr = msg->data[5]; + mod->bec.txerr = msg->data[6]; + complete(&mod->buserror_comp); + break; + case INQUIRY_TERMINATION: + mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON; + complete(&mod->termination_comp); + break; + default: + dev_err(mod->dev, "received an unknown inquiry response\n"); + break; + } +} + +static void ican3_handle_unknown_message(struct ican3_dev *mod, + struct ican3_msg *msg) +{ + dev_warn(mod->dev, "received unknown message: spec 0x%.2x length %d\n", + msg->spec, le16_to_cpu(msg->len)); +} + +/* + * Handle a control message from the firmware + */ +static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) +{ + dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__, + mod->num, msg->spec, le16_to_cpu(msg->len)); + + switch (msg->spec) { + case MSG_IDVERS: + ican3_handle_idvers(mod, msg); + break; + case MSG_MSGLOST: + case MSG_FMSGLOST: + ican3_handle_msglost(mod, msg); + break; + case MSG_CEVTIND: + ican3_handle_cevtind(mod, msg); + break; + case MSG_INQUIRY: + ican3_handle_inquiry(mod, msg); + break; + default: + ican3_handle_unknown_message(mod, msg); + break; + } +} + +/* + * Check that there is room in the TX ring to transmit another skb + * + * LOCKING: must hold mod->lock + */ +static bool ican3_txok(struct ican3_dev *mod) +{ + struct ican3_fast_desc __iomem *desc; + u8 control; + + /* copy the control bits of the descriptor */ + ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); + desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc)); + control = ioread8(&desc->control); + + /* if the control bits are not valid, then we have no more space */ + if (!(control & DESC_VALID)) + return false; + + return true; +} + +/* + * Receive one CAN frame from the hardware + * + * CONTEXT: must be called from user context + */ +static int ican3_recv_skb(struct ican3_dev *mod) +{ + struct net_device *ndev = mod->ndev; + struct net_device_stats *stats = &ndev->stats; + struct ican3_fast_desc desc; + void __iomem *desc_addr; + struct can_frame *cf; + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&mod->lock, flags); + + /* copy the whole descriptor */ + ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); + desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc)); + memcpy_fromio(&desc, desc_addr, sizeof(desc)); + + spin_unlock_irqrestore(&mod->lock, flags); + + /* check that we actually have a CAN frame */ + if (!(desc.control & DESC_VALID)) + return -ENOBUFS; + + /* allocate an skb */ + skb = alloc_can_skb(ndev, &cf); + if (unlikely(skb == NULL)) { + stats->rx_dropped++; + goto err_noalloc; + } + + /* convert the ICAN3 frame into Linux CAN format */ + ican3_to_can_frame(mod, &desc, cf); + + /* receive the skb, update statistics */ + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + +err_noalloc: + /* toggle the valid bit and return the descriptor to the ring */ + desc.control ^= DESC_VALID; + + spin_lock_irqsave(&mod->lock, flags); + + ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); + memcpy_toio(desc_addr, &desc, 1); + + /* update the next buffer pointer */ + mod->fastrx_num = (desc.control & DESC_WRAP) ? 0 + : (mod->fastrx_num + 1); + + /* there are still more buffers to process */ + spin_unlock_irqrestore(&mod->lock, flags); + return 0; +} + +static int ican3_napi(struct napi_struct *napi, int budget) +{ + struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi); + struct ican3_msg msg; + unsigned long flags; + int received = 0; + int ret; + + /* process all communication messages */ + while (true) { + ret = ican3_recv_msg(mod, &msg); + if (ret) + break; + + ican3_handle_message(mod, &msg); + } + + /* process all CAN frames from the fast interface */ + while (received < budget) { + ret = ican3_recv_skb(mod); + if (ret) + break; + + received++; + } + + /* We have processed all packets that the adapter had, but it + * was less than our budget, stop polling */ + if (received < budget) + napi_complete(napi); + + spin_lock_irqsave(&mod->lock, flags); + + /* Wake up the transmit queue if necessary */ + if (netif_queue_stopped(mod->ndev) && ican3_txok(mod)) + netif_wake_queue(mod->ndev); + + spin_unlock_irqrestore(&mod->lock, flags); + + /* re-enable interrupt generation */ + iowrite8(1 << mod->num, &mod->ctrl->int_enable); + return received; +} + +static irqreturn_t ican3_irq(int irq, void *dev_id) +{ + struct ican3_dev *mod = dev_id; + u8 stat; + + /* + * The interrupt status register on this device reports interrupts + * as zeroes instead of using ones like most other devices + */ + stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num); + if (stat == (1 << mod->num)) + return IRQ_NONE; + + /* clear the MODULbus interrupt from the microcontroller */ + ioread8(&mod->dpmctrl->interrupt); + + /* disable interrupt generation, schedule the NAPI poller */ + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + napi_schedule(&mod->napi); + return IRQ_HANDLED; +} + +/* + * Firmware reset, startup, and shutdown + */ + +/* + * Reset an ICAN module to its power-on state + * + * CONTEXT: no network device registered + */ +static int ican3_reset_module(struct ican3_dev *mod) +{ + u8 val = 1 << mod->num; + unsigned long start; + u8 runold, runnew; + + /* disable interrupts so no more work is scheduled */ + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + + /* the first unallocated page in the DPM is #9 */ + mod->free_page = DPM_FREE_START; + + ican3_set_page(mod, QUEUE_OLD_CONTROL); + runold = ioread8(mod->dpm + TARGET_RUNNING); + + /* reset the module */ + iowrite8(val, &mod->ctrl->reset_assert); + iowrite8(val, &mod->ctrl->reset_deassert); + + /* wait until the module has finished resetting and is running */ + start = jiffies; + do { + ican3_set_page(mod, QUEUE_OLD_CONTROL); + runnew = ioread8(mod->dpm + TARGET_RUNNING); + if (runnew == (runold ^ 0xff)) + return 0; + + msleep(10); + } while (time_before(jiffies, start + HZ / 4)); + + dev_err(mod->dev, "failed to reset CAN module\n"); + return -ETIMEDOUT; +} + +static void __devexit ican3_shutdown_module(struct ican3_dev *mod) +{ + ican3_msg_disconnect(mod); + ican3_reset_module(mod); +} + +/* + * Startup an ICAN module, bringing it into fast mode + */ +static int __devinit ican3_startup_module(struct ican3_dev *mod) +{ + int ret; + + ret = ican3_reset_module(mod); + if (ret) { + dev_err(mod->dev, "unable to reset module\n"); + return ret; + } + + /* re-enable interrupts so we can send messages */ + iowrite8(1 << mod->num, &mod->ctrl->int_enable); + + ret = ican3_msg_connect(mod); + if (ret) { + dev_err(mod->dev, "unable to connect to module\n"); + return ret; + } + + ican3_init_new_host_interface(mod); + ret = ican3_msg_newhostif(mod); + if (ret) { + dev_err(mod->dev, "unable to switch to new-style interface\n"); + return ret; + } + + /* default to "termination on" */ + ret = ican3_set_termination(mod, true); + if (ret) { + dev_err(mod->dev, "unable to enable termination\n"); + return ret; + } + + /* default to "bus errors enabled" */ + ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX); + if (ret) { + dev_err(mod->dev, "unable to set bus-error\n"); + return ret; + } + + ican3_init_fast_host_interface(mod); + ret = ican3_msg_fasthostif(mod); + if (ret) { + dev_err(mod->dev, "unable to switch to fast host interface\n"); + return ret; + } + + ret = ican3_set_id_filter(mod, true); + if (ret) { + dev_err(mod->dev, "unable to set acceptance filter\n"); + return ret; + } + + return 0; +} + +/* + * CAN Network Device + */ + +static int ican3_open(struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + u8 quota; + int ret; + + /* open the CAN layer */ + ret = open_candev(ndev); + if (ret) { + dev_err(mod->dev, "unable to start CAN layer\n"); + return ret; + } + + /* set the bus error generation state appropriately */ + if (mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + quota = ICAN3_BUSERR_QUOTA_MAX; + else + quota = 0; + + ret = ican3_set_buserror(mod, quota); + if (ret) { + dev_err(mod->dev, "unable to set bus-error\n"); + close_candev(ndev); + return ret; + } + + /* bring the bus online */ + ret = ican3_set_bus_state(mod, true); + if (ret) { + dev_err(mod->dev, "unable to set bus-on\n"); + close_candev(ndev); + return ret; + } + + /* start up the network device */ + mod->can.state = CAN_STATE_ERROR_ACTIVE; + netif_start_queue(ndev); + + return 0; +} + +static int ican3_stop(struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + int ret; + + /* stop the network device xmit routine */ + netif_stop_queue(ndev); + mod->can.state = CAN_STATE_STOPPED; + + /* bring the bus offline, stop receiving packets */ + ret = ican3_set_bus_state(mod, false); + if (ret) { + dev_err(mod->dev, "unable to set bus-off\n"); + return ret; + } + + /* close the CAN layer */ + close_candev(ndev); + return 0; +} + +static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct ican3_fast_desc desc; + void __iomem *desc_addr; + unsigned long flags; + + spin_lock_irqsave(&mod->lock, flags); + + /* check that we can actually transmit */ + if (!ican3_txok(mod)) { + dev_err(mod->dev, "no free descriptors, stopping queue\n"); + netif_stop_queue(ndev); + spin_unlock_irqrestore(&mod->lock, flags); + return NETDEV_TX_BUSY; + } + + /* copy the control bits of the descriptor */ + ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); + desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc)); + memset(&desc, 0, sizeof(desc)); + memcpy_fromio(&desc, desc_addr, 1); + + /* convert the Linux CAN frame into ICAN3 format */ + can_frame_to_ican3(mod, cf, &desc); + + /* + * the programming manual says that you must set the IVALID bit, then + * interrupt, then set the valid bit. Quite weird, but it seems to be + * required for this to work + */ + desc.control |= DESC_IVALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* generate a MODULbus interrupt to the microcontroller */ + iowrite8(0x01, &mod->dpmctrl->interrupt); + + desc.control ^= DESC_VALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* update the next buffer pointer */ + mod->fasttx_num = (desc.control & DESC_WRAP) ? 0 + : (mod->fasttx_num + 1); + + /* update statistics */ + stats->tx_packets++; + stats->tx_bytes += cf->can_dlc; + kfree_skb(skb); + + /* + * This hardware doesn't have TX-done notifications, so we'll try and + * emulate it the best we can using ECHO skbs. Get the next TX + * descriptor, and see if we have room to send. If not, stop the queue. + * It will be woken when the ECHO skb for the current packet is recv'd. + */ + + /* copy the control bits of the descriptor */ + if (!ican3_txok(mod)) + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&mod->lock, flags); + return NETDEV_TX_OK; +} + +static const struct net_device_ops ican3_netdev_ops = { + .ndo_open = ican3_open, + .ndo_stop = ican3_stop, + .ndo_start_xmit = ican3_xmit, +}; + +/* + * Low-level CAN Device + */ + +/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */ +static struct can_bittiming_const ican3_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, +}; + +/* + * This routine was stolen from drivers/net/can/sja1000/sja1000.c + * + * The bittiming register command for the ICAN3 just sets the bit timing + * registers on the SJA1000 chip directly + */ +static int ican3_set_bittiming(struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + struct can_bittiming *bt = &mod->can.bittiming; + struct ican3_msg msg; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CBTRREQ; + msg.len = cpu_to_le16(4); + msg.data[0] = 0x00; + msg.data[1] = 0x00; + msg.data[2] = btr0; + msg.data[3] = btr1; + + return ican3_send_msg(mod, &msg); +} + +static int ican3_set_mode(struct net_device *ndev, enum can_mode mode) +{ + struct ican3_dev *mod = netdev_priv(ndev); + int ret; + + if (mode != CAN_MODE_START) + return -ENOTSUPP; + + /* bring the bus online */ + ret = ican3_set_bus_state(mod, true); + if (ret) { + dev_err(mod->dev, "unable to set bus-on\n"); + return ret; + } + + /* start up the network device */ + mod->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + + return 0; +} + +static int ican3_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ican3_dev *mod = netdev_priv(ndev); + int ret; + + ret = ican3_send_inquiry(mod, INQUIRY_STATUS); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&mod->buserror_comp, HZ); + if (ret <= 0) { + dev_info(mod->dev, "%s timed out\n", __func__); + return -ETIMEDOUT; + } + + bec->rxerr = mod->bec.rxerr; + bec->txerr = mod->bec.txerr; + return 0; +} + +/* + * Sysfs Attributes + */ + +static ssize_t ican3_sysfs_show_term(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); + int ret; + + ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&mod->termination_comp, HZ); + if (ret <= 0) { + dev_info(mod->dev, "%s timed out\n", __func__); + return -ETIMEDOUT; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", mod->termination_enabled); +} + +static ssize_t ican3_sysfs_set_term(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); + unsigned long enable; + int ret; + + if (strict_strtoul(buf, 0, &enable)) + return -EINVAL; + + ret = ican3_set_termination(mod, enable); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR(termination, S_IWUSR | S_IRUGO, ican3_sysfs_show_term, + ican3_sysfs_set_term); + +static struct attribute *ican3_sysfs_attrs[] = { + &dev_attr_termination.attr, + NULL, +}; + +static struct attribute_group ican3_sysfs_attr_group = { + .attrs = ican3_sysfs_attrs, +}; + +/* + * PCI Subsystem + */ + +static int __devinit ican3_probe(struct platform_device *pdev) +{ + struct janz_platform_data *pdata; + struct net_device *ndev; + struct ican3_dev *mod; + struct resource *res; + struct device *dev; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENXIO; + + dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno); + + /* save the struct device for printing */ + dev = &pdev->dev; + + /* allocate the CAN device and private data */ + ndev = alloc_candev(sizeof(*mod), 0); + if (!ndev) { + dev_err(dev, "unable to allocate CANdev\n"); + ret = -ENOMEM; + goto out_return; + } + + platform_set_drvdata(pdev, ndev); + mod = netdev_priv(ndev); + mod->ndev = ndev; + mod->dev = &pdev->dev; + mod->num = pdata->modno; + netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS); + spin_lock_init(&mod->lock); + init_completion(&mod->termination_comp); + init_completion(&mod->buserror_comp); + + /* setup device-specific sysfs attributes */ + ndev->sysfs_groups[0] = &ican3_sysfs_attr_group; + + /* the first unallocated page in the DPM is 9 */ + mod->free_page = DPM_FREE_START; + + ndev->netdev_ops = &ican3_netdev_ops; + ndev->flags |= IFF_ECHO; + SET_NETDEV_DEV(ndev, &pdev->dev); + + mod->can.clock.freq = ICAN3_CAN_CLOCK; + mod->can.bittiming_const = &ican3_bittiming_const; + mod->can.do_set_bittiming = ican3_set_bittiming; + mod->can.do_set_mode = ican3_set_mode; + mod->can.do_get_berr_counter = ican3_get_berr_counter; + mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES + | CAN_CTRLMODE_BERR_REPORTING; + + /* find our IRQ number */ + mod->irq = platform_get_irq(pdev, 0); + if (mod->irq < 0) { + dev_err(dev, "IRQ line not found\n"); + ret = -ENODEV; + goto out_free_ndev; + } + + ndev->irq = mod->irq; + + /* get access to the MODULbus registers for this module */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "MODULbus registers not found\n"); + ret = -ENODEV; + goto out_free_ndev; + } + + mod->dpm = ioremap(res->start, resource_size(res)); + if (!mod->dpm) { + dev_err(dev, "MODULbus registers not ioremap\n"); + ret = -ENOMEM; + goto out_free_ndev; + } + + mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE; + + /* get access to the control registers for this module */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "CONTROL registers not found\n"); + ret = -ENODEV; + goto out_iounmap_dpm; + } + + mod->ctrl = ioremap(res->start, resource_size(res)); + if (!mod->ctrl) { + dev_err(dev, "CONTROL registers not ioremap\n"); + ret = -ENOMEM; + goto out_iounmap_dpm; + } + + /* disable our IRQ, then hookup the IRQ handler */ + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_iounmap_ctrl; + } + + /* reset and initialize the CAN controller into fast mode */ + napi_enable(&mod->napi); + ret = ican3_startup_module(mod); + if (ret) { + dev_err(dev, "%s: unable to start CANdev\n", __func__); + goto out_free_irq; + } + + /* register with the Linux CAN layer */ + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "%s: unable to register CANdev\n", __func__); + goto out_free_irq; + } + + dev_info(dev, "module %d: registered CAN device\n", pdata->modno); + return 0; + +out_free_irq: + napi_disable(&mod->napi); + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + free_irq(mod->irq, mod); +out_iounmap_ctrl: + iounmap(mod->ctrl); +out_iounmap_dpm: + iounmap(mod->dpm); +out_free_ndev: + free_candev(ndev); +out_return: + return ret; +} + +static int __devexit ican3_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct ican3_dev *mod = netdev_priv(ndev); + + /* unregister the netdevice, stop interrupts */ + unregister_netdev(ndev); + napi_disable(&mod->napi); + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + free_irq(mod->irq, mod); + + /* put the module into reset */ + ican3_shutdown_module(mod); + + /* unmap all registers */ + iounmap(mod->ctrl); + iounmap(mod->dpm); + + free_candev(ndev); + + return 0; +} + +static struct platform_driver ican3_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ican3_probe, + .remove = __devexit_p(ican3_remove), +}; + +module_platform_driver(ican3_driver); + +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); +MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:janz-ican3"); diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c new file mode 100644 index 00000000..346785c5 --- /dev/null +++ b/drivers/net/can/mcp251x.c @@ -0,0 +1,1214 @@ +/* + * CAN bus driver for Microchip 251x CAN Controller with SPI Interface + * + * MCP2510 support and bug fixes by Christian Pellegrin + * <chripell@evolware.org> + * + * Copyright 2009 Christian Pellegrin EVOL S.r.l. + * + * 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, + * .power_enable = mcp251x_power_enable, + * .transceiver_enable = NULL, + * }; + * + * static struct spi_board_info spi_board_info[] = { + * { + * .modalias = "mcp2510", + * // or "mcp2515" depending on your controller + * .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/can/core.h> +#include <linux/can/dev.h> +#include <linux/can/platform/mcp251x.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/freezer.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/uaccess.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 CNF1_SJW_SHIFT 6 +#define CNF2 0x29 +# define CNF2_BTLMODE 0x80 +# define CNF2_SAM 0x40 +# define CNF2_PS1_SHIFT 3 +#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 CANINTF_RX (CANINTF_RX0IF | CANINTF_RX1IF) +# define CANINTF_TX (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF) +# define CANINTF_ERR (CANINTF_ERRIF) +#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 + TXBCTRL_OFF) +# define TXBCTRL_ABTF 0x40 +# define TXBCTRL_MLOA 0x20 +# define TXBCTRL_TXERR 0x10 +# define TXBCTRL_TXREQ 0x08 +#define TXBSIDH(n) (((n) * 0x10) + 0x30 + TXBSIDH_OFF) +# define SIDH_SHIFT 3 +#define TXBSIDL(n) (((n) * 0x10) + 0x30 + TXBSIDL_OFF) +# define SIDL_SID_MASK 7 +# define SIDL_SID_SHIFT 5 +# define SIDL_EXIDE_SHIFT 3 +# define SIDL_EID_SHIFT 16 +# define SIDL_EID_MASK 3 +#define TXBEID8(n) (((n) * 0x10) + 0x30 + TXBEID8_OFF) +#define TXBEID0(n) (((n) * 0x10) + 0x30 + TXBEID0_OFF) +#define TXBDLC(n) (((n) * 0x10) + 0x30 + TXBDLC_OFF) +# define DLC_RTR_SHIFT 6 +#define TXBCTRL_OFF 0 +#define TXBSIDH_OFF 1 +#define TXBSIDL_OFF 2 +#define TXBEID8_OFF 3 +#define TXBEID0_OFF 4 +#define TXBDLC_OFF 5 +#define TXBDAT_OFF 6 +#define RXBCTRL(n) (((n) * 0x10) + 0x60 + RXBCTRL_OFF) +# define RXBCTRL_BUKT 0x04 +# define RXBCTRL_RXM0 0x20 +# define RXBCTRL_RXM1 0x40 +#define RXBSIDH(n) (((n) * 0x10) + 0x60 + RXBSIDH_OFF) +# define RXBSIDH_SHIFT 3 +#define RXBSIDL(n) (((n) * 0x10) + 0x60 + RXBSIDL_OFF) +# define RXBSIDL_IDE 0x08 +# define RXBSIDL_SRR 0x10 +# define RXBSIDL_EID 3 +# define RXBSIDL_SHIFT 5 +#define RXBEID8(n) (((n) * 0x10) + 0x60 + RXBEID8_OFF) +#define RXBEID0(n) (((n) * 0x10) + 0x60 + RXBEID0_OFF) +#define RXBDLC(n) (((n) * 0x10) + 0x60 + RXBDLC_OFF) +# define RXBDLC_LEN_MASK 0x0f +# define RXBDLC_RTR 0x40 +#define RXBCTRL_OFF 0 +#define RXBSIDH_OFF 1 +#define RXBSIDL_OFF 2 +#define RXBEID8_OFF 3 +#define RXBEID0_OFF 4 +#define RXBDLC_OFF 5 +#define RXBDAT_OFF 6 +#define RXFSIDH(n) ((n) * 4) +#define RXFSIDL(n) ((n) * 4 + 1) +#define RXFEID8(n) ((n) * 4 + 2) +#define RXFEID0(n) ((n) * 4 + 3) +#define RXMSIDH(n) ((n) * 4 + 0x20) +#define RXMSIDL(n) ((n) * 4 + 0x21) +#define RXMEID8(n) ((n) * 4 + 0x22) +#define RXMEID0(n) ((n) * 4 + 0x23) + +#define GET_BYTE(val, byte) \ + (((val) >> ((byte) * 8)) & 0xff) +#define SET_BYTE(val, byte) \ + (((val) & 0xff) << ((byte) * 8)) + +/* + * 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 (6 + CAN_FRAME_MAX_DATA_LEN) +#define CAN_FRAME_MAX_BITS 128 + +#define TX_ECHO_SKB_MAX 1 + +#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 = { + .name = DEVICE_NAME, + .tseg1_min = 3, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +enum mcp251x_model { + CAN_MCP251X_MCP2510 = 0x2510, + CAN_MCP251X_MCP2515 = 0x2515, +}; + +struct mcp251x_priv { + struct can_priv can; + struct net_device *net; + struct spi_device *spi; + enum mcp251x_model model; + + struct mutex mcp_lock; /* SPI device 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; + int tx_len; + + struct workqueue_struct *wq; + struct work_struct tx_work; + struct work_struct restart_work; + + int force_quit; + int after_suspend; +#define AFTER_SUSPEND_UP 1 +#define AFTER_SUSPEND_DOWN 2 +#define AFTER_SUSPEND_POWER 4 +#define AFTER_SUSPEND_RESTART 8 + int restart_tx; +}; + +#define MCP251X_IS(_model) \ +static inline int mcp251x_is_##_model(struct spi_device *spi) \ +{ \ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); \ + return priv->model == CAN_MCP251X_MCP##_model; \ +} + +MCP251X_IS(2510); +MCP251X_IS(2515); + +static void mcp251x_clean(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + + if (priv->tx_skb || priv->tx_len) + net->stats.tx_errors++; + if (priv->tx_skb) + dev_kfree_skb(priv->tx_skb); + if (priv->tx_len) + can_free_echo_skb(priv->net, 0); + priv->tx_skb = NULL; + priv->tx_len = 0; +} + +/* + * Note about handling of error return of mcp251x_spi_trans: accessing + * registers via SPI is not really different conceptually than using + * normal I/O assembler instructions, although it's much more + * complicated from a practical POV. So it's not advisable to always + * check the return value of this function. Imagine that every + * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0) + * error();", it would be a great mess (well there are some situation + * when exception handling C++ like could be useful after all). So we + * just check that transfers are OK at the beginning of our + * conversation with the chip and to avoid doing really nasty things + * (like injecting bogus packets in the network stack). + */ +static int mcp251x_spi_trans(struct spi_device *spi, int len) +{ + 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 = len, + .cs_change = 0, + }; + struct spi_message m; + int ret; + + 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) + dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret); + return ret; +} + +static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + u8 val = 0; + + priv->spi_tx_buf[0] = INSTRUCTION_READ; + priv->spi_tx_buf[1] = reg; + + mcp251x_spi_trans(spi, 3); + val = priv->spi_rx_buf[2]; + + return val; +} + +static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg, + uint8_t *v1, uint8_t *v2) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + priv->spi_tx_buf[0] = INSTRUCTION_READ; + priv->spi_tx_buf[1] = reg; + + mcp251x_spi_trans(spi, 4); + + *v1 = priv->spi_rx_buf[2]; + *v2 = priv->spi_rx_buf[3]; +} + +static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + priv->spi_tx_buf[0] = INSTRUCTION_WRITE; + priv->spi_tx_buf[1] = reg; + priv->spi_tx_buf[2] = val; + + mcp251x_spi_trans(spi, 3); +} + +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); + + 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; + + mcp251x_spi_trans(spi, 4); +} + +static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf, + int len, int tx_buf_idx) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + if (mcp251x_is_2510(spi)) { + int i; + + for (i = 1; i < TXBDAT_OFF + len; i++) + mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + i, + buf[i]); + } else { + memcpy(priv->spi_tx_buf, buf, TXBDAT_OFF + len); + mcp251x_spi_trans(spi, TXBDAT_OFF + len); + } +} + +static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame, + int tx_buf_idx) +{ + u32 sid, eid, exide, rtr; + u8 buf[SPI_TRANSFER_BUF_LEN]; + + 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 */ + + buf[TXBCTRL_OFF] = INSTRUCTION_LOAD_TXB(tx_buf_idx); + buf[TXBSIDH_OFF] = sid >> SIDH_SHIFT; + buf[TXBSIDL_OFF] = ((sid & SIDL_SID_MASK) << SIDL_SID_SHIFT) | + (exide << SIDL_EXIDE_SHIFT) | + ((eid >> SIDL_EID_SHIFT) & SIDL_EID_MASK); + buf[TXBEID8_OFF] = GET_BYTE(eid, 1); + buf[TXBEID0_OFF] = GET_BYTE(eid, 0); + buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc; + memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc); + mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx); + mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ); +} + +static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, + int buf_idx) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + if (mcp251x_is_2510(spi)) { + int i, len; + + for (i = 1; i < RXBDAT_OFF; i++) + buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); + + len = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); + for (; i < (RXBDAT_OFF + len); i++) + buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); + } else { + priv->spi_tx_buf[RXBCTRL_OFF] = INSTRUCTION_READ_RXB(buf_idx); + mcp251x_spi_trans(spi, SPI_TRANSFER_BUF_LEN); + memcpy(buf, priv->spi_rx_buf, SPI_TRANSFER_BUF_LEN); + } +} + +static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct sk_buff *skb; + struct can_frame *frame; + u8 buf[SPI_TRANSFER_BUF_LEN]; + + skb = alloc_can_skb(priv->net, &frame); + if (!skb) { + dev_err(&spi->dev, "cannot allocate RX skb\n"); + priv->net->stats.rx_dropped++; + return; + } + + mcp251x_hw_rx_frame(spi, buf, buf_idx); + if (buf[RXBSIDL_OFF] & RXBSIDL_IDE) { + /* Extended ID format */ + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= + /* Extended ID part */ + SET_BYTE(buf[RXBSIDL_OFF] & RXBSIDL_EID, 2) | + SET_BYTE(buf[RXBEID8_OFF], 1) | + SET_BYTE(buf[RXBEID0_OFF], 0) | + /* Standard ID part */ + (((buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | + (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT)) << 18); + /* Remote transmission request */ + if (buf[RXBDLC_OFF] & RXBDLC_RTR) + frame->can_id |= CAN_RTR_FLAG; + } else { + /* Standard ID format */ + frame->can_id = + (buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | + (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT); + if (buf[RXBSIDL_OFF] & RXBSIDL_SRR) + frame->can_id |= CAN_RTR_FLAG; + } + /* Data length */ + frame->can_dlc = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); + memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc); + + priv->net->stats.rx_packets++; + priv->net->stats.rx_bytes += frame->can_dlc; + netif_rx_ni(skb); +} + +static void mcp251x_hw_sleep(struct spi_device *spi) +{ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP); +} + +static netdev_tx_t 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; + + if (priv->tx_skb || priv->tx_len) { + dev_warn(&spi->dev, "hard_xmit called while tx busy\n"); + return NETDEV_TX_BUSY; + } + + if (can_dropped_invalid_skb(net, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(net); + priv->tx_skb = skb; + 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); + + switch (mode) { + case CAN_MODE_START: + mcp251x_clean(net); + /* We have to delay work since SPI I/O may sleep */ + priv->can.state = CAN_STATE_ERROR_ACTIVE; + priv->restart_tx = 1; + if (priv->can.restart_ms == 0) + priv->after_suspend = AFTER_SUSPEND_RESTART; + queue_work(priv->wq, &priv->restart_work); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int 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 if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + /* Put device into listen-only mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LISTEN_ONLY); + } 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) & CANCTRL_REQOP_MASK) { + schedule(); + if (time_after(jiffies, timeout)) { + dev_err(&spi->dev, "MCP251x didn't" + " enter in normal mode\n"); + return -EBUSY; + } + } + } + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +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; + + mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << CNF1_SJW_SHIFT) | + (bt->brp - 1)); + mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE | + (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ? + CNF2_SAM : 0) | + ((bt->phase_seg1 - 1) << CNF2_PS1_SHIFT) | + (bt->prop_seg - 1)); + mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK, + (bt->phase_seg2 - 1)); + dev_info(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n", + mcp251x_read_reg(spi, CNF1), + mcp251x_read_reg(spi, CNF2), + mcp251x_read_reg(spi, CNF3)); + + return 0; +} + +static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv, + struct spi_device *spi) +{ + mcp251x_do_set_bittiming(net); + + mcp251x_write_reg(spi, RXBCTRL(0), + RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1); + mcp251x_write_reg(spi, RXBCTRL(1), + RXBCTRL_RXM0 | RXBCTRL_RXM1); + return 0; +} + +static int mcp251x_hw_reset(struct spi_device *spi) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + int ret; + unsigned long timeout; + + priv->spi_tx_buf[0] = INSTRUCTION_RESET; + ret = spi_write(spi, priv->spi_tx_buf, 1); + if (ret) { + dev_err(&spi->dev, "reset failed: ret = %d\n", ret); + return -EIO; + } + + /* Wait for reset to finish */ + timeout = jiffies + HZ; + mdelay(10); + while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) + != CANCTRL_REQOP_CONF) { + schedule(); + if (time_after(jiffies, timeout)) { + dev_err(&spi->dev, "MCP251x didn't" + " enter in conf mode after reset\n"); + return -EBUSY; + } + } + return 0; +} + +static int mcp251x_hw_probe(struct spi_device *spi) +{ + int st1, st2; + + mcp251x_hw_reset(spi); + + /* + * Please note that these are "magic values" based on after + * reset defaults taken from data sheet which allows us to see + * if we really have a chip on the bus (we avoid common all + * zeroes or all ones situations) + */ + st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE; + st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17; + + dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2); + + /* Check for power up default values */ + return (st1 == 0x80 && st2 == 0x07) ? 1 : 0; +} + +static void mcp251x_open_clean(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; + + free_irq(spi->irq, priv); + mcp251x_hw_sleep(spi); + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + close_candev(net); +} + +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; + + close_candev(net); + + priv->force_quit = 1; + free_irq(spi->irq, priv); + destroy_workqueue(priv->wq); + priv->wq = NULL; + + mutex_lock(&priv->mcp_lock); + + /* Disable and clear pending interrupts */ + mcp251x_write_reg(spi, CANINTE, 0x00); + mcp251x_write_reg(spi, CANINTF, 0x00); + + mcp251x_write_reg(spi, TXBCTRL(0), 0); + mcp251x_clean(net); + + mcp251x_hw_sleep(spi); + + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + + priv->can.state = CAN_STATE_STOPPED; + + mutex_unlock(&priv->mcp_lock); + + return 0; +} + +static void mcp251x_error_skb(struct net_device *net, int can_id, int data1) +{ + struct sk_buff *skb; + struct can_frame *frame; + + skb = alloc_can_err_skb(net, &frame); + if (skb) { + frame->can_id |= can_id; + frame->data[1] = data1; + netif_rx_ni(skb); + } else { + netdev_err(net, "cannot allocate error skb\n"); + } +} + +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 net_device *net = priv->net; + struct can_frame *frame; + + mutex_lock(&priv->mcp_lock); + if (priv->tx_skb) { + if (priv->can.state == CAN_STATE_BUS_OFF) { + mcp251x_clean(net); + } else { + 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); + priv->tx_len = 1 + frame->can_dlc; + can_put_echo_skb(priv->tx_skb, net, 0); + priv->tx_skb = NULL; + } + } + mutex_unlock(&priv->mcp_lock); +} + +static void mcp251x_restart_work_handler(struct work_struct *ws) +{ + struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv, + restart_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->mcp_lock); + if (priv->after_suspend) { + mdelay(10); + mcp251x_hw_reset(spi); + mcp251x_setup(net, priv, spi); + if (priv->after_suspend & AFTER_SUSPEND_RESTART) { + mcp251x_set_normal_mode(spi); + } else if (priv->after_suspend & AFTER_SUSPEND_UP) { + netif_device_attach(net); + mcp251x_clean(net); + mcp251x_set_normal_mode(spi); + netif_wake_queue(net); + } else { + mcp251x_hw_sleep(spi); + } + priv->after_suspend = 0; + priv->force_quit = 0; + } + + if (priv->restart_tx) { + priv->restart_tx = 0; + mcp251x_write_reg(spi, TXBCTRL(0), 0); + mcp251x_clean(net); + netif_wake_queue(net); + mcp251x_error_skb(net, CAN_ERR_RESTARTED, 0); + } + mutex_unlock(&priv->mcp_lock); +} + +static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) +{ + struct mcp251x_priv *priv = dev_id; + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->mcp_lock); + while (!priv->force_quit) { + enum can_state new_state; + u8 intf, eflag; + u8 clear_intf = 0; + int can_id = 0, data1 = 0; + + mcp251x_read_2regs(spi, CANINTF, &intf, &eflag); + + /* mask out flags we don't care about */ + intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; + + /* receive buffer 0 */ + if (intf & CANINTF_RX0IF) { + mcp251x_hw_rx(spi, 0); + /* + * Free one buffer ASAP + * (The MCP2515 does this automatically.) + */ + if (mcp251x_is_2510(spi)) + mcp251x_write_bits(spi, CANINTF, CANINTF_RX0IF, 0x00); + } + + /* receive buffer 1 */ + if (intf & CANINTF_RX1IF) { + mcp251x_hw_rx(spi, 1); + /* the MCP2515 does this automatically */ + if (mcp251x_is_2510(spi)) + clear_intf |= CANINTF_RX1IF; + } + + /* any error or tx interrupt we need to clear? */ + if (intf & (CANINTF_ERR | CANINTF_TX)) + clear_intf |= intf & (CANINTF_ERR | CANINTF_TX); + if (clear_intf) + mcp251x_write_bits(spi, CANINTF, clear_intf, 0x00); + + if (eflag) + mcp251x_write_bits(spi, EFLG, eflag, 0x00); + + /* Update can state */ + if (eflag & EFLG_TXBO) { + new_state = CAN_STATE_BUS_OFF; + can_id |= CAN_ERR_BUSOFF; + } else if (eflag & EFLG_TXEP) { + new_state = CAN_STATE_ERROR_PASSIVE; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_TX_PASSIVE; + } else if (eflag & EFLG_RXEP) { + new_state = CAN_STATE_ERROR_PASSIVE; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_PASSIVE; + } else if (eflag & EFLG_TXWAR) { + new_state = CAN_STATE_ERROR_WARNING; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_TX_WARNING; + } else if (eflag & EFLG_RXWAR) { + new_state = CAN_STATE_ERROR_WARNING; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_WARNING; + } else { + new_state = CAN_STATE_ERROR_ACTIVE; + } + + /* Update can state statistics */ + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (new_state >= CAN_STATE_ERROR_WARNING && + new_state <= CAN_STATE_BUS_OFF) + priv->can.can_stats.error_warning++; + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + if (new_state >= CAN_STATE_ERROR_PASSIVE && + new_state <= CAN_STATE_BUS_OFF) + priv->can.can_stats.error_passive++; + break; + default: + break; + } + priv->can.state = new_state; + + if (intf & CANINTF_ERRIF) { + /* Handle overflow counters */ + if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) { + if (eflag & EFLG_RX0OVR) { + net->stats.rx_over_errors++; + net->stats.rx_errors++; + } + if (eflag & EFLG_RX1OVR) { + net->stats.rx_over_errors++; + net->stats.rx_errors++; + } + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_OVERFLOW; + } + mcp251x_error_skb(net, can_id, data1); + } + + if (priv->can.state == CAN_STATE_BUS_OFF) { + if (priv->can.restart_ms == 0) { + priv->force_quit = 1; + can_bus_off(net); + mcp251x_hw_sleep(spi); + break; + } + } + + if (intf == 0) + break; + + if (intf & CANINTF_TX) { + net->stats.tx_packets++; + net->stats.tx_bytes += priv->tx_len - 1; + if (priv->tx_len) { + can_get_echo_skb(net, 0); + priv->tx_len = 0; + } + netif_wake_queue(net); + } + + } + mutex_unlock(&priv->mcp_lock); + return IRQ_HANDLED; +} + +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; + int ret; + + ret = open_candev(net); + if (ret) { + dev_err(&spi->dev, "unable to set initial baudrate!\n"); + return ret; + } + + mutex_lock(&priv->mcp_lock); + if (pdata->transceiver_enable) + pdata->transceiver_enable(1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_len = 0; + + ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, + pdata->irq_flags ? pdata->irq_flags : IRQF_TRIGGER_FALLING, + DEVICE_NAME, priv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + close_candev(net); + goto open_unlock; + } + + priv->wq = create_freezable_workqueue("mcp251x_wq"); + INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); + INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler); + + ret = mcp251x_hw_reset(spi); + if (ret) { + mcp251x_open_clean(net); + goto open_unlock; + } + ret = mcp251x_setup(net, priv, spi); + if (ret) { + mcp251x_open_clean(net); + goto open_unlock; + } + ret = mcp251x_set_normal_mode(spi); + if (ret) { + mcp251x_open_clean(net); + goto open_unlock; + } + netif_wake_queue(net); + +open_unlock: + mutex_unlock(&priv->mcp_lock); + return ret; +} + +static const struct net_device_ops mcp251x_netdev_ops = { + .ndo_open = mcp251x_open, + .ndo_stop = mcp251x_stop, + .ndo_start_xmit = mcp251x_hard_start_xmit, +}; + +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_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); + if (!net) { + ret = -ENOMEM; + goto error_alloc; + } + + net->netdev_ops = &mcp251x_netdev_ops; + net->flags |= IFF_ECHO; + + priv = netdev_priv(net); + priv->can.bittiming_const = &mcp251x_bittiming_const; + priv->can.do_set_mode = mcp251x_do_set_mode; + priv->can.clock.freq = pdata->oscillator_frequency / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; + priv->model = spi_get_device_id(spi)->driver_data; + priv->net = net; + dev_set_drvdata(&spi->dev, priv); + + priv->spi = spi; + mutex_init(&priv->mcp_lock); + + /* If requested, allocate DMA buffers */ + if (mcp251x_enable_dma) { + spi->dev.coherent_dma_mask = ~0; + + /* + * 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_rx_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); + + /* Configure the SPI bus */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + spi_setup(spi); + + /* Here is OK to not lock the MCP, no one knows about it yet */ + 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_candev(net); + if (!ret) { + dev_info(&spi->dev, "probed\n"); + return ret; + } +error_probe: + 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: + if (pdata->power_enable) + pdata->power_enable(0); + 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_candev(net); + free_candev(net); + + 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; + + priv->force_quit = 1; + disable_irq(spi->irq); + /* + * Note: at this point neither IST nor workqueues are running. + * open/stop cannot be called anyway so locking is not needed + */ + 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->restart_work); + } else { + if (priv->after_suspend & AFTER_SUSPEND_UP) { + if (pdata->transceiver_enable) + pdata->transceiver_enable(1); + queue_work(priv->wq, &priv->restart_work); + } else { + priv->after_suspend = 0; + } + } + priv->force_quit = 0; + enable_irq(spi->irq); + return 0; +} +#else +#define mcp251x_can_suspend NULL +#define mcp251x_can_resume NULL +#endif + +static const struct spi_device_id mcp251x_id_table[] = { + { "mcp2510", CAN_MCP251X_MCP2510 }, + { "mcp2515", CAN_MCP251X_MCP2515 }, + { }, +}; + +MODULE_DEVICE_TABLE(spi, mcp251x_id_table); + +static struct spi_driver mcp251x_can_driver = { + .driver = { + .name = DEVICE_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .id_table = mcp251x_id_table, + .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/drivers/net/can/mscan/Kconfig b/drivers/net/can/mscan/Kconfig new file mode 100644 index 00000000..d3870695 --- /dev/null +++ b/drivers/net/can/mscan/Kconfig @@ -0,0 +1,24 @@ +config CAN_MSCAN + depends on CAN_DEV && (PPC || M68K) + tristate "Support for Freescale MSCAN based chips" + ---help--- + The Motorola Scalable Controller Area Network (MSCAN) definition + is based on the MSCAN12 definition which is the specific + implementation of the Motorola Scalable CAN concept targeted for + the Motorola MC68HC12 Microcontroller Family. + +if CAN_MSCAN + +config CAN_MPC5XXX + tristate "Freescale MPC5xxx onboard CAN controller" + depends on (PPC_MPC52xx || PPC_MPC512x) + ---help--- + If you say yes here you get support for Freescale's MPC5xxx + onboard CAN controller. Currently, the MPC5200, MPC5200B and + MPC5121 (Rev. 2 and later) are supported. + + This driver can also be built as a module. If so, the module + will be called mscan-mpc5xxx.ko. + +endif + diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile new file mode 100644 index 00000000..c9fab17c --- /dev/null +++ b/drivers/net/can/mscan/Makefile @@ -0,0 +1,5 @@ + +obj-$(CONFIG_CAN_MPC5XXX) += mscan-mpc5xxx.o +mscan-mpc5xxx-objs := mscan.o mpc5xxx_can.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c new file mode 100644 index 00000000..5caa572d --- /dev/null +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -0,0 +1,418 @@ +/* + * CAN bus driver for the Freescale MPC5xxx embedded CPU. + * + * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2009 Wolfram Sang, Pengutronix <w.sang@pengutronix.de> + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/netdevice.h> +#include <linux/can/dev.h> +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <asm/mpc52xx.h> + +#include "mscan.h" + +#define DRV_NAME "mpc5xxx_can" + +struct mpc5xxx_can_data { + unsigned int type; + u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name, + int *mscan_clksrc); +}; + +#ifdef CONFIG_PPC_MPC52xx +static struct of_device_id __devinitdata mpc52xx_cdm_ids[] = { + { .compatible = "fsl,mpc5200-cdm", }, + {} +}; + +static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, + const char *clock_name, + int *mscan_clksrc) +{ + unsigned int pvr; + struct mpc52xx_cdm __iomem *cdm; + struct device_node *np_cdm; + unsigned int freq; + u32 val; + + pvr = mfspr(SPRN_PVR); + + /* + * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock + * (IP_CLK) can be selected as MSCAN clock source. According to + * the MPC5200 user's manual, the oscillator clock is the better + * choice as it has less jitter. For this reason, it is selected + * by default. Unfortunately, it can not be selected for the old + * MPC5200 Rev. A chips due to a hardware bug (check errata). + */ + if (clock_name && strcmp(clock_name, "ip") == 0) + *mscan_clksrc = MSCAN_CLKSRC_BUS; + else + *mscan_clksrc = MSCAN_CLKSRC_XTAL; + + freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node); + if (!freq) + return 0; + + if (*mscan_clksrc == MSCAN_CLKSRC_BUS || pvr == 0x80822011) + return freq; + + /* Determine SYS_XTAL_IN frequency from the clock domain settings */ + np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids); + if (!np_cdm) { + dev_err(&ofdev->dev, "can't get clock node!\n"); + return 0; + } + cdm = of_iomap(np_cdm, 0); + + if (in_8(&cdm->ipb_clk_sel) & 0x1) + freq *= 2; + val = in_be32(&cdm->rstcfg); + + freq *= (val & (1 << 5)) ? 8 : 4; + freq /= (val & (1 << 6)) ? 12 : 16; + + of_node_put(np_cdm); + iounmap(cdm); + + return freq; +} +#else /* !CONFIG_PPC_MPC52xx */ +static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, + const char *clock_name, + int *mscan_clksrc) +{ + return 0; +} +#endif /* CONFIG_PPC_MPC52xx */ + +#ifdef CONFIG_PPC_MPC512x +struct mpc512x_clockctl { + u32 spmr; /* System PLL Mode Reg */ + u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ + u32 scfr1; /* System Clk Freq Reg 1 */ + u32 scfr2; /* System Clk Freq Reg 2 */ + u32 reserved; + u32 bcr; /* Bread Crumb Reg */ + u32 pccr[12]; /* PSC Clk Ctrl Reg 0-11 */ + u32 spccr; /* SPDIF Clk Ctrl Reg */ + u32 cccr; /* CFM Clk Ctrl Reg */ + u32 dccr; /* DIU Clk Cnfg Reg */ + u32 mccr[4]; /* MSCAN Clk Ctrl Reg 1-3 */ +}; + +static struct of_device_id __devinitdata mpc512x_clock_ids[] = { + { .compatible = "fsl,mpc5121-clock", }, + {} +}; + +static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, + const char *clock_name, + int *mscan_clksrc) +{ + struct mpc512x_clockctl __iomem *clockctl; + struct device_node *np_clock; + struct clk *sys_clk, *ref_clk; + int plen, clockidx, clocksrc = -1; + u32 sys_freq, val, clockdiv = 1, freq = 0; + const u32 *pval; + + np_clock = of_find_matching_node(NULL, mpc512x_clock_ids); + if (!np_clock) { + dev_err(&ofdev->dev, "couldn't find clock node\n"); + return 0; + } + clockctl = of_iomap(np_clock, 0); + if (!clockctl) { + dev_err(&ofdev->dev, "couldn't map clock registers\n"); + goto exit_put; + } + + /* Determine the MSCAN device index from the physical address */ + pval = of_get_property(ofdev->dev.of_node, "reg", &plen); + BUG_ON(!pval || plen < sizeof(*pval)); + clockidx = (*pval & 0x80) ? 1 : 0; + if (*pval & 0x2000) + clockidx += 2; + + /* + * Clock source and divider selection: 3 different clock sources + * can be selected: "ip", "ref" or "sys". For the latter two, a + * clock divider can be defined as well. If the clock source is + * not specified by the device tree, we first try to find an + * optimal CAN source clock based on the system clock. If that + * is not posslible, the reference clock will be used. + */ + if (clock_name && !strcmp(clock_name, "ip")) { + *mscan_clksrc = MSCAN_CLKSRC_IPS; + freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node); + } else { + *mscan_clksrc = MSCAN_CLKSRC_BUS; + + pval = of_get_property(ofdev->dev.of_node, + "fsl,mscan-clock-divider", &plen); + if (pval && plen == sizeof(*pval)) + clockdiv = *pval; + if (!clockdiv) + clockdiv = 1; + + if (!clock_name || !strcmp(clock_name, "sys")) { + sys_clk = clk_get(&ofdev->dev, "sys_clk"); + if (!sys_clk) { + dev_err(&ofdev->dev, "couldn't get sys_clk\n"); + goto exit_unmap; + } + /* Get and round up/down sys clock rate */ + sys_freq = 1000000 * + ((clk_get_rate(sys_clk) + 499999) / 1000000); + + if (!clock_name) { + /* A multiple of 16 MHz would be optimal */ + if ((sys_freq % 16000000) == 0) { + clocksrc = 0; + clockdiv = sys_freq / 16000000; + freq = sys_freq / clockdiv; + } + } else { + clocksrc = 0; + freq = sys_freq / clockdiv; + } + } + + if (clocksrc < 0) { + ref_clk = clk_get(&ofdev->dev, "ref_clk"); + if (!ref_clk) { + dev_err(&ofdev->dev, "couldn't get ref_clk\n"); + goto exit_unmap; + } + clocksrc = 1; + freq = clk_get_rate(ref_clk) / clockdiv; + } + } + + /* Disable clock */ + out_be32(&clockctl->mccr[clockidx], 0x0); + if (clocksrc >= 0) { + /* Set source and divider */ + val = (clocksrc << 14) | ((clockdiv - 1) << 17); + out_be32(&clockctl->mccr[clockidx], val); + /* Enable clock */ + out_be32(&clockctl->mccr[clockidx], val | 0x10000); + } + + /* Enable MSCAN clock domain */ + val = in_be32(&clockctl->sccr[1]); + if (!(val & (1 << 25))) + out_be32(&clockctl->sccr[1], val | (1 << 25)); + + dev_dbg(&ofdev->dev, "using '%s' with frequency divider %d\n", + *mscan_clksrc == MSCAN_CLKSRC_IPS ? "ips_clk" : + clocksrc == 1 ? "ref_clk" : "sys_clk", clockdiv); + +exit_unmap: + iounmap(clockctl); +exit_put: + of_node_put(np_clock); + return freq; +} +#else /* !CONFIG_PPC_MPC512x */ +static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, + const char *clock_name, + int *mscan_clksrc) +{ + return 0; +} +#endif /* CONFIG_PPC_MPC512x */ + +static struct of_device_id mpc5xxx_can_table[]; +static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev) +{ + const struct of_device_id *match; + struct mpc5xxx_can_data *data; + struct device_node *np = ofdev->dev.of_node; + struct net_device *dev; + struct mscan_priv *priv; + void __iomem *base; + const char *clock_name = NULL; + int irq, mscan_clksrc = 0; + int err = -ENOMEM; + + match = of_match_device(mpc5xxx_can_table, &ofdev->dev); + if (!match) + return -EINVAL; + data = match->data; + + base = of_iomap(np, 0); + if (!base) { + dev_err(&ofdev->dev, "couldn't ioremap\n"); + return err; + } + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_unmap_mem; + } + + dev = alloc_mscandev(); + if (!dev) + goto exit_dispose_irq; + + priv = netdev_priv(dev); + priv->reg_base = base; + dev->irq = irq; + + clock_name = of_get_property(np, "fsl,mscan-clock-source", NULL); + + BUG_ON(!data); + priv->type = data->type; + priv->can.clock.freq = data->get_clock(ofdev, clock_name, + &mscan_clksrc); + if (!priv->can.clock.freq) { + dev_err(&ofdev->dev, "couldn't get MSCAN clock properties\n"); + goto exit_free_mscan; + } + + SET_NETDEV_DEV(dev, &ofdev->dev); + + err = register_mscandev(dev, mscan_clksrc); + if (err) { + dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free_mscan; + } + + dev_set_drvdata(&ofdev->dev, dev); + + dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n", + priv->reg_base, dev->irq, priv->can.clock.freq); + + return 0; + +exit_free_mscan: + free_candev(dev); +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_unmap_mem: + iounmap(base); + + return err; +} + +static int __devexit mpc5xxx_can_remove(struct platform_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_priv *priv = netdev_priv(dev); + + dev_set_drvdata(&ofdev->dev, NULL); + + unregister_mscandev(dev); + iounmap(priv->reg_base); + irq_dispose_mapping(dev->irq); + free_candev(dev); + + return 0; +} + +#ifdef CONFIG_PM +static struct mscan_regs saved_regs; +static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + + _memcpy_fromio(&saved_regs, regs, sizeof(*regs)); + + return 0; +} + +static int mpc5xxx_can_resume(struct platform_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + + regs->canctl0 |= MSCAN_INITRQ; + while (!(regs->canctl1 & MSCAN_INITAK)) + udelay(10); + + regs->canctl1 = saved_regs.canctl1; + regs->canbtr0 = saved_regs.canbtr0; + regs->canbtr1 = saved_regs.canbtr1; + regs->canidac = saved_regs.canidac; + + /* restore masks, buffers etc. */ + _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0, + sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0)); + + regs->canctl0 &= ~MSCAN_INITRQ; + regs->cantbsel = saved_regs.cantbsel; + regs->canrier = saved_regs.canrier; + regs->cantier = saved_regs.cantier; + regs->canctl0 = saved_regs.canctl0; + + return 0; +} +#endif + +static struct mpc5xxx_can_data __devinitdata mpc5200_can_data = { + .type = MSCAN_TYPE_MPC5200, + .get_clock = mpc52xx_can_get_clock, +}; + +static struct mpc5xxx_can_data __devinitdata mpc5121_can_data = { + .type = MSCAN_TYPE_MPC5121, + .get_clock = mpc512x_can_get_clock, +}; + +static struct of_device_id __devinitdata mpc5xxx_can_table[] = { + { .compatible = "fsl,mpc5200-mscan", .data = &mpc5200_can_data, }, + /* Note that only MPC5121 Rev. 2 (and later) is supported */ + { .compatible = "fsl,mpc5121-mscan", .data = &mpc5121_can_data, }, + {}, +}; + +static struct platform_driver mpc5xxx_can_driver = { + .driver = { + .name = "mpc5xxx_can", + .owner = THIS_MODULE, + .of_match_table = mpc5xxx_can_table, + }, + .probe = mpc5xxx_can_probe, + .remove = __devexit_p(mpc5xxx_can_remove), +#ifdef CONFIG_PM + .suspend = mpc5xxx_can_suspend, + .resume = mpc5xxx_can_resume, +#endif +}; + +module_platform_driver(mpc5xxx_can_driver); + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Freescale MPC5xxx CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c new file mode 100644 index 00000000..41a2a2dd --- /dev/null +++ b/drivers/net/can/mscan/mscan.c @@ -0,0 +1,722 @@ +/* + * CAN bus driver for the alone generic (as possible as) MSCAN controller. + * + * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2008-2009 Pengutronix <kernel@pengutronix.de> + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/list.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/io.h> + +#include "mscan.h" + +static struct can_bittiming_const mscan_bittiming_const = { + .name = "mscan", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +struct mscan_state { + u8 mode; + u8 canrier; + u8 cantier; +}; + +static enum can_state state_map[] = { + CAN_STATE_ERROR_ACTIVE, + CAN_STATE_ERROR_WARNING, + CAN_STATE_ERROR_PASSIVE, + CAN_STATE_BUS_OFF +}; + +static int mscan_set_mode(struct net_device *dev, u8 mode) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + int ret = 0; + int i; + u8 canctl1; + + if (mode != MSCAN_NORMAL_MODE) { + if (priv->tx_active) { + /* Abort transfers before going to sleep */# + out_8(®s->cantarq, priv->tx_active); + /* Suppress TX done interrupts */ + out_8(®s->cantier, 0); + } + + canctl1 = in_8(®s->canctl1); + if ((mode & MSCAN_SLPRQ) && !(canctl1 & MSCAN_SLPAK)) { + setbits8(®s->canctl0, MSCAN_SLPRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + if (in_8(®s->canctl1) & MSCAN_SLPAK) + break; + udelay(100); + } + /* + * The mscan controller will fail to enter sleep mode, + * while there are irregular activities on bus, like + * somebody keeps retransmitting. This behavior is + * undocumented and seems to differ between mscan built + * in mpc5200b and mpc5200. We proceed in that case, + * since otherwise the slprq will be kept set and the + * controller will get stuck. NOTE: INITRQ or CSWAI + * will abort all active transmit actions, if still + * any, at once. + */ + if (i >= MSCAN_SET_MODE_RETRIES) + netdev_dbg(dev, + "device failed to enter sleep mode. " + "We proceed anyhow.\n"); + else + priv->can.state = CAN_STATE_SLEEPING; + } + + if ((mode & MSCAN_INITRQ) && !(canctl1 & MSCAN_INITAK)) { + setbits8(®s->canctl0, MSCAN_INITRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + if (in_8(®s->canctl1) & MSCAN_INITAK) + break; + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + } + if (!ret) + priv->can.state = CAN_STATE_STOPPED; + + if (mode & MSCAN_CSWAI) + setbits8(®s->canctl0, MSCAN_CSWAI); + + } else { + canctl1 = in_8(®s->canctl1); + if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) { + clrbits8(®s->canctl0, MSCAN_SLPRQ | MSCAN_INITRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + canctl1 = in_8(®s->canctl1); + if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK))) + break; + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + else + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + } + return ret; +} + +static int mscan_start(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + u8 canrflg; + int err; + + out_8(®s->canrier, 0); + + INIT_LIST_HEAD(&priv->tx_head); + priv->prev_buf_id = 0; + priv->cur_pri = 0; + priv->tx_active = 0; + priv->shadow_canrier = 0; + priv->flags = 0; + + if (priv->type == MSCAN_TYPE_MPC5121) { + /* Clear pending bus-off condition */ + if (in_8(®s->canmisc) & MSCAN_BOHOLD) + out_8(®s->canmisc, MSCAN_BOHOLD); + } + + err = mscan_set_mode(dev, MSCAN_NORMAL_MODE); + if (err) + return err; + + canrflg = in_8(®s->canrflg); + priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; + priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + out_8(®s->cantier, 0); + + /* Enable receive interrupts. */ + out_8(®s->canrier, MSCAN_RX_INTS_ENABLE); + + return 0; +} + +static int mscan_restart(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + + if (priv->type == MSCAN_TYPE_MPC5121) { + struct mscan_regs __iomem *regs = priv->reg_base; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + WARN(!(in_8(®s->canmisc) & MSCAN_BOHOLD), + "bus-off state expected\n"); + out_8(®s->canmisc, MSCAN_BOHOLD); + /* Re-enable receive interrupts. */ + out_8(®s->canrier, MSCAN_RX_INTS_ENABLE); + } else { + if (priv->can.state <= CAN_STATE_BUS_OFF) + mscan_set_mode(dev, MSCAN_INIT_MODE); + return mscan_start(dev); + } + + return 0; +} + +static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct can_frame *frame = (struct can_frame *)skb->data; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + int i, rtr, buf_id; + u32 can_id; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + out_8(®s->cantier, 0); + + i = ~priv->tx_active & MSCAN_TXE; + buf_id = ffs(i) - 1; + switch (hweight8(i)) { + case 0: + netif_stop_queue(dev); + netdev_err(dev, "Tx Ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + case 1: + /* + * if buf_id < 3, then current frame will be send out of order, + * since buffer with lower id have higher priority (hell..) + */ + netif_stop_queue(dev); + case 2: + if (buf_id < priv->prev_buf_id) { + priv->cur_pri++; + if (priv->cur_pri == 0xff) { + set_bit(F_TX_WAIT_ALL, &priv->flags); + netif_stop_queue(dev); + } + } + set_bit(F_TX_PROGRESS, &priv->flags); + break; + } + priv->prev_buf_id = buf_id; + out_8(®s->cantbsel, i); + + rtr = frame->can_id & CAN_RTR_FLAG; + + /* RTR is always the lowest bit of interest, then IDs follow */ + if (frame->can_id & CAN_EFF_FLAG) { + can_id = (frame->can_id & CAN_EFF_MASK) + << (MSCAN_EFF_RTR_SHIFT + 1); + if (rtr) + can_id |= 1 << MSCAN_EFF_RTR_SHIFT; + out_be16(®s->tx.idr3_2, can_id); + + can_id >>= 16; + /* EFF_FLAGS are between the IDs :( */ + can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) + | MSCAN_EFF_FLAGS; + } else { + can_id = (frame->can_id & CAN_SFF_MASK) + << (MSCAN_SFF_RTR_SHIFT + 1); + if (rtr) + can_id |= 1 << MSCAN_SFF_RTR_SHIFT; + } + out_be16(®s->tx.idr1_0, can_id); + + if (!rtr) { + void __iomem *data = ®s->tx.dsr1_0; + u16 *payload = (u16 *)frame->data; + + for (i = 0; i < frame->can_dlc / 2; i++) { + out_be16(data, *payload++); + data += 2 + _MSCAN_RESERVED_DSR_SIZE; + } + /* write remaining byte if necessary */ + if (frame->can_dlc & 1) + out_8(data, frame->data[frame->can_dlc - 1]); + } + + out_8(®s->tx.dlr, frame->can_dlc); + out_8(®s->tx.tbpr, priv->cur_pri); + + /* Start transmission. */ + out_8(®s->cantflg, 1 << buf_id); + + if (!test_bit(F_TX_PROGRESS, &priv->flags)) + dev->trans_start = jiffies; + + list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head); + + can_put_echo_skb(skb, dev, buf_id); + + /* Enable interrupt. */ + priv->tx_active |= 1 << buf_id; + out_8(®s->cantier, priv->tx_active); + + return NETDEV_TX_OK; +} + +/* This function returns the old state to see where we came from */ +static enum can_state check_set_state(struct net_device *dev, u8 canrflg) +{ + struct mscan_priv *priv = netdev_priv(dev); + enum can_state state, old_state = priv->can.state; + + if (canrflg & MSCAN_CSCIF && old_state <= CAN_STATE_BUS_OFF) { + state = state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + priv->can.state = state; + } + return old_state; +} + +static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + u32 can_id; + int i; + + can_id = in_be16(®s->rx.idr1_0); + if (can_id & (1 << 3)) { + frame->can_id = CAN_EFF_FLAG; + can_id = ((can_id << 16) | in_be16(®s->rx.idr3_2)); + can_id = ((can_id & 0xffe00000) | + ((can_id & 0x7ffff) << 2)) >> 2; + } else { + can_id >>= 4; + frame->can_id = 0; + } + + frame->can_id |= can_id >> 1; + if (can_id & 1) + frame->can_id |= CAN_RTR_FLAG; + + frame->can_dlc = get_can_dlc(in_8(®s->rx.dlr) & 0xf); + + if (!(frame->can_id & CAN_RTR_FLAG)) { + void __iomem *data = ®s->rx.dsr1_0; + u16 *payload = (u16 *)frame->data; + + for (i = 0; i < frame->can_dlc / 2; i++) { + *payload++ = in_be16(data); + data += 2 + _MSCAN_RESERVED_DSR_SIZE; + } + /* read remaining byte if necessary */ + if (frame->can_dlc & 1) + frame->data[frame->can_dlc - 1] = in_8(data); + } + + out_8(®s->canrflg, MSCAN_RXF); +} + +static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame, + u8 canrflg) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + struct net_device_stats *stats = &dev->stats; + enum can_state old_state; + + netdev_dbg(dev, "error interrupt (canrflg=%#x)\n", canrflg); + frame->can_id = CAN_ERR_FLAG; + + if (canrflg & MSCAN_OVRIF) { + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + } else { + frame->data[1] = 0; + } + + old_state = check_set_state(dev, canrflg); + /* State changed */ + if (old_state != priv->can.state) { + switch (priv->can.state) { + case CAN_STATE_ERROR_WARNING: + frame->can_id |= CAN_ERR_CRTL; + priv->can.can_stats.error_warning++; + if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) < + (canrflg & MSCAN_RSTAT_MSK)) + frame->data[1] |= CAN_ERR_CRTL_RX_WARNING; + if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) < + (canrflg & MSCAN_TSTAT_MSK)) + frame->data[1] |= CAN_ERR_CRTL_TX_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + frame->can_id |= CAN_ERR_CRTL; + priv->can.can_stats.error_passive++; + frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + frame->can_id |= CAN_ERR_BUSOFF; + /* + * The MSCAN on the MPC5200 does recover from bus-off + * automatically. To avoid that we stop the chip doing + * a light-weight stop (we are in irq-context). + */ + if (priv->type != MSCAN_TYPE_MPC5121) { + out_8(®s->cantier, 0); + out_8(®s->canrier, 0); + setbits8(®s->canctl0, + MSCAN_SLPRQ | MSCAN_INITRQ); + } + can_bus_off(dev); + break; + default: + break; + } + } + priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; + frame->can_dlc = CAN_ERR_DLC; + out_8(®s->canrflg, MSCAN_ERR_IF); +} + +static int mscan_rx_poll(struct napi_struct *napi, int quota) +{ + struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi); + struct net_device *dev = napi->dev; + struct mscan_regs __iomem *regs = priv->reg_base; + struct net_device_stats *stats = &dev->stats; + int npackets = 0; + int ret = 1; + struct sk_buff *skb; + struct can_frame *frame; + u8 canrflg; + + while (npackets < quota) { + canrflg = in_8(®s->canrflg); + if (!(canrflg & (MSCAN_RXF | MSCAN_ERR_IF))) + break; + + skb = alloc_can_skb(dev, &frame); + if (!skb) { + if (printk_ratelimit()) + netdev_notice(dev, "packet dropped\n"); + stats->rx_dropped++; + out_8(®s->canrflg, canrflg); + continue; + } + + if (canrflg & MSCAN_RXF) + mscan_get_rx_frame(dev, frame); + else if (canrflg & MSCAN_ERR_IF) + mscan_get_err_frame(dev, frame, canrflg); + + stats->rx_packets++; + stats->rx_bytes += frame->can_dlc; + npackets++; + netif_receive_skb(skb); + } + + if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) { + napi_complete(&priv->napi); + clear_bit(F_RX_PROGRESS, &priv->flags); + if (priv->can.state < CAN_STATE_BUS_OFF) + out_8(®s->canrier, priv->shadow_canrier); + ret = 0; + } + return ret; +} + +static irqreturn_t mscan_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + struct net_device_stats *stats = &dev->stats; + u8 cantier, cantflg, canrflg; + irqreturn_t ret = IRQ_NONE; + + cantier = in_8(®s->cantier) & MSCAN_TXE; + cantflg = in_8(®s->cantflg) & cantier; + + if (cantier && cantflg) { + struct list_head *tmp, *pos; + + list_for_each_safe(pos, tmp, &priv->tx_head) { + struct tx_queue_entry *entry = + list_entry(pos, struct tx_queue_entry, list); + u8 mask = entry->mask; + + if (!(cantflg & mask)) + continue; + + out_8(®s->cantbsel, mask); + stats->tx_bytes += in_8(®s->tx.dlr); + stats->tx_packets++; + can_get_echo_skb(dev, entry->id); + priv->tx_active &= ~mask; + list_del(pos); + } + + if (list_empty(&priv->tx_head)) { + clear_bit(F_TX_WAIT_ALL, &priv->flags); + clear_bit(F_TX_PROGRESS, &priv->flags); + priv->cur_pri = 0; + } else { + dev->trans_start = jiffies; + } + + if (!test_bit(F_TX_WAIT_ALL, &priv->flags)) + netif_wake_queue(dev); + + out_8(®s->cantier, priv->tx_active); + ret = IRQ_HANDLED; + } + + canrflg = in_8(®s->canrflg); + if ((canrflg & ~MSCAN_STAT_MSK) && + !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) { + if (canrflg & ~MSCAN_STAT_MSK) { + priv->shadow_canrier = in_8(®s->canrier); + out_8(®s->canrier, 0); + napi_schedule(&priv->napi); + ret = IRQ_HANDLED; + } else { + clear_bit(F_RX_PROGRESS, &priv->flags); + } + } + return ret; +} + +static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct mscan_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + ret = mscan_restart(dev); + if (ret) + break; + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static int mscan_do_set_bittiming(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw); + btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) | + BTR1_SET_TSEG2(bt->phase_seg2) | + BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)); + + netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + out_8(®s->canbtr0, btr0); + out_8(®s->canbtr1, btr1); + + return 0; +} + +static int mscan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + + bec->txerr = in_8(®s->cantxerr); + bec->rxerr = in_8(®s->canrxerr); + + return 0; +} + +static int mscan_open(struct net_device *dev) +{ + int ret; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + + /* common open */ + ret = open_candev(dev); + if (ret) + return ret; + + napi_enable(&priv->napi); + + ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev); + if (ret < 0) { + netdev_err(dev, "failed to attach interrupt\n"); + goto exit_napi_disable; + } + + priv->open_time = jiffies; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + setbits8(®s->canctl1, MSCAN_LISTEN); + else + clrbits8(®s->canctl1, MSCAN_LISTEN); + + ret = mscan_start(dev); + if (ret) + goto exit_free_irq; + + netif_start_queue(dev); + + return 0; + +exit_free_irq: + priv->open_time = 0; + free_irq(dev->irq, dev); +exit_napi_disable: + napi_disable(&priv->napi); + close_candev(dev); + return ret; +} + +static int mscan_close(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + + netif_stop_queue(dev); + napi_disable(&priv->napi); + + out_8(®s->cantier, 0); + out_8(®s->canrier, 0); + mscan_set_mode(dev, MSCAN_INIT_MODE); + close_candev(dev); + free_irq(dev->irq, dev); + priv->open_time = 0; + + return 0; +} + +static const struct net_device_ops mscan_netdev_ops = { + .ndo_open = mscan_open, + .ndo_stop = mscan_close, + .ndo_start_xmit = mscan_start_xmit, +}; + +int register_mscandev(struct net_device *dev, int mscan_clksrc) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + u8 ctl1; + + ctl1 = in_8(®s->canctl1); + if (mscan_clksrc) + ctl1 |= MSCAN_CLKSRC; + else + ctl1 &= ~MSCAN_CLKSRC; + + if (priv->type == MSCAN_TYPE_MPC5121) { + priv->can.do_get_berr_counter = mscan_get_berr_counter; + ctl1 |= MSCAN_BORM; /* bus-off recovery upon request */ + } + + ctl1 |= MSCAN_CANE; + out_8(®s->canctl1, ctl1); + udelay(100); + + /* acceptance mask/acceptance code (accept everything) */ + out_be16(®s->canidar1_0, 0); + out_be16(®s->canidar3_2, 0); + out_be16(®s->canidar5_4, 0); + out_be16(®s->canidar7_6, 0); + + out_be16(®s->canidmr1_0, 0xffff); + out_be16(®s->canidmr3_2, 0xffff); + out_be16(®s->canidmr5_4, 0xffff); + out_be16(®s->canidmr7_6, 0xffff); + /* Two 32 bit Acceptance Filters */ + out_8(®s->canidac, MSCAN_AF_32BIT); + + mscan_set_mode(dev, MSCAN_INIT_MODE); + + return register_candev(dev); +} + +void unregister_mscandev(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs __iomem *regs = priv->reg_base; + mscan_set_mode(dev, MSCAN_INIT_MODE); + clrbits8(®s->canctl1, MSCAN_CANE); + unregister_candev(dev); +} + +struct net_device *alloc_mscandev(void) +{ + struct net_device *dev; + struct mscan_priv *priv; + int i; + + dev = alloc_candev(sizeof(struct mscan_priv), MSCAN_ECHO_SKB_MAX); + if (!dev) + return NULL; + priv = netdev_priv(dev); + + dev->netdev_ops = &mscan_netdev_ops; + + dev->flags |= IFF_ECHO; /* we support local echo */ + + netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8); + + priv->can.bittiming_const = &mscan_bittiming_const; + priv->can.do_set_bittiming = mscan_do_set_bittiming; + priv->can.do_set_mode = mscan_do_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LISTENONLY; + + for (i = 0; i < TX_QUEUE_SIZE; i++) { + priv->tx_queue[i].id = i; + priv->tx_queue[i].mask = 1 << i; + } + + return dev; +} + +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips"); diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h new file mode 100644 index 00000000..b43e9f5d --- /dev/null +++ b/drivers/net/can/mscan/mscan.h @@ -0,0 +1,302 @@ +/* + * Definitions of consts/structs to drive the Freescale MSCAN. + * + * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * + * 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 + */ + +#ifndef __MSCAN_H__ +#define __MSCAN_H__ + +#include <linux/types.h> + +/* MSCAN control register 0 (CANCTL0) bits */ +#define MSCAN_RXFRM 0x80 +#define MSCAN_RXACT 0x40 +#define MSCAN_CSWAI 0x20 +#define MSCAN_SYNCH 0x10 +#define MSCAN_TIME 0x08 +#define MSCAN_WUPE 0x04 +#define MSCAN_SLPRQ 0x02 +#define MSCAN_INITRQ 0x01 + +/* MSCAN control register 1 (CANCTL1) bits */ +#define MSCAN_CANE 0x80 +#define MSCAN_CLKSRC 0x40 +#define MSCAN_LOOPB 0x20 +#define MSCAN_LISTEN 0x10 +#define MSCAN_BORM 0x08 +#define MSCAN_WUPM 0x04 +#define MSCAN_SLPAK 0x02 +#define MSCAN_INITAK 0x01 + +/* Use the MPC5XXX MSCAN variant? */ +#ifdef CONFIG_PPC +#define MSCAN_FOR_MPC5XXX +#endif + +#ifdef MSCAN_FOR_MPC5XXX +#define MSCAN_CLKSRC_BUS 0 +#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC +#define MSCAN_CLKSRC_IPS MSCAN_CLKSRC +#else +#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC +#define MSCAN_CLKSRC_XTAL 0 +#endif + +/* MSCAN receiver flag register (CANRFLG) bits */ +#define MSCAN_WUPIF 0x80 +#define MSCAN_CSCIF 0x40 +#define MSCAN_RSTAT1 0x20 +#define MSCAN_RSTAT0 0x10 +#define MSCAN_TSTAT1 0x08 +#define MSCAN_TSTAT0 0x04 +#define MSCAN_OVRIF 0x02 +#define MSCAN_RXF 0x01 +#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF) +#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0) +#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0) +#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK) + +#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \ + MSCAN_TSTAT1 | MSCAN_TSTAT0) +#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2) +#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4) +#define MSCAN_STATE_ACTIVE 0 +#define MSCAN_STATE_WARNING 1 +#define MSCAN_STATE_PASSIVE 2 +#define MSCAN_STATE_BUSOFF 3 + +/* MSCAN receiver interrupt enable register (CANRIER) bits */ +#define MSCAN_WUPIE 0x80 +#define MSCAN_CSCIE 0x40 +#define MSCAN_RSTATE1 0x20 +#define MSCAN_RSTATE0 0x10 +#define MSCAN_TSTATE1 0x08 +#define MSCAN_TSTATE0 0x04 +#define MSCAN_OVRIE 0x02 +#define MSCAN_RXFIE 0x01 + +/* MSCAN transmitter flag register (CANTFLG) bits */ +#define MSCAN_TXE2 0x04 +#define MSCAN_TXE1 0x02 +#define MSCAN_TXE0 0x01 +#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0) + +/* MSCAN transmitter interrupt enable register (CANTIER) bits */ +#define MSCAN_TXIE2 0x04 +#define MSCAN_TXIE1 0x02 +#define MSCAN_TXIE0 0x01 +#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0) + +/* MSCAN transmitter message abort request (CANTARQ) bits */ +#define MSCAN_ABTRQ2 0x04 +#define MSCAN_ABTRQ1 0x02 +#define MSCAN_ABTRQ0 0x01 + +/* MSCAN transmitter message abort ack (CANTAAK) bits */ +#define MSCAN_ABTAK2 0x04 +#define MSCAN_ABTAK1 0x02 +#define MSCAN_ABTAK0 0x01 + +/* MSCAN transmit buffer selection (CANTBSEL) bits */ +#define MSCAN_TX2 0x04 +#define MSCAN_TX1 0x02 +#define MSCAN_TX0 0x01 + +/* MSCAN ID acceptance control register (CANIDAC) bits */ +#define MSCAN_IDAM1 0x20 +#define MSCAN_IDAM0 0x10 +#define MSCAN_IDHIT2 0x04 +#define MSCAN_IDHIT1 0x02 +#define MSCAN_IDHIT0 0x01 + +#define MSCAN_AF_32BIT 0x00 +#define MSCAN_AF_16BIT MSCAN_IDAM0 +#define MSCAN_AF_8BIT MSCAN_IDAM1 +#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1) +#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1)) + +/* MSCAN Miscellaneous Register (CANMISC) bits */ +#define MSCAN_BOHOLD 0x01 + +/* MSCAN Identifier Register (IDR) bits */ +#define MSCAN_SFF_RTR_SHIFT 4 +#define MSCAN_EFF_RTR_SHIFT 0 +#define MSCAN_EFF_FLAGS 0x18 /* IDE + SRR */ + +#ifdef MSCAN_FOR_MPC5XXX +#define _MSCAN_RESERVED_(n, num) u8 _res##n[num] +#define _MSCAN_RESERVED_DSR_SIZE 2 +#else +#define _MSCAN_RESERVED_(n, num) +#define _MSCAN_RESERVED_DSR_SIZE 0 +#endif + +/* Structure of the hardware registers */ +struct mscan_regs { + /* (see doc S12MSCANV3/D) MPC5200 MSCAN */ + u8 canctl0; /* + 0x00 0x00 */ + u8 canctl1; /* + 0x01 0x01 */ + _MSCAN_RESERVED_(1, 2); /* + 0x02 */ + u8 canbtr0; /* + 0x04 0x02 */ + u8 canbtr1; /* + 0x05 0x03 */ + _MSCAN_RESERVED_(2, 2); /* + 0x06 */ + u8 canrflg; /* + 0x08 0x04 */ + u8 canrier; /* + 0x09 0x05 */ + _MSCAN_RESERVED_(3, 2); /* + 0x0a */ + u8 cantflg; /* + 0x0c 0x06 */ + u8 cantier; /* + 0x0d 0x07 */ + _MSCAN_RESERVED_(4, 2); /* + 0x0e */ + u8 cantarq; /* + 0x10 0x08 */ + u8 cantaak; /* + 0x11 0x09 */ + _MSCAN_RESERVED_(5, 2); /* + 0x12 */ + u8 cantbsel; /* + 0x14 0x0a */ + u8 canidac; /* + 0x15 0x0b */ + u8 reserved; /* + 0x16 0x0c */ + _MSCAN_RESERVED_(6, 2); /* + 0x17 */ + u8 canmisc; /* + 0x19 0x0d */ + _MSCAN_RESERVED_(7, 2); /* + 0x1a */ + u8 canrxerr; /* + 0x1c 0x0e */ + u8 cantxerr; /* + 0x1d 0x0f */ + _MSCAN_RESERVED_(8, 2); /* + 0x1e */ + u16 canidar1_0; /* + 0x20 0x10 */ + _MSCAN_RESERVED_(9, 2); /* + 0x22 */ + u16 canidar3_2; /* + 0x24 0x12 */ + _MSCAN_RESERVED_(10, 2); /* + 0x26 */ + u16 canidmr1_0; /* + 0x28 0x14 */ + _MSCAN_RESERVED_(11, 2); /* + 0x2a */ + u16 canidmr3_2; /* + 0x2c 0x16 */ + _MSCAN_RESERVED_(12, 2); /* + 0x2e */ + u16 canidar5_4; /* + 0x30 0x18 */ + _MSCAN_RESERVED_(13, 2); /* + 0x32 */ + u16 canidar7_6; /* + 0x34 0x1a */ + _MSCAN_RESERVED_(14, 2); /* + 0x36 */ + u16 canidmr5_4; /* + 0x38 0x1c */ + _MSCAN_RESERVED_(15, 2); /* + 0x3a */ + u16 canidmr7_6; /* + 0x3c 0x1e */ + _MSCAN_RESERVED_(16, 2); /* + 0x3e */ + struct { + u16 idr1_0; /* + 0x40 0x20 */ + _MSCAN_RESERVED_(17, 2); /* + 0x42 */ + u16 idr3_2; /* + 0x44 0x22 */ + _MSCAN_RESERVED_(18, 2); /* + 0x46 */ + u16 dsr1_0; /* + 0x48 0x24 */ + _MSCAN_RESERVED_(19, 2); /* + 0x4a */ + u16 dsr3_2; /* + 0x4c 0x26 */ + _MSCAN_RESERVED_(20, 2); /* + 0x4e */ + u16 dsr5_4; /* + 0x50 0x28 */ + _MSCAN_RESERVED_(21, 2); /* + 0x52 */ + u16 dsr7_6; /* + 0x54 0x2a */ + _MSCAN_RESERVED_(22, 2); /* + 0x56 */ + u8 dlr; /* + 0x58 0x2c */ + u8 reserved; /* + 0x59 0x2d */ + _MSCAN_RESERVED_(23, 2); /* + 0x5a */ + u16 time; /* + 0x5c 0x2e */ + } rx; + _MSCAN_RESERVED_(24, 2); /* + 0x5e */ + struct { + u16 idr1_0; /* + 0x60 0x30 */ + _MSCAN_RESERVED_(25, 2); /* + 0x62 */ + u16 idr3_2; /* + 0x64 0x32 */ + _MSCAN_RESERVED_(26, 2); /* + 0x66 */ + u16 dsr1_0; /* + 0x68 0x34 */ + _MSCAN_RESERVED_(27, 2); /* + 0x6a */ + u16 dsr3_2; /* + 0x6c 0x36 */ + _MSCAN_RESERVED_(28, 2); /* + 0x6e */ + u16 dsr5_4; /* + 0x70 0x38 */ + _MSCAN_RESERVED_(29, 2); /* + 0x72 */ + u16 dsr7_6; /* + 0x74 0x3a */ + _MSCAN_RESERVED_(30, 2); /* + 0x76 */ + u8 dlr; /* + 0x78 0x3c */ + u8 tbpr; /* + 0x79 0x3d */ + _MSCAN_RESERVED_(31, 2); /* + 0x7a */ + u16 time; /* + 0x7c 0x3e */ + } tx; + _MSCAN_RESERVED_(32, 2); /* + 0x7e */ +} __packed; + +#undef _MSCAN_RESERVED_ +#define MSCAN_REGION sizeof(struct mscan) + +#define MSCAN_NORMAL_MODE 0 +#define MSCAN_SLEEP_MODE MSCAN_SLPRQ +#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ) +#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ) +#define MSCAN_SET_MODE_RETRIES 255 +#define MSCAN_ECHO_SKB_MAX 3 +#define MSCAN_RX_INTS_ENABLE (MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE | \ + MSCAN_RSTATE1 | MSCAN_RSTATE0 | \ + MSCAN_TSTATE1 | MSCAN_TSTATE0) + +/* MSCAN type variants */ +enum { + MSCAN_TYPE_MPC5200, + MSCAN_TYPE_MPC5121 +}; + +#define BTR0_BRP_MASK 0x3f +#define BTR0_SJW_SHIFT 6 +#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT) + +#define BTR1_TSEG1_MASK 0xf +#define BTR1_TSEG2_SHIFT 4 +#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT) +#define BTR1_SAM_SHIFT 7 + +#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK) +#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \ + BTR0_SJW_MASK) + +#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK) +#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \ + BTR1_TSEG2_MASK) +#define BTR1_SET_SAM(sam) ((sam) ? 1 << BTR1_SAM_SHIFT : 0) + +#define F_RX_PROGRESS 0 +#define F_TX_PROGRESS 1 +#define F_TX_WAIT_ALL 2 + +#define TX_QUEUE_SIZE 3 + +struct tx_queue_entry { + struct list_head list; + u8 mask; + u8 id; +}; + +struct mscan_priv { + struct can_priv can; /* must be the first member */ + unsigned int type; /* MSCAN type variants */ + long open_time; + unsigned long flags; + void __iomem *reg_base; /* ioremap'ed address to registers */ + u8 shadow_statflg; + u8 shadow_canrier; + u8 cur_pri; + u8 prev_buf_id; + u8 tx_active; + + struct list_head tx_head; + struct tx_queue_entry tx_queue[TX_QUEUE_SIZE]; + struct napi_struct napi; +}; + +extern struct net_device *alloc_mscandev(void); +extern int register_mscandev(struct net_device *dev, int mscan_clksrc); +extern void unregister_mscandev(struct net_device *dev); + +#endif /* __MSCAN_H__ */ diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c new file mode 100644 index 00000000..2bb215e0 --- /dev/null +++ b/drivers/net/can/pch_can.c @@ -0,0 +1,1291 @@ +/* + * Copyright (C) 1999 - 2010 Intel Corporation. + * Copyright (C) 2010 LAPIS SEMICONDUCTOR CO., 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; version 2 of the License. + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#define PCH_CTRL_INIT BIT(0) /* The INIT bit of CANCONT register. */ +#define PCH_CTRL_IE BIT(1) /* The IE bit of CAN control register */ +#define PCH_CTRL_IE_SIE_EIE (BIT(3) | BIT(2) | BIT(1)) +#define PCH_CTRL_CCE BIT(6) +#define PCH_CTRL_OPT BIT(7) /* The OPT bit of CANCONT register. */ +#define PCH_OPT_SILENT BIT(3) /* The Silent bit of CANOPT reg. */ +#define PCH_OPT_LBACK BIT(4) /* The LoopBack bit of CANOPT reg. */ + +#define PCH_CMASK_RX_TX_SET 0x00f3 +#define PCH_CMASK_RX_TX_GET 0x0073 +#define PCH_CMASK_ALL 0xff +#define PCH_CMASK_NEWDAT BIT(2) +#define PCH_CMASK_CLRINTPND BIT(3) +#define PCH_CMASK_CTRL BIT(4) +#define PCH_CMASK_ARB BIT(5) +#define PCH_CMASK_MASK BIT(6) +#define PCH_CMASK_RDWR BIT(7) +#define PCH_IF_MCONT_NEWDAT BIT(15) +#define PCH_IF_MCONT_MSGLOST BIT(14) +#define PCH_IF_MCONT_INTPND BIT(13) +#define PCH_IF_MCONT_UMASK BIT(12) +#define PCH_IF_MCONT_TXIE BIT(11) +#define PCH_IF_MCONT_RXIE BIT(10) +#define PCH_IF_MCONT_RMTEN BIT(9) +#define PCH_IF_MCONT_TXRQXT BIT(8) +#define PCH_IF_MCONT_EOB BIT(7) +#define PCH_IF_MCONT_DLC (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#define PCH_MASK2_MDIR_MXTD (BIT(14) | BIT(15)) +#define PCH_ID2_DIR BIT(13) +#define PCH_ID2_XTD BIT(14) +#define PCH_ID_MSGVAL BIT(15) +#define PCH_IF_CREQ_BUSY BIT(15) + +#define PCH_STATUS_INT 0x8000 +#define PCH_RP 0x00008000 +#define PCH_REC 0x00007f00 +#define PCH_TEC 0x000000ff + +#define PCH_TX_OK BIT(3) +#define PCH_RX_OK BIT(4) +#define PCH_EPASSIV BIT(5) +#define PCH_EWARN BIT(6) +#define PCH_BUS_OFF BIT(7) + +/* bit position of certain controller bits. */ +#define PCH_BIT_BRP_SHIFT 0 +#define PCH_BIT_SJW_SHIFT 6 +#define PCH_BIT_TSEG1_SHIFT 8 +#define PCH_BIT_TSEG2_SHIFT 12 +#define PCH_BIT_BRPE_BRPE_SHIFT 6 + +#define PCH_MSK_BITT_BRP 0x3f +#define PCH_MSK_BRPE_BRPE 0x3c0 +#define PCH_MSK_CTRL_IE_SIE_EIE 0x07 +#define PCH_COUNTER_LIMIT 10 + +#define PCH_CAN_CLK 50000000 /* 50MHz */ + +/* + * Define the number of message object. + * PCH CAN communications are done via Message RAM. + * The Message RAM consists of 32 message objects. + */ +#define PCH_RX_OBJ_NUM 26 +#define PCH_TX_OBJ_NUM 6 +#define PCH_RX_OBJ_START 1 +#define PCH_RX_OBJ_END PCH_RX_OBJ_NUM +#define PCH_TX_OBJ_START (PCH_RX_OBJ_END + 1) +#define PCH_TX_OBJ_END (PCH_RX_OBJ_NUM + PCH_TX_OBJ_NUM) + +#define PCH_FIFO_THRESH 16 + +/* TxRqst2 show status of MsgObjNo.17~32 */ +#define PCH_TREQ2_TX_MASK (((1 << PCH_TX_OBJ_NUM) - 1) <<\ + (PCH_RX_OBJ_END - 16)) + +enum pch_ifreg { + PCH_RX_IFREG, + PCH_TX_IFREG, +}; + +enum pch_can_err { + PCH_STUF_ERR = 1, + PCH_FORM_ERR, + PCH_ACK_ERR, + PCH_BIT1_ERR, + PCH_BIT0_ERR, + PCH_CRC_ERR, + PCH_LEC_ALL, +}; + +enum pch_can_mode { + PCH_CAN_ENABLE, + PCH_CAN_DISABLE, + PCH_CAN_ALL, + PCH_CAN_NONE, + PCH_CAN_STOP, + PCH_CAN_RUN, +}; + +struct pch_can_if_regs { + u32 creq; + u32 cmask; + u32 mask1; + u32 mask2; + u32 id1; + u32 id2; + u32 mcont; + u32 data[4]; + u32 rsv[13]; +}; + +struct pch_can_regs { + u32 cont; + u32 stat; + u32 errc; + u32 bitt; + u32 intr; + u32 opt; + u32 brpe; + u32 reserve; + struct pch_can_if_regs ifregs[2]; /* [0]=if1 [1]=if2 */ + u32 reserve1[8]; + u32 treq1; + u32 treq2; + u32 reserve2[6]; + u32 data1; + u32 data2; + u32 reserve3[6]; + u32 canipend1; + u32 canipend2; + u32 reserve4[6]; + u32 canmval1; + u32 canmval2; + u32 reserve5[37]; + u32 srst; +}; + +struct pch_can_priv { + struct can_priv can; + struct pci_dev *dev; + u32 tx_enable[PCH_TX_OBJ_END]; + u32 rx_enable[PCH_TX_OBJ_END]; + u32 rx_link[PCH_TX_OBJ_END]; + u32 int_enables; + struct net_device *ndev; + struct pch_can_regs __iomem *regs; + struct napi_struct napi; + int tx_obj; /* Point next Tx Obj index */ + int use_msi; +}; + +static struct can_bittiming_const pch_can_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, /* 6bit + extended 4bit */ + .brp_inc = 1, +}; + +static DEFINE_PCI_DEVICE_TABLE(pch_pci_tbl) = { + {PCI_VENDOR_ID_INTEL, 0x8818, PCI_ANY_ID, PCI_ANY_ID,}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, pch_pci_tbl); + +static inline void pch_can_bit_set(void __iomem *addr, u32 mask) +{ + iowrite32(ioread32(addr) | mask, addr); +} + +static inline void pch_can_bit_clear(void __iomem *addr, u32 mask) +{ + iowrite32(ioread32(addr) & ~mask, addr); +} + +static void pch_can_set_run_mode(struct pch_can_priv *priv, + enum pch_can_mode mode) +{ + switch (mode) { + case PCH_CAN_RUN: + pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_INIT); + break; + + case PCH_CAN_STOP: + pch_can_bit_set(&priv->regs->cont, PCH_CTRL_INIT); + break; + + default: + netdev_err(priv->ndev, "%s -> Invalid Mode.\n", __func__); + break; + } +} + +static void pch_can_set_optmode(struct pch_can_priv *priv) +{ + u32 reg_val = ioread32(&priv->regs->opt); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg_val |= PCH_OPT_SILENT; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg_val |= PCH_OPT_LBACK; + + pch_can_bit_set(&priv->regs->cont, PCH_CTRL_OPT); + iowrite32(reg_val, &priv->regs->opt); +} + +static void pch_can_rw_msg_obj(void __iomem *creq_addr, u32 num) +{ + int counter = PCH_COUNTER_LIMIT; + u32 ifx_creq; + + iowrite32(num, creq_addr); + while (counter) { + ifx_creq = ioread32(creq_addr) & PCH_IF_CREQ_BUSY; + if (!ifx_creq) + break; + counter--; + udelay(1); + } + if (!counter) + pr_err("%s:IF1 BUSY Flag is set forever.\n", __func__); +} + +static void pch_can_set_int_enables(struct pch_can_priv *priv, + enum pch_can_mode interrupt_no) +{ + switch (interrupt_no) { + case PCH_CAN_DISABLE: + pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE); + break; + + case PCH_CAN_ALL: + pch_can_bit_set(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); + break; + + case PCH_CAN_NONE: + pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); + break; + + default: + netdev_err(priv->ndev, "Invalid interrupt number.\n"); + break; + } +} + +static void pch_can_set_rxtx(struct pch_can_priv *priv, u32 buff_num, + int set, enum pch_ifreg dir) +{ + u32 ie; + + if (dir) + ie = PCH_IF_MCONT_TXIE; + else + ie = PCH_IF_MCONT_RXIE; + + /* Reading the Msg buffer from Message RAM to IF1/2 registers. */ + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); + + /* Setting the IF1/2MASK1 register to access MsgVal and RxIE bits */ + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_ARB | PCH_CMASK_CTRL, + &priv->regs->ifregs[dir].cmask); + + if (set) { + /* Setting the MsgVal and RxIE/TxIE bits */ + pch_can_bit_set(&priv->regs->ifregs[dir].mcont, ie); + pch_can_bit_set(&priv->regs->ifregs[dir].id2, PCH_ID_MSGVAL); + } else { + /* Clearing the MsgVal and RxIE/TxIE bits */ + pch_can_bit_clear(&priv->regs->ifregs[dir].mcont, ie); + pch_can_bit_clear(&priv->regs->ifregs[dir].id2, PCH_ID_MSGVAL); + } + + pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); +} + +static void pch_can_set_rx_all(struct pch_can_priv *priv, int set) +{ + int i; + + /* Traversing to obtain the object configured as receivers. */ + for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) + pch_can_set_rxtx(priv, i, set, PCH_RX_IFREG); +} + +static void pch_can_set_tx_all(struct pch_can_priv *priv, int set) +{ + int i; + + /* Traversing to obtain the object configured as transmit object. */ + for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) + pch_can_set_rxtx(priv, i, set, PCH_TX_IFREG); +} + +static u32 pch_can_int_pending(struct pch_can_priv *priv) +{ + return ioread32(&priv->regs->intr) & 0xffff; +} + +static void pch_can_clear_if_buffers(struct pch_can_priv *priv) +{ + int i; /* Msg Obj ID (1~32) */ + + for (i = PCH_RX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { + iowrite32(PCH_CMASK_RX_TX_SET, &priv->regs->ifregs[0].cmask); + iowrite32(0xffff, &priv->regs->ifregs[0].mask1); + iowrite32(0xffff, &priv->regs->ifregs[0].mask2); + iowrite32(0x0, &priv->regs->ifregs[0].id1); + iowrite32(0x0, &priv->regs->ifregs[0].id2); + iowrite32(0x0, &priv->regs->ifregs[0].mcont); + iowrite32(0x0, &priv->regs->ifregs[0].data[0]); + iowrite32(0x0, &priv->regs->ifregs[0].data[1]); + iowrite32(0x0, &priv->regs->ifregs[0].data[2]); + iowrite32(0x0, &priv->regs->ifregs[0].data[3]); + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | + PCH_CMASK_ARB | PCH_CMASK_CTRL, + &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); + } +} + +static void pch_can_config_rx_tx_buffers(struct pch_can_priv *priv) +{ + int i; + + for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); + + iowrite32(0x0, &priv->regs->ifregs[0].id1); + iowrite32(0x0, &priv->regs->ifregs[0].id2); + + pch_can_bit_set(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_UMASK); + + /* In case FIFO mode, Last EoB of Rx Obj must be 1 */ + if (i == PCH_RX_OBJ_END) + pch_can_bit_set(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_EOB); + else + pch_can_bit_clear(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_EOB); + + iowrite32(0, &priv->regs->ifregs[0].mask1); + pch_can_bit_clear(&priv->regs->ifregs[0].mask2, + 0x1fff | PCH_MASK2_MDIR_MXTD); + + /* Setting CMASK for writing */ + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | + PCH_CMASK_CTRL, &priv->regs->ifregs[0].cmask); + + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); + } + + for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[1].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); + + /* Resetting DIR bit for reception */ + iowrite32(0x0, &priv->regs->ifregs[1].id1); + iowrite32(PCH_ID2_DIR, &priv->regs->ifregs[1].id2); + + /* Setting EOB bit for transmitter */ + iowrite32(PCH_IF_MCONT_EOB | PCH_IF_MCONT_UMASK, + &priv->regs->ifregs[1].mcont); + + iowrite32(0, &priv->regs->ifregs[1].mask1); + pch_can_bit_clear(&priv->regs->ifregs[1].mask2, 0x1fff); + + /* Setting CMASK for writing */ + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | + PCH_CMASK_CTRL, &priv->regs->ifregs[1].cmask); + + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); + } +} + +static void pch_can_init(struct pch_can_priv *priv) +{ + /* Stopping the Can device. */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Clearing all the message object buffers. */ + pch_can_clear_if_buffers(priv); + + /* Configuring the respective message object as either rx/tx object. */ + pch_can_config_rx_tx_buffers(priv); + + /* Enabling the interrupts. */ + pch_can_set_int_enables(priv, PCH_CAN_ALL); +} + +static void pch_can_release(struct pch_can_priv *priv) +{ + /* Stooping the CAN device. */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Disabling the interrupts. */ + pch_can_set_int_enables(priv, PCH_CAN_NONE); + + /* Disabling all the receive object. */ + pch_can_set_rx_all(priv, 0); + + /* Disabling all the transmit object. */ + pch_can_set_tx_all(priv, 0); +} + +/* This function clears interrupt(s) from the CAN device. */ +static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) +{ + /* Clear interrupt for transmit object */ + if ((mask >= PCH_RX_OBJ_START) && (mask <= PCH_RX_OBJ_END)) { + /* Setting CMASK for clearing the reception interrupts. */ + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, + &priv->regs->ifregs[0].cmask); + + /* Clearing the Dir bit. */ + pch_can_bit_clear(&priv->regs->ifregs[0].id2, PCH_ID2_DIR); + + /* Clearing NewDat & IntPnd */ + pch_can_bit_clear(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND); + + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, mask); + } else if ((mask >= PCH_TX_OBJ_START) && (mask <= PCH_TX_OBJ_END)) { + /* + * Setting CMASK for clearing interrupts for frame transmission. + */ + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, + &priv->regs->ifregs[1].cmask); + + /* Resetting the ID registers. */ + pch_can_bit_set(&priv->regs->ifregs[1].id2, + PCH_ID2_DIR | (0x7ff << 2)); + iowrite32(0x0, &priv->regs->ifregs[1].id1); + + /* Claring NewDat, TxRqst & IntPnd */ + pch_can_bit_clear(&priv->regs->ifregs[1].mcont, + PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND | + PCH_IF_MCONT_TXRQXT); + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, mask); + } +} + +static void pch_can_reset(struct pch_can_priv *priv) +{ + /* write to sw reset register */ + iowrite32(1, &priv->regs->srst); + iowrite32(0, &priv->regs->srst); +} + +static void pch_can_error(struct net_device *ndev, u32 status) +{ + struct sk_buff *skb; + struct pch_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf; + u32 errc, lec; + struct net_device_stats *stats = &(priv->ndev->stats); + enum can_state state = priv->can.state; + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) + return; + + if (status & PCH_BUS_OFF) { + pch_can_set_tx_all(priv, 0); + pch_can_set_rx_all(priv, 0); + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(ndev); + } + + errc = ioread32(&priv->regs->errc); + /* Warning interrupt. */ + if (status & PCH_EWARN) { + state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + cf->can_id |= CAN_ERR_CRTL; + if (((errc & PCH_REC) >> 8) > 96) + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; + if ((errc & PCH_TEC) > 96) + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; + netdev_dbg(ndev, + "%s -> Error Counter is more than 96.\n", __func__); + } + /* Error passive interrupt. */ + if (status & PCH_EPASSIV) { + priv->can.can_stats.error_passive++; + state = CAN_STATE_ERROR_PASSIVE; + cf->can_id |= CAN_ERR_CRTL; + if (errc & PCH_RP) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if ((errc & PCH_TEC) > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + netdev_dbg(ndev, + "%s -> CAN controller is ERROR PASSIVE .\n", __func__); + } + + lec = status & PCH_LEC_ALL; + switch (lec) { + case PCH_STUF_ERR: + cf->data[2] |= CAN_ERR_PROT_STUFF; + priv->can.can_stats.bus_error++; + stats->rx_errors++; + break; + case PCH_FORM_ERR: + cf->data[2] |= CAN_ERR_PROT_FORM; + priv->can.can_stats.bus_error++; + stats->rx_errors++; + break; + case PCH_ACK_ERR: + cf->can_id |= CAN_ERR_ACK; + priv->can.can_stats.bus_error++; + stats->rx_errors++; + break; + case PCH_BIT1_ERR: + case PCH_BIT0_ERR: + cf->data[2] |= CAN_ERR_PROT_BIT; + priv->can.can_stats.bus_error++; + stats->rx_errors++; + break; + case PCH_CRC_ERR: + cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + priv->can.can_stats.bus_error++; + stats->rx_errors++; + break; + case PCH_LEC_ALL: /* Written by CPU. No error status */ + break; + } + + cf->data[6] = errc & PCH_TEC; + cf->data[7] = (errc & PCH_REC) >> 8; + + priv->can.state = state; + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static irqreturn_t pch_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct pch_can_priv *priv = netdev_priv(ndev); + + if (!pch_can_int_pending(priv)) + return IRQ_NONE; + + pch_can_set_int_enables(priv, PCH_CAN_NONE); + napi_schedule(&priv->napi); + return IRQ_HANDLED; +} + +static void pch_fifo_thresh(struct pch_can_priv *priv, int obj_id) +{ + if (obj_id < PCH_FIFO_THRESH) { + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | + PCH_CMASK_ARB, &priv->regs->ifregs[0].cmask); + + /* Clearing the Dir bit. */ + pch_can_bit_clear(&priv->regs->ifregs[0].id2, PCH_ID2_DIR); + + /* Clearing NewDat & IntPnd */ + pch_can_bit_clear(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_INTPND); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_id); + } else if (obj_id > PCH_FIFO_THRESH) { + pch_can_int_clr(priv, obj_id); + } else if (obj_id == PCH_FIFO_THRESH) { + int cnt; + for (cnt = 0; cnt < PCH_FIFO_THRESH; cnt++) + pch_can_int_clr(priv, cnt + 1); + } +} + +static void pch_can_rx_msg_lost(struct net_device *ndev, int obj_id) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &(priv->ndev->stats); + struct sk_buff *skb; + struct can_frame *cf; + + netdev_dbg(priv->ndev, "Msg Obj is overwritten.\n"); + pch_can_bit_clear(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_MSGLOST); + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, + &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_id); + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) + return; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + + netif_receive_skb(skb); +} + +static int pch_can_rx_normal(struct net_device *ndev, u32 obj_num, int quota) +{ + u32 reg; + canid_t id; + int rcv_pkts = 0; + struct sk_buff *skb; + struct can_frame *cf; + struct pch_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &(priv->ndev->stats); + int i; + u32 id2; + u16 data_reg; + + do { + /* Reading the message object from the Message RAM */ + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_num); + + /* Reading the MCONT register. */ + reg = ioread32(&priv->regs->ifregs[0].mcont); + + if (reg & PCH_IF_MCONT_EOB) + break; + + /* If MsgLost bit set. */ + if (reg & PCH_IF_MCONT_MSGLOST) { + pch_can_rx_msg_lost(ndev, obj_num); + rcv_pkts++; + quota--; + obj_num++; + continue; + } else if (!(reg & PCH_IF_MCONT_NEWDAT)) { + obj_num++; + continue; + } + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + netdev_err(ndev, "alloc_can_skb Failed\n"); + return rcv_pkts; + } + + /* Get Received data */ + id2 = ioread32(&priv->regs->ifregs[0].id2); + if (id2 & PCH_ID2_XTD) { + id = (ioread32(&priv->regs->ifregs[0].id1) & 0xffff); + id |= (((id2) & 0x1fff) << 16); + cf->can_id = id | CAN_EFF_FLAG; + } else { + id = (id2 >> 2) & CAN_SFF_MASK; + cf->can_id = id; + } + + if (id2 & PCH_ID2_DIR) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_dlc = get_can_dlc((ioread32(&priv->regs-> + ifregs[0].mcont)) & 0xF); + + for (i = 0; i < cf->can_dlc; i += 2) { + data_reg = ioread16(&priv->regs->ifregs[0].data[i / 2]); + cf->data[i] = data_reg; + cf->data[i + 1] = data_reg >> 8; + } + + netif_receive_skb(skb); + rcv_pkts++; + stats->rx_packets++; + quota--; + stats->rx_bytes += cf->can_dlc; + + pch_fifo_thresh(priv, obj_num); + obj_num++; + } while (quota > 0); + + return rcv_pkts; +} + +static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &(priv->ndev->stats); + u32 dlc; + + can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1); + iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND, + &priv->regs->ifregs[1].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat); + dlc = get_can_dlc(ioread32(&priv->regs->ifregs[1].mcont) & + PCH_IF_MCONT_DLC); + stats->tx_bytes += dlc; + stats->tx_packets++; + if (int_stat == PCH_TX_OBJ_END) + netif_wake_queue(ndev); +} + +static int pch_can_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct pch_can_priv *priv = netdev_priv(ndev); + u32 int_stat; + u32 reg_stat; + int quota_save = quota; + + int_stat = pch_can_int_pending(priv); + if (!int_stat) + goto end; + + if (int_stat == PCH_STATUS_INT) { + reg_stat = ioread32(&priv->regs->stat); + + if ((reg_stat & (PCH_BUS_OFF | PCH_LEC_ALL)) && + ((reg_stat & PCH_LEC_ALL) != PCH_LEC_ALL)) { + pch_can_error(ndev, reg_stat); + quota--; + } + + if (reg_stat & (PCH_TX_OK | PCH_RX_OK)) + pch_can_bit_clear(&priv->regs->stat, + reg_stat & (PCH_TX_OK | PCH_RX_OK)); + + int_stat = pch_can_int_pending(priv); + } + + if (quota == 0) + goto end; + + if ((int_stat >= PCH_RX_OBJ_START) && (int_stat <= PCH_RX_OBJ_END)) { + quota -= pch_can_rx_normal(ndev, int_stat, quota); + } else if ((int_stat >= PCH_TX_OBJ_START) && + (int_stat <= PCH_TX_OBJ_END)) { + /* Handle transmission interrupt */ + pch_can_tx_complete(ndev, int_stat); + } + +end: + napi_complete(napi); + pch_can_set_int_enables(priv, PCH_CAN_ALL); + + return quota_save - quota; +} + +static int pch_set_bittiming(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + const struct can_bittiming *bt = &priv->can.bittiming; + u32 canbit; + u32 bepe; + + /* Setting the CCE bit for accessing the Can Timing register. */ + pch_can_bit_set(&priv->regs->cont, PCH_CTRL_CCE); + + canbit = (bt->brp - 1) & PCH_MSK_BITT_BRP; + canbit |= (bt->sjw - 1) << PCH_BIT_SJW_SHIFT; + canbit |= (bt->phase_seg1 + bt->prop_seg - 1) << PCH_BIT_TSEG1_SHIFT; + canbit |= (bt->phase_seg2 - 1) << PCH_BIT_TSEG2_SHIFT; + bepe = ((bt->brp - 1) & PCH_MSK_BRPE_BRPE) >> PCH_BIT_BRPE_BRPE_SHIFT; + iowrite32(canbit, &priv->regs->bitt); + iowrite32(bepe, &priv->regs->brpe); + pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_CCE); + + return 0; +} + +static void pch_can_start(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + + if (priv->can.state != CAN_STATE_STOPPED) + pch_can_reset(priv); + + pch_set_bittiming(ndev); + pch_can_set_optmode(priv); + + pch_can_set_tx_all(priv, 1); + pch_can_set_rx_all(priv, 1); + + /* Setting the CAN to run mode. */ + pch_can_set_run_mode(priv, PCH_CAN_RUN); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return; +} + +static int pch_can_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + pch_can_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static int pch_can_open(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + int retval; + + /* Regstering the interrupt. */ + retval = request_irq(priv->dev->irq, pch_can_interrupt, IRQF_SHARED, + ndev->name, ndev); + if (retval) { + netdev_err(ndev, "request_irq failed.\n"); + goto req_irq_err; + } + + /* Open common can device */ + retval = open_candev(ndev); + if (retval) { + netdev_err(ndev, "open_candev() failed %d\n", retval); + goto err_open_candev; + } + + pch_can_init(priv); + pch_can_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; + +err_open_candev: + free_irq(priv->dev->irq, ndev); +req_irq_err: + pch_can_release(priv); + + return retval; +} + +static int pch_close(struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + pch_can_release(priv); + free_irq(priv->dev->irq, ndev); + close_candev(ndev); + priv->can.state = CAN_STATE_STOPPED; + return 0; +} + +static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct pch_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + int tx_obj_no; + int i; + u32 id2; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + tx_obj_no = priv->tx_obj; + if (priv->tx_obj == PCH_TX_OBJ_END) { + if (ioread32(&priv->regs->treq2) & PCH_TREQ2_TX_MASK) + netif_stop_queue(ndev); + + priv->tx_obj = PCH_TX_OBJ_START; + } else { + priv->tx_obj++; + } + + /* Setting the CMASK register. */ + pch_can_bit_set(&priv->regs->ifregs[1].cmask, PCH_CMASK_ALL); + + /* If ID extended is set. */ + if (cf->can_id & CAN_EFF_FLAG) { + iowrite32(cf->can_id & 0xffff, &priv->regs->ifregs[1].id1); + id2 = ((cf->can_id >> 16) & 0x1fff) | PCH_ID2_XTD; + } else { + iowrite32(0, &priv->regs->ifregs[1].id1); + id2 = (cf->can_id & CAN_SFF_MASK) << 2; + } + + id2 |= PCH_ID_MSGVAL; + + /* If remote frame has to be transmitted.. */ + if (!(cf->can_id & CAN_RTR_FLAG)) + id2 |= PCH_ID2_DIR; + + iowrite32(id2, &priv->regs->ifregs[1].id2); + + /* Copy data to register */ + for (i = 0; i < cf->can_dlc; i += 2) { + iowrite16(cf->data[i] | (cf->data[i + 1] << 8), + &priv->regs->ifregs[1].data[i / 2]); + } + + can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1); + + /* Set the size of the data. Update if2_mcont */ + iowrite32(cf->can_dlc | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT | + PCH_IF_MCONT_TXIE, &priv->regs->ifregs[1].mcont); + + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, tx_obj_no); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops pch_can_netdev_ops = { + .ndo_open = pch_can_open, + .ndo_stop = pch_close, + .ndo_start_xmit = pch_xmit, +}; + +static void __devexit pch_can_remove(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + struct pch_can_priv *priv = netdev_priv(ndev); + + unregister_candev(priv->ndev); + if (priv->use_msi) + pci_disable_msi(priv->dev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + pch_can_reset(priv); + pci_iounmap(pdev, priv->regs); + free_candev(priv->ndev); +} + +#ifdef CONFIG_PM +static void pch_can_set_int_custom(struct pch_can_priv *priv) +{ + /* Clearing the IE, SIE and EIE bits of Can control register. */ + pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); + + /* Appropriately setting them. */ + pch_can_bit_set(&priv->regs->cont, + ((priv->int_enables & PCH_MSK_CTRL_IE_SIE_EIE) << 1)); +} + +/* This function retrieves interrupt enabled for the CAN device. */ +static u32 pch_can_get_int_enables(struct pch_can_priv *priv) +{ + /* Obtaining the status of IE, SIE and EIE interrupt bits. */ + return (ioread32(&priv->regs->cont) & PCH_CTRL_IE_SIE_EIE) >> 1; +} + +static u32 pch_can_get_rxtx_ir(struct pch_can_priv *priv, u32 buff_num, + enum pch_ifreg dir) +{ + u32 ie, enable; + + if (dir) + ie = PCH_IF_MCONT_RXIE; + else + ie = PCH_IF_MCONT_TXIE; + + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); + + if (((ioread32(&priv->regs->ifregs[dir].id2)) & PCH_ID_MSGVAL) && + ((ioread32(&priv->regs->ifregs[dir].mcont)) & ie)) + enable = 1; + else + enable = 0; + + return enable; +} + +static void pch_can_set_rx_buffer_link(struct pch_can_priv *priv, + u32 buffer_num, int set) +{ + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, + &priv->regs->ifregs[0].cmask); + if (set) + pch_can_bit_clear(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_EOB); + else + pch_can_bit_set(&priv->regs->ifregs[0].mcont, PCH_IF_MCONT_EOB); + + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); +} + +static u32 pch_can_get_rx_buffer_link(struct pch_can_priv *priv, u32 buffer_num) +{ + u32 link; + + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); + + if (ioread32(&priv->regs->ifregs[0].mcont) & PCH_IF_MCONT_EOB) + link = 0; + else + link = 1; + return link; +} + +static int pch_can_get_buffer_status(struct pch_can_priv *priv) +{ + return (ioread32(&priv->regs->treq1) & 0xffff) | + (ioread32(&priv->regs->treq2) << 16); +} + +static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int i; + int retval; + u32 buf_stat; /* Variable for reading the transmit buffer status. */ + int counter = PCH_COUNTER_LIMIT; + + struct net_device *dev = pci_get_drvdata(pdev); + struct pch_can_priv *priv = netdev_priv(dev); + + /* Stop the CAN controller */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Indicate that we are aboutto/in suspend */ + priv->can.state = CAN_STATE_STOPPED; + + /* Waiting for all transmission to complete. */ + while (counter) { + buf_stat = pch_can_get_buffer_status(priv); + if (!buf_stat) + break; + counter--; + udelay(1); + } + if (!counter) + dev_err(&pdev->dev, "%s -> Transmission time out.\n", __func__); + + /* Save interrupt configuration and then disable them */ + priv->int_enables = pch_can_get_int_enables(priv); + pch_can_set_int_enables(priv, PCH_CAN_DISABLE); + + /* Save Tx buffer enable state */ + for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) + priv->tx_enable[i - 1] = pch_can_get_rxtx_ir(priv, i, + PCH_TX_IFREG); + + /* Disable all Transmit buffers */ + pch_can_set_tx_all(priv, 0); + + /* Save Rx buffer enable state */ + for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { + priv->rx_enable[i - 1] = pch_can_get_rxtx_ir(priv, i, + PCH_RX_IFREG); + priv->rx_link[i - 1] = pch_can_get_rx_buffer_link(priv, i); + } + + /* Disable all Receive buffers */ + pch_can_set_rx_all(priv, 0); + retval = pci_save_state(pdev); + if (retval) { + dev_err(&pdev->dev, "pci_save_state failed.\n"); + } else { + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + } + + return retval; +} + +static int pch_can_resume(struct pci_dev *pdev) +{ + int i; + int retval; + struct net_device *dev = pci_get_drvdata(pdev); + struct pch_can_priv *priv = netdev_priv(dev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "pci_enable_device failed.\n"); + return retval; + } + + pci_enable_wake(pdev, PCI_D3hot, 0); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Disabling all interrupts. */ + pch_can_set_int_enables(priv, PCH_CAN_DISABLE); + + /* Setting the CAN device in Stop Mode. */ + pch_can_set_run_mode(priv, PCH_CAN_STOP); + + /* Configuring the transmit and receive buffers. */ + pch_can_config_rx_tx_buffers(priv); + + /* Restore the CAN state */ + pch_set_bittiming(dev); + + /* Listen/Active */ + pch_can_set_optmode(priv); + + /* Enabling the transmit buffer. */ + for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) + pch_can_set_rxtx(priv, i, priv->tx_enable[i - 1], PCH_TX_IFREG); + + /* Configuring the receive buffer and enabling them. */ + for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { + /* Restore buffer link */ + pch_can_set_rx_buffer_link(priv, i, priv->rx_link[i - 1]); + + /* Restore buffer enables */ + pch_can_set_rxtx(priv, i, priv->rx_enable[i - 1], PCH_RX_IFREG); + } + + /* Enable CAN Interrupts */ + pch_can_set_int_custom(priv); + + /* Restore Run Mode */ + pch_can_set_run_mode(priv, PCH_CAN_RUN); + + return retval; +} +#else +#define pch_can_suspend NULL +#define pch_can_resume NULL +#endif + +static int pch_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct pch_can_priv *priv = netdev_priv(dev); + u32 errc = ioread32(&priv->regs->errc); + + bec->txerr = errc & PCH_TEC; + bec->rxerr = (errc & PCH_REC) >> 8; + + return 0; +} + +static int __devinit pch_can_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct net_device *ndev; + struct pch_can_priv *priv; + int rc; + void __iomem *addr; + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "Failed pci_enable_device %d\n", rc); + goto probe_exit_endev; + } + + rc = pci_request_regions(pdev, KBUILD_MODNAME); + if (rc) { + dev_err(&pdev->dev, "Failed pci_request_regions %d\n", rc); + goto probe_exit_pcireq; + } + + addr = pci_iomap(pdev, 1, 0); + if (!addr) { + rc = -EIO; + dev_err(&pdev->dev, "Failed pci_iomap\n"); + goto probe_exit_ipmap; + } + + ndev = alloc_candev(sizeof(struct pch_can_priv), PCH_TX_OBJ_END); + if (!ndev) { + rc = -ENOMEM; + dev_err(&pdev->dev, "Failed alloc_candev\n"); + goto probe_exit_alloc_candev; + } + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->regs = addr; + priv->dev = pdev; + priv->can.bittiming_const = &pch_can_bittiming_const; + priv->can.do_set_mode = pch_can_do_set_mode; + priv->can.do_get_berr_counter = pch_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK; + priv->tx_obj = PCH_TX_OBJ_START; /* Point head of Tx Obj */ + + ndev->irq = pdev->irq; + ndev->flags |= IFF_ECHO; + + pci_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &pch_can_netdev_ops; + priv->can.clock.freq = PCH_CAN_CLK; /* Hz */ + + netif_napi_add(ndev, &priv->napi, pch_can_poll, PCH_RX_OBJ_END); + + rc = pci_enable_msi(priv->dev); + if (rc) { + netdev_err(ndev, "PCH CAN opened without MSI\n"); + priv->use_msi = 0; + } else { + netdev_err(ndev, "PCH CAN opened with MSI\n"); + pci_set_master(pdev); + priv->use_msi = 1; + } + + rc = register_candev(ndev); + if (rc) { + dev_err(&pdev->dev, "Failed register_candev %d\n", rc); + goto probe_exit_reg_candev; + } + + return 0; + +probe_exit_reg_candev: + if (priv->use_msi) + pci_disable_msi(priv->dev); + free_candev(ndev); +probe_exit_alloc_candev: + pci_iounmap(pdev, addr); +probe_exit_ipmap: + pci_release_regions(pdev); +probe_exit_pcireq: + pci_disable_device(pdev); +probe_exit_endev: + return rc; +} + +static struct pci_driver pch_can_pci_driver = { + .name = "pch_can", + .id_table = pch_pci_tbl, + .probe = pch_can_probe, + .remove = __devexit_p(pch_can_remove), + .suspend = pch_can_suspend, + .resume = pch_can_resume, +}; + +static int __init pch_can_pci_init(void) +{ + return pci_register_driver(&pch_can_pci_driver); +} +module_init(pch_can_pci_init); + +static void __exit pch_can_pci_exit(void) +{ + pci_unregister_driver(&pch_can_pci_driver); +} +module_exit(pch_can_pci_exit); + +MODULE_DESCRIPTION("Intel EG20T PCH CAN(Controller Area Network) Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.94"); diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig new file mode 100644 index 00000000..b60d6c5f --- /dev/null +++ b/drivers/net/can/sja1000/Kconfig @@ -0,0 +1,108 @@ +menuconfig CAN_SJA1000 + tristate "Philips/NXP SJA1000 devices" + depends on CAN_DEV && HAS_IOMEM + +if CAN_SJA1000 + +config CAN_SJA1000_ISA + tristate "ISA Bus based legacy SJA1000 driver" + ---help--- + This driver adds legacy support for SJA1000 chips connected to + the ISA bus using I/O port, memory mapped or indirect access. + +config CAN_SJA1000_PLATFORM + tristate "Generic Platform Bus based SJA1000 driver" + ---help--- + This driver adds support for the SJA1000 chips connected to + the "platform bus" (Linux abstraction for directly to the + processor attached devices). Which can be found on various + boards from Phytec (http://www.phytec.de) like the PCM027, + PCM038. + +config CAN_SJA1000_OF_PLATFORM + tristate "Generic OF Platform Bus based SJA1000 driver" + depends on PPC_OF + ---help--- + This driver adds support for the SJA1000 chips connected to + the OpenFirmware "platform bus" found on embedded systems with + OpenFirmware bindings, e.g. if you have a PowerPC based system + you may want to enable this option. + +config CAN_EMS_PCMCIA + tristate "EMS CPC-CARD Card" + depends on PCMCIA + ---help--- + This driver is for the one or two channel CPC-CARD cards from + EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). + +config CAN_EMS_PCI + tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P Card" + depends on PCI + ---help--- + This driver is for the one, two or four channel CPC-PCI, + CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche + (http://www.ems-wuensche.de). + +config CAN_PEAK_PCMCIA + tristate "PEAK PCAN-PC Card" + depends on PCMCIA + ---help--- + This driver is for the PCAN-PC Card PCMCIA adapter (1 or 2 channels) + from PEAK-System (http://www.peak-system.com). To compile this + driver as a module, choose M here: the module will be called + peak_pcmcia. + +config CAN_PEAK_PCI + tristate "PEAK PCAN-PCI/PCIe/miniPCI Cards" + depends on PCI + ---help--- + This driver is for the PCAN-PCI/PCIe/miniPCI cards + (1, 2, 3 or 4 channels) from PEAK-System Technik + (http://www.peak-system.com). + +config CAN_PEAK_PCIEC + bool "PEAK PCAN-ExpressCard Cards" + depends on CAN_PEAK_PCI + select I2C + select I2C_ALGOBIT + default y + ---help--- + Say Y here if you want to use a PCAN-ExpressCard from PEAK-System + Technik. This will also automatically select I2C and I2C_ALGO + configuration options. + +config CAN_KVASER_PCI + tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" + depends on PCI + ---help--- + This driver is for the the PCIcanx and PCIcan cards (1, 2 or + 4 channel) from Kvaser (http://www.kvaser.com). + +config CAN_PLX_PCI + tristate "PLX90xx PCI-bridge based Cards" + depends on PCI + ---help--- + This driver is for CAN interface cards based on + the PLX90xx PCI bridge. + Driver supports now: + - Adlink PCI-7841/cPCI-7841 card (http://www.adlinktech.com/) + - Adlink PCI-7841/cPCI-7841 SE card + - esd CAN-PCI/CPCI/PCI104/200 (http://www.esd.eu/) + - esd CAN-PCI/PMC/266 + - esd CAN-PCIe/2000 + - Marathon CAN-bus-PCI card (http://www.marathon.ru/) + - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/) + - IXXAT Automation PC-I 04/PCI card (http://www.ixxat.com/) + +config CAN_TSCAN1 + tristate "TS-CAN1 PC104 boards" + depends on ISA + help + This driver is for Technologic Systems' TSCAN-1 PC104 boards. + http://www.embeddedarm.com/products/board-detail.php?product=TS-CAN1 + The driver supports multiple boards and automatically configures them: + PLD IO base addresses are read from jumpers JP1 and JP2, + IRQ numbers are read from jumpers JP4 and JP5, + SJA1000 IO base addresses are chosen heuristically (first that works). + +endif diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile new file mode 100644 index 00000000..b3d05cbf --- /dev/null +++ b/drivers/net/can/sja1000/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the SJA1000 CAN controller drivers. +# + +obj-$(CONFIG_CAN_SJA1000) += sja1000.o +obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o +obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o +obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o +obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o +obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o +obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o +obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o +obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o +obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o +obj-$(CONFIG_CAN_TSCAN1) += tscan1.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c new file mode 100644 index 00000000..36f4f978 --- /dev/null +++ b/drivers/net/can/sja1000/ems_pci.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com> + * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/can/dev.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "ems_pci" + +MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-PCI/PCIe/104P CAN cards"); +MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe/104P CAN card"); +MODULE_LICENSE("GPL v2"); + +#define EMS_PCI_V1_MAX_CHAN 2 +#define EMS_PCI_V2_MAX_CHAN 4 +#define EMS_PCI_MAX_CHAN EMS_PCI_V2_MAX_CHAN + +struct ems_pci_card { + int version; + int channels; + + struct pci_dev *pci_dev; + struct net_device *net_dev[EMS_PCI_MAX_CHAN]; + + void __iomem *conf_addr; + void __iomem *base_addr; +}; + +#define EMS_PCI_CAN_CLOCK (16000000 / 2) + +/* + * Register definitions and descriptions are from LinCAN 0.3.3. + * + * PSB4610 PITA-2 bridge control registers + */ +#define PITA2_ICR 0x00 /* Interrupt Control Register */ +#define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */ +#define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */ + +#define PITA2_MISC 0x1c /* Miscellaneous Register */ +#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */ + +/* + * Register definitions for the PLX 9030 + */ +#define PLX_ICSR 0x4c /* Interrupt Control/Status register */ +#define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */ +#define PLX_ICSR_PCIINT_ENA 0x0040 /* PCI Interrupt Enable */ +#define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */ +#define PLX_ICSR_ENA_CLR (PLX_ICSR_LINTI1_ENA | PLX_ICSR_PCIINT_ENA | \ + PLX_ICSR_LINTI1_CLR) + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode, push-pull and the correct polarity. + */ +#define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 7 + * (meaning direct oscillator output) because the second SJA1000 chip + * is driven by the first one CLKOUT output. + */ +#define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +#define EMS_PCI_V1_BASE_BAR 1 +#define EMS_PCI_V1_CONF_SIZE 4096 /* size of PITA control area */ +#define EMS_PCI_V2_BASE_BAR 2 +#define EMS_PCI_V2_CONF_SIZE 128 /* size of PLX control area */ +#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */ +#define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */ + +#define EMS_PCI_BASE_SIZE 4096 /* size of controller area */ + +static DEFINE_PCI_DEVICE_TABLE(ems_pci_tbl) = { + /* CPC-PCI v1 */ + {PCI_VENDOR_ID_SIEMENS, 0x2104, PCI_ANY_ID, PCI_ANY_ID,}, + /* CPC-PCI v2 */ + {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4000}, + /* CPC-104P v2 */ + {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4002}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ems_pci_tbl); + +/* + * Helper to read internal registers from card logic (not CAN) + */ +static u8 ems_pci_v1_readb(struct ems_pci_card *card, unsigned int port) +{ + return readb(card->base_addr + (port * 4)); +} + +static u8 ems_pci_v1_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + (port * 4)); +} + +static void ems_pci_v1_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + writeb(val, priv->reg_base + (port * 4)); +} + +static void ems_pci_v1_post_irq(const struct sja1000_priv *priv) +{ + struct ems_pci_card *card = (struct ems_pci_card *)priv->priv; + + /* reset int flag of pita */ + writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, + card->conf_addr + PITA2_ICR); +} + +static u8 ems_pci_v2_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + port); +} + +static void ems_pci_v2_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + writeb(val, priv->reg_base + port); +} + +static void ems_pci_v2_post_irq(const struct sja1000_priv *priv) +{ + struct ems_pci_card *card = (struct ems_pci_card *)priv->priv; + + writel(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR); +} + +/* + * Check if a CAN controller is present at the specified location + * by trying to set 'em into the PeliCAN mode + */ +static inline int ems_pci_check_chan(const struct sja1000_priv *priv) +{ + unsigned char res; + + /* Make sure SJA1000 is in reset mode */ + priv->write_reg(priv, REG_MOD, 1); + + priv->write_reg(priv, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + res = priv->read_reg(priv, REG_CDR); + + if (res == CDR_PELICAN) + return 1; + + return 0; +} + +static void ems_pci_del_card(struct pci_dev *pdev) +{ + struct ems_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + int i = 0; + + for (i = 0; i < card->channels; i++) { + dev = card->net_dev[i]; + + if (!dev) + continue; + + dev_info(&pdev->dev, "Removing %s.\n", dev->name); + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + if (card->base_addr != NULL) + pci_iounmap(card->pci_dev, card->base_addr); + + if (card->conf_addr != NULL) + pci_iounmap(card->pci_dev, card->conf_addr); + + kfree(card); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static void ems_pci_card_reset(struct ems_pci_card *card) +{ + /* Request board reset */ + writeb(0, card->base_addr); +} + +/* + * Probe PCI device for EMS CAN signature and register each available + * CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int __devinit ems_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sja1000_priv *priv; + struct net_device *dev; + struct ems_pci_card *card; + int max_chan, conf_size, base_bar; + int err, i; + + /* Enabling PCI device */ + if (pci_enable_device(pdev) < 0) { + dev_err(&pdev->dev, "Enabling PCI device failed\n"); + return -ENODEV; + } + + /* Allocating card structures to hold addresses, ... */ + card = kzalloc(sizeof(struct ems_pci_card), GFP_KERNEL); + if (card == NULL) { + dev_err(&pdev->dev, "Unable to allocate memory\n"); + pci_disable_device(pdev); + return -ENOMEM; + } + + pci_set_drvdata(pdev, card); + + card->pci_dev = pdev; + + card->channels = 0; + + if (pdev->vendor == PCI_VENDOR_ID_PLX) { + card->version = 2; /* CPC-PCI v2 */ + max_chan = EMS_PCI_V2_MAX_CHAN; + base_bar = EMS_PCI_V2_BASE_BAR; + conf_size = EMS_PCI_V2_CONF_SIZE; + } else { + card->version = 1; /* CPC-PCI v1 */ + max_chan = EMS_PCI_V1_MAX_CHAN; + base_bar = EMS_PCI_V1_BASE_BAR; + conf_size = EMS_PCI_V1_CONF_SIZE; + } + + /* Remap configuration space and controller memory area */ + card->conf_addr = pci_iomap(pdev, 0, conf_size); + if (card->conf_addr == NULL) { + err = -ENOMEM; + goto failure_cleanup; + } + + card->base_addr = pci_iomap(pdev, base_bar, EMS_PCI_BASE_SIZE); + if (card->base_addr == NULL) { + err = -ENOMEM; + goto failure_cleanup; + } + + if (card->version == 1) { + /* Configure PITA-2 parallel interface (enable MUX) */ + writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC); + + /* Check for unique EMS CAN signature */ + if (ems_pci_v1_readb(card, 0) != 0x55 || + ems_pci_v1_readb(card, 1) != 0xAA || + ems_pci_v1_readb(card, 2) != 0x01 || + ems_pci_v1_readb(card, 3) != 0xCB || + ems_pci_v1_readb(card, 4) != 0x11) { + dev_err(&pdev->dev, + "Not EMS Dr. Thomas Wuensche interface\n"); + err = -ENODEV; + goto failure_cleanup; + } + } + + ems_pci_card_reset(card); + + /* Detect available channels */ + for (i = 0; i < max_chan; i++) { + dev = alloc_sja1000dev(0); + if (dev == NULL) { + err = -ENOMEM; + goto failure_cleanup; + } + + card->net_dev[i] = dev; + priv = netdev_priv(dev); + priv->priv = card; + priv->irq_flags = IRQF_SHARED; + + dev->irq = pdev->irq; + priv->reg_base = card->base_addr + EMS_PCI_CAN_BASE_OFFSET + + (i * EMS_PCI_CAN_CTRL_SIZE); + if (card->version == 1) { + priv->read_reg = ems_pci_v1_read_reg; + priv->write_reg = ems_pci_v1_write_reg; + priv->post_irq = ems_pci_v1_post_irq; + } else { + priv->read_reg = ems_pci_v2_read_reg; + priv->write_reg = ems_pci_v2_write_reg; + priv->post_irq = ems_pci_v2_post_irq; + } + + /* Check if channel is present */ + if (ems_pci_check_chan(priv)) { + priv->can.clock.freq = EMS_PCI_CAN_CLOCK; + priv->ocr = EMS_PCI_OCR; + priv->cdr = EMS_PCI_CDR; + + SET_NETDEV_DEV(dev, &pdev->dev); + + if (card->version == 1) + /* reset int flag of pita */ + writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, + card->conf_addr + PITA2_ICR); + else + /* enable IRQ in PLX 9030 */ + writel(PLX_ICSR_ENA_CLR, + card->conf_addr + PLX_ICSR); + + /* Register SJA1000 device */ + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "Registering device failed " + "(err=%d)\n", err); + free_sja1000dev(dev); + goto failure_cleanup; + } + + card->channels++; + + dev_info(&pdev->dev, "Channel #%d at 0x%p, irq %d\n", + i + 1, priv->reg_base, dev->irq); + } else { + free_sja1000dev(dev); + } + } + + return 0; + +failure_cleanup: + dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err); + + ems_pci_del_card(pdev); + + return err; +} + +static struct pci_driver ems_pci_driver = { + .name = DRV_NAME, + .id_table = ems_pci_tbl, + .probe = ems_pci_add_card, + .remove = ems_pci_del_card, +}; + +static int __init ems_pci_init(void) +{ + return pci_register_driver(&ems_pci_driver); +} + +static void __exit ems_pci_exit(void) +{ + pci_unregister_driver(&ems_pci_driver); +} + +module_init(ems_pci_init); +module_exit(ems_pci_exit); + diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c new file mode 100644 index 00000000..075a5457 --- /dev/null +++ b/drivers/net/can/sja1000/ems_pcmcia.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2008 Sebastian Haas (initial chardev implementation) + * Copyright (C) 2010 Markus Plessing <plessing@ems-wuensche.com> + * Rework for mainline by Oliver Hartkopp <socketcan@hartkopp.net> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include "sja1000.h" + +#define DRV_NAME "ems_pcmcia" + +MODULE_AUTHOR("Markus Plessing <plessing@ems-wuensche.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-CARD cards"); +MODULE_SUPPORTED_DEVICE("EMS CPC-CARD CAN card"); +MODULE_LICENSE("GPL v2"); + +#define EMS_PCMCIA_MAX_CHAN 2 + +struct ems_pcmcia_card { + int channels; + struct pcmcia_device *pcmcia_dev; + struct net_device *net_dev[EMS_PCMCIA_MAX_CHAN]; + void __iomem *base_addr; +}; + +#define EMS_PCMCIA_CAN_CLOCK (16000000 / 2) + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode , push-pull and the correct polarity. + */ +#define EMS_PCMCIA_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 7 + * (meaning direct oscillator output) because the second SJA1000 chip + * is driven by the first one CLKOUT output. + */ +#define EMS_PCMCIA_CDR (CDR_CBP | CDR_CLKOUT_MASK) +#define EMS_PCMCIA_MEM_SIZE 4096 /* Size of the remapped io-memory */ +#define EMS_PCMCIA_CAN_BASE_OFFSET 0x100 /* Offset where controllers starts */ +#define EMS_PCMCIA_CAN_CTRL_SIZE 0x80 /* Memory size for each controller */ + +#define EMS_CMD_RESET 0x00 /* Perform a reset of the card */ +#define EMS_CMD_MAP 0x03 /* Map CAN controllers into card' memory */ +#define EMS_CMD_UMAP 0x02 /* Unmap CAN controllers from card' memory */ + +static struct pcmcia_device_id ems_pcmcia_tbl[] = { + PCMCIA_DEVICE_PROD_ID123("EMS_T_W", "CPC-Card", "V2.0", 0xeab1ea23, + 0xa338573f, 0xe4575800), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, ems_pcmcia_tbl); + +static u8 ems_pcmcia_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + port); +} + +static void ems_pcmcia_write_reg(const struct sja1000_priv *priv, int port, + u8 val) +{ + writeb(val, priv->reg_base + port); +} + +static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id) +{ + struct ems_pcmcia_card *card = dev_id; + struct net_device *dev; + irqreturn_t retval = IRQ_NONE; + int i, again; + + /* Card not present */ + if (readw(card->base_addr) != 0xAA55) + return IRQ_HANDLED; + + do { + again = 0; + + /* Check interrupt for each channel */ + for (i = 0; i < card->channels; i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + if (sja1000_interrupt(irq, dev) == IRQ_HANDLED) + again = 1; + } + /* At least one channel handled the interrupt */ + if (again) + retval = IRQ_HANDLED; + + } while (again); + + return retval; +} + +/* + * Check if a CAN controller is present at the specified location + * by trying to set 'em into the PeliCAN mode + */ +static inline int ems_pcmcia_check_chan(struct sja1000_priv *priv) +{ + /* Make sure SJA1000 is in reset mode */ + ems_pcmcia_write_reg(priv, REG_MOD, 1); + ems_pcmcia_write_reg(priv, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + if (ems_pcmcia_read_reg(priv, REG_CDR) == CDR_PELICAN) + return 1; + + return 0; +} + +static void ems_pcmcia_del_card(struct pcmcia_device *pdev) +{ + struct ems_pcmcia_card *card = pdev->priv; + struct net_device *dev; + int i; + + free_irq(pdev->irq, card); + + for (i = 0; i < card->channels; i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + printk(KERN_INFO "%s: removing %s on channel #%d\n", + DRV_NAME, dev->name, i); + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + writeb(EMS_CMD_UMAP, card->base_addr); + iounmap(card->base_addr); + kfree(card); + + pdev->priv = NULL; +} + +/* + * Probe PCI device for EMS CAN signature and register each available + * CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int __devinit ems_pcmcia_add_card(struct pcmcia_device *pdev, + unsigned long base) +{ + struct sja1000_priv *priv; + struct net_device *dev; + struct ems_pcmcia_card *card; + int err, i; + + /* Allocating card structures to hold addresses, ... */ + card = kzalloc(sizeof(struct ems_pcmcia_card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + pdev->priv = card; + card->channels = 0; + + card->base_addr = ioremap(base, EMS_PCMCIA_MEM_SIZE); + if (!card->base_addr) { + err = -ENOMEM; + goto failure_cleanup; + } + + /* Check for unique EMS CAN signature */ + if (readw(card->base_addr) != 0xAA55) { + err = -ENODEV; + goto failure_cleanup; + } + + /* Request board reset */ + writeb(EMS_CMD_RESET, card->base_addr); + + /* Make sure CAN controllers are mapped into card's memory space */ + writeb(EMS_CMD_MAP, card->base_addr); + + /* Detect available channels */ + for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) { + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto failure_cleanup; + } + + card->net_dev[i] = dev; + priv = netdev_priv(dev); + priv->priv = card; + SET_NETDEV_DEV(dev, &pdev->dev); + + priv->irq_flags = IRQF_SHARED; + dev->irq = pdev->irq; + priv->reg_base = card->base_addr + EMS_PCMCIA_CAN_BASE_OFFSET + + (i * EMS_PCMCIA_CAN_CTRL_SIZE); + + /* Check if channel is present */ + if (ems_pcmcia_check_chan(priv)) { + priv->read_reg = ems_pcmcia_read_reg; + priv->write_reg = ems_pcmcia_write_reg; + priv->can.clock.freq = EMS_PCMCIA_CAN_CLOCK; + priv->ocr = EMS_PCMCIA_OCR; + priv->cdr = EMS_PCMCIA_CDR; + priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER; + + /* Register SJA1000 device */ + err = register_sja1000dev(dev); + if (err) { + free_sja1000dev(dev); + goto failure_cleanup; + } + + card->channels++; + + printk(KERN_INFO "%s: registered %s on channel " + "#%d at 0x%p, irq %d\n", DRV_NAME, dev->name, + i, priv->reg_base, dev->irq); + } else + free_sja1000dev(dev); + } + + err = request_irq(dev->irq, &ems_pcmcia_interrupt, IRQF_SHARED, + DRV_NAME, card); + if (!err) + return 0; + +failure_cleanup: + ems_pcmcia_del_card(pdev); + return err; +} + +/* + * Setup PCMCIA socket and probe for EMS CPC-CARD + */ +static int __devinit ems_pcmcia_probe(struct pcmcia_device *dev) +{ + int csval; + + /* General socket configuration */ + dev->config_flags |= CONF_ENABLE_IRQ; + dev->config_index = 1; + dev->config_regs = PRESENT_OPTION; + + /* The io structure describes IO port mapping */ + dev->resource[0]->end = 16; + dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + dev->resource[1]->end = 16; + dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; + dev->io_lines = 5; + + /* Allocate a memory window */ + dev->resource[2]->flags = + (WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE); + dev->resource[2]->start = dev->resource[2]->end = 0; + + csval = pcmcia_request_window(dev, dev->resource[2], 0); + if (csval) { + dev_err(&dev->dev, "pcmcia_request_window failed (err=%d)\n", + csval); + return 0; + } + + csval = pcmcia_map_mem_page(dev, dev->resource[2], dev->config_base); + if (csval) { + dev_err(&dev->dev, "pcmcia_map_mem_page failed (err=%d)\n", + csval); + return 0; + } + + csval = pcmcia_enable_device(dev); + if (csval) { + dev_err(&dev->dev, "pcmcia_enable_device failed (err=%d)\n", + csval); + return 0; + } + + ems_pcmcia_add_card(dev, dev->resource[2]->start); + return 0; +} + +/* + * Release claimed resources + */ +static void ems_pcmcia_remove(struct pcmcia_device *dev) +{ + ems_pcmcia_del_card(dev); + pcmcia_disable_device(dev); +} + +static struct pcmcia_driver ems_pcmcia_driver = { + .name = DRV_NAME, + .probe = ems_pcmcia_probe, + .remove = ems_pcmcia_remove, + .id_table = ems_pcmcia_tbl, +}; + +static int __init ems_pcmcia_init(void) +{ + return pcmcia_register_driver(&ems_pcmcia_driver); +} +module_init(ems_pcmcia_init); + +static void __exit ems_pcmcia_exit(void) +{ + pcmcia_unregister_driver(&ems_pcmcia_driver); +} +module_exit(ems_pcmcia_exit); diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c new file mode 100644 index 00000000..ed004ceb --- /dev/null +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se> + * + * Parts of this software are based on (derived) the following: + * + * - Kvaser linux driver, version 4.72 BETA + * Copyright (C) 2002-2007 KVASER AB + * + * - Lincan driver, version 0.3.3, OCERA project + * Copyright (C) 2004 Pavel Pisa + * Copyright (C) 2001 Arnaud Westenberg + * + * - Socketcan SJA1000 drivers + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/can/dev.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "kvaser_pci" + +MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>"); +MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards"); +MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card"); +MODULE_LICENSE("GPL v2"); + +#define MAX_NO_OF_CHANNELS 4 /* max no of channels on a single card */ + +struct kvaser_pci { + int channel; + struct pci_dev *pci_dev; + struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1]; + void __iomem *conf_addr; + void __iomem *res_addr; + int no_channels; + u8 xilinx_ver; +}; + +#define KVASER_PCI_CAN_CLOCK (16000000 / 2) + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode , push-pull and the correct polarity. + */ +#define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 0 + * (meaning divide-by-2), the Pelican bit, and the clock-off bit + * (you will have no need for CLKOUT anyway). + */ +#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +/* + * These register values are valid for revision 14 of the Xilinx logic. + */ +#define XILINX_VERINT 7 /* Lower nibble simulate interrupts, + high nibble version number. */ + +#define XILINX_PRESUMED_VERSION 14 + +/* + * Important S5920 registers + */ +#define S5920_INTCSR 0x38 +#define S5920_PTCR 0x60 +#define INTCSR_ADDON_INTENABLE_M 0x2000 + + +#define KVASER_PCI_PORT_BYTES 0x20 + +#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */ +#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */ +#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */ + +#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */ +#define KVASER_PCI_DEVICE_ID1 0x8406 + +#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */ +#define KVASER_PCI_DEVICE_ID2 0x0008 + +static DEFINE_PCI_DEVICE_TABLE(kvaser_pci_tbl) = { + {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,}, + {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,}, + { 0,} +}; + +MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl); + +static u8 kvaser_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return ioread8(priv->reg_base + port); +} + +static void kvaser_pci_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + iowrite8(val, priv->reg_base + port); +} + +static void kvaser_pci_disable_irq(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct kvaser_pci *board = priv->priv; + u32 intcsr; + + /* Disable interrupts from card */ + intcsr = ioread32(board->conf_addr + S5920_INTCSR); + intcsr &= ~INTCSR_ADDON_INTENABLE_M; + iowrite32(intcsr, board->conf_addr + S5920_INTCSR); +} + +static void kvaser_pci_enable_irq(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct kvaser_pci *board = priv->priv; + u32 tmp_en_io; + + /* Enable interrupts from card */ + tmp_en_io = ioread32(board->conf_addr + S5920_INTCSR); + tmp_en_io |= INTCSR_ADDON_INTENABLE_M; + iowrite32(tmp_en_io, board->conf_addr + S5920_INTCSR); +} + +static int number_of_sja1000_chip(void __iomem *base_addr) +{ + u8 status; + int i; + + for (i = 0; i < MAX_NO_OF_CHANNELS; i++) { + /* reset chip */ + iowrite8(MOD_RM, base_addr + + (i * KVASER_PCI_PORT_BYTES) + REG_MOD); + status = ioread8(base_addr + + (i * KVASER_PCI_PORT_BYTES) + REG_MOD); + /* check reset bit */ + if (!(status & MOD_RM)) + break; + } + + return i; +} + +static void kvaser_pci_del_chan(struct net_device *dev) +{ + struct sja1000_priv *priv; + struct kvaser_pci *board; + int i; + + if (!dev) + return; + priv = netdev_priv(dev); + board = priv->priv; + if (!board) + return; + + dev_info(&board->pci_dev->dev, "Removing device %s\n", + dev->name); + + /* Disable PCI interrupts */ + kvaser_pci_disable_irq(dev); + + for (i = 0; i < board->no_channels - 1; i++) { + if (board->slave_dev[i]) { + dev_info(&board->pci_dev->dev, "Removing device %s\n", + board->slave_dev[i]->name); + unregister_sja1000dev(board->slave_dev[i]); + free_sja1000dev(board->slave_dev[i]); + } + } + unregister_sja1000dev(dev); + + pci_iounmap(board->pci_dev, priv->reg_base); + pci_iounmap(board->pci_dev, board->conf_addr); + pci_iounmap(board->pci_dev, board->res_addr); + + free_sja1000dev(dev); +} + +static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, + struct net_device **master_dev, + void __iomem *conf_addr, + void __iomem *res_addr, + void __iomem *base_addr) +{ + struct net_device *dev; + struct sja1000_priv *priv; + struct kvaser_pci *board; + int err, init_step; + + dev = alloc_sja1000dev(sizeof(struct kvaser_pci)); + if (dev == NULL) + return -ENOMEM; + + priv = netdev_priv(dev); + board = priv->priv; + + board->pci_dev = pdev; + board->channel = channel; + + /* S5920 */ + board->conf_addr = conf_addr; + + /* XILINX board wide address */ + board->res_addr = res_addr; + + if (channel == 0) { + board->xilinx_ver = + ioread8(board->res_addr + XILINX_VERINT) >> 4; + init_step = 2; + + /* Assert PTADR# - we're in passive mode so the other bits are + not important */ + iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR); + + /* Enable interrupts from card */ + kvaser_pci_enable_irq(dev); + } else { + struct sja1000_priv *master_priv = netdev_priv(*master_dev); + struct kvaser_pci *master_board = master_priv->priv; + master_board->slave_dev[channel - 1] = dev; + master_board->no_channels = channel + 1; + board->xilinx_ver = master_board->xilinx_ver; + } + + priv->reg_base = base_addr + channel * KVASER_PCI_PORT_BYTES; + + priv->read_reg = kvaser_pci_read_reg; + priv->write_reg = kvaser_pci_write_reg; + + priv->can.clock.freq = KVASER_PCI_CAN_CLOCK; + + priv->ocr = KVASER_PCI_OCR; + priv->cdr = KVASER_PCI_CDR; + + priv->irq_flags = IRQF_SHARED; + dev->irq = pdev->irq; + + init_step = 4; + + dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n", + priv->reg_base, board->conf_addr, dev->irq); + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Register SJA1000 device */ + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "Registering device failed (err=%d)\n", + err); + goto failure; + } + + if (channel == 0) + *master_dev = dev; + + return 0; + +failure: + kvaser_pci_del_chan(dev); + return err; +} + +static int __devinit kvaser_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + struct net_device *master_dev = NULL; + struct sja1000_priv *priv; + struct kvaser_pci *board; + int no_channels; + void __iomem *base_addr = NULL; + void __iomem *conf_addr = NULL; + void __iomem *res_addr = NULL; + int i; + + dev_info(&pdev->dev, "initializing device %04x:%04x\n", + pdev->vendor, pdev->device); + + err = pci_enable_device(pdev); + if (err) + goto failure; + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto failure_release_pci; + + /* S5920 */ + conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE); + if (conf_addr == NULL) { + err = -ENODEV; + goto failure_release_regions; + } + + /* XILINX board wide address */ + res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE); + if (res_addr == NULL) { + err = -ENOMEM; + goto failure_iounmap; + } + + base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE); + if (base_addr == NULL) { + err = -ENOMEM; + goto failure_iounmap; + } + + no_channels = number_of_sja1000_chip(base_addr); + if (no_channels == 0) { + err = -ENOMEM; + goto failure_iounmap; + } + + for (i = 0; i < no_channels; i++) { + err = kvaser_pci_add_chan(pdev, i, &master_dev, + conf_addr, res_addr, + base_addr); + if (err) + goto failure_cleanup; + } + + priv = netdev_priv(master_dev); + board = priv->priv; + + dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n", + board->xilinx_ver, board->no_channels); + + pci_set_drvdata(pdev, master_dev); + return 0; + +failure_cleanup: + kvaser_pci_del_chan(master_dev); + +failure_iounmap: + if (conf_addr != NULL) + pci_iounmap(pdev, conf_addr); + if (res_addr != NULL) + pci_iounmap(pdev, res_addr); + if (base_addr != NULL) + pci_iounmap(pdev, base_addr); + +failure_release_regions: + pci_release_regions(pdev); + +failure_release_pci: + pci_disable_device(pdev); + +failure: + return err; + +} + +static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + kvaser_pci_del_chan(dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static struct pci_driver kvaser_pci_driver = { + .name = DRV_NAME, + .id_table = kvaser_pci_tbl, + .probe = kvaser_pci_init_one, + .remove = __devexit_p(kvaser_pci_remove_one), +}; + +static int __init kvaser_pci_init(void) +{ + return pci_register_driver(&kvaser_pci_driver); +} + +static void __exit kvaser_pci_exit(void) +{ + pci_unregister_driver(&kvaser_pci_driver); +} + +module_init(kvaser_pci_init); +module_exit(kvaser_pci_exit); diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c new file mode 100644 index 00000000..5f92b865 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pci.c @@ -0,0 +1,762 @@ +/* + * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Derived from the PCAN project file driver/src/pcan_pci.c: + * + * Copyright (C) 2001-2006 PEAK System-Technik GmbH + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "sja1000.h" + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe/PCIeC miniPCI CAN cards"); +MODULE_LICENSE("GPL v2"); + +#define DRV_NAME "peak_pci" + +struct peak_pciec_card; +struct peak_pci_chan { + void __iomem *cfg_base; /* Common for all channels */ + struct net_device *prev_dev; /* Chain of network devices */ + u16 icr_mask; /* Interrupt mask for fast ack */ + struct peak_pciec_card *pciec_card; /* only for PCIeC LEDs */ +}; + +#define PEAK_PCI_CAN_CLOCK (16000000 / 2) + +#define PEAK_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) +#define PEAK_PCI_OCR OCR_TX0_PUSHPULL + +/* + * Important PITA registers + */ +#define PITA_ICR 0x00 /* Interrupt control register */ +#define PITA_GPIOICR 0x18 /* GPIO interface control register */ +#define PITA_MISC 0x1C /* Miscellaneous register */ + +#define PEAK_PCI_CFG_SIZE 0x1000 /* Size of the config PCI bar */ +#define PEAK_PCI_CHAN_SIZE 0x0400 /* Size used by the channel */ + +#define PEAK_PCI_VENDOR_ID 0x001C /* The PCI device and vendor IDs */ +#define PEAK_PCI_DEVICE_ID 0x0001 /* for PCI/PCIe slot cards */ +#define PEAK_PCIEC_DEVICE_ID 0x0002 /* for ExpressCard slot cards */ +#define PEAK_PCIE_DEVICE_ID 0x0003 /* for nextgen PCIe slot cards */ +#define PEAK_MPCI_DEVICE_ID 0x0008 /* The miniPCI slot cards */ + +#define PEAK_PCI_CHAN_MAX 4 + +static const u16 peak_pci_icr_masks[PEAK_PCI_CHAN_MAX] = { + 0x02, 0x01, 0x40, 0x80 +}; + +static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = { + {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_MPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, +#ifdef CONFIG_CAN_PEAK_PCIEC + {PEAK_PCI_VENDOR_ID, PEAK_PCIEC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, +#endif + {0,} +}; + +MODULE_DEVICE_TABLE(pci, peak_pci_tbl); + +#ifdef CONFIG_CAN_PEAK_PCIEC +/* + * PCAN-ExpressCard needs I2C bit-banging configuration option. + */ + +/* GPIOICR byte access offsets */ +#define PITA_GPOUT 0x18 /* GPx output value */ +#define PITA_GPIN 0x19 /* GPx input value */ +#define PITA_GPOEN 0x1A /* configure GPx as ouput pin */ + +/* I2C GP bits */ +#define PITA_GPIN_SCL 0x01 /* Serial Clock Line */ +#define PITA_GPIN_SDA 0x04 /* Serial DAta line */ + +#define PCA9553_1_SLAVEADDR (0xC4 >> 1) + +/* PCA9553 LS0 fields values */ +enum { + PCA9553_LOW, + PCA9553_HIGHZ, + PCA9553_PWM0, + PCA9553_PWM1 +}; + +/* LEDs control */ +#define PCA9553_ON PCA9553_LOW +#define PCA9553_OFF PCA9553_HIGHZ +#define PCA9553_SLOW PCA9553_PWM0 +#define PCA9553_FAST PCA9553_PWM1 + +#define PCA9553_LED(c) (1 << (c)) +#define PCA9553_LED_STATE(s, c) ((s) << ((c) << 1)) + +#define PCA9553_LED_ON(c) PCA9553_LED_STATE(PCA9553_ON, c) +#define PCA9553_LED_OFF(c) PCA9553_LED_STATE(PCA9553_OFF, c) +#define PCA9553_LED_SLOW(c) PCA9553_LED_STATE(PCA9553_SLOW, c) +#define PCA9553_LED_FAST(c) PCA9553_LED_STATE(PCA9553_FAST, c) +#define PCA9553_LED_MASK(c) PCA9553_LED_STATE(0x03, c) + +#define PCA9553_LED_OFF_ALL (PCA9553_LED_OFF(0) | PCA9553_LED_OFF(1)) + +#define PCA9553_LS0_INIT 0x40 /* initial value (!= from 0x00) */ + +struct peak_pciec_chan { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; + +struct peak_pciec_card { + void __iomem *cfg_base; /* Common for all channels */ + void __iomem *reg_base; /* first channel base address */ + u8 led_cache; /* leds state cache */ + + /* PCIExpressCard i2c data */ + struct i2c_algo_bit_data i2c_bit; + struct i2c_adapter led_chip; + struct delayed_work led_work; /* led delayed work */ + int chan_count; + struct peak_pciec_chan channel[PEAK_PCI_CHAN_MAX]; +}; + +/* "normal" pci register write callback is overloaded for leds control */ +static void peak_pci_write_reg(const struct sja1000_priv *priv, + int port, u8 val); + +static inline void pita_set_scl_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SCL; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static inline void pita_set_sda_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SDA; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void peak_pciec_init_pita_gpio(struct peak_pciec_card *card) +{ + /* raise SCL & SDA GPIOs to high-Z */ + pita_set_scl_highz(card); + pita_set_sda_highz(card); +} + +static void pita_setsda(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output sda always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SDA; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output sda with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SDA; + else + gp_outen |= PITA_GPIN_SDA; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void pita_setscl(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output scl always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SCL; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output scl with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SCL; + else + gp_outen |= PITA_GPIN_SCL; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static int pita_getsda(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_sda_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SDA) ? 1 : 0; +} + +static int pita_getscl(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_scl_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SCL) ? 1 : 0; +} + +/* + * write commands to the LED chip though the I2C-bus of the PCAN-PCIeC + */ +static int peak_pciec_write_pca9553(struct peak_pciec_card *card, + u8 offset, u8 data) +{ + u8 buffer[2] = { + offset, + data + }; + struct i2c_msg msg = { + .addr = PCA9553_1_SLAVEADDR, + .len = 2, + .buf = buffer, + }; + int ret; + + /* cache led mask */ + if ((offset == 5) && (data == card->led_cache)) + return 0; + + ret = i2c_transfer(&card->led_chip, &msg, 1); + if (ret < 0) + return ret; + + if (offset == 5) + card->led_cache = data; + + return 0; +} + +/* + * delayed work callback used to control the LEDs + */ +static void peak_pciec_led_work(struct work_struct *work) +{ + struct peak_pciec_card *card = + container_of(work, struct peak_pciec_card, led_work.work); + struct net_device *netdev; + u8 new_led = card->led_cache; + int i, up_count = 0; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_ON(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_SLOW(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); + + /* restart timer (except if no more configured channels) */ + if (up_count) + schedule_delayed_work(&card->led_work, HZ); +} + +/* + * set LEDs blinking state + */ +static void peak_pciec_set_leds(struct peak_pciec_card *card, u8 led_mask, u8 s) +{ + u8 new_led = card->led_cache; + int i; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCA9553_LED(i)) { + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_STATE(s, i); + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); +} + +/* + * start one second delayed work to control LEDs + */ +static void peak_pciec_start_led_work(struct peak_pciec_card *card) +{ + if (!delayed_work_pending(&card->led_work)) + schedule_delayed_work(&card->led_work, HZ); +} + +/* + * stop LEDs delayed work + */ +static void peak_pciec_stop_led_work(struct peak_pciec_card *card) +{ + cancel_delayed_work_sync(&card->led_work); +} + +/* + * initialize the PCA9553 4-bit I2C-bus LED chip + */ +static int peak_pciec_init_leds(struct peak_pciec_card *card) +{ + int err; + + /* prescaler for frequency 0: "SLOW" = 1 Hz = "44" */ + err = peak_pciec_write_pca9553(card, 1, 44 / 1); + if (err) + return err; + + /* duty cycle 0: 50% */ + err = peak_pciec_write_pca9553(card, 2, 0x80); + if (err) + return err; + + /* prescaler for frequency 1: "FAST" = 5 Hz */ + err = peak_pciec_write_pca9553(card, 3, 44 / 5); + if (err) + return err; + + /* duty cycle 1: 50% */ + err = peak_pciec_write_pca9553(card, 4, 0x80); + if (err) + return err; + + /* switch LEDs to initial state */ + return peak_pciec_write_pca9553(card, 5, PCA9553_LS0_INIT); +} + +/* + * restore LEDs state to off peak_pciec_leds_exit + */ +static void peak_pciec_leds_exit(struct peak_pciec_card *card) +{ + /* switch LEDs to off */ + peak_pciec_write_pca9553(card, 5, PCA9553_LED_OFF_ALL); +} + +/* + * normal write sja1000 register method overloaded to catch when controller + * is started or stopped, to control leds + */ +static void peak_pciec_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + struct peak_pci_chan *chan = priv->priv; + struct peak_pciec_card *card = chan->pciec_card; + int c = (priv->reg_base - card->reg_base) / PEAK_PCI_CHAN_SIZE; + + /* sja1000 register changes control the leds state */ + if (port == REG_MOD) + switch (val) { + case MOD_RM: + /* Reset Mode: set led on */ + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_ON); + break; + case 0x00: + /* Normal Mode: led slow blinking and start led timer */ + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_SLOW); + peak_pciec_start_led_work(card); + break; + default: + break; + } + + /* call base function */ + peak_pci_write_reg(priv, port, val); +} + +static struct i2c_algo_bit_data peak_pciec_i2c_bit_ops = { + .setsda = pita_setsda, + .setscl = pita_setscl, + .getsda = pita_getsda, + .getscl = pita_getscl, + .udelay = 10, + .timeout = HZ, +}; + +static int peak_pciec_probe(struct pci_dev *pdev, struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct peak_pci_chan *chan = priv->priv; + struct peak_pciec_card *card; + int err; + + /* copy i2c object address from 1st channel */ + if (chan->prev_dev) { + struct sja1000_priv *prev_priv = netdev_priv(chan->prev_dev); + struct peak_pci_chan *prev_chan = prev_priv->priv; + + card = prev_chan->pciec_card; + if (!card) + return -ENODEV; + + /* channel is the first one: do the init part */ + } else { + /* create the bit banging I2C adapter structure */ + card = kzalloc(sizeof(struct peak_pciec_card), GFP_KERNEL); + if (!card) { + dev_err(&pdev->dev, + "failed allocating memory for i2c chip\n"); + return -ENOMEM; + } + + card->cfg_base = chan->cfg_base; + card->reg_base = priv->reg_base; + + card->led_chip.owner = THIS_MODULE; + card->led_chip.dev.parent = &pdev->dev; + card->led_chip.algo_data = &card->i2c_bit; + strncpy(card->led_chip.name, "peak_i2c", + sizeof(card->led_chip.name)); + + card->i2c_bit = peak_pciec_i2c_bit_ops; + card->i2c_bit.udelay = 10; + card->i2c_bit.timeout = HZ; + card->i2c_bit.data = card; + + peak_pciec_init_pita_gpio(card); + + err = i2c_bit_add_bus(&card->led_chip); + if (err) { + dev_err(&pdev->dev, "i2c init failed\n"); + goto pciec_init_err_1; + } + + err = peak_pciec_init_leds(card); + if (err) { + dev_err(&pdev->dev, "leds hardware init failed\n"); + goto pciec_init_err_2; + } + + INIT_DELAYED_WORK(&card->led_work, peak_pciec_led_work); + /* PCAN-ExpressCard needs its own callback for leds */ + priv->write_reg = peak_pciec_write_reg; + } + + chan->pciec_card = card; + card->channel[card->chan_count++].netdev = dev; + + return 0; + +pciec_init_err_2: + i2c_del_adapter(&card->led_chip); + +pciec_init_err_1: + peak_pciec_init_pita_gpio(card); + kfree(card); + + return err; +} + +static void peak_pciec_remove(struct peak_pciec_card *card) +{ + peak_pciec_stop_led_work(card); + peak_pciec_leds_exit(card); + i2c_del_adapter(&card->led_chip); + peak_pciec_init_pita_gpio(card); + kfree(card); +} + +#else /* CONFIG_CAN_PEAK_PCIEC */ + +/* + * Placebo functions when PCAN-ExpressCard support is not selected + */ +static inline int peak_pciec_probe(struct pci_dev *pdev, struct net_device *dev) +{ + return -ENODEV; +} + +static inline void peak_pciec_remove(struct peak_pciec_card *card) +{ +} +#endif /* CONFIG_CAN_PEAK_PCIEC */ + +static u8 peak_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + (port << 2)); +} + +static void peak_pci_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + writeb(val, priv->reg_base + (port << 2)); +} + +static void peak_pci_post_irq(const struct sja1000_priv *priv) +{ + struct peak_pci_chan *chan = priv->priv; + u16 icr; + + /* Select and clear in PITA stored interrupt */ + icr = readw(chan->cfg_base + PITA_ICR); + if (icr & chan->icr_mask) + writew(chan->icr_mask, chan->cfg_base + PITA_ICR); +} + +static int __devinit peak_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sja1000_priv *priv; + struct peak_pci_chan *chan; + struct net_device *dev; + void __iomem *cfg_base, *reg_base; + u16 sub_sys_id, icr; + int i, err, channels; + + err = pci_enable_device(pdev); + if (err) + return err; + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto failure_disable_pci; + + err = pci_read_config_word(pdev, 0x2e, &sub_sys_id); + if (err) + goto failure_release_regions; + + dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n", + pdev->vendor, pdev->device, sub_sys_id); + + err = pci_write_config_word(pdev, 0x44, 0); + if (err) + goto failure_release_regions; + + if (sub_sys_id >= 12) + channels = 4; + else if (sub_sys_id >= 10) + channels = 3; + else if (sub_sys_id >= 4) + channels = 2; + else + channels = 1; + + cfg_base = pci_iomap(pdev, 0, PEAK_PCI_CFG_SIZE); + if (!cfg_base) { + dev_err(&pdev->dev, "failed to map PCI resource #0\n"); + goto failure_release_regions; + } + + reg_base = pci_iomap(pdev, 1, PEAK_PCI_CHAN_SIZE * channels); + if (!reg_base) { + dev_err(&pdev->dev, "failed to map PCI resource #1\n"); + goto failure_unmap_cfg_base; + } + + /* Set GPIO control register */ + writew(0x0005, cfg_base + PITA_GPIOICR + 2); + /* Enable all channels of this card */ + writeb(0x00, cfg_base + PITA_GPIOICR); + /* Toggle reset */ + writeb(0x05, cfg_base + PITA_MISC + 3); + mdelay(5); + /* Leave parport mux mode */ + writeb(0x04, cfg_base + PITA_MISC + 3); + + icr = readw(cfg_base + PITA_ICR + 2); + + for (i = 0; i < channels; i++) { + dev = alloc_sja1000dev(sizeof(struct peak_pci_chan)); + if (!dev) { + err = -ENOMEM; + goto failure_remove_channels; + } + + priv = netdev_priv(dev); + chan = priv->priv; + + chan->cfg_base = cfg_base; + priv->reg_base = reg_base + i * PEAK_PCI_CHAN_SIZE; + + priv->read_reg = peak_pci_read_reg; + priv->write_reg = peak_pci_write_reg; + priv->post_irq = peak_pci_post_irq; + + priv->can.clock.freq = PEAK_PCI_CAN_CLOCK; + priv->ocr = PEAK_PCI_OCR; + priv->cdr = PEAK_PCI_CDR; + /* Neither a slave nor a single device distributes the clock */ + if (channels == 1 || i > 0) + priv->cdr |= CDR_CLK_OFF; + + /* Setup interrupt handling */ + priv->irq_flags = IRQF_SHARED; + dev->irq = pdev->irq; + + chan->icr_mask = peak_pci_icr_masks[i]; + icr |= chan->icr_mask; + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Create chain of SJA1000 devices */ + chan->prev_dev = pci_get_drvdata(pdev); + pci_set_drvdata(pdev, dev); + + /* + * PCAN-ExpressCard needs some additional i2c init. + * This must be done *before* register_sja1000dev() but + * *after* devices linkage + */ + if (pdev->device == PEAK_PCIEC_DEVICE_ID) { + err = peak_pciec_probe(pdev, dev); + if (err) { + dev_err(&pdev->dev, + "failed to probe device (err %d)\n", + err); + goto failure_free_dev; + } + } + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "failed to register device\n"); + goto failure_free_dev; + } + + dev_info(&pdev->dev, + "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", + dev->name, priv->reg_base, chan->cfg_base, dev->irq); + } + + /* Enable interrupts */ + writew(icr, cfg_base + PITA_ICR + 2); + + return 0; + +failure_free_dev: + pci_set_drvdata(pdev, chan->prev_dev); + free_sja1000dev(dev); + +failure_remove_channels: + /* Disable interrupts */ + writew(0x0, cfg_base + PITA_ICR + 2); + + chan = NULL; + for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { + unregister_sja1000dev(dev); + free_sja1000dev(dev); + priv = netdev_priv(dev); + chan = priv->priv; + } + + /* free any PCIeC resources too */ + if (chan && chan->pciec_card) + peak_pciec_remove(chan->pciec_card); + + pci_iounmap(pdev, reg_base); + +failure_unmap_cfg_base: + pci_iounmap(pdev, cfg_base); + +failure_release_regions: + pci_release_regions(pdev); + +failure_disable_pci: + pci_disable_device(pdev); + + return err; +} + +static void __devexit peak_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); /* Last device */ + struct sja1000_priv *priv = netdev_priv(dev); + struct peak_pci_chan *chan = priv->priv; + void __iomem *cfg_base = chan->cfg_base; + void __iomem *reg_base = priv->reg_base; + + /* Disable interrupts */ + writew(0x0, cfg_base + PITA_ICR + 2); + + /* Loop over all registered devices */ + while (1) { + dev_info(&pdev->dev, "removing device %s\n", dev->name); + unregister_sja1000dev(dev); + free_sja1000dev(dev); + dev = chan->prev_dev; + + if (!dev) { + /* do that only for first channel */ + if (chan->pciec_card) + peak_pciec_remove(chan->pciec_card); + break; + } + priv = netdev_priv(dev); + chan = priv->priv; + } + + pci_iounmap(pdev, reg_base); + pci_iounmap(pdev, cfg_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + + pci_set_drvdata(pdev, NULL); +} + +static struct pci_driver peak_pci_driver = { + .name = DRV_NAME, + .id_table = peak_pci_tbl, + .probe = peak_pci_probe, + .remove = __devexit_p(peak_pci_remove), +}; + +static int __init peak_pci_init(void) +{ + return pci_register_driver(&peak_pci_driver); +} +module_init(peak_pci_init); + +static void __exit peak_pci_exit(void) +{ + pci_unregister_driver(&peak_pci_driver); +} +module_exit(peak_pci_exit); diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c new file mode 100644 index 00000000..ec6bd9d1 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * CAN driver for PEAK-System PCAN-PC Card + * Derived from the PCAN project file driver/src/pcan_pccard.c + * Copyright (C) 2006-2010 PEAK System-Technik GmbH + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/io.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include "sja1000.h" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("CAN driver for PEAK-System PCAN-PC Cards"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN-PC Card"); + +/* PEAK-System PCMCIA driver name */ +#define PCC_NAME "peak_pcmcia" + +#define PCC_CHAN_MAX 2 + +#define PCC_CAN_CLOCK (16000000 / 2) + +#define PCC_MANF_ID 0x0377 +#define PCC_CARD_ID 0x0001 + +#define PCC_CHAN_SIZE 0x20 +#define PCC_CHAN_OFF(c) ((c) * PCC_CHAN_SIZE) +#define PCC_COMN_OFF (PCC_CHAN_OFF(PCC_CHAN_MAX)) +#define PCC_COMN_SIZE 0x40 + +/* common area registers */ +#define PCC_CCR 0x00 +#define PCC_CSR 0x02 +#define PCC_CPR 0x04 +#define PCC_SPI_DIR 0x06 +#define PCC_SPI_DOR 0x08 +#define PCC_SPI_ADR 0x0a +#define PCC_SPI_IR 0x0c +#define PCC_FW_MAJOR 0x10 +#define PCC_FW_MINOR 0x12 + +/* CCR bits */ +#define PCC_CCR_CLK_16 0x00 +#define PCC_CCR_CLK_10 0x01 +#define PCC_CCR_CLK_21 0x02 +#define PCC_CCR_CLK_8 0x03 +#define PCC_CCR_CLK_MASK PCC_CCR_CLK_8 + +#define PCC_CCR_RST_CHAN(c) (0x01 << ((c) + 2)) +#define PCC_CCR_RST_ALL (PCC_CCR_RST_CHAN(0) | PCC_CCR_RST_CHAN(1)) +#define PCC_CCR_RST_MASK PCC_CCR_RST_ALL + +/* led selection bits */ +#define PCC_LED(c) (1 << (c)) +#define PCC_LED_ALL (PCC_LED(0) | PCC_LED(1)) + +/* led state value */ +#define PCC_LED_ON 0x00 +#define PCC_LED_FAST 0x01 +#define PCC_LED_SLOW 0x02 +#define PCC_LED_OFF 0x03 + +#define PCC_CCR_LED_CHAN(s, c) ((s) << (((c) + 2) << 1)) + +#define PCC_CCR_LED_ON_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_ON, c) +#define PCC_CCR_LED_FAST_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_FAST, c) +#define PCC_CCR_LED_SLOW_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_SLOW, c) +#define PCC_CCR_LED_OFF_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_OFF, c) +#define PCC_CCR_LED_MASK_CHAN(c) PCC_CCR_LED_OFF_CHAN(c) +#define PCC_CCR_LED_OFF_ALL (PCC_CCR_LED_OFF_CHAN(0) | \ + PCC_CCR_LED_OFF_CHAN(1)) +#define PCC_CCR_LED_MASK PCC_CCR_LED_OFF_ALL + +#define PCC_CCR_INIT (PCC_CCR_CLK_16 | PCC_CCR_RST_ALL | PCC_CCR_LED_OFF_ALL) + +/* CSR bits */ +#define PCC_CSR_SPI_BUSY 0x04 + +/* time waiting for SPI busy (prevent from infinite loop) */ +#define PCC_SPI_MAX_BUSY_WAIT_MS 3 + +/* max count of reading the SPI status register waiting for a change */ +/* (prevent from infinite loop) */ +#define PCC_WRITE_MAX_LOOP 1000 + +/* max nb of int handled by that isr in one shot (prevent from infinite loop) */ +#define PCC_ISR_MAX_LOOP 10 + +/* EEPROM chip instruction set */ +/* note: EEPROM Read/Write instructions include A8 bit */ +#define PCC_EEP_WRITE(a) (0x02 | (((a) & 0x100) >> 5)) +#define PCC_EEP_READ(a) (0x03 | (((a) & 0x100) >> 5)) +#define PCC_EEP_WRDI 0x04 /* EEPROM Write Disable */ +#define PCC_EEP_RDSR 0x05 /* EEPROM Read Status Register */ +#define PCC_EEP_WREN 0x06 /* EEPROM Write Enable */ + +/* EEPROM Status Register bits */ +#define PCC_EEP_SR_WEN 0x02 /* EEPROM SR Write Enable bit */ +#define PCC_EEP_SR_WIP 0x01 /* EEPROM SR Write In Progress bit */ + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode, push-pull and the correct polarity. + */ +#define PCC_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 7 + * (meaning direct oscillator output) because the second SJA1000 chip + * is driven by the first one CLKOUT output. + */ +#define PCC_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +struct pcan_channel { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; + +/* PCAN-PC Card private structure */ +struct pcan_pccard { + struct pcmcia_device *pdev; + int chan_count; + struct pcan_channel channel[PCC_CHAN_MAX]; + u8 ccr; + u8 fw_major; + u8 fw_minor; + void __iomem *ioport_addr; + struct timer_list led_timer; +}; + +static struct pcmcia_device_id pcan_table[] = { + PCMCIA_DEVICE_MANF_CARD(PCC_MANF_ID, PCC_CARD_ID), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, pcan_table); + +static void pcan_set_leds(struct pcan_pccard *card, u8 mask, u8 state); + +/* + * start timer which controls leds state + */ +static void pcan_start_led_timer(struct pcan_pccard *card) +{ + if (!timer_pending(&card->led_timer)) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * stop the timer which controls leds state + */ +static void pcan_stop_led_timer(struct pcan_pccard *card) +{ + del_timer_sync(&card->led_timer); +} + +/* + * read a sja1000 register + */ +static u8 pcan_read_canreg(const struct sja1000_priv *priv, int port) +{ + return ioread8(priv->reg_base + port); +} + +/* + * write a sja1000 register + */ +static void pcan_write_canreg(const struct sja1000_priv *priv, int port, u8 v) +{ + struct pcan_pccard *card = priv->priv; + int c = (priv->reg_base - card->ioport_addr) / PCC_CHAN_SIZE; + + /* sja1000 register changes control the leds state */ + if (port == REG_MOD) + switch (v) { + case MOD_RM: + /* Reset Mode: set led on */ + pcan_set_leds(card, PCC_LED(c), PCC_LED_ON); + break; + case 0x00: + /* Normal Mode: led slow blinking and start led timer */ + pcan_set_leds(card, PCC_LED(c), PCC_LED_SLOW); + pcan_start_led_timer(card); + break; + default: + break; + } + + iowrite8(v, priv->reg_base + port); +} + +/* + * read a register from the common area + */ +static u8 pcan_read_reg(struct pcan_pccard *card, int port) +{ + return ioread8(card->ioport_addr + PCC_COMN_OFF + port); +} + +/* + * write a register into the common area + */ +static void pcan_write_reg(struct pcan_pccard *card, int port, u8 v) +{ + /* cache ccr value */ + if (port == PCC_CCR) { + if (card->ccr == v) + return; + card->ccr = v; + } + + iowrite8(v, card->ioport_addr + PCC_COMN_OFF + port); +} + +/* + * check whether the card is present by checking its fw version numbers + * against values read at probing time. + */ +static inline int pcan_pccard_present(struct pcan_pccard *card) +{ + return ((pcan_read_reg(card, PCC_FW_MAJOR) == card->fw_major) && + (pcan_read_reg(card, PCC_FW_MINOR) == card->fw_minor)); +} + +/* + * wait for SPI engine while it is busy + */ +static int pcan_wait_spi_busy(struct pcan_pccard *card) +{ + unsigned long timeout = jiffies + + msecs_to_jiffies(PCC_SPI_MAX_BUSY_WAIT_MS) + 1; + + /* be sure to read status at least once after sleeping */ + while (pcan_read_reg(card, PCC_CSR) & PCC_CSR_SPI_BUSY) { + if (time_after(jiffies, timeout)) + return -EBUSY; + schedule(); + } + + return 0; +} + +/* + * write data in device eeprom + */ +static int pcan_write_eeprom(struct pcan_pccard *card, u16 addr, u8 v) +{ + u8 status; + int err, i; + + /* write instruction enabling write */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WREN); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* wait until write enabled */ + for (i = 0; i < PCC_WRITE_MAX_LOOP; i++) { + /* write instruction reading the status register */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_RDSR); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* get status register value and check write enable bit */ + status = pcan_read_reg(card, PCC_SPI_DIR); + if (status & PCC_EEP_SR_WEN) + break; + } + + if (i >= PCC_WRITE_MAX_LOOP) { + dev_err(&card->pdev->dev, + "stop waiting to be allowed to write in eeprom\n"); + return -EIO; + } + + /* set address and data */ + pcan_write_reg(card, PCC_SPI_ADR, addr & 0xff); + pcan_write_reg(card, PCC_SPI_DOR, v); + + /* + * write instruction with bit[3] set according to address value: + * if addr refers to upper half of the memory array: bit[3] = 1 + */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WRITE(addr)); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* wait while write in progress */ + for (i = 0; i < PCC_WRITE_MAX_LOOP; i++) { + /* write instruction reading the status register */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_RDSR); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* get status register value and check write in progress bit */ + status = pcan_read_reg(card, PCC_SPI_DIR); + if (!(status & PCC_EEP_SR_WIP)) + break; + } + + if (i >= PCC_WRITE_MAX_LOOP) { + dev_err(&card->pdev->dev, + "stop waiting for write in eeprom to complete\n"); + return -EIO; + } + + /* write instruction disabling write */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WRDI); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + return 0; + +we_spi_err: + dev_err(&card->pdev->dev, + "stop waiting (spi engine always busy) err %d\n", err); + + return err; +} + +static void pcan_set_leds(struct pcan_pccard *card, u8 led_mask, u8 state) +{ + u8 ccr = card->ccr; + int i; + + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCC_LED(i)) { + /* clear corresponding led bits in ccr */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + /* then set new bits */ + ccr |= PCC_CCR_LED_CHAN(state, i); + } + + /* real write only if something has changed in ccr */ + pcan_write_reg(card, PCC_CCR, ccr); +} + +/* + * enable/disable CAN connectors power + */ +static inline void pcan_set_can_power(struct pcan_pccard *card, int onoff) +{ + int err; + + err = pcan_write_eeprom(card, 0, !!onoff); + if (err) + dev_err(&card->pdev->dev, + "failed setting power %s to can connectors (err %d)\n", + (onoff) ? "on" : "off", err); +} + +/* + * set leds state according to channel activity + */ +static void pcan_led_timer(unsigned long arg) +{ + struct pcan_pccard *card = (struct pcan_pccard *)arg; + struct net_device *netdev; + int i, up_count = 0; + u8 ccr; + + ccr = card->ccr; + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_ON_CHAN(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_SLOW_CHAN(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + } + + /* write the new leds state */ + pcan_write_reg(card, PCC_CCR, ccr); + + /* restart timer (except if no more configured channels) */ + if (up_count) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * interrupt service routine + */ +static irqreturn_t pcan_isr(int irq, void *dev_id) +{ + struct pcan_pccard *card = dev_id; + int irq_handled; + + /* prevent from infinite loop */ + for (irq_handled = 0; irq_handled < PCC_ISR_MAX_LOOP; irq_handled++) { + /* handle shared interrupt and next loop */ + int nothing_to_handle = 1; + int i; + + /* check interrupt for each channel */ + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + + /* + * check whether the card is present before calling + * sja1000_interrupt() to speed up hotplug detection + */ + if (!pcan_pccard_present(card)) { + /* card unplugged during isr */ + return IRQ_NONE; + } + + /* + * should check whether all or SJA1000_MAX_IRQ + * interrupts have been handled: loop again to be sure. + */ + netdev = card->channel[i].netdev; + if (netdev && + sja1000_interrupt(irq, netdev) == IRQ_HANDLED) + nothing_to_handle = 0; + } + + if (nothing_to_handle) + break; + } + + return (irq_handled) ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * free all resources used by the channels and switch off leds and can power + */ +static void pcan_free_channels(struct pcan_pccard *card) +{ + int i; + u8 led_mask = 0; + + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + char name[IFNAMSIZ]; + + led_mask |= PCC_LED(i); + + netdev = card->channel[i].netdev; + if (!netdev) + continue; + + strncpy(name, netdev->name, IFNAMSIZ); + + unregister_sja1000dev(netdev); + + free_sja1000dev(netdev); + + dev_info(&card->pdev->dev, "%s removed\n", name); + } + + /* do it only if device not removed */ + if (pcan_pccard_present(card)) { + pcan_set_leds(card, led_mask, PCC_LED_OFF); + pcan_set_can_power(card, 0); + } +} + +/* + * check if a CAN controller is present at the specified location + */ +static inline int pcan_channel_present(struct sja1000_priv *priv) +{ + /* make sure SJA1000 is in reset mode */ + pcan_write_canreg(priv, REG_MOD, 1); + pcan_write_canreg(priv, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + if (pcan_read_canreg(priv, REG_CDR) == CDR_PELICAN) + return 1; + + return 0; +} + +static int pcan_add_channels(struct pcan_pccard *card) +{ + struct pcmcia_device *pdev = card->pdev; + int i, err = 0; + u8 ccr = PCC_CCR_INIT; + + /* init common registers (reset channels and leds off) */ + card->ccr = ~ccr; + pcan_write_reg(card, PCC_CCR, ccr); + + /* wait 2ms before unresetting channels */ + mdelay(2); + + ccr &= ~PCC_CCR_RST_ALL; + pcan_write_reg(card, PCC_CCR, ccr); + + /* create one network device per channel detected */ + for (i = 0; i < ARRAY_SIZE(card->channel); i++) { + struct net_device *netdev; + struct sja1000_priv *priv; + + netdev = alloc_sja1000dev(0); + if (!netdev) { + err = -ENOMEM; + break; + } + + /* update linkages */ + priv = netdev_priv(netdev); + priv->priv = card; + SET_NETDEV_DEV(netdev, &pdev->dev); + + priv->irq_flags = IRQF_SHARED; + netdev->irq = pdev->irq; + priv->reg_base = card->ioport_addr + PCC_CHAN_OFF(i); + + /* check if channel is present */ + if (!pcan_channel_present(priv)) { + dev_err(&pdev->dev, "channel %d not present\n", i); + free_sja1000dev(netdev); + continue; + } + + priv->read_reg = pcan_read_canreg; + priv->write_reg = pcan_write_canreg; + priv->can.clock.freq = PCC_CAN_CLOCK; + priv->ocr = PCC_OCR; + priv->cdr = PCC_CDR; + + /* Neither a slave device distributes the clock */ + if (i > 0) + priv->cdr |= CDR_CLK_OFF; + + priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER; + + /* register SJA1000 device */ + err = register_sja1000dev(netdev); + if (err) { + free_sja1000dev(netdev); + continue; + } + + card->channel[i].netdev = netdev; + card->chan_count++; + + /* set corresponding led on in the new ccr */ + ccr &= ~PCC_CCR_LED_OFF_CHAN(i); + + dev_info(&pdev->dev, + "%s on channel %d at 0x%p irq %d\n", + netdev->name, i, priv->reg_base, pdev->irq); + } + + /* write new ccr (change leds state) */ + pcan_write_reg(card, PCC_CCR, ccr); + + return err; +} + +static int pcan_conf_check(struct pcmcia_device *pdev, void *priv_data) +{ + pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; /* only */ + pdev->io_lines = 10; + + /* This reserves IO space but doesn't actually enable it */ + return pcmcia_request_io(pdev); +} + +/* + * free all resources used by the device + */ +static void pcan_free(struct pcmcia_device *pdev) +{ + struct pcan_pccard *card = pdev->priv; + + if (!card) + return; + + free_irq(pdev->irq, card); + pcan_stop_led_timer(card); + + pcan_free_channels(card); + + ioport_unmap(card->ioport_addr); + + kfree(card); + pdev->priv = NULL; +} + +/* + * setup PCMCIA socket and probe for PEAK-System PC-CARD + */ +static int __devinit pcan_probe(struct pcmcia_device *pdev) +{ + struct pcan_pccard *card; + int err; + + pdev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + err = pcmcia_loop_config(pdev, pcan_conf_check, NULL); + if (err) { + dev_err(&pdev->dev, "pcmcia_loop_config() error %d\n", err); + goto probe_err_1; + } + + if (!pdev->irq) { + dev_err(&pdev->dev, "no irq assigned\n"); + err = -ENODEV; + goto probe_err_1; + } + + err = pcmcia_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "pcmcia_enable_device failed err=%d\n", + err); + goto probe_err_1; + } + + card = kzalloc(sizeof(struct pcan_pccard), GFP_KERNEL); + if (!card) { + dev_err(&pdev->dev, "couldn't allocate card memory\n"); + err = -ENOMEM; + goto probe_err_2; + } + + card->pdev = pdev; + pdev->priv = card; + + /* sja1000 api uses iomem */ + card->ioport_addr = ioport_map(pdev->resource[0]->start, + resource_size(pdev->resource[0])); + if (!card->ioport_addr) { + dev_err(&pdev->dev, "couldn't map io port into io memory\n"); + err = -ENOMEM; + goto probe_err_3; + } + card->fw_major = pcan_read_reg(card, PCC_FW_MAJOR); + card->fw_minor = pcan_read_reg(card, PCC_FW_MINOR); + + /* display board name and firware version */ + dev_info(&pdev->dev, "PEAK-System pcmcia card %s fw %d.%d\n", + pdev->prod_id[1] ? pdev->prod_id[1] : "PCAN-PC Card", + card->fw_major, card->fw_minor); + + /* detect available channels */ + pcan_add_channels(card); + if (!card->chan_count) + goto probe_err_4; + + /* init the timer which controls the leds */ + init_timer(&card->led_timer); + card->led_timer.function = pcan_led_timer; + card->led_timer.data = (unsigned long)card; + + /* request the given irq */ + err = request_irq(pdev->irq, &pcan_isr, IRQF_SHARED, PCC_NAME, card); + if (err) { + dev_err(&pdev->dev, "couldn't request irq%d\n", pdev->irq); + goto probe_err_5; + } + + /* power on the connectors */ + pcan_set_can_power(card, 1); + + return 0; + +probe_err_5: + /* unregister can devices from network */ + pcan_free_channels(card); + +probe_err_4: + ioport_unmap(card->ioport_addr); + +probe_err_3: + kfree(card); + pdev->priv = NULL; + +probe_err_2: + pcmcia_disable_device(pdev); + +probe_err_1: + return err; +} + +/* + * release claimed resources + */ +static void pcan_remove(struct pcmcia_device *pdev) +{ + pcan_free(pdev); + pcmcia_disable_device(pdev); +} + +static struct pcmcia_driver pcan_driver = { + .name = PCC_NAME, + .probe = pcan_probe, + .remove = pcan_remove, + .id_table = pcan_table, +}; + +static int __init pcan_init(void) +{ + return pcmcia_register_driver(&pcan_driver); +} +module_init(pcan_init); + +static void __exit pcan_exit(void) +{ + pcmcia_unregister_driver(&pcan_driver); +} +module_exit(pcan_exit); diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c new file mode 100644 index 00000000..a227586d --- /dev/null +++ b/drivers/net/can/sja1000/plx_pci.c @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2008-2010 Pavel Cheblakov <P.B.Cheblakov@inp.nsk.su> + * + * Derived from the ems_pci.c driver: + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com> + * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/can/dev.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_plx_pci" + +MODULE_AUTHOR("Pavel Cheblakov <P.B.Cheblakov@inp.nsk.su>"); +MODULE_DESCRIPTION("Socket-CAN driver for PLX90xx PCI-bridge cards with " + "the SJA1000 chips"); +MODULE_SUPPORTED_DEVICE("Adlink PCI-7841/cPCI-7841, " + "Adlink PCI-7841/cPCI-7841 SE, " + "Marathon CAN-bus-PCI, " + "TEWS TECHNOLOGIES TPMC810, " + "esd CAN-PCI/CPCI/PCI104/200, " + "esd CAN-PCI/PMC/266, " + "esd CAN-PCIe/2000, " + "IXXAT PC-I 04/PCI") +MODULE_LICENSE("GPL v2"); + +#define PLX_PCI_MAX_CHAN 2 + +struct plx_pci_card { + int channels; /* detected channels count */ + struct net_device *net_dev[PLX_PCI_MAX_CHAN]; + void __iomem *conf_addr; + + /* Pointer to device-dependent reset function */ + void (*reset_func)(struct pci_dev *pdev); +}; + +#define PLX_PCI_CAN_CLOCK (16000000 / 2) + +/* PLX9030/9050/9052 registers */ +#define PLX_INTCSR 0x4c /* Interrupt Control/Status */ +#define PLX_CNTRL 0x50 /* User I/O, Direct Slave Response, + * Serial EEPROM, and Initialization + * Control register + */ + +#define PLX_LINT1_EN 0x1 /* Local interrupt 1 enable */ +#define PLX_LINT2_EN (1 << 3) /* Local interrupt 2 enable */ +#define PLX_PCI_INT_EN (1 << 6) /* PCI Interrupt Enable */ +#define PLX_PCI_RESET (1 << 30) /* PCI Adapter Software Reset */ + +/* PLX9056 registers */ +#define PLX9056_INTCSR 0x68 /* Interrupt Control/Status */ +#define PLX9056_CNTRL 0x6c /* Control / Software Reset */ + +#define PLX9056_LINTI (1 << 11) +#define PLX9056_PCI_INT_EN (1 << 8) +#define PLX9056_PCI_RCR (1 << 29) /* Read Configuration Registers */ + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode, push-pull and the correct polarity. + */ +#define PLX_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 7 + * (meaning direct oscillator output) because the second SJA1000 chip + * is driven by the first one CLKOUT output. + */ +#define PLX_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +/* SJA1000 Control Register in the BasicCAN Mode */ +#define REG_CR 0x00 + +/* States of some SJA1000 registers after hardware reset in the BasicCAN mode*/ +#define REG_CR_BASICCAN_INITIAL 0x21 +#define REG_CR_BASICCAN_INITIAL_MASK 0xa1 +#define REG_SR_BASICCAN_INITIAL 0x0c +#define REG_IR_BASICCAN_INITIAL 0xe0 + +/* States of some SJA1000 registers after hardware reset in the PeliCAN mode*/ +#define REG_MOD_PELICAN_INITIAL 0x01 +#define REG_SR_PELICAN_INITIAL 0x3c +#define REG_IR_PELICAN_INITIAL 0x00 + +#define ADLINK_PCI_VENDOR_ID 0x144A +#define ADLINK_PCI_DEVICE_ID 0x7841 + +#define ESD_PCI_SUB_SYS_ID_PCI200 0x0004 +#define ESD_PCI_SUB_SYS_ID_PCI266 0x0009 +#define ESD_PCI_SUB_SYS_ID_PMC266 0x000e +#define ESD_PCI_SUB_SYS_ID_CPCI200 0x010b +#define ESD_PCI_SUB_SYS_ID_PCIE2000 0x0200 +#define ESD_PCI_SUB_SYS_ID_PCI104200 0x0501 + +#define IXXAT_PCI_VENDOR_ID 0x10b5 +#define IXXAT_PCI_DEVICE_ID 0x9050 +#define IXXAT_PCI_SUB_SYS_ID 0x2540 + +#define MARATHON_PCI_DEVICE_ID 0x2715 + +#define TEWS_PCI_VENDOR_ID 0x1498 +#define TEWS_PCI_DEVICE_ID_TMPC810 0x032A + +static void plx_pci_reset_common(struct pci_dev *pdev); +static void plx_pci_reset_marathon(struct pci_dev *pdev); +static void plx9056_pci_reset_common(struct pci_dev *pdev); + +struct plx_pci_channel_map { + u32 bar; + u32 offset; + u32 size; /* 0x00 - auto, e.g. length of entire bar */ +}; + +struct plx_pci_card_info { + const char *name; + int channel_count; + u32 can_clock; + u8 ocr; /* output control register */ + u8 cdr; /* clock divider register */ + + /* Parameters for mapping local configuration space */ + struct plx_pci_channel_map conf_map; + + /* Parameters for mapping the SJA1000 chips */ + struct plx_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CHAN]; + + /* Pointer to device-dependent reset function */ + void (*reset_func)(struct pci_dev *pdev); +}; + +static struct plx_pci_card_info plx_pci_card_info_adlink __devinitdata = { + "Adlink PCI-7841/cPCI-7841", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {1, 0x00, 0x00}, { {2, 0x00, 0x80}, {2, 0x80, 0x80} }, + &plx_pci_reset_common + /* based on PLX9052 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_adlink_se __devinitdata = { + "Adlink PCI-7841/cPCI-7841 SE", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x80}, {2, 0x80, 0x80} }, + &plx_pci_reset_common + /* based on PLX9052 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_esd200 __devinitdata = { + "esd CAN-PCI/CPCI/PCI104/200", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x80}, {2, 0x100, 0x80} }, + &plx_pci_reset_common + /* based on PLX9030/9050 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_esd266 __devinitdata = { + "esd CAN-PCI/PMC/266", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x80}, {2, 0x100, 0x80} }, + &plx9056_pci_reset_common + /* based on PLX9056 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_esd2000 __devinitdata = { + "esd CAN-PCIe/2000", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x80}, {2, 0x100, 0x80} }, + &plx9056_pci_reset_common + /* based on PEX8311 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_ixxat __devinitdata = { + "IXXAT PC-I 04/PCI", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x80}, {2, 0x200, 0x80} }, + &plx_pci_reset_common + /* based on PLX9050 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_marathon __devinitdata = { + "Marathon CAN-bus-PCI", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {4, 0x00, 0x00} }, + &plx_pci_reset_marathon + /* based on PLX9052 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_tews __devinitdata = { + "TEWS TECHNOLOGIES TPMC810", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x000, 0x80}, {2, 0x100, 0x80} }, + &plx_pci_reset_common + /* based on PLX9030 */ +}; + +static DEFINE_PCI_DEVICE_TABLE(plx_pci_tbl) = { + { + /* Adlink PCI-7841/cPCI-7841 */ + ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_NETWORK_OTHER << 8, ~0, + (kernel_ulong_t)&plx_pci_card_info_adlink + }, + { + /* Adlink PCI-7841/cPCI-7841 SE */ + ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_OTHER << 8, ~0, + (kernel_ulong_t)&plx_pci_card_info_adlink_se + }, + { + /* esd CAN-PCI/200 */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI200, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_esd200 + }, + { + /* esd CAN-CPCI/200 */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_CPCI200, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_esd200 + }, + { + /* esd CAN-PCI104/200 */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI104200, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_esd200 + }, + { + /* esd CAN-PCI/266 */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI266, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_esd266 + }, + { + /* esd CAN-PMC/266 */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PMC266, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_esd266 + }, + { + /* esd CAN-PCIE/2000 */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCIE2000, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_esd2000 + }, + { + /* IXXAT PC-I 04/PCI card */ + IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, + PCI_ANY_ID, IXXAT_PCI_SUB_SYS_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_ixxat + }, + { + /* Marathon CAN-bus-PCI card */ + PCI_VENDOR_ID_PLX, MARATHON_PCI_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_marathon + }, + { + /* TEWS TECHNOLOGIES TPMC810 card */ + TEWS_PCI_VENDOR_ID, TEWS_PCI_DEVICE_ID_TMPC810, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_tews + }, + { 0,} +}; +MODULE_DEVICE_TABLE(pci, plx_pci_tbl); + +static u8 plx_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return ioread8(priv->reg_base + port); +} + +static void plx_pci_write_reg(const struct sja1000_priv *priv, int port, u8 val) +{ + iowrite8(val, priv->reg_base + port); +} + +/* + * Check if a CAN controller is present at the specified location + * by trying to switch 'em from the Basic mode into the PeliCAN mode. + * Also check states of some registers in reset mode. + */ +static inline int plx_pci_check_sja1000(const struct sja1000_priv *priv) +{ + int flag = 0; + + /* + * Check registers after hardware reset (the Basic mode) + * See states on p. 10 of the Datasheet. + */ + if ((priv->read_reg(priv, REG_CR) & REG_CR_BASICCAN_INITIAL_MASK) == + REG_CR_BASICCAN_INITIAL && + (priv->read_reg(priv, REG_SR) == REG_SR_BASICCAN_INITIAL) && + (priv->read_reg(priv, REG_IR) == REG_IR_BASICCAN_INITIAL)) + flag = 1; + + /* Bring the SJA1000 into the PeliCAN mode*/ + priv->write_reg(priv, REG_CDR, CDR_PELICAN); + + /* + * Check registers after reset in the PeliCAN mode. + * See states on p. 23 of the Datasheet. + */ + if (priv->read_reg(priv, REG_MOD) == REG_MOD_PELICAN_INITIAL && + priv->read_reg(priv, REG_SR) == REG_SR_PELICAN_INITIAL && + priv->read_reg(priv, REG_IR) == REG_IR_PELICAN_INITIAL) + return flag; + + return 0; +} + +/* + * PLX9030/50/52 software reset + * Also LRESET# asserts and brings to reset device on the Local Bus (if wired). + * For most cards it's enough for reset the SJA1000 chips. + */ +static void plx_pci_reset_common(struct pci_dev *pdev) +{ + struct plx_pci_card *card = pci_get_drvdata(pdev); + u32 cntrl; + + cntrl = ioread32(card->conf_addr + PLX_CNTRL); + cntrl |= PLX_PCI_RESET; + iowrite32(cntrl, card->conf_addr + PLX_CNTRL); + udelay(100); + cntrl ^= PLX_PCI_RESET; + iowrite32(cntrl, card->conf_addr + PLX_CNTRL); +}; + +/* + * PLX9056 software reset + * Assert LRESET# and reset device(s) on the Local Bus (if wired). + */ +static void plx9056_pci_reset_common(struct pci_dev *pdev) +{ + struct plx_pci_card *card = pci_get_drvdata(pdev); + u32 cntrl; + + /* issue a local bus reset */ + cntrl = ioread32(card->conf_addr + PLX9056_CNTRL); + cntrl |= PLX_PCI_RESET; + iowrite32(cntrl, card->conf_addr + PLX9056_CNTRL); + udelay(100); + cntrl ^= PLX_PCI_RESET; + iowrite32(cntrl, card->conf_addr + PLX9056_CNTRL); + + /* reload local configuration from EEPROM */ + cntrl |= PLX9056_PCI_RCR; + iowrite32(cntrl, card->conf_addr + PLX9056_CNTRL); + + /* + * There is no safe way to poll for the end + * of reconfiguration process. Waiting for 10ms + * is safe. + */ + mdelay(10); + + cntrl ^= PLX9056_PCI_RCR; + iowrite32(cntrl, card->conf_addr + PLX9056_CNTRL); +}; + +/* Special reset function for Marathon card */ +static void plx_pci_reset_marathon(struct pci_dev *pdev) +{ + void __iomem *reset_addr; + int i; + static const int reset_bar[2] = {3, 5}; + + plx_pci_reset_common(pdev); + + for (i = 0; i < 2; i++) { + reset_addr = pci_iomap(pdev, reset_bar[i], 0); + if (!reset_addr) { + dev_err(&pdev->dev, "Failed to remap reset " + "space %d (BAR%d)\n", i, reset_bar[i]); + } else { + /* reset the SJA1000 chip */ + iowrite8(0x1, reset_addr); + udelay(100); + pci_iounmap(pdev, reset_addr); + } + } +} + +static void plx_pci_del_card(struct pci_dev *pdev) +{ + struct plx_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + struct sja1000_priv *priv; + int i = 0; + + for (i = 0; i < PLX_PCI_MAX_CHAN; i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + dev_info(&pdev->dev, "Removing %s\n", dev->name); + unregister_sja1000dev(dev); + priv = netdev_priv(dev); + if (priv->reg_base) + pci_iounmap(pdev, priv->reg_base); + free_sja1000dev(dev); + } + + card->reset_func(pdev); + + /* + * Disable interrupts from PCI-card and disable local + * interrupts + */ + if (pdev->device != PCI_DEVICE_ID_PLX_9056) + iowrite32(0x0, card->conf_addr + PLX_INTCSR); + else + iowrite32(0x0, card->conf_addr + PLX9056_INTCSR); + + if (card->conf_addr) + pci_iounmap(pdev, card->conf_addr); + + kfree(card); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +/* + * Probe PLX90xx based device for the SJA1000 chips and register each + * available CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int __devinit plx_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sja1000_priv *priv; + struct net_device *dev; + struct plx_pci_card *card; + struct plx_pci_card_info *ci; + int err, i; + u32 val; + void __iomem *addr; + + ci = (struct plx_pci_card_info *)ent->driver_data; + + if (pci_enable_device(pdev) < 0) { + dev_err(&pdev->dev, "Failed to enable PCI device\n"); + return -ENODEV; + } + + dev_info(&pdev->dev, "Detected \"%s\" card at slot #%i\n", + ci->name, PCI_SLOT(pdev->devfn)); + + /* Allocate card structures to hold addresses, ... */ + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) { + dev_err(&pdev->dev, "Unable to allocate memory\n"); + pci_disable_device(pdev); + return -ENOMEM; + } + + pci_set_drvdata(pdev, card); + + card->channels = 0; + + /* Remap PLX90xx configuration space */ + addr = pci_iomap(pdev, ci->conf_map.bar, ci->conf_map.size); + if (!addr) { + err = -ENOMEM; + dev_err(&pdev->dev, "Failed to remap configuration space " + "(BAR%d)\n", ci->conf_map.bar); + goto failure_cleanup; + } + card->conf_addr = addr + ci->conf_map.offset; + + ci->reset_func(pdev); + card->reset_func = ci->reset_func; + + /* Detect available channels */ + for (i = 0; i < ci->channel_count; i++) { + struct plx_pci_channel_map *cm = &ci->chan_map_tbl[i]; + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto failure_cleanup; + } + + card->net_dev[i] = dev; + priv = netdev_priv(dev); + priv->priv = card; + priv->irq_flags = IRQF_SHARED; + + dev->irq = pdev->irq; + + /* + * Remap IO space of the SJA1000 chips + * This is device-dependent mapping + */ + addr = pci_iomap(pdev, cm->bar, cm->size); + if (!addr) { + err = -ENOMEM; + dev_err(&pdev->dev, "Failed to remap BAR%d\n", cm->bar); + goto failure_cleanup; + } + + priv->reg_base = addr + cm->offset; + priv->read_reg = plx_pci_read_reg; + priv->write_reg = plx_pci_write_reg; + + /* Check if channel is present */ + if (plx_pci_check_sja1000(priv)) { + priv->can.clock.freq = ci->can_clock; + priv->ocr = ci->ocr; + priv->cdr = ci->cdr; + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Register SJA1000 device */ + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "Registering device failed " + "(err=%d)\n", err); + goto failure_cleanup; + } + + card->channels++; + + dev_info(&pdev->dev, "Channel #%d at 0x%p, irq %d " + "registered as %s\n", i + 1, priv->reg_base, + dev->irq, dev->name); + } else { + dev_err(&pdev->dev, "Channel #%d not detected\n", + i + 1); + free_sja1000dev(dev); + card->net_dev[i] = NULL; + } + } + + if (!card->channels) { + err = -ENODEV; + goto failure_cleanup; + } + + /* + * Enable interrupts from PCI-card (PLX90xx) and enable Local_1, + * Local_2 interrupts from the SJA1000 chips + */ + if (pdev->device != PCI_DEVICE_ID_PLX_9056) { + val = ioread32(card->conf_addr + PLX_INTCSR); + if (pdev->subsystem_vendor == PCI_VENDOR_ID_ESDGMBH) + val |= PLX_LINT1_EN | PLX_PCI_INT_EN; + else + val |= PLX_LINT1_EN | PLX_LINT2_EN | PLX_PCI_INT_EN; + iowrite32(val, card->conf_addr + PLX_INTCSR); + } else { + iowrite32(PLX9056_LINTI | PLX9056_PCI_INT_EN, + card->conf_addr + PLX9056_INTCSR); + } + return 0; + +failure_cleanup: + dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err); + + plx_pci_del_card(pdev); + + return err; +} + +static struct pci_driver plx_pci_driver = { + .name = DRV_NAME, + .id_table = plx_pci_tbl, + .probe = plx_pci_add_card, + .remove = plx_pci_del_card, +}; + +static int __init plx_pci_init(void) +{ + return pci_register_driver(&plx_pci_driver); +} + +static void __exit plx_pci_exit(void) +{ + pci_unregister_driver(&plx_pci_driver); +} + +module_init(plx_pci_init); +module_exit(plx_pci_exit); diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c new file mode 100644 index 00000000..5e104723 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.c @@ -0,0 +1,667 @@ +/* + * sja1000.c - Philips SJA1000 network device driver + * + * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + */ + +#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/can/dev.h> +#include <linux/can/error.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000" + +MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRV_NAME "CAN netdevice driver"); + +static struct can_bittiming_const sja1000_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 sja1000_write_cmdreg(struct sja1000_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); + priv->write_reg(priv, REG_CMR, val); + priv->read_reg(priv, REG_SR); + spin_unlock_irqrestore(&priv->cmdreg_lock, flags); +} + +static int sja1000_is_absent(struct sja1000_priv *priv) +{ + return (priv->read_reg(priv, REG_MOD) == 0xFF); +} + +static int sja1000_probe_chip(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + if (priv->reg_base && sja1000_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 sja1000_priv *priv = netdev_priv(dev); + unsigned char status = priv->read_reg(priv, REG_MOD); + int i; + + /* disable interrupts */ + priv->write_reg(priv, REG_IER, IRQ_OFF); + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if (status & MOD_RM) { + priv->can.state = CAN_STATE_STOPPED; + return; + } + + priv->write_reg(priv, REG_MOD, MOD_RM); /* reset chip */ + udelay(10); + status = priv->read_reg(priv, REG_MOD); + } + + netdev_err(dev, "setting SJA1000 into reset mode failed!\n"); +} + +static void set_normal_mode(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + unsigned char status = priv->read_reg(priv, REG_MOD); + int i; + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if ((status & MOD_RM) == 0) { + priv->can.state = CAN_STATE_ERROR_ACTIVE; + /* enable interrupts */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + priv->write_reg(priv, REG_IER, IRQ_ALL); + else + priv->write_reg(priv, REG_IER, + IRQ_ALL & ~IRQ_BEI); + return; + } + + /* set chip to normal mode */ + priv->write_reg(priv, REG_MOD, 0x00); + udelay(10); + status = priv->read_reg(priv, REG_MOD); + } + + netdev_err(dev, "setting SJA1000 into normal mode failed!\n"); +} + +static void sja1000_start(struct net_device *dev) +{ + struct sja1000_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 */ + priv->write_reg(priv, REG_TXERR, 0x0); + priv->write_reg(priv, REG_RXERR, 0x0); + priv->read_reg(priv, REG_ECC); + + /* leave reset mode */ + set_normal_mode(dev); +} + +static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + sja1000_start(dev); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int sja1000_set_bittiming(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + priv->write_reg(priv, REG_BTR0, btr0); + priv->write_reg(priv, REG_BTR1, btr1); + + return 0; +} + +static int sja1000_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + bec->txerr = priv->read_reg(priv, REG_TXERR); + bec->rxerr = priv->read_reg(priv, REG_RXERR); + + return 0; +} + +/* + * initialize SJA1000 chip: + * - reset chip + * - set output mode + * - set baudrate + * - enable interrupts + * - start operating mode + */ +static void chipset_init(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + /* set clock divider and output control register */ + priv->write_reg(priv, REG_CDR, priv->cdr | CDR_PELICAN); + + /* set acceptance filter (accept all) */ + priv->write_reg(priv, REG_ACCC0, 0x00); + priv->write_reg(priv, REG_ACCC1, 0x00); + priv->write_reg(priv, REG_ACCC2, 0x00); + priv->write_reg(priv, REG_ACCC3, 0x00); + + priv->write_reg(priv, REG_ACCM0, 0xFF); + priv->write_reg(priv, REG_ACCM1, 0xFF); + priv->write_reg(priv, REG_ACCM2, 0xFF); + priv->write_reg(priv, REG_ACCM3, 0xFF); + + priv->write_reg(priv, REG_OCR, priv->ocr | OCR_MODE_NORMAL); +} + +/* + * 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 sja1000_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct can_frame *cf = (struct can_frame *)skb->data; + uint8_t fi; + uint8_t dlc; + canid_t id; + uint8_t dreg; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + fi = dlc = cf->can_dlc; + id = cf->can_id; + + if (id & CAN_RTR_FLAG) + fi |= FI_RTR; + + if (id & CAN_EFF_FLAG) { + fi |= FI_FF; + dreg = EFF_BUF; + priv->write_reg(priv, REG_FI, fi); + priv->write_reg(priv, REG_ID1, (id & 0x1fe00000) >> (5 + 16)); + priv->write_reg(priv, REG_ID2, (id & 0x001fe000) >> (5 + 8)); + priv->write_reg(priv, REG_ID3, (id & 0x00001fe0) >> 5); + priv->write_reg(priv, REG_ID4, (id & 0x0000001f) << 3); + } else { + dreg = SFF_BUF; + priv->write_reg(priv, REG_FI, fi); + priv->write_reg(priv, REG_ID1, (id & 0x000007f8) >> 3); + priv->write_reg(priv, REG_ID2, (id & 0x00000007) << 5); + } + + for (i = 0; i < dlc; i++) + priv->write_reg(priv, dreg++, cf->data[i]); + + can_put_echo_skb(skb, dev, 0); + + sja1000_write_cmdreg(priv, CMD_TR); + + return NETDEV_TX_OK; +} + +static void sja1000_rx(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + uint8_t fi; + uint8_t dreg; + canid_t id; + int i; + + /* create zero'ed CAN frame buffer */ + skb = alloc_can_skb(dev, &cf); + if (skb == NULL) + return; + + fi = priv->read_reg(priv, REG_FI); + + if (fi & FI_FF) { + /* extended frame format (EFF) */ + dreg = EFF_BUF; + id = (priv->read_reg(priv, REG_ID1) << (5 + 16)) + | (priv->read_reg(priv, REG_ID2) << (5 + 8)) + | (priv->read_reg(priv, REG_ID3) << 5) + | (priv->read_reg(priv, REG_ID4) >> 3); + id |= CAN_EFF_FLAG; + } else { + /* standard frame format (SFF) */ + dreg = SFF_BUF; + id = (priv->read_reg(priv, REG_ID1) << 3) + | (priv->read_reg(priv, REG_ID2) >> 5); + } + + cf->can_dlc = get_can_dlc(fi & 0x0F); + if (fi & FI_RTR) { + id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = priv->read_reg(priv, dreg++); + } + + cf->can_id = id; + + /* release receive buffer */ + sja1000_write_cmdreg(priv, CMD_RRB); + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) +{ + struct sja1000_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; + uint8_t ecc, alc; + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + if (isrc & IRQ_DOI) { + /* 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++; + sja1000_write_cmdreg(priv, CMD_CDO); /* clear bit */ + } + + if (isrc & IRQ_EI) { + /* error warning interrupt */ + netdev_dbg(dev, "error warning interrupt\n"); + + if (status & SR_BS) { + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } else if (status & SR_ES) { + state = CAN_STATE_ERROR_WARNING; + } else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & IRQ_BEI) { + /* bus error interrupt */ + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + ecc = priv->read_reg(priv, REG_ECC); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & ECC_MASK) { + case ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & ECC_SEG; + break; + } + /* Error occurred during transmission? */ + if ((ecc & ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + if (isrc & IRQ_EPI) { + /* error passive interrupt */ + netdev_dbg(dev, "error passive interrupt\n"); + if (status & SR_ES) + state = CAN_STATE_ERROR_PASSIVE; + else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & IRQ_ALI) { + /* arbitration lost interrupt */ + netdev_dbg(dev, "arbitration lost interrupt\n"); + alc = priv->read_reg(priv, REG_ALC); + priv->can.can_stats.arbitration_lost++; + stats->tx_errors++; + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = alc & 0x1f; + } + + if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING || + state == CAN_STATE_ERROR_PASSIVE)) { + uint8_t rxerr = priv->read_reg(priv, REG_RXERR); + uint8_t txerr = priv->read_reg(priv, REG_TXERR); + 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 sja1000_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + uint8_t isrc, status; + int n = 0; + + /* Shared interrupts and IRQ off? */ + if (priv->read_reg(priv, REG_IER) == IRQ_OFF) + return IRQ_NONE; + + if (priv->pre_irq) + priv->pre_irq(priv); + + while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) { + n++; + status = priv->read_reg(priv, REG_SR); + /* check for absent controller due to hw unplug */ + if (status == 0xFF && sja1000_is_absent(priv)) + return IRQ_NONE; + + if (isrc & IRQ_WUI) + netdev_warn(dev, "wakeup interrupt\n"); + + if (isrc & IRQ_TI) { + /* transmission complete interrupt */ + stats->tx_bytes += priv->read_reg(priv, REG_FI) & 0xf; + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + } + if (isrc & IRQ_RI) { + /* receive interrupt */ + while (status & SR_RBS) { + sja1000_rx(dev); + status = priv->read_reg(priv, REG_SR); + /* check for absent controller */ + if (status == 0xFF && sja1000_is_absent(priv)) + return IRQ_NONE; + } + } + if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { + /* error interrupt */ + if (sja1000_err(dev, isrc, status)) + break; + } + } + + if (priv->post_irq) + priv->post_irq(priv); + + if (n >= SJA1000_MAX_IRQ) + netdev_dbg(dev, "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} +EXPORT_SYMBOL_GPL(sja1000_interrupt); + +static int sja1000_open(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + int err; + + /* set chip into reset mode */ + set_reset_mode(dev); + + /* common open */ + err = open_candev(dev); + if (err) + return err; + + /* register interrupt handler, if not done by the device driver */ + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) { + err = request_irq(dev->irq, sja1000_interrupt, priv->irq_flags, + dev->name, (void *)dev); + if (err) { + close_candev(dev); + return -EAGAIN; + } + } + + /* init and start chi */ + sja1000_start(dev); + priv->open_time = jiffies; + + netif_start_queue(dev); + + return 0; +} + +static int sja1000_close(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + set_reset_mode(dev); + + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) + free_irq(dev->irq, (void *)dev); + + close_candev(dev); + + priv->open_time = 0; + + return 0; +} + +struct net_device *alloc_sja1000dev(int sizeof_priv) +{ + struct net_device *dev; + struct sja1000_priv *priv; + + dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv, + SJA1000_ECHO_SKB_MAX); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + + priv->dev = dev; + priv->can.bittiming_const = &sja1000_bittiming_const; + priv->can.do_set_bittiming = sja1000_set_bittiming; + priv->can.do_set_mode = sja1000_set_mode; + priv->can.do_get_berr_counter = sja1000_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_BERR_REPORTING; + + spin_lock_init(&priv->cmdreg_lock); + + if (sizeof_priv) + priv->priv = (void *)priv + sizeof(struct sja1000_priv); + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_sja1000dev); + +void free_sja1000dev(struct net_device *dev) +{ + free_candev(dev); +} +EXPORT_SYMBOL_GPL(free_sja1000dev); + +static const struct net_device_ops sja1000_netdev_ops = { + .ndo_open = sja1000_open, + .ndo_stop = sja1000_close, + .ndo_start_xmit = sja1000_start_xmit, +}; + +int register_sja1000dev(struct net_device *dev) +{ + if (!sja1000_probe_chip(dev)) + return -ENODEV; + + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &sja1000_netdev_ops; + + set_reset_mode(dev); + chipset_init(dev); + + return register_candev(dev); +} +EXPORT_SYMBOL_GPL(register_sja1000dev); + +void unregister_sja1000dev(struct net_device *dev) +{ + set_reset_mode(dev); + unregister_candev(dev); +} +EXPORT_SYMBOL_GPL(unregister_sja1000dev); + +static __init int sja1000_init(void) +{ + printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME); + + return 0; +} + +module_init(sja1000_init); + +static __exit void sja1000_exit(void) +{ + printk(KERN_INFO "%s: driver removed\n", DRV_NAME); +} + +module_exit(sja1000_exit); diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h new file mode 100644 index 00000000..23fff068 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.h @@ -0,0 +1,183 @@ +/* + * sja1000.h - Philips SJA1000 network device driver + * + * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + */ + +#ifndef SJA1000_DEV_H +#define SJA1000_DEV_H + +#include <linux/irqreturn.h> +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> + +#define SJA1000_ECHO_SKB_MAX 1 /* the SJA1000 has one TX buffer object */ + +#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ + +/* SJA1000 registers - manual section 6.4 (Pelican Mode) */ +#define REG_MOD 0x00 +#define REG_CMR 0x01 +#define REG_SR 0x02 +#define REG_IR 0x03 +#define REG_IER 0x04 +#define REG_ALC 0x0B +#define REG_ECC 0x0C +#define REG_EWL 0x0D +#define REG_RXERR 0x0E +#define REG_TXERR 0x0F +#define REG_ACCC0 0x10 +#define REG_ACCC1 0x11 +#define REG_ACCC2 0x12 +#define REG_ACCC3 0x13 +#define REG_ACCM0 0x14 +#define REG_ACCM1 0x15 +#define REG_ACCM2 0x16 +#define REG_ACCM3 0x17 +#define REG_RMC 0x1D +#define REG_RBSA 0x1E + +/* Common registers - manual section 6.5 */ +#define REG_BTR0 0x06 +#define REG_BTR1 0x07 +#define REG_OCR 0x08 +#define REG_CDR 0x1F + +#define REG_FI 0x10 +#define SFF_BUF 0x13 +#define EFF_BUF 0x15 + +#define FI_FF 0x80 +#define FI_RTR 0x40 + +#define REG_ID1 0x11 +#define REG_ID2 0x12 +#define REG_ID3 0x13 +#define REG_ID4 0x14 + +#define CAN_RAM 0x20 + +/* mode register */ +#define MOD_RM 0x01 +#define MOD_LOM 0x02 +#define MOD_STM 0x04 +#define MOD_AFM 0x08 +#define MOD_SM 0x10 + +/* commands */ +#define CMD_SRR 0x10 +#define CMD_CDO 0x08 +#define CMD_RRB 0x04 +#define CMD_AT 0x02 +#define CMD_TR 0x01 + +/* interrupt sources */ +#define IRQ_BEI 0x80 +#define IRQ_ALI 0x40 +#define IRQ_EPI 0x20 +#define IRQ_WUI 0x10 +#define IRQ_DOI 0x08 +#define IRQ_EI 0x04 +#define IRQ_TI 0x02 +#define IRQ_RI 0x01 +#define IRQ_ALL 0xFF +#define IRQ_OFF 0x00 + +/* status register content */ +#define SR_BS 0x80 +#define SR_ES 0x40 +#define SR_TS 0x20 +#define SR_RS 0x10 +#define SR_TCS 0x08 +#define SR_TBS 0x04 +#define SR_DOS 0x02 +#define SR_RBS 0x01 + +#define SR_CRIT (SR_BS|SR_ES) + +/* ECC register */ +#define ECC_SEG 0x1F +#define ECC_DIR 0x20 +#define ECC_ERR 6 +#define ECC_BIT 0x00 +#define ECC_FORM 0x40 +#define ECC_STUFF 0x80 +#define ECC_MASK 0xc0 + +/* + * Flags for sja1000priv.flags + */ +#define SJA1000_CUSTOM_IRQ_HANDLER 0x1 + +/* + * SJA1000 private data structure + */ +struct sja1000_priv { + struct can_priv can; /* must be the first member */ + int open_time; + struct sk_buff *echo_skb; + + /* the lower-layer is responsible for appropriate locking */ + u8 (*read_reg) (const struct sja1000_priv *priv, int reg); + void (*write_reg) (const struct sja1000_priv *priv, int reg, u8 val); + void (*pre_irq) (const struct sja1000_priv *priv); + void (*post_irq) (const struct sja1000_priv *priv); + + void *priv; /* for board-specific data */ + struct net_device *dev; + + void __iomem *reg_base; /* ioremap'ed address to registers */ + unsigned long irq_flags; /* for request_irq() */ + spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */ + + u16 flags; /* custom mode flags */ + u8 ocr; /* output control register */ + u8 cdr; /* clock divider register */ +}; + +struct net_device *alloc_sja1000dev(int sizeof_priv); +void free_sja1000dev(struct net_device *dev); +int register_sja1000dev(struct net_device *dev); +void unregister_sja1000dev(struct net_device *dev); + +irqreturn_t sja1000_interrupt(int irq, void *dev_id); + +#endif /* SJA1000_DEV_H */ diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c new file mode 100644 index 00000000..90c5c2df --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_isa.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com> + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_isa" + +#define MAXDEV 8 + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus"); +MODULE_LICENSE("GPL v2"); + +#define CLK_DEFAULT 16000000 /* 16 MHz */ +#define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF) +#define OCR_DEFAULT OCR_TX0_PUSHPULL + +static unsigned long port[MAXDEV]; +static unsigned long mem[MAXDEV]; +static int __devinitdata irq[MAXDEV]; +static int __devinitdata clk[MAXDEV]; +static unsigned char __devinitdata cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static unsigned char __devinitdata ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; + +module_param_array(port, ulong, NULL, S_IRUGO); +MODULE_PARM_DESC(port, "I/O port number"); + +module_param_array(mem, ulong, NULL, S_IRUGO); +MODULE_PARM_DESC(mem, "I/O memory address"); + +module_param_array(indirect, int, NULL, S_IRUGO); +MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); + +module_param_array(irq, int, NULL, S_IRUGO); +MODULE_PARM_DESC(irq, "IRQ number"); + +module_param_array(clk, int, NULL, S_IRUGO); +MODULE_PARM_DESC(clk, "External oscillator clock frequency " + "(default=16000000 [16 MHz])"); + +module_param_array(cdr, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(cdr, "Clock divider register " + "(default=0x48 [CDR_CBP | CDR_CLK_OFF])"); + +module_param_array(ocr, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(ocr, "Output control register " + "(default=0x18 [OCR_TX0_PUSHPULL])"); + +#define SJA1000_IOSIZE 0x20 +#define SJA1000_IOSIZE_INDIRECT 0x02 + +static struct platform_device *sja1000_isa_devs[MAXDEV]; + +static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg) +{ + return readb(priv->reg_base + reg); +} + +static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv, + int reg, u8 val) +{ + writeb(val, priv->reg_base + reg); +} + +static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg) +{ + return inb((unsigned long)priv->reg_base + reg); +} + +static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv, + int reg, u8 val) +{ + outb(val, (unsigned long)priv->reg_base + reg); +} + +static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv, + int reg) +{ + unsigned long base = (unsigned long)priv->reg_base; + + outb(reg, base); + return inb(base + 1); +} + +static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv, + int reg, u8 val) +{ + unsigned long base = (unsigned long)priv->reg_base; + + outb(reg, base); + outb(val, base + 1); +} + +static int __devinit sja1000_isa_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct sja1000_priv *priv; + void __iomem *base = NULL; + int iosize = SJA1000_IOSIZE; + int idx = pdev->id; + int err; + + dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n", + idx, port[idx], mem[idx], irq[idx]); + + if (mem[idx]) { + if (!request_mem_region(mem[idx], iosize, DRV_NAME)) { + err = -EBUSY; + goto exit; + } + base = ioremap_nocache(mem[idx], iosize); + if (!base) { + err = -ENOMEM; + goto exit_release; + } + } else { + if (indirect[idx] > 0 || + (indirect[idx] == -1 && indirect[0] > 0)) + iosize = SJA1000_IOSIZE_INDIRECT; + if (!request_region(port[idx], iosize, DRV_NAME)) { + err = -EBUSY; + goto exit; + } + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_unmap; + } + priv = netdev_priv(dev); + + dev->irq = irq[idx]; + priv->irq_flags = IRQF_SHARED; + if (mem[idx]) { + priv->reg_base = base; + dev->base_addr = mem[idx]; + priv->read_reg = sja1000_isa_mem_read_reg; + priv->write_reg = sja1000_isa_mem_write_reg; + } else { + priv->reg_base = (void __iomem *)port[idx]; + dev->base_addr = port[idx]; + + if (iosize == SJA1000_IOSIZE_INDIRECT) { + priv->read_reg = sja1000_isa_port_read_reg_indirect; + priv->write_reg = sja1000_isa_port_write_reg_indirect; + } else { + priv->read_reg = sja1000_isa_port_read_reg; + priv->write_reg = sja1000_isa_port_write_reg; + } + } + + if (clk[idx]) + priv->can.clock.freq = clk[idx] / 2; + else if (clk[0]) + priv->can.clock.freq = clk[0] / 2; + else + priv->can.clock.freq = CLK_DEFAULT / 2; + + if (ocr[idx] != 0xff) + priv->ocr = ocr[idx]; + else if (ocr[0] != 0xff) + priv->ocr = ocr[0]; + else + priv->ocr = OCR_DEFAULT; + + if (cdr[idx] != 0xff) + priv->cdr = cdr[idx]; + else if (cdr[0] != 0xff) + priv->cdr = cdr[0]; + else + priv->cdr = CDR_DEFAULT; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_unmap; + } + + dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n", + DRV_NAME, priv->reg_base, dev->irq); + return 0; + + exit_unmap: + if (mem[idx]) + iounmap(base); + exit_release: + if (mem[idx]) + release_mem_region(mem[idx], iosize); + else + release_region(port[idx], iosize); + exit: + return err; +} + +static int __devexit sja1000_isa_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct sja1000_priv *priv = netdev_priv(dev); + int idx = pdev->id; + + unregister_sja1000dev(dev); + dev_set_drvdata(&pdev->dev, NULL); + + if (mem[idx]) { + iounmap(priv->reg_base); + release_mem_region(mem[idx], SJA1000_IOSIZE); + } else { + if (priv->read_reg == sja1000_isa_port_read_reg_indirect) + release_region(port[idx], SJA1000_IOSIZE_INDIRECT); + else + release_region(port[idx], SJA1000_IOSIZE); + } + free_sja1000dev(dev); + + return 0; +} + +static struct platform_driver sja1000_isa_driver = { + .probe = sja1000_isa_probe, + .remove = __devexit_p(sja1000_isa_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init sja1000_isa_init(void) +{ + int idx, err; + + for (idx = 0; idx < MAXDEV; idx++) { + if ((port[idx] || mem[idx]) && irq[idx]) { + sja1000_isa_devs[idx] = + platform_device_alloc(DRV_NAME, idx); + if (!sja1000_isa_devs[idx]) { + err = -ENOMEM; + goto exit_free_devices; + } + err = platform_device_add(sja1000_isa_devs[idx]); + if (err) { + platform_device_put(sja1000_isa_devs[idx]); + goto exit_free_devices; + } + pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, " + "irq=%d\n", + DRV_NAME, idx, port[idx], mem[idx], irq[idx]); + } else if (idx == 0 || port[idx] || mem[idx]) { + pr_err("%s: insufficient parameters supplied\n", + DRV_NAME); + err = -EINVAL; + goto exit_free_devices; + } + } + + err = platform_driver_register(&sja1000_isa_driver); + if (err) + goto exit_free_devices; + + pr_info("Legacy %s driver for max. %d devices registered\n", + DRV_NAME, MAXDEV); + + return 0; + +exit_free_devices: + while (--idx >= 0) { + if (sja1000_isa_devs[idx]) + platform_device_unregister(sja1000_isa_devs[idx]); + } + + return err; +} + +static void __exit sja1000_isa_exit(void) +{ + int idx; + + platform_driver_unregister(&sja1000_isa_driver); + for (idx = 0; idx < MAXDEV; idx++) { + if (sja1000_isa_devs[idx]) + platform_device_unregister(sja1000_isa_devs[idx]); + } +} + +module_init(sja1000_isa_init); +module_exit(sja1000_isa_exit); diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c new file mode 100644 index 00000000..f2683eb6 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -0,0 +1,223 @@ +/* + * Driver for SJA1000 CAN controllers on the OpenFirmware platform bus + * + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * + * 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. + */ + +/* This is a generic driver for SJA1000 chips on the OpenFirmware platform + * bus found on embedded PowerPC systems. You need a SJA1000 CAN node + * definition in your flattened device tree source (DTS) file similar to: + * + * can@3,100 { + * compatible = "nxp,sja1000"; + * reg = <3 0x100 0x80>; + * interrupts = <2 0>; + * interrupt-parent = <&mpic>; + * nxp,external-clock-frequency = <16000000>; + * }; + * + * See "Documentation/devicetree/bindings/net/can/sja1000.txt" for further + * information. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/can/dev.h> + +#include <linux/of_platform.h> +#include <asm/prom.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_of_platform" + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus"); +MODULE_LICENSE("GPL v2"); + +#define SJA1000_OFP_CAN_CLOCK (16000000 / 2) + +#define SJA1000_OFP_OCR OCR_TX0_PULLDOWN +#define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF) + +static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg) +{ + return in_8(priv->reg_base + reg); +} + +static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, + int reg, u8 val) +{ + out_8(priv->reg_base + reg, val); +} + +static int __devexit sja1000_ofp_remove(struct platform_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct sja1000_priv *priv = netdev_priv(dev); + struct device_node *np = ofdev->dev.of_node; + struct resource res; + + dev_set_drvdata(&ofdev->dev, NULL); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + iounmap(priv->reg_base); + irq_dispose_mapping(dev->irq); + + of_address_to_resource(np, 0, &res); + release_mem_region(res.start, resource_size(&res)); + + return 0; +} + +static int __devinit sja1000_ofp_probe(struct platform_device *ofdev) +{ + struct device_node *np = ofdev->dev.of_node; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource res; + const u32 *prop; + int err, irq, res_size, prop_size; + void __iomem *base; + + err = of_address_to_resource(np, 0, &res); + if (err) { + dev_err(&ofdev->dev, "invalid address\n"); + return err; + } + + res_size = resource_size(&res); + + if (!request_mem_region(res.start, res_size, DRV_NAME)) { + dev_err(&ofdev->dev, "couldn't request %pR\n", &res); + return -EBUSY; + } + + base = ioremap_nocache(res.start, res_size); + if (!base) { + dev_err(&ofdev->dev, "couldn't ioremap %pR\n", &res); + err = -ENOMEM; + goto exit_release_mem; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_unmap_mem; + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_dispose_irq; + } + + priv = netdev_priv(dev); + + priv->read_reg = sja1000_ofp_read_reg; + priv->write_reg = sja1000_ofp_write_reg; + + prop = of_get_property(np, "nxp,external-clock-frequency", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->can.clock.freq = *prop / 2; + else + priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */ + + prop = of_get_property(np, "nxp,tx-output-mode", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->ocr |= *prop & OCR_MODE_MASK; + else + priv->ocr |= OCR_MODE_NORMAL; /* default */ + + prop = of_get_property(np, "nxp,tx-output-config", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->ocr |= (*prop << OCR_TX_SHIFT) & OCR_TX_MASK; + else + priv->ocr |= OCR_TX0_PULLDOWN; /* default */ + + prop = of_get_property(np, "nxp,clock-out-frequency", &prop_size); + if (prop && (prop_size == sizeof(u32)) && *prop) { + u32 divider = priv->can.clock.freq * 2 / *prop; + + if (divider > 1) + priv->cdr |= divider / 2 - 1; + else + priv->cdr |= CDR_CLKOUT_MASK; + } else { + priv->cdr |= CDR_CLK_OFF; /* default */ + } + + prop = of_get_property(np, "nxp,no-comparator-bypass", NULL); + if (!prop) + priv->cdr |= CDR_CBP; /* default */ + + priv->irq_flags = IRQF_SHARED; + priv->reg_base = base; + + dev->irq = irq; + + dev_info(&ofdev->dev, + "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n", + priv->reg_base, dev->irq, priv->can.clock.freq, + priv->ocr, priv->cdr); + + dev_set_drvdata(&ofdev->dev, dev); + SET_NETDEV_DEV(dev, &ofdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free_sja1000; + } + + return 0; + +exit_free_sja1000: + free_sja1000dev(dev); +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_unmap_mem: + iounmap(base); +exit_release_mem: + release_mem_region(res.start, res_size); + + return err; +} + +static struct of_device_id __devinitdata sja1000_ofp_table[] = { + {.compatible = "nxp,sja1000"}, + {}, +}; +MODULE_DEVICE_TABLE(of, sja1000_ofp_table); + +static struct platform_driver sja1000_ofp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = sja1000_ofp_table, + }, + .probe = sja1000_ofp_probe, + .remove = __devexit_p(sja1000_ofp_remove), +}; + +module_platform_driver(sja1000_ofp_driver); diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c new file mode 100644 index 00000000..4f50145f --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005 Sascha Hauer, Pengutronix + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_platform" + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); +MODULE_LICENSE("GPL v2"); + +static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg) +{ + return ioread8(priv->reg_base + reg); +} + +static void sp_write_reg8(const struct sja1000_priv *priv, int reg, u8 val) +{ + iowrite8(val, priv->reg_base + reg); +} + +static u8 sp_read_reg16(const struct sja1000_priv *priv, int reg) +{ + return ioread8(priv->reg_base + reg * 2); +} + +static void sp_write_reg16(const struct sja1000_priv *priv, int reg, u8 val) +{ + iowrite8(val, priv->reg_base + reg * 2); +} + +static u8 sp_read_reg32(const struct sja1000_priv *priv, int reg) +{ + return ioread8(priv->reg_base + reg * 4); +} + +static void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val) +{ + iowrite8(val, priv->reg_base + reg * 4); +} + +static int sp_probe(struct platform_device *pdev) +{ + int err; + void __iomem *addr; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource *res_mem, *res_irq; + struct sja1000_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + err = -ENODEV; + goto exit; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_mem || !res_irq) { + err = -ENODEV; + goto exit; + } + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + DRV_NAME)) { + err = -EBUSY; + goto exit; + } + + addr = ioremap_nocache(res_mem->start, resource_size(res_mem)); + if (!addr) { + err = -ENOMEM; + goto exit_release; + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_iounmap; + } + priv = netdev_priv(dev); + + dev->irq = res_irq->start; + priv->irq_flags = res_irq->flags & (IRQF_TRIGGER_MASK | IRQF_SHARED); + priv->reg_base = addr; + /* The CAN clock frequency is half the oscillator clock frequency */ + priv->can.clock.freq = pdata->osc_freq / 2; + priv->ocr = pdata->ocr; + priv->cdr = pdata->cdr; + + switch (res_mem->flags & IORESOURCE_MEM_TYPE_MASK) { + case IORESOURCE_MEM_32BIT: + priv->read_reg = sp_read_reg32; + priv->write_reg = sp_write_reg32; + break; + case IORESOURCE_MEM_16BIT: + priv->read_reg = sp_read_reg16; + priv->write_reg = sp_write_reg16; + break; + case IORESOURCE_MEM_8BIT: + default: + priv->read_reg = sp_read_reg8; + priv->write_reg = sp_write_reg8; + break; + } + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + + dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n", + DRV_NAME, priv->reg_base, dev->irq); + return 0; + + exit_free: + free_sja1000dev(dev); + exit_iounmap: + iounmap(addr); + exit_release: + release_mem_region(res_mem->start, resource_size(res_mem)); + exit: + return err; +} + +static int sp_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct sja1000_priv *priv = netdev_priv(dev); + struct resource *res; + + unregister_sja1000dev(dev); + dev_set_drvdata(&pdev->dev, NULL); + + if (priv->reg_base) + iounmap(priv->reg_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + free_sja1000dev(dev); + + return 0; +} + +static struct platform_driver sp_driver = { + .probe = sp_probe, + .remove = sp_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(sp_driver); diff --git a/drivers/net/can/sja1000/tscan1.c b/drivers/net/can/sja1000/tscan1.c new file mode 100644 index 00000000..9756099a --- /dev/null +++ b/drivers/net/can/sja1000/tscan1.c @@ -0,0 +1,216 @@ +/* + * tscan1.c: driver for Technologic Systems TS-CAN1 PC104 boards + * + * Copyright 2010 Andre B. Oliveira + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * References: + * - Getting started with TS-CAN1, Technologic Systems, Jun 2009 + * http://www.embeddedarm.com/documentation/ts-can1-manual.pdf + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/isa.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include "sja1000.h" + +MODULE_DESCRIPTION("Driver for Technologic Systems TS-CAN1 PC104 boards"); +MODULE_AUTHOR("Andre B. Oliveira <anbadeol@gmail.com>"); +MODULE_LICENSE("GPL"); + +/* Maximum number of boards (one in each JP1:JP2 setting of IO address) */ +#define TSCAN1_MAXDEV 4 + +/* PLD registers address offsets */ +#define TSCAN1_ID1 0 +#define TSCAN1_ID2 1 +#define TSCAN1_VERSION 2 +#define TSCAN1_LED 3 +#define TSCAN1_PAGE 4 +#define TSCAN1_MODE 5 +#define TSCAN1_JUMPERS 6 + +/* PLD board identifier registers magic values */ +#define TSCAN1_ID1_VALUE 0xf6 +#define TSCAN1_ID2_VALUE 0xb9 + +/* PLD mode register SJA1000 IO enable bit */ +#define TSCAN1_MODE_ENABLE 0x40 + +/* PLD jumpers register bits */ +#define TSCAN1_JP4 0x10 +#define TSCAN1_JP5 0x20 + +/* PLD IO base addresses start */ +#define TSCAN1_PLD_ADDRESS 0x150 + +/* PLD register space size */ +#define TSCAN1_PLD_SIZE 8 + +/* SJA1000 register space size */ +#define TSCAN1_SJA1000_SIZE 32 + +/* SJA1000 crystal frequency (16MHz) */ +#define TSCAN1_SJA1000_XTAL 16000000 + +/* SJA1000 IO base addresses */ +static const unsigned short tscan1_sja1000_addresses[] __devinitconst = { + 0x100, 0x120, 0x180, 0x1a0, 0x200, 0x240, 0x280, 0x320 +}; + +/* Read SJA1000 register */ +static u8 tscan1_read(const struct sja1000_priv *priv, int reg) +{ + return inb((unsigned long)priv->reg_base + reg); +} + +/* Write SJA1000 register */ +static void tscan1_write(const struct sja1000_priv *priv, int reg, u8 val) +{ + outb(val, (unsigned long)priv->reg_base + reg); +} + +/* Probe for a TS-CAN1 board with JP2:JP1 jumper setting ID */ +static int __devinit tscan1_probe(struct device *dev, unsigned id) +{ + struct net_device *netdev; + struct sja1000_priv *priv; + unsigned long pld_base, sja1000_base; + int irq, i; + + pld_base = TSCAN1_PLD_ADDRESS + id * TSCAN1_PLD_SIZE; + if (!request_region(pld_base, TSCAN1_PLD_SIZE, dev_name(dev))) + return -EBUSY; + + if (inb(pld_base + TSCAN1_ID1) != TSCAN1_ID1_VALUE || + inb(pld_base + TSCAN1_ID2) != TSCAN1_ID2_VALUE) { + release_region(pld_base, TSCAN1_PLD_SIZE); + return -ENODEV; + } + + switch (inb(pld_base + TSCAN1_JUMPERS) & (TSCAN1_JP4 | TSCAN1_JP5)) { + case TSCAN1_JP4: + irq = 6; + break; + case TSCAN1_JP5: + irq = 7; + break; + case TSCAN1_JP4 | TSCAN1_JP5: + irq = 5; + break; + default: + dev_err(dev, "invalid JP4:JP5 setting (no IRQ)\n"); + release_region(pld_base, TSCAN1_PLD_SIZE); + return -EINVAL; + } + + netdev = alloc_sja1000dev(0); + if (!netdev) { + release_region(pld_base, TSCAN1_PLD_SIZE); + return -ENOMEM; + } + + dev_set_drvdata(dev, netdev); + SET_NETDEV_DEV(netdev, dev); + + netdev->base_addr = pld_base; + netdev->irq = irq; + + priv = netdev_priv(netdev); + priv->read_reg = tscan1_read; + priv->write_reg = tscan1_write; + priv->can.clock.freq = TSCAN1_SJA1000_XTAL / 2; + priv->cdr = CDR_CBP | CDR_CLK_OFF; + priv->ocr = OCR_TX0_PUSHPULL; + + /* Select the first SJA1000 IO address that is free and that works */ + for (i = 0; i < ARRAY_SIZE(tscan1_sja1000_addresses); i++) { + sja1000_base = tscan1_sja1000_addresses[i]; + if (!request_region(sja1000_base, TSCAN1_SJA1000_SIZE, + dev_name(dev))) + continue; + + /* Set SJA1000 IO base address and enable it */ + outb(TSCAN1_MODE_ENABLE | i, pld_base + TSCAN1_MODE); + + priv->reg_base = (void __iomem *)sja1000_base; + if (!register_sja1000dev(netdev)) { + /* SJA1000 probe succeeded; turn LED off and return */ + outb(0, pld_base + TSCAN1_LED); + netdev_info(netdev, "TS-CAN1 at 0x%lx 0x%lx irq %d\n", + pld_base, sja1000_base, irq); + return 0; + } + + /* SJA1000 probe failed; release and try next address */ + outb(0, pld_base + TSCAN1_MODE); + release_region(sja1000_base, TSCAN1_SJA1000_SIZE); + } + + dev_err(dev, "failed to assign SJA1000 IO address\n"); + dev_set_drvdata(dev, NULL); + free_sja1000dev(netdev); + release_region(pld_base, TSCAN1_PLD_SIZE); + return -ENXIO; +} + +static int __devexit tscan1_remove(struct device *dev, unsigned id /*unused*/) +{ + struct net_device *netdev; + struct sja1000_priv *priv; + unsigned long pld_base, sja1000_base; + + netdev = dev_get_drvdata(dev); + unregister_sja1000dev(netdev); + dev_set_drvdata(dev, NULL); + + priv = netdev_priv(netdev); + pld_base = netdev->base_addr; + sja1000_base = (unsigned long)priv->reg_base; + + outb(0, pld_base + TSCAN1_MODE); /* disable SJA1000 IO space */ + + release_region(sja1000_base, TSCAN1_SJA1000_SIZE); + release_region(pld_base, TSCAN1_PLD_SIZE); + + free_sja1000dev(netdev); + + return 0; +} + +static struct isa_driver tscan1_isa_driver = { + .probe = tscan1_probe, + .remove = __devexit_p(tscan1_remove), + .driver = { + .name = "tscan1", + }, +}; + +static int __init tscan1_init(void) +{ + return isa_register_driver(&tscan1_isa_driver, TSCAN1_MAXDEV); +} +module_init(tscan1_init); + +static void __exit tscan1_exit(void) +{ + isa_unregister_driver(&tscan1_isa_driver); +} +module_exit(tscan1_exit); diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c new file mode 100644 index 00000000..034c16b6 --- /dev/null +++ b/drivers/net/can/slcan.c @@ -0,0 +1,714 @@ +/* + * slcan.c - serial line CAN interface driver (using tty line discipline) + * + * This file is derived from linux/drivers/net/slip/slip.c + * + * slip.c Authors : Laurence Culhane <loz@holmes.demon.co.uk> + * Fred N. van Kempen <waltje@uwalt.nl.mugnet.org> + * slcan.c Author : Oliver Hartkopp <socketcan@hartkopp.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This 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. You can also get it + * at http://www.gnu.org/licenses/gpl.html + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/uaccess.h> +#include <linux/bitops.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/rtnetlink.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/can.h> + +static __initdata const char banner[] = + KERN_INFO "slcan: serial line CAN interface driver\n"; + +MODULE_ALIAS_LDISC(N_SLCAN); +MODULE_DESCRIPTION("serial line CAN interface"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>"); + +#define SLCAN_MAGIC 0x53CA + +static int maxdev = 10; /* MAX number of SLCAN channels; + This can be overridden with + insmod slcan.ko maxdev=nnn */ +module_param(maxdev, int, 0); +MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces"); + +/* maximum rx buffer len: extended CAN frame with timestamp */ +#define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1) + +struct slcan { + int magic; + + /* Various fields. */ + struct tty_struct *tty; /* ptr to TTY structure */ + struct net_device *dev; /* easy for intr handling */ + spinlock_t lock; + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char rbuff[SLC_MTU]; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char xbuff[SLC_MTU]; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next XMIT byte */ + int xleft; /* bytes left in XMIT queue */ + + unsigned long flags; /* Flag values/ mode etc */ +#define SLF_INUSE 0 /* Channel in use */ +#define SLF_ERROR 1 /* Parity, etc. error */ +}; + +static struct net_device **slcan_devs; + + /************************************************************************ + * SLCAN ENCAPSULATION FORMAT * + ************************************************************************/ + +/* + * A CAN frame has a can_id (11 bit standard frame format OR 29 bit extended + * frame format) a data length code (can_dlc) which can be from 0 to 8 + * and up to <can_dlc> data bytes as payload. + * Additionally a CAN frame may become a remote transmission frame if the + * RTR-bit is set. This causes another ECU to send a CAN frame with the + * given can_id. + * + * The SLCAN ASCII representation of these different frame types is: + * <type> <id> <dlc> <data>* + * + * Extended frames (29 bit) are defined by capital characters in the type. + * RTR frames are defined as 'r' types - normal frames have 't' type: + * t => 11 bit data frame + * r => 11 bit RTR frame + * T => 29 bit data frame + * R => 29 bit RTR frame + * + * The <id> is 3 (standard) or 8 (extended) bytes in ASCII Hex (base64). + * The <dlc> is a one byte ASCII number ('0' - '8') + * The <data> section has at much ASCII Hex bytes as defined by the <dlc> + * + * Examples: + * + * t1230 : can_id 0x123, can_dlc 0, no data + * t4563112233 : can_id 0x456, can_dlc 3, data 0x11 0x22 0x33 + * T12ABCDEF2AA55 : extended can_id 0x12ABCDEF, can_dlc 2, data 0xAA 0x55 + * r1230 : can_id 0x123, can_dlc 0, no data, remote transmission request + * + */ + + /************************************************************************ + * STANDARD SLCAN DECAPSULATION * + ************************************************************************/ + +/* Send one completely decapsulated can_frame to the network layer */ +static void slc_bump(struct slcan *sl) +{ + struct sk_buff *skb; + struct can_frame cf; + int i, dlc_pos, tmp; + unsigned long ultmp; + char cmd = sl->rbuff[0]; + + if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R')) + return; + + if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */ + dlc_pos = 4; /* dlc position tiiid */ + else + dlc_pos = 9; /* dlc position Tiiiiiiiid */ + + if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9'))) + return; + + cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */ + + sl->rbuff[dlc_pos] = 0; /* terminate can_id string */ + + if (strict_strtoul(sl->rbuff+1, 16, &ultmp)) + return; + + cf.can_id = ultmp; + + if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */ + cf.can_id |= CAN_EFF_FLAG; + + if ((cmd | 0x20) == 'r') /* RTR frame */ + cf.can_id |= CAN_RTR_FLAG; + + *(u64 *) (&cf.data) = 0; /* clear payload */ + + for (i = 0, dlc_pos++; i < cf.can_dlc; i++) { + tmp = hex_to_bin(sl->rbuff[dlc_pos++]); + if (tmp < 0) + return; + cf.data[i] = (tmp << 4); + tmp = hex_to_bin(sl->rbuff[dlc_pos++]); + if (tmp < 0) + return; + cf.data[i] |= tmp; + } + + skb = dev_alloc_skb(sizeof(struct can_frame)); + if (!skb) + return; + + skb->dev = sl->dev; + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + memcpy(skb_put(skb, sizeof(struct can_frame)), + &cf, sizeof(struct can_frame)); + netif_rx_ni(skb); + + sl->dev->stats.rx_packets++; + sl->dev->stats.rx_bytes += cf.can_dlc; +} + +/* parse tty input stream */ +static void slcan_unesc(struct slcan *sl, unsigned char s) +{ + + if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */ + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && + (sl->rcount > 4)) { + slc_bump(sl); + } + sl->rcount = 0; + } else { + if (!test_bit(SLF_ERROR, &sl->flags)) { + if (sl->rcount < SLC_MTU) { + sl->rbuff[sl->rcount++] = s; + return; + } else { + sl->dev->stats.rx_over_errors++; + set_bit(SLF_ERROR, &sl->flags); + } + } + } +} + + /************************************************************************ + * STANDARD SLCAN ENCAPSULATION * + ************************************************************************/ + +/* Encapsulate one can_frame and stuff into a TTY queue. */ +static void slc_encaps(struct slcan *sl, struct can_frame *cf) +{ + int actual, idx, i; + char cmd; + + if (cf->can_id & CAN_RTR_FLAG) + cmd = 'R'; /* becomes 'r' in standard frame format */ + else + cmd = 'T'; /* becomes 't' in standard frame format */ + + if (cf->can_id & CAN_EFF_FLAG) + sprintf(sl->xbuff, "%c%08X%d", cmd, + cf->can_id & CAN_EFF_MASK, cf->can_dlc); + else + sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20, + cf->can_id & CAN_SFF_MASK, cf->can_dlc); + + idx = strlen(sl->xbuff); + + for (i = 0; i < cf->can_dlc; i++) + sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]); + + strcat(sl->xbuff, "\r"); /* add terminating character */ + + /* Order of next two lines is *very* important. + * When we are sending a little amount of data, + * the transfer may be completed inside the ops->write() + * routine, because it's running with interrupts enabled. + * In this case we *never* got WRITE_WAKEUP event, + * if we did not request it before write operation. + * 14 Oct 1994 Dmitry Gorodchanin. + */ + set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); + actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); + sl->xleft = strlen(sl->xbuff) - actual; + sl->xhead = sl->xbuff + actual; + sl->dev->stats.tx_bytes += cf->can_dlc; +} + +/* + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + */ +static void slcan_write_wakeup(struct tty_struct *tty) +{ + int actual; + struct slcan *sl = (struct slcan *) tty->disc_data; + + /* First make sure we're connected. */ + if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) + return; + + if (sl->xleft <= 0) { + /* Now serial buffer is almost free & we can start + * transmission of another packet */ + sl->dev->stats.tx_packets++; + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + netif_wake_queue(sl->dev); + return; + } + + actual = tty->ops->write(tty, sl->xhead, sl->xleft); + sl->xleft -= actual; + sl->xhead += actual; +} + +/* Send a can_frame to a TTY queue. */ +static netdev_tx_t slc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct slcan *sl = netdev_priv(dev); + + if (skb->len != sizeof(struct can_frame)) + goto out; + + spin_lock(&sl->lock); + if (!netif_running(dev)) { + spin_unlock(&sl->lock); + printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name); + goto out; + } + if (sl->tty == NULL) { + spin_unlock(&sl->lock); + goto out; + } + + netif_stop_queue(sl->dev); + slc_encaps(sl, (struct can_frame *) skb->data); /* encaps & send */ + spin_unlock(&sl->lock); + +out: + kfree_skb(skb); + return NETDEV_TX_OK; +} + + +/****************************************** + * Routines looking at netdevice side. + ******************************************/ + +/* Netdevice UP -> DOWN routine */ +static int slc_close(struct net_device *dev) +{ + struct slcan *sl = netdev_priv(dev); + + spin_lock_bh(&sl->lock); + if (sl->tty) { + /* TTY discipline is running. */ + clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); + } + netif_stop_queue(dev); + sl->rcount = 0; + sl->xleft = 0; + spin_unlock_bh(&sl->lock); + + return 0; +} + +/* Netdevice DOWN -> UP routine */ +static int slc_open(struct net_device *dev) +{ + struct slcan *sl = netdev_priv(dev); + + if (sl->tty == NULL) + return -ENODEV; + + sl->flags &= (1 << SLF_INUSE); + netif_start_queue(dev); + return 0; +} + +/* Hook the destructor so we can free slcan devs at the right point in time */ +static void slc_free_netdev(struct net_device *dev) +{ + int i = dev->base_addr; + free_netdev(dev); + slcan_devs[i] = NULL; +} + +static const struct net_device_ops slc_netdev_ops = { + .ndo_open = slc_open, + .ndo_stop = slc_close, + .ndo_start_xmit = slc_xmit, +}; + +static void slc_setup(struct net_device *dev) +{ + dev->netdev_ops = &slc_netdev_ops; + dev->destructor = slc_free_netdev; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; + + dev->mtu = sizeof(struct can_frame); + dev->type = ARPHRD_CAN; + + /* New-style flags. */ + dev->flags = IFF_NOARP; + dev->features = NETIF_F_HW_CSUM; +} + +/****************************************** + Routines looking at TTY side. + ******************************************/ + +/* + * Handle the 'receiver data ready' interrupt. + * This function is called by the 'tty_io' module in the kernel when + * a block of SLCAN data has been received, which can now be decapsulated + * and sent on to some IP layer for further processing. This will not + * be re-entered while running but other ldisc functions may be called + * in parallel + */ + +static void slcan_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) +{ + struct slcan *sl = (struct slcan *) tty->disc_data; + + if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) + return; + + /* Read the characters out of the buffer */ + while (count--) { + if (fp && *fp++) { + if (!test_and_set_bit(SLF_ERROR, &sl->flags)) + sl->dev->stats.rx_errors++; + cp++; + continue; + } + slcan_unesc(sl, *cp++); + } +} + +/************************************ + * slcan_open helper routines. + ************************************/ + +/* Collect hanged up channels */ +static void slc_sync(void) +{ + int i; + struct net_device *dev; + struct slcan *sl; + + for (i = 0; i < maxdev; i++) { + dev = slcan_devs[i]; + if (dev == NULL) + break; + + sl = netdev_priv(dev); + if (sl->tty) + continue; + if (dev->flags & IFF_UP) + dev_close(dev); + } +} + +/* Find a free SLCAN channel, and link in this `tty' line. */ +static struct slcan *slc_alloc(dev_t line) +{ + int i; + char name[IFNAMSIZ]; + struct net_device *dev = NULL; + struct slcan *sl; + + for (i = 0; i < maxdev; i++) { + dev = slcan_devs[i]; + if (dev == NULL) + break; + + } + + /* Sorry, too many, all slots in use */ + if (i >= maxdev) + return NULL; + + sprintf(name, "slcan%d", i); + dev = alloc_netdev(sizeof(*sl), name, slc_setup); + if (!dev) + return NULL; + + dev->base_addr = i; + sl = netdev_priv(dev); + + /* Initialize channel control data */ + sl->magic = SLCAN_MAGIC; + sl->dev = dev; + spin_lock_init(&sl->lock); + slcan_devs[i] = dev; + + return sl; +} + +/* + * Open the high-level part of the SLCAN channel. + * This function is called by the TTY module when the + * SLCAN line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free SLCAN channel... + * + * Called in process context serialized from other ldisc calls. + */ + +static int slcan_open(struct tty_struct *tty) +{ + struct slcan *sl; + int err; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + + /* RTnetlink lock is misused here to serialize concurrent + opens of slcan channels. There are better ways, but it is + the simplest one. + */ + rtnl_lock(); + + /* Collect hanged up channels. */ + slc_sync(); + + sl = tty->disc_data; + + err = -EEXIST; + /* First make sure we're not already connected. */ + if (sl && sl->magic == SLCAN_MAGIC) + goto err_exit; + + /* OK. Find a free SLCAN channel to use. */ + err = -ENFILE; + sl = slc_alloc(tty_devnum(tty)); + if (sl == NULL) + goto err_exit; + + sl->tty = tty; + tty->disc_data = sl; + + if (!test_bit(SLF_INUSE, &sl->flags)) { + /* Perform the low-level SLCAN initialization. */ + sl->rcount = 0; + sl->xleft = 0; + + set_bit(SLF_INUSE, &sl->flags); + + err = register_netdevice(sl->dev); + if (err) + goto err_free_chan; + } + + /* Done. We have linked the TTY line to a channel. */ + rtnl_unlock(); + tty->receive_room = 65536; /* We don't flow control */ + + /* TTY layer expects 0 on success */ + return 0; + +err_free_chan: + sl->tty = NULL; + tty->disc_data = NULL; + clear_bit(SLF_INUSE, &sl->flags); + +err_exit: + rtnl_unlock(); + + /* Count references from TTY module */ + return err; +} + +/* + * Close down a SLCAN channel. + * This means flushing out any pending queues, and then returning. This + * call is serialized against other ldisc functions. + * + * We also use this method for a hangup event. + */ + +static void slcan_close(struct tty_struct *tty) +{ + struct slcan *sl = (struct slcan *) tty->disc_data; + + /* First make sure we're connected. */ + if (!sl || sl->magic != SLCAN_MAGIC || sl->tty != tty) + return; + + tty->disc_data = NULL; + sl->tty = NULL; + + /* Flush network side */ + unregister_netdev(sl->dev); + /* This will complete via sl_free_netdev */ +} + +static int slcan_hangup(struct tty_struct *tty) +{ + slcan_close(tty); + return 0; +} + +/* Perform I/O control on an active SLCAN channel. */ +static int slcan_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct slcan *sl = (struct slcan *) tty->disc_data; + unsigned int tmp; + + /* First make sure we're connected. */ + if (!sl || sl->magic != SLCAN_MAGIC) + return -EINVAL; + + switch (cmd) { + case SIOCGIFNAME: + tmp = strlen(sl->dev->name) + 1; + if (copy_to_user((void __user *)arg, sl->dev->name, tmp)) + return -EFAULT; + return 0; + + case SIOCSIFHWADDR: + return -EINVAL; + + default: + return tty_mode_ioctl(tty, file, cmd, arg); + } +} + +static struct tty_ldisc_ops slc_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "slcan", + .open = slcan_open, + .close = slcan_close, + .hangup = slcan_hangup, + .ioctl = slcan_ioctl, + .receive_buf = slcan_receive_buf, + .write_wakeup = slcan_write_wakeup, +}; + +static int __init slcan_init(void) +{ + int status; + + if (maxdev < 4) + maxdev = 4; /* Sanity */ + + printk(banner); + printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev); + + slcan_devs = kzalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL); + if (!slcan_devs) + return -ENOMEM; + + /* Fill in our line protocol discipline, and register it */ + status = tty_register_ldisc(N_SLCAN, &slc_ldisc); + if (status) { + printk(KERN_ERR "slcan: can't register line discipline\n"); + kfree(slcan_devs); + } + return status; +} + +static void __exit slcan_exit(void) +{ + int i; + struct net_device *dev; + struct slcan *sl; + unsigned long timeout = jiffies + HZ; + int busy = 0; + + if (slcan_devs == NULL) + return; + + /* First of all: check for active disciplines and hangup them. + */ + do { + if (busy) + msleep_interruptible(100); + + busy = 0; + for (i = 0; i < maxdev; i++) { + dev = slcan_devs[i]; + if (!dev) + continue; + sl = netdev_priv(dev); + spin_lock_bh(&sl->lock); + if (sl->tty) { + busy++; + tty_hangup(sl->tty); + } + spin_unlock_bh(&sl->lock); + } + } while (busy && time_before(jiffies, timeout)); + + /* FIXME: hangup is async so we should wait when doing this second + phase */ + + for (i = 0; i < maxdev; i++) { + dev = slcan_devs[i]; + if (!dev) + continue; + slcan_devs[i] = NULL; + + sl = netdev_priv(dev); + if (sl->tty) { + printk(KERN_ERR "%s: tty discipline still running\n", + dev->name); + /* Intentionally leak the control block. */ + dev->destructor = NULL; + } + + unregister_netdev(dev); + } + + kfree(slcan_devs); + slcan_devs = NULL; + + i = tty_unregister_ldisc(N_SLCAN); + if (i) + printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i); +} + +module_init(slcan_init); +module_exit(slcan_exit); diff --git a/drivers/net/can/softing/Kconfig b/drivers/net/can/softing/Kconfig new file mode 100644 index 00000000..5de46a9a --- /dev/null +++ b/drivers/net/can/softing/Kconfig @@ -0,0 +1,30 @@ +config CAN_SOFTING + tristate "Softing Gmbh CAN generic support" + depends on CAN_DEV && HAS_IOMEM + ---help--- + Support for CAN cards from Softing Gmbh & some cards + from Vector Gmbh. + Softing Gmbh CAN cards come with 1 or 2 physical busses. + Those cards typically use Dual Port RAM to communicate + with the host CPU. The interface is then identical for PCI + and PCMCIA cards. This driver operates on a platform device, + which has been created by softing_cs or softing_pci driver. + Warning: + The API of the card does not allow fine control per bus, but + controls the 2 busses on the card together. + As such, some actions (start/stop/busoff recovery) on 1 bus + must bring down the other bus too temporarily. + +config CAN_SOFTING_CS + tristate "Softing Gmbh CAN pcmcia cards" + depends on PCMCIA + depends on CAN_SOFTING + ---help--- + Support for PCMCIA cards from Softing Gmbh & some cards + from Vector Gmbh. + You need firmware for these, which you can get at + http://developer.berlios.de/projects/socketcan/ + This version of the driver is written against + firmware version 4.6 (softing-fw-4.6-binaries.tar.gz) + In order to use the card as CAN device, you need the Softing generic + support too. diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile new file mode 100644 index 00000000..c5e5016c --- /dev/null +++ b/drivers/net/can/softing/Makefile @@ -0,0 +1,6 @@ + +softing-y := softing_main.o softing_fw.o +obj-$(CONFIG_CAN_SOFTING) += softing.o +obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h new file mode 100644 index 00000000..afd7d85b --- /dev/null +++ b/drivers/net/can/softing/softing.h @@ -0,0 +1,167 @@ +/* + * softing common interfaces + * + * by Kurt Van Dijck, 2008-2010 + */ + +#include <linux/atomic.h> +#include <linux/netdevice.h> +#include <linux/ktime.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "softing_platform.h" + +struct softing; + +struct softing_priv { + struct can_priv can; /* must be the first member! */ + struct net_device *netdev; + struct softing *card; + struct { + int pending; + /* variables which hold the circular buffer */ + int echo_put; + int echo_get; + } tx; + struct can_bittiming_const btr_const; + int index; + uint8_t output; + uint16_t chip; +}; +#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev)) + +struct softing { + const struct softing_platform_data *pdat; + struct platform_device *pdev; + struct net_device *net[2]; + spinlock_t spin; /* protect this structure & DPRAM access */ + ktime_t ts_ref; + ktime_t ts_overflow; /* timestamp overflow value, in ktime */ + + struct { + /* indication of firmware status */ + int up; + /* protection of the 'up' variable */ + struct mutex lock; + } fw; + struct { + int nr; + int requested; + int svc_count; + unsigned int dpram_position; + } irq; + struct { + int pending; + int last_bus; + /* + * keep the bus that last tx'd a message, + * in order to let every netdev queue resume + */ + } tx; + __iomem uint8_t *dpram; + unsigned long dpram_phys; + unsigned long dpram_size; + struct { + uint16_t fw_version, hw_version, license, serial; + uint16_t chip[2]; + unsigned int freq; /* remote cpu's operating frequency */ + } id; +}; + +extern int softing_default_output(struct net_device *netdev); + +extern ktime_t softing_raw2ktime(struct softing *card, u32 raw); + +extern int softing_chip_poweron(struct softing *card); + +extern int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg); + +/* Load firmware after reset */ +extern int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *virt, unsigned int size, int offset); + +/* Load final application firmware after bootloader */ +extern int softing_load_app_fw(const char *file, struct softing *card); + +/* + * enable or disable irq + * only called with fw.lock locked + */ +extern int softing_enable_irq(struct softing *card, int enable); + +/* start/stop 1 bus on card */ +extern int softing_startstop(struct net_device *netdev, int up); + +/* netif_rx() */ +extern int softing_netdev_rx(struct net_device *netdev, + const struct can_frame *msg, ktime_t ktime); + +/* SOFTING DPRAM mappings */ +#define DPRAM_RX 0x0000 + #define DPRAM_RX_SIZE 32 + #define DPRAM_RX_CNT 16 +#define DPRAM_RX_RD 0x0201 /* uint8_t */ +#define DPRAM_RX_WR 0x0205 /* uint8_t */ +#define DPRAM_RX_LOST 0x0207 /* uint8_t */ + +#define DPRAM_FCT_PARAM 0x0300 /* int16_t [20] */ +#define DPRAM_FCT_RESULT 0x0328 /* int16_t */ +#define DPRAM_FCT_HOST 0x032b /* uint16_t */ + +#define DPRAM_INFO_BUSSTATE 0x0331 /* uint16_t */ +#define DPRAM_INFO_BUSSTATE2 0x0335 /* uint16_t */ +#define DPRAM_INFO_ERRSTATE 0x0339 /* uint16_t */ +#define DPRAM_INFO_ERRSTATE2 0x033d /* uint16_t */ +#define DPRAM_RESET 0x0341 /* uint16_t */ +#define DPRAM_CLR_RECV_FIFO 0x0345 /* uint16_t */ +#define DPRAM_RESET_TIME 0x034d /* uint16_t */ +#define DPRAM_TIME 0x0350 /* uint64_t */ +#define DPRAM_WR_START 0x0358 /* uint8_t */ +#define DPRAM_WR_END 0x0359 /* uint8_t */ +#define DPRAM_RESET_RX_FIFO 0x0361 /* uint16_t */ +#define DPRAM_RESET_TX_FIFO 0x0364 /* uint8_t */ +#define DPRAM_READ_FIFO_LEVEL 0x0365 /* uint8_t */ +#define DPRAM_RX_FIFO_LEVEL 0x0366 /* uint16_t */ +#define DPRAM_TX_FIFO_LEVEL 0x0366 /* uint16_t */ + +#define DPRAM_TX 0x0400 /* uint16_t */ + #define DPRAM_TX_SIZE 16 + #define DPRAM_TX_CNT 32 +#define DPRAM_TX_RD 0x0601 /* uint8_t */ +#define DPRAM_TX_WR 0x0605 /* uint8_t */ + +#define DPRAM_COMMAND 0x07e0 /* uint16_t */ +#define DPRAM_RECEIPT 0x07f0 /* uint16_t */ +#define DPRAM_IRQ_TOHOST 0x07fe /* uint8_t */ +#define DPRAM_IRQ_TOCARD 0x07ff /* uint8_t */ + +#define DPRAM_V2_RESET 0x0e00 /* uint8_t */ +#define DPRAM_V2_IRQ_TOHOST 0x0e02 /* uint8_t */ + +#define TXMAX (DPRAM_TX_CNT - 1) + +/* DPRAM return codes */ +#define RES_NONE 0 +#define RES_OK 1 +#define RES_NOK 2 +#define RES_UNKNOWN 3 +/* DPRAM flags */ +#define CMD_TX 0x01 +#define CMD_ACK 0x02 +#define CMD_XTD 0x04 +#define CMD_RTR 0x08 +#define CMD_ERR 0x10 +#define CMD_BUS2 0x80 + +/* returned fifo entry bus state masks */ +#define SF_MASK_BUSOFF 0x80 +#define SF_MASK_EPASSIVE 0x60 + +/* bus states */ +#define STATE_BUSOFF 2 +#define STATE_EPASSIVE 1 +#define STATE_EACTIVE 0 diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c new file mode 100644 index 00000000..c0e1b1eb --- /dev/null +++ b/drivers/net/can/softing/softing_cs.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * 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 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +#include "softing_platform.h" + +static int softingcs_index; +static spinlock_t softingcs_index_lock; + +static int softingcs_reset(struct platform_device *pdev, int v); +static int softingcs_enable_irq(struct platform_device *pdev, int v); + +/* + * platform_data descriptions + */ +#define MHZ (1000*1000) +static const struct softing_platform_data softingcs_platform_data[] = { +{ + .name = "CANcard", + .manf = 0x0168, .prod = 0x001, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-NEC", + .manf = 0x0168, .prod = 0x002, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-SJA", + .manf = 0x0168, .prod = 0x004, + .generation = 1, + .nbus = 2, + .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-2", + .manf = 0x0168, .prod = 0x005, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + .name = "Vector-CANcard", + .manf = 0x0168, .prod = 0x081, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "Vector-CANcard-SJA", + .manf = 0x0168, .prod = 0x084, + .generation = 1, + .nbus = 2, + .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "Vector-CANcard-2", + .manf = 0x0168, .prod = 0x085, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + .name = "EDICcard-NEC", + .manf = 0x0168, .prod = 0x102, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "EDICcard-2", + .manf = 0x0168, .prod = 0x105, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + 0, 0, +}, +}; + +MODULE_FIRMWARE(fw_dir "bcard.bin"); +MODULE_FIRMWARE(fw_dir "ldcard.bin"); +MODULE_FIRMWARE(fw_dir "cancard.bin"); +MODULE_FIRMWARE(fw_dir "cansja.bin"); + +MODULE_FIRMWARE(fw_dir "bcard2.bin"); +MODULE_FIRMWARE(fw_dir "ldcard2.bin"); +MODULE_FIRMWARE(fw_dir "cancrd2.bin"); + +static __devinit const struct softing_platform_data +*softingcs_find_platform_data(unsigned int manf, unsigned int prod) +{ + const struct softing_platform_data *lp; + + for (lp = softingcs_platform_data; lp->manf; ++lp) { + if ((lp->manf == manf) && (lp->prod == prod)) + return lp; + } + return NULL; +} + +/* + * platformdata callbacks + */ +static int softingcs_reset(struct platform_device *pdev, int v) +{ + struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); + + dev_dbg(&pdev->dev, "pcmcia config [2] %02x\n", v ? 0 : 0x20); + return pcmcia_write_config_byte(pcmcia, 2, v ? 0 : 0x20); +} + +static int softingcs_enable_irq(struct platform_device *pdev, int v) +{ + struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); + + dev_dbg(&pdev->dev, "pcmcia config [0] %02x\n", v ? 0x60 : 0); + return pcmcia_write_config_byte(pcmcia, 0, v ? 0x60 : 0); +} + +/* + * pcmcia check + */ +static __devinit int softingcs_probe_config(struct pcmcia_device *pcmcia, + void *priv_data) +{ + struct softing_platform_data *pdat = priv_data; + struct resource *pres; + int memspeed = 0; + + WARN_ON(!pdat); + pres = pcmcia->resource[PCMCIA_IOMEM_0]; + if (resource_size(pres) < 0x1000) + return -ERANGE; + + pres->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE; + if (pdat->generation < 2) { + pres->flags |= WIN_USE_WAIT | WIN_DATA_WIDTH_8; + memspeed = 3; + } else { + pres->flags |= WIN_DATA_WIDTH_16; + } + return pcmcia_request_window(pcmcia, pres, memspeed); +} + +static __devexit void softingcs_remove(struct pcmcia_device *pcmcia) +{ + struct platform_device *pdev = pcmcia->priv; + + /* free bits */ + platform_device_unregister(pdev); + /* release pcmcia stuff */ + pcmcia_disable_device(pcmcia); +} + +/* + * platform_device wrapper + * pdev->resource has 2 entries: io & irq + */ +static void softingcs_pdev_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + kfree(pdev); +} + +static __devinit int softingcs_probe(struct pcmcia_device *pcmcia) +{ + int ret; + struct platform_device *pdev; + const struct softing_platform_data *pdat; + struct resource *pres; + struct dev { + struct platform_device pdev; + struct resource res[2]; + } *dev; + + /* find matching platform_data */ + pdat = softingcs_find_platform_data(pcmcia->manf_id, pcmcia->card_id); + if (!pdat) + return -ENOTTY; + + /* setup pcmcia device */ + pcmcia->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IOMEM | + CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + ret = pcmcia_loop_config(pcmcia, softingcs_probe_config, (void *)pdat); + if (ret) + goto pcmcia_failed; + + ret = pcmcia_enable_device(pcmcia); + if (ret < 0) + goto pcmcia_failed; + + pres = pcmcia->resource[PCMCIA_IOMEM_0]; + if (!pres) { + ret = -EBADF; + goto pcmcia_bad; + } + + /* create softing platform device */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto mem_failed; + } + dev->pdev.resource = dev->res; + dev->pdev.num_resources = ARRAY_SIZE(dev->res); + dev->pdev.dev.release = softingcs_pdev_release; + + pdev = &dev->pdev; + pdev->dev.platform_data = (void *)pdat; + pdev->dev.parent = &pcmcia->dev; + pcmcia->priv = pdev; + + /* platform device resources */ + pdev->resource[0].flags = IORESOURCE_MEM; + pdev->resource[0].start = pres->start; + pdev->resource[0].end = pres->end; + + pdev->resource[1].flags = IORESOURCE_IRQ; + pdev->resource[1].start = pcmcia->irq; + pdev->resource[1].end = pdev->resource[1].start; + + /* platform device setup */ + spin_lock(&softingcs_index_lock); + pdev->id = softingcs_index++; + spin_unlock(&softingcs_index_lock); + pdev->name = "softing"; + dev_set_name(&pdev->dev, "softingcs.%i", pdev->id); + ret = platform_device_register(pdev); + if (ret < 0) + goto platform_failed; + + dev_info(&pcmcia->dev, "created %s\n", dev_name(&pdev->dev)); + return 0; + +platform_failed: + kfree(dev); +mem_failed: +pcmcia_bad: +pcmcia_failed: + pcmcia_disable_device(pcmcia); + pcmcia->priv = NULL; + return ret ?: -ENODEV; +} + +static const struct pcmcia_device_id softingcs_ids[] = { + /* softing */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005), + /* vector, manufacturer? */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085), + /* EDIC */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, softingcs_ids); + +static struct pcmcia_driver softingcs_driver = { + .owner = THIS_MODULE, + .name = "softingcs", + .id_table = softingcs_ids, + .probe = softingcs_probe, + .remove = __devexit_p(softingcs_remove), +}; + +static int __init softingcs_start(void) +{ + spin_lock_init(&softingcs_index_lock); + return pcmcia_register_driver(&softingcs_driver); +} + +static void __exit softingcs_stop(void) +{ + pcmcia_unregister_driver(&softingcs_driver); +} + +module_init(softingcs_start); +module_exit(softingcs_stop); + +MODULE_DESCRIPTION("softing CANcard driver" + ", links PCMCIA card to softing driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c new file mode 100644 index 00000000..31059617 --- /dev/null +++ b/drivers/net/can/softing/softing_fw.c @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * 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 + */ + +#include <linux/firmware.h> +#include <linux/sched.h> +#include <asm/div64.h> +#include <asm/io.h> + +#include "softing.h" + +/* + * low level DPRAM command. + * Make sure that card->dpram[DPRAM_FCT_HOST] is preset + */ +static int _softing_fct_cmd(struct softing *card, int16_t cmd, uint16_t vector, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(cmd, &card->dpram[DPRAM_FCT_PARAM]); + iowrite8(vector >> 8, &card->dpram[DPRAM_FCT_HOST + 1]); + iowrite8(vector, &card->dpram[DPRAM_FCT_HOST]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 1 * HZ; + /* wait for card */ + do { + /* DPRAM_FCT_HOST is _not_ aligned */ + ret = ioread8(&card->dpram[DPRAM_FCT_HOST]) + + (ioread8(&card->dpram[DPRAM_FCT_HOST + 1]) << 8); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + /* read return-value now */ + return ioread16(&card->dpram[DPRAM_FCT_RESULT]); + + if ((ret != vector) || time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (1); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "firmware %s failed (%i)\n", msg, ret); + return ret; +} + +static int softing_fct_cmd(struct softing *card, int16_t cmd, const char *msg) +{ + int ret; + + ret = _softing_fct_cmd(card, cmd, 0, msg); + if (ret > 0) { + dev_alert(&card->pdev->dev, "%s returned %u\n", msg, ret); + ret = -EIO; + } + return ret; +} + +int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(RES_NONE, &card->dpram[DPRAM_RECEIPT]); + iowrite16(cmd, &card->dpram[DPRAM_COMMAND]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 3 * HZ; + /* wait for card */ + do { + ret = ioread16(&card->dpram[DPRAM_RECEIPT]); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + return 0; + if (time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (!signal_pending(current)); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "bootloader %s failed (%i)\n", msg, ret); + return ret; +} + +static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, + uint16_t *plen, const uint8_t **pdat) +{ + uint16_t checksum[2]; + const uint8_t *mem; + const uint8_t *end; + + /* + * firmware records are a binary, unaligned stream composed of: + * uint16_t type; + * uint32_t addr; + * uint16_t len; + * uint8_t dat[len]; + * uint16_t checksum; + * all values in little endian. + * We could define a struct for this, with __attribute__((packed)), + * but would that solve the alignment in _all_ cases (cfr. the + * struct itself may be an odd address)? + * + * I chose to use leXX_to_cpup() since this solves both + * endianness & alignment. + */ + mem = *pmem; + *ptype = le16_to_cpup((void *)&mem[0]); + *paddr = le32_to_cpup((void *)&mem[2]); + *plen = le16_to_cpup((void *)&mem[6]); + *pdat = &mem[8]; + /* verify checksum */ + end = &mem[8 + *plen]; + checksum[0] = le16_to_cpup((void *)end); + for (checksum[1] = 0; mem < end; ++mem) + checksum[1] += *mem; + if (checksum[0] != checksum[1]) + return -EINVAL; + /* increment */ + *pmem += 10 + *plen; + return 0; +} + +int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *dpram, unsigned int size, int offset) +{ + const struct firmware *fw; + int ret; + const uint8_t *mem, *end, *dat; + uint16_t type, len; + uint32_t addr; + uint8_t *buf = NULL; + int buflen = 0; + int8_t type_end = 0; + + ret = request_firmware(&fw, file, &card->pdev->dev); + if (ret < 0) + return ret; + dev_dbg(&card->pdev->dev, "%s, firmware(%s) got %u bytes" + ", offset %c0x%04x\n", + card->pdat->name, file, (unsigned int)fw->size, + (offset >= 0) ? '+' : '-', (unsigned int)abs(offset)); + /* parse the firmware */ + mem = fw->data; + end = &mem[fw->size]; + /* look for header record */ + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret < 0) + goto failed; + if (type != 0xffff) + goto failed; + if (strncmp("Structured Binary Format, Softing GmbH" , dat, len)) { + ret = -EINVAL; + goto failed; + } + /* ok, we had a header */ + while (mem < end) { + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret < 0) + goto failed; + if (type == 3) { + /* start address, not used here */ + continue; + } else if (type == 1) { + /* eof */ + type_end = 1; + break; + } else if (type != 0) { + ret = -EINVAL; + goto failed; + } + + if ((addr + len + offset) > size) + goto failed; + memcpy_toio(&dpram[addr + offset], dat, len); + /* be sure to flush caches from IO space */ + mb(); + if (len > buflen) { + /* align buflen */ + buflen = (len + (1024-1)) & ~(1024-1); + buf = krealloc(buf, buflen, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto failed; + } + } + /* verify record data */ + memcpy_fromio(buf, &dpram[addr + offset], len); + if (memcmp(buf, dat, len)) { + /* is not ok */ + dev_alert(&card->pdev->dev, "DPRAM readback failed\n"); + ret = -EIO; + goto failed; + } + } + if (!type_end) + /* no end record seen */ + goto failed; + ret = 0; +failed: + kfree(buf); + release_firmware(fw); + if (ret < 0) + dev_info(&card->pdev->dev, "firmware %s failed\n", file); + return ret; +} + +int softing_load_app_fw(const char *file, struct softing *card) +{ + const struct firmware *fw; + const uint8_t *mem, *end, *dat; + int ret, j; + uint16_t type, len; + uint32_t addr, start_addr = 0; + unsigned int sum, rx_sum; + int8_t type_end = 0, type_entrypoint = 0; + + ret = request_firmware(&fw, file, &card->pdev->dev); + if (ret) { + dev_alert(&card->pdev->dev, "request_firmware(%s) got %i\n", + file, ret); + return ret; + } + dev_dbg(&card->pdev->dev, "firmware(%s) got %lu bytes\n", + file, (unsigned long)fw->size); + /* parse the firmware */ + mem = fw->data; + end = &mem[fw->size]; + /* look for header record */ + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret) + goto failed; + ret = -EINVAL; + if (type != 0xffff) { + dev_alert(&card->pdev->dev, "firmware starts with type 0x%x\n", + type); + goto failed; + } + if (strncmp("Structured Binary Format, Softing GmbH", dat, len)) { + dev_alert(&card->pdev->dev, "firmware string '%.*s' fault\n", + len, dat); + goto failed; + } + /* ok, we had a header */ + while (mem < end) { + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret) + goto failed; + + if (type == 3) { + /* start address */ + start_addr = addr; + type_entrypoint = 1; + continue; + } else if (type == 1) { + /* eof */ + type_end = 1; + break; + } else if (type != 0) { + dev_alert(&card->pdev->dev, + "unknown record type 0x%04x\n", type); + ret = -EINVAL; + goto failed; + } + + /* regualar data */ + for (sum = 0, j = 0; j < len; ++j) + sum += dat[j]; + /* work in 16bit (target) */ + sum &= 0xffff; + + memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len); + iowrite32(card->pdat->app.offs + card->pdat->app.addr, + &card->dpram[DPRAM_COMMAND + 2]); + iowrite32(addr, &card->dpram[DPRAM_COMMAND + 6]); + iowrite16(len, &card->dpram[DPRAM_COMMAND + 10]); + iowrite8(1, &card->dpram[DPRAM_COMMAND + 12]); + ret = softing_bootloader_command(card, 1, "loading app."); + if (ret < 0) + goto failed; + /* verify checksum */ + rx_sum = ioread16(&card->dpram[DPRAM_RECEIPT + 2]); + if (rx_sum != sum) { + dev_alert(&card->pdev->dev, "SRAM seems to be damaged" + ", wanted 0x%04x, got 0x%04x\n", sum, rx_sum); + ret = -EIO; + goto failed; + } + } + if (!type_end || !type_entrypoint) + goto failed; + /* start application in card */ + iowrite32(start_addr, &card->dpram[DPRAM_COMMAND + 2]); + iowrite8(1, &card->dpram[DPRAM_COMMAND + 6]); + ret = softing_bootloader_command(card, 3, "start app."); + if (ret < 0) + goto failed; + ret = 0; +failed: + release_firmware(fw); + if (ret < 0) + dev_info(&card->pdev->dev, "firmware %s failed\n", file); + return ret; +} + +static int softing_reset_chip(struct softing *card) +{ + int ret; + + do { + /* reset chip */ + iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO]); + iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO+1]); + iowrite8(1, &card->dpram[DPRAM_RESET]); + iowrite8(0, &card->dpram[DPRAM_RESET+1]); + + ret = softing_fct_cmd(card, 0, "reset_can"); + if (!ret) + break; + if (signal_pending(current)) + /* don't wait any longer */ + break; + } while (1); + card->tx.pending = 0; + return ret; +} + +int softing_chip_poweron(struct softing *card) +{ + int ret; + /* sync */ + ret = _softing_fct_cmd(card, 99, 0x55, "sync-a"); + if (ret < 0) + goto failed; + + ret = _softing_fct_cmd(card, 99, 0xaa, "sync-b"); + if (ret < 0) + goto failed; + + ret = softing_reset_chip(card); + if (ret < 0) + goto failed; + /* get_serial */ + ret = softing_fct_cmd(card, 43, "get_serial_number"); + if (ret < 0) + goto failed; + card->id.serial = ioread32(&card->dpram[DPRAM_FCT_PARAM]); + /* get_version */ + ret = softing_fct_cmd(card, 12, "get_version"); + if (ret < 0) + goto failed; + card->id.fw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 2]); + card->id.hw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 4]); + card->id.license = ioread16(&card->dpram[DPRAM_FCT_PARAM + 6]); + card->id.chip[0] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 8]); + card->id.chip[1] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 10]); + return 0; +failed: + return ret; +} + +static void softing_initialize_timestamp(struct softing *card) +{ + uint64_t ovf; + + card->ts_ref = ktime_get(); + + /* 16MHz is the reference */ + ovf = 0x100000000ULL * 16; + do_div(ovf, card->pdat->freq ?: 16); + + card->ts_overflow = ktime_add_us(ktime_set(0, 0), ovf); +} + +ktime_t softing_raw2ktime(struct softing *card, u32 raw) +{ + uint64_t rawl; + ktime_t now, real_offset; + ktime_t target; + ktime_t tmp; + + now = ktime_get(); + real_offset = ktime_sub(ktime_get_real(), now); + + /* find nsec from card */ + rawl = raw * 16; + do_div(rawl, card->pdat->freq ?: 16); + target = ktime_add_us(card->ts_ref, rawl); + /* test for overflows */ + tmp = ktime_add(target, card->ts_overflow); + while (unlikely(ktime_to_ns(tmp) > ktime_to_ns(now))) { + card->ts_ref = ktime_add(card->ts_ref, card->ts_overflow); + target = tmp; + tmp = ktime_add(target, card->ts_overflow); + } + return ktime_add(target, real_offset); +} + +static inline int softing_error_reporting(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + ? 1 : 0; +} + +int softing_startstop(struct net_device *dev, int up) +{ + int ret; + struct softing *card; + struct softing_priv *priv; + struct net_device *netdev; + int bus_bitmask_start; + int j, error_reporting; + struct can_frame msg; + const struct can_bittiming *bt; + + priv = netdev_priv(dev); + card = priv->card; + + if (!card->fw.up) + return -EIO; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return ret; + + bus_bitmask_start = 0; + if (dev && up) + /* prepare to start this bus as well */ + bus_bitmask_start |= (1 << priv->index); + /* bring netdevs down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + + if (dev != netdev) + netif_stop_queue(netdev); + + if (netif_running(netdev)) { + if (dev != netdev) + bus_bitmask_start |= (1 << j); + priv->tx.pending = 0; + priv->tx.echo_put = 0; + priv->tx.echo_get = 0; + /* + * this bus' may just have called open_candev() + * which is rather stupid to call close_candev() + * already + * but we may come here from busoff recovery too + * in which case the echo_skb _needs_ flushing too. + * just be sure to call open_candev() again + */ + close_candev(netdev); + } + priv->can.state = CAN_STATE_STOPPED; + } + card->tx.pending = 0; + + softing_enable_irq(card, 0); + ret = softing_reset_chip(card); + if (ret) + goto failed; + if (!bus_bitmask_start) + /* no busses to be brought up */ + goto card_done; + + if ((bus_bitmask_start & 1) && (bus_bitmask_start & 2) + && (softing_error_reporting(card->net[0]) + != softing_error_reporting(card->net[1]))) { + dev_alert(&card->pdev->dev, + "err_reporting flag differs for busses\n"); + goto invalid; + } + error_reporting = 0; + if (bus_bitmask_start & 1) { + netdev = card->net[0]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip 1 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 1, "initialize_chip[0]"); + if (ret < 0) + goto failed; + /* set mode */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 3, "set_mode[0]"); + if (ret < 0) + goto failed; + /* set filter */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 7, "set_filter[0]"); + if (ret < 0) + goto failed; + /* set output control */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 5, "set_output[0]"); + if (ret < 0) + goto failed; + } + if (bus_bitmask_start & 2) { + netdev = card->net[1]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip2 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 2, "initialize_chip[1]"); + if (ret < 0) + goto failed; + /* set mode2 */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 4, "set_mode[1]"); + if (ret < 0) + goto failed; + /* set filter2 */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 8, "set_filter[1]"); + if (ret < 0) + goto failed; + /* set output control2 */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 6, "set_output[1]"); + if (ret < 0) + goto failed; + } + /* enable_error_frame */ + /* + * Error reporting is switched off at the moment since + * the receiving of them is not yet 100% verified + * This should be enabled sooner or later + * + if (error_reporting) { + ret = softing_fct_cmd(card, 51, "enable_error_frame"); + if (ret < 0) + goto failed; + } + */ + /* initialize interface */ + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 12]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 14]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 16]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 18]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 20]); + ret = softing_fct_cmd(card, 17, "initialize_interface"); + if (ret < 0) + goto failed; + /* enable_fifo */ + ret = softing_fct_cmd(card, 36, "enable_fifo"); + if (ret < 0) + goto failed; + /* enable fifo tx ack */ + ret = softing_fct_cmd(card, 13, "fifo_tx_ack[0]"); + if (ret < 0) + goto failed; + /* enable fifo tx ack2 */ + ret = softing_fct_cmd(card, 14, "fifo_tx_ack[1]"); + if (ret < 0) + goto failed; + /* start_chip */ + ret = softing_fct_cmd(card, 11, "start_chip"); + if (ret < 0) + goto failed; + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE]); + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE2]); + if (card->pdat->generation < 2) { + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + /* flush the DPRAM caches */ + wmb(); + } + + softing_initialize_timestamp(card); + + /* + * do socketcan notifications/status changes + * from here, no errors should occur, or the failed: part + * must be reviewed + */ + memset(&msg, 0, sizeof(msg)); + msg.can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED; + msg.can_dlc = CAN_ERR_DLC; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!(bus_bitmask_start & (1 << j))) + continue; + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + open_candev(netdev); + if (dev != netdev) { + /* notify other busses on the restart */ + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + ++priv->can.can_stats.restarts; + } + netif_wake_queue(netdev); + } + + /* enable interrupts */ + ret = softing_enable_irq(card, 1); + if (ret) + goto failed; +card_done: + mutex_unlock(&card->fw.lock); + return 0; +invalid: + ret = -EINVAL; +failed: + softing_enable_irq(card, 0); + softing_reset_chip(card); + mutex_unlock(&card->fw.lock); + /* bring all other interfaces down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + dev_close(netdev); + } + return ret; +} + +int softing_default_output(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + struct softing *card = priv->card; + + switch (priv->chip) { + case 1000: + return (card->pdat->generation < 2) ? 0xfb : 0xfa; + case 5: + return 0x60; + default: + return 0x40; + } +} diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c new file mode 100644 index 00000000..a7c77c74 --- /dev/null +++ b/drivers/net/can/softing/softing_main.c @@ -0,0 +1,882 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * 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 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <asm/io.h> + +#include "softing.h" + +#define TX_ECHO_SKB_MAX (((TXMAX+1)/2)-1) + +/* + * test is a specific CAN netdev + * is online (ie. up 'n running, not sleeping, not busoff + */ +static inline int canif_is_active(struct net_device *netdev) +{ + struct can_priv *can = netdev_priv(netdev); + + if (!netif_running(netdev)) + return 0; + return (can->state <= CAN_STATE_ERROR_PASSIVE); +} + +/* reset DPRAM */ +static inline void softing_set_reset_dpram(struct softing *card) +{ + if (card->pdat->generation >= 2) { + spin_lock_bh(&card->spin); + iowrite8(ioread8(&card->dpram[DPRAM_V2_RESET]) & ~1, + &card->dpram[DPRAM_V2_RESET]); + spin_unlock_bh(&card->spin); + } +} + +static inline void softing_clr_reset_dpram(struct softing *card) +{ + if (card->pdat->generation >= 2) { + spin_lock_bh(&card->spin); + iowrite8(ioread8(&card->dpram[DPRAM_V2_RESET]) | 1, + &card->dpram[DPRAM_V2_RESET]); + spin_unlock_bh(&card->spin); + } +} + +/* trigger the tx queue-ing */ +static netdev_tx_t softing_netdev_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct softing_priv *priv = netdev_priv(dev); + struct softing *card = priv->card; + int ret; + uint8_t *ptr; + uint8_t fifo_wr, fifo_rd; + struct can_frame *cf = (struct can_frame *)skb->data; + uint8_t buf[DPRAM_TX_SIZE]; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + spin_lock(&card->spin); + + ret = NETDEV_TX_BUSY; + if (!card->fw.up || + (card->tx.pending >= TXMAX) || + (priv->tx.pending >= TX_ECHO_SKB_MAX)) + goto xmit_done; + fifo_wr = ioread8(&card->dpram[DPRAM_TX_WR]); + fifo_rd = ioread8(&card->dpram[DPRAM_TX_RD]); + if (fifo_wr == fifo_rd) + /* fifo full */ + goto xmit_done; + memset(buf, 0, sizeof(buf)); + ptr = buf; + *ptr = CMD_TX; + if (cf->can_id & CAN_RTR_FLAG) + *ptr |= CMD_RTR; + if (cf->can_id & CAN_EFF_FLAG) + *ptr |= CMD_XTD; + if (priv->index) + *ptr |= CMD_BUS2; + ++ptr; + *ptr++ = cf->can_dlc; + *ptr++ = (cf->can_id >> 0); + *ptr++ = (cf->can_id >> 8); + if (cf->can_id & CAN_EFF_FLAG) { + *ptr++ = (cf->can_id >> 16); + *ptr++ = (cf->can_id >> 24); + } else { + /* increment 1, not 2 as you might think */ + ptr += 1; + } + if (!(cf->can_id & CAN_RTR_FLAG)) + memcpy(ptr, &cf->data[0], cf->can_dlc); + memcpy_toio(&card->dpram[DPRAM_TX + DPRAM_TX_SIZE * fifo_wr], + buf, DPRAM_TX_SIZE); + if (++fifo_wr >= DPRAM_TX_CNT) + fifo_wr = 0; + iowrite8(fifo_wr, &card->dpram[DPRAM_TX_WR]); + card->tx.last_bus = priv->index; + ++card->tx.pending; + ++priv->tx.pending; + can_put_echo_skb(skb, dev, priv->tx.echo_put); + ++priv->tx.echo_put; + if (priv->tx.echo_put >= TX_ECHO_SKB_MAX) + priv->tx.echo_put = 0; + /* can_put_echo_skb() saves the skb, safe to return TX_OK */ + ret = NETDEV_TX_OK; +xmit_done: + spin_unlock(&card->spin); + if (card->tx.pending >= TXMAX) { + int j; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->net[j]) + netif_stop_queue(card->net[j]); + } + } + if (ret != NETDEV_TX_OK) + netif_stop_queue(dev); + + return ret; +} + +/* + * shortcut for skb delivery + */ +int softing_netdev_rx(struct net_device *netdev, const struct can_frame *msg, + ktime_t ktime) +{ + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_skb(netdev, &cf); + if (!skb) + return -ENOMEM; + memcpy(cf, msg, sizeof(*msg)); + skb->tstamp = ktime; + return netif_rx(skb); +} + +/* + * softing_handle_1 + * pop 1 entry from the DPRAM queue, and process + */ +static int softing_handle_1(struct softing *card) +{ + struct net_device *netdev; + struct softing_priv *priv; + ktime_t ktime; + struct can_frame msg; + int cnt = 0, lost_msg; + uint8_t fifo_rd, fifo_wr, cmd; + uint8_t *ptr; + uint32_t tmp_u32; + uint8_t buf[DPRAM_RX_SIZE]; + + memset(&msg, 0, sizeof(msg)); + /* test for lost msgs */ + lost_msg = ioread8(&card->dpram[DPRAM_RX_LOST]); + if (lost_msg) { + int j; + /* reset condition */ + iowrite8(0, &card->dpram[DPRAM_RX_LOST]); + /* prepare msg */ + msg.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL; + msg.can_dlc = CAN_ERR_DLC; + msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + /* + * service to all busses, we don't know which it was applicable + * but only service busses that are online + */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + if (!canif_is_active(netdev)) + /* a dead bus has no overflows */ + continue; + ++netdev->stats.rx_over_errors; + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + } + /* prepare for other use */ + memset(&msg, 0, sizeof(msg)); + ++cnt; + } + + fifo_rd = ioread8(&card->dpram[DPRAM_RX_RD]); + fifo_wr = ioread8(&card->dpram[DPRAM_RX_WR]); + + if (++fifo_rd >= DPRAM_RX_CNT) + fifo_rd = 0; + if (fifo_wr == fifo_rd) + return cnt; + + memcpy_fromio(buf, &card->dpram[DPRAM_RX + DPRAM_RX_SIZE*fifo_rd], + DPRAM_RX_SIZE); + mb(); + /* trigger dual port RAM */ + iowrite8(fifo_rd, &card->dpram[DPRAM_RX_RD]); + + ptr = buf; + cmd = *ptr++; + if (cmd == 0xff) + /* not quite useful, probably the card has got out */ + return 0; + netdev = card->net[0]; + if (cmd & CMD_BUS2) + netdev = card->net[1]; + priv = netdev_priv(netdev); + + if (cmd & CMD_ERR) { + uint8_t can_state, state; + + state = *ptr++; + + msg.can_id = CAN_ERR_FLAG; + msg.can_dlc = CAN_ERR_DLC; + + if (state & SF_MASK_BUSOFF) { + can_state = CAN_STATE_BUS_OFF; + msg.can_id |= CAN_ERR_BUSOFF; + state = STATE_BUSOFF; + } else if (state & SF_MASK_EPASSIVE) { + can_state = CAN_STATE_ERROR_PASSIVE; + msg.can_id |= CAN_ERR_CRTL; + msg.data[1] = CAN_ERR_CRTL_TX_PASSIVE; + state = STATE_EPASSIVE; + } else { + can_state = CAN_STATE_ERROR_ACTIVE; + msg.can_id |= CAN_ERR_CRTL; + state = STATE_EACTIVE; + } + /* update DPRAM */ + iowrite8(state, &card->dpram[priv->index ? + DPRAM_INFO_BUSSTATE2 : DPRAM_INFO_BUSSTATE]); + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + + ++netdev->stats.rx_errors; + /* update internal status */ + if (can_state != priv->can.state) { + priv->can.state = can_state; + if (can_state == CAN_STATE_ERROR_PASSIVE) + ++priv->can.can_stats.error_passive; + else if (can_state == CAN_STATE_BUS_OFF) { + /* this calls can_close_cleanup() */ + can_bus_off(netdev); + netif_stop_queue(netdev); + } + /* trigger socketcan */ + softing_netdev_rx(netdev, &msg, ktime); + } + + } else { + if (cmd & CMD_RTR) + msg.can_id |= CAN_RTR_FLAG; + msg.can_dlc = get_can_dlc(*ptr++); + if (cmd & CMD_XTD) { + msg.can_id |= CAN_EFF_FLAG; + msg.can_id |= le32_to_cpup((void *)ptr); + ptr += 4; + } else { + msg.can_id |= le16_to_cpup((void *)ptr); + ptr += 2; + } + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + if (!(msg.can_id & CAN_RTR_FLAG)) + memcpy(&msg.data[0], ptr, 8); + ptr += 8; + /* update socket */ + if (cmd & CMD_ACK) { + /* acknowledge, was tx msg */ + struct sk_buff *skb; + skb = priv->can.echo_skb[priv->tx.echo_get]; + if (skb) + skb->tstamp = ktime; + can_get_echo_skb(netdev, priv->tx.echo_get); + ++priv->tx.echo_get; + if (priv->tx.echo_get >= TX_ECHO_SKB_MAX) + priv->tx.echo_get = 0; + if (priv->tx.pending) + --priv->tx.pending; + if (card->tx.pending) + --card->tx.pending; + ++netdev->stats.tx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.tx_bytes += msg.can_dlc; + } else { + int ret; + + ret = softing_netdev_rx(netdev, &msg, ktime); + if (ret == NET_RX_SUCCESS) { + ++netdev->stats.rx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.rx_bytes += msg.can_dlc; + } else { + ++netdev->stats.rx_dropped; + } + } + } + ++cnt; + return cnt; +} + +/* + * real interrupt handler + */ +static irqreturn_t softing_irq_thread(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + struct net_device *netdev; + struct softing_priv *priv; + int j, offset, work_done; + + work_done = 0; + spin_lock_bh(&card->spin); + while (softing_handle_1(card) > 0) { + ++card->irq.svc_count; + ++work_done; + } + spin_unlock_bh(&card->spin); + /* resume tx queue's */ + offset = card->tx.last_bus; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->tx.pending >= TXMAX) + break; + netdev = card->net[(j + offset + 1) % card->pdat->nbus]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + if (!canif_is_active(netdev)) + /* it makes no sense to wake dead busses */ + continue; + if (priv->tx.pending >= TX_ECHO_SKB_MAX) + continue; + ++work_done; + netif_wake_queue(netdev); + } + return work_done ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * interrupt routines: + * schedule the 'real interrupt handler' + */ +static irqreturn_t softing_irq_v2(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_V2_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + return (1 == ir) ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +static irqreturn_t softing_irq_v1(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_IRQ_TOHOST]); + return ir ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +/* + * netdev/candev inter-operability + */ +static int softing_netdev_open(struct net_device *ndev) +{ + int ret; + + /* check or determine and set bittime */ + ret = open_candev(ndev); + if (!ret) + ret = softing_startstop(ndev, 1); + return ret; +} + +static int softing_netdev_stop(struct net_device *ndev) +{ + int ret; + + netif_stop_queue(ndev); + + /* softing cycle does close_candev() */ + ret = softing_startstop(ndev, 0); + return ret; +} + +static int softing_candev_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + /* softing_startstop does close_candev() */ + ret = softing_startstop(ndev, 1); + return ret; + case CAN_MODE_STOP: + case CAN_MODE_SLEEP: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Softing device management helpers + */ +int softing_enable_irq(struct softing *card, int enable) +{ + int ret; + + if (!card->irq.nr) { + return 0; + } else if (card->irq.requested && !enable) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } else if (!card->irq.requested && enable) { + ret = request_threaded_irq(card->irq.nr, + (card->pdat->generation >= 2) ? + softing_irq_v2 : softing_irq_v1, + softing_irq_thread, IRQF_SHARED, + dev_name(&card->pdev->dev), card); + if (ret) { + dev_alert(&card->pdev->dev, + "request_threaded_irq(%u) failed\n", + card->irq.nr); + return ret; + } + card->irq.requested = 1; + } + return 0; +} + +static void softing_card_shutdown(struct softing *card) +{ + int fw_up = 0; + + if (mutex_lock_interruptible(&card->fw.lock)) + /* return -ERESTARTSYS */; + fw_up = card->fw.up; + card->fw.up = 0; + + if (card->irq.requested && card->irq.nr) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } + if (fw_up) { + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + } + mutex_unlock(&card->fw.lock); +} + +static __devinit int softing_card_boot(struct softing *card) +{ + int ret, j; + static const uint8_t stream[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }; + unsigned char back[sizeof(stream)]; + + if (mutex_lock_interruptible(&card->fw.lock)) + return -ERESTARTSYS; + if (card->fw.up) { + mutex_unlock(&card->fw.lock); + return 0; + } + /* reset board */ + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 1); + /* boot card */ + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + for (j = 0; (j + sizeof(stream)) < card->dpram_size; + j += sizeof(stream)) { + + memcpy_toio(&card->dpram[j], stream, sizeof(stream)); + /* flush IO cache */ + mb(); + memcpy_fromio(back, &card->dpram[j], sizeof(stream)); + + if (!memcmp(back, stream, sizeof(stream))) + continue; + /* memory is not equal */ + dev_alert(&card->pdev->dev, "dpram failed at 0x%04x\n", j); + ret = -EIO; + goto failed; + } + wmb(); + /* load boot firmware */ + ret = softing_load_fw(card->pdat->boot.fw, card, card->dpram, + card->dpram_size, + card->pdat->boot.offs - card->pdat->boot.addr); + if (ret < 0) + goto failed; + /* load loader firmware */ + ret = softing_load_fw(card->pdat->load.fw, card, card->dpram, + card->dpram_size, + card->pdat->load.offs - card->pdat->load.addr); + if (ret < 0) + goto failed; + + if (card->pdat->reset) + card->pdat->reset(card->pdev, 0); + softing_clr_reset_dpram(card); + ret = softing_bootloader_command(card, 0, "card boot"); + if (ret < 0) + goto failed; + ret = softing_load_app_fw(card->pdat->app.fw, card); + if (ret < 0) + goto failed; + + ret = softing_chip_poweron(card); + if (ret < 0) + goto failed; + + card->fw.up = 1; + mutex_unlock(&card->fw.lock); + return 0; +failed: + card->fw.up = 0; + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + mutex_unlock(&card->fw.lock); + return ret; +} + +/* + * netdev sysfs + */ +static ssize_t show_channel(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "%i\n", priv->index); +} + +static ssize_t show_chip(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "%i\n", priv->chip); +} + +static ssize_t show_output(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "0x%02x\n", priv->output); +} + +static ssize_t store_output(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + struct softing *card = priv->card; + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + val &= 0xFF; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return -ERESTARTSYS; + if (netif_running(ndev)) { + mutex_unlock(&card->fw.lock); + return -EBUSY; + } + priv->output = val; + mutex_unlock(&card->fw.lock); + return count; +} + +static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); +static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL); +static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output); + +static const struct attribute *const netdev_sysfs_attrs[] = { + &dev_attr_channel.attr, + &dev_attr_chip.attr, + &dev_attr_output.attr, + NULL, +}; +static const struct attribute_group netdev_sysfs_group = { + .name = NULL, + .attrs = (struct attribute **)netdev_sysfs_attrs, +}; + +static const struct net_device_ops softing_netdev_ops = { + .ndo_open = softing_netdev_open, + .ndo_stop = softing_netdev_stop, + .ndo_start_xmit = softing_netdev_start_xmit, +}; + +static const struct can_bittiming_const softing_btr_const = { + .name = "softing", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, /* overruled */ + .brp_min = 1, + .brp_max = 32, /* overruled */ + .brp_inc = 1, +}; + + +static __devinit struct net_device *softing_netdev_create(struct softing *card, + uint16_t chip_id) +{ + struct net_device *netdev; + struct softing_priv *priv; + + netdev = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX); + if (!netdev) { + dev_alert(&card->pdev->dev, "alloc_candev failed\n"); + return NULL; + } + priv = netdev_priv(netdev); + priv->netdev = netdev; + priv->card = card; + memcpy(&priv->btr_const, &softing_btr_const, sizeof(priv->btr_const)); + priv->btr_const.brp_max = card->pdat->max_brp; + priv->btr_const.sjw_max = card->pdat->max_sjw; + priv->can.bittiming_const = &priv->btr_const; + priv->can.clock.freq = 8000000; + priv->chip = chip_id; + priv->output = softing_default_output(netdev); + SET_NETDEV_DEV(netdev, &card->pdev->dev); + + netdev->flags |= IFF_ECHO; + netdev->netdev_ops = &softing_netdev_ops; + priv->can.do_set_mode = softing_candev_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + return netdev; +} + +static __devinit int softing_netdev_register(struct net_device *netdev) +{ + int ret; + + netdev->sysfs_groups[0] = &netdev_sysfs_group; + ret = register_candev(netdev); + if (ret) { + dev_alert(&netdev->dev, "register failed\n"); + return ret; + } + return 0; +} + +static void softing_netdev_cleanup(struct net_device *netdev) +{ + unregister_candev(netdev); + free_candev(netdev); +} + +/* + * sysfs for Platform device + */ +#define DEV_ATTR_RO(name, member) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + return sprintf(buf, "%u\n", card->member); \ +} \ +static DEVICE_ATTR(name, 0444, show_##name, NULL) + +#define DEV_ATTR_RO_STR(name, member) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + return sprintf(buf, "%s\n", card->member); \ +} \ +static DEVICE_ATTR(name, 0444, show_##name, NULL) + +DEV_ATTR_RO(serial, id.serial); +DEV_ATTR_RO_STR(firmware, pdat->app.fw); +DEV_ATTR_RO(firmware_version, id.fw_version); +DEV_ATTR_RO_STR(hardware, pdat->name); +DEV_ATTR_RO(hardware_version, id.hw_version); +DEV_ATTR_RO(license, id.license); +DEV_ATTR_RO(frequency, id.freq); +DEV_ATTR_RO(txpending, tx.pending); + +static struct attribute *softing_pdev_attrs[] = { + &dev_attr_serial.attr, + &dev_attr_firmware.attr, + &dev_attr_firmware_version.attr, + &dev_attr_hardware.attr, + &dev_attr_hardware_version.attr, + &dev_attr_license.attr, + &dev_attr_frequency.attr, + &dev_attr_txpending.attr, + NULL, +}; + +static const struct attribute_group softing_pdev_group = { + .name = NULL, + .attrs = softing_pdev_attrs, +}; + +/* + * platform driver + */ +static __devexit int softing_pdev_remove(struct platform_device *pdev) +{ + struct softing *card = platform_get_drvdata(pdev); + int j; + + /* first, disable card*/ + softing_card_shutdown(card); + + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!card->net[j]) + continue; + softing_netdev_cleanup(card->net[j]); + card->net[j] = NULL; + } + sysfs_remove_group(&pdev->dev.kobj, &softing_pdev_group); + + iounmap(card->dpram); + kfree(card); + return 0; +} + +static __devinit int softing_pdev_probe(struct platform_device *pdev) +{ + const struct softing_platform_data *pdat = pdev->dev.platform_data; + struct softing *card; + struct net_device *netdev; + struct softing_priv *priv; + struct resource *pres; + int ret; + int j; + + if (!pdat) { + dev_warn(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + if (pdat->nbus > ARRAY_SIZE(card->net)) { + dev_warn(&pdev->dev, "%u nets??\n", pdat->nbus); + return -EINVAL; + } + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + card->pdat = pdat; + card->pdev = pdev; + platform_set_drvdata(pdev, card); + mutex_init(&card->fw.lock); + spin_lock_init(&card->spin); + + ret = -EINVAL; + pres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pres) + goto platform_resource_failed; + card->dpram_phys = pres->start; + card->dpram_size = resource_size(pres); + card->dpram = ioremap_nocache(card->dpram_phys, card->dpram_size); + if (!card->dpram) { + dev_alert(&card->pdev->dev, "dpram ioremap failed\n"); + goto ioremap_failed; + } + + pres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (pres) + card->irq.nr = pres->start; + + /* reset card */ + ret = softing_card_boot(card); + if (ret < 0) { + dev_alert(&pdev->dev, "failed to boot\n"); + goto boot_failed; + } + + /* only now, the chip's are known */ + card->id.freq = card->pdat->freq; + + ret = sysfs_create_group(&pdev->dev.kobj, &softing_pdev_group); + if (ret < 0) { + dev_alert(&card->pdev->dev, "sysfs failed\n"); + goto sysfs_failed; + } + + ret = -ENOMEM; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + card->net[j] = netdev = + softing_netdev_create(card, card->id.chip[j]); + if (!netdev) { + dev_alert(&pdev->dev, "failed to make can[%i]", j); + goto netdev_failed; + } + priv = netdev_priv(card->net[j]); + priv->index = j; + ret = softing_netdev_register(netdev); + if (ret) { + free_candev(netdev); + card->net[j] = NULL; + dev_alert(&card->pdev->dev, + "failed to register can[%i]\n", j); + goto netdev_failed; + } + } + dev_info(&card->pdev->dev, "%s ready.\n", card->pdat->name); + return 0; + +netdev_failed: + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!card->net[j]) + continue; + softing_netdev_cleanup(card->net[j]); + } + sysfs_remove_group(&pdev->dev.kobj, &softing_pdev_group); +sysfs_failed: + softing_card_shutdown(card); +boot_failed: + iounmap(card->dpram); +ioremap_failed: +platform_resource_failed: + kfree(card); + return ret; +} + +static struct platform_driver softing_driver = { + .driver = { + .name = "softing", + .owner = THIS_MODULE, + }, + .probe = softing_pdev_probe, + .remove = __devexit_p(softing_pdev_remove), +}; + +module_platform_driver(softing_driver); + +MODULE_ALIAS("platform:softing"); +MODULE_DESCRIPTION("Softing DPRAM CAN driver"); +MODULE_AUTHOR("Kurt Van Dijck <kurt.van.dijck@eia.be>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/softing/softing_platform.h b/drivers/net/can/softing/softing_platform.h new file mode 100644 index 00000000..ebbf6981 --- /dev/null +++ b/drivers/net/can/softing/softing_platform.h @@ -0,0 +1,40 @@ + +#include <linux/platform_device.h> + +#ifndef _SOFTING_DEVICE_H_ +#define _SOFTING_DEVICE_H_ + +/* softing firmware directory prefix */ +#define fw_dir "softing-4.6/" + +struct softing_platform_data { + unsigned int manf; + unsigned int prod; + /* + * generation + * 1st with NEC or SJA1000 + * 8bit, exclusive interrupt, ... + * 2nd only SJA1000 + * 16bit, shared interrupt + */ + int generation; + int nbus; /* # busses on device */ + unsigned int freq; /* operating frequency in Hz */ + unsigned int max_brp; + unsigned int max_sjw; + unsigned long dpram_size; + const char *name; + struct { + unsigned long offs; + unsigned long addr; + const char *fw; + } boot, load, app; + /* + * reset() function + * bring pdev in or out of reset, depending on value + */ + int (*reset)(struct platform_device *pdev, int value); + int (*enable_irq)(struct platform_device *pdev, int value); +}; + +#endif diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c new file mode 100644 index 00000000..4accd7ec --- /dev/null +++ b/drivers/net/can/ti_hecc.c @@ -0,0 +1,1057 @@ +/* + * TI HECC (CAN) device driver + * + * This driver supports TI's HECC (High End CAN Controller module) and the + * specs for the same is available at <http://www.ti.com> + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed as is WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Your platform definitions should specify module ram offsets and interrupt + * number to use as follows: + * + * static struct ti_hecc_platform_data am3517_evm_hecc_pdata = { + * .scc_hecc_offset = 0, + * .scc_ram_offset = 0x3000, + * .hecc_ram_offset = 0x3000, + * .mbx_offset = 0x2000, + * .int_line = 0, + * .revision = 1, + * .transceiver_switch = hecc_phy_control, + * }; + * + * Please see include/linux/can/platform/ti_hecc.h for description of + * above fields. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/platform/ti_hecc.h> + +#define DRV_NAME "ti_hecc" +#define HECC_MODULE_VERSION "0.7" +MODULE_VERSION(HECC_MODULE_VERSION); +#define DRV_DESC "TI High End CAN Controller Driver " HECC_MODULE_VERSION + +/* TX / RX Mailbox Configuration */ +#define HECC_MAX_MAILBOXES 32 /* hardware mailboxes - do not change */ +#define MAX_TX_PRIO 0x3F /* hardware value - do not change */ + +/* + * Important Note: TX mailbox configuration + * TX mailboxes should be restricted to the number of SKB buffers to avoid + * maintaining SKB buffers separately. TX mailboxes should be a power of 2 + * for the mailbox logic to work. Top mailbox numbers are reserved for RX + * and lower mailboxes for TX. + * + * HECC_MAX_TX_MBOX HECC_MB_TX_SHIFT + * 4 (default) 2 + * 8 3 + * 16 4 + */ +#define HECC_MB_TX_SHIFT 2 /* as per table above */ +#define HECC_MAX_TX_MBOX BIT(HECC_MB_TX_SHIFT) + +#define HECC_TX_PRIO_SHIFT (HECC_MB_TX_SHIFT) +#define HECC_TX_PRIO_MASK (MAX_TX_PRIO << HECC_MB_TX_SHIFT) +#define HECC_TX_MB_MASK (HECC_MAX_TX_MBOX - 1) +#define HECC_TX_MASK ((HECC_MAX_TX_MBOX - 1) | HECC_TX_PRIO_MASK) +#define HECC_TX_MBOX_MASK (~(BIT(HECC_MAX_TX_MBOX) - 1)) +#define HECC_DEF_NAPI_WEIGHT HECC_MAX_RX_MBOX + +/* + * Important Note: RX mailbox configuration + * RX mailboxes are further logically split into two - main and buffer + * mailboxes. The goal is to get all packets into main mailboxes as + * driven by mailbox number and receive priority (higher to lower) and + * buffer mailboxes are used to receive pkts while main mailboxes are being + * processed. This ensures in-order packet reception. + * + * Here are the recommended values for buffer mailbox. Note that RX mailboxes + * start after TX mailboxes: + * + * HECC_MAX_RX_MBOX HECC_RX_BUFFER_MBOX No of buffer mailboxes + * 28 12 8 + * 16 20 4 + */ + +#define HECC_MAX_RX_MBOX (HECC_MAX_MAILBOXES - HECC_MAX_TX_MBOX) +#define HECC_RX_BUFFER_MBOX 12 /* as per table above */ +#define HECC_RX_FIRST_MBOX (HECC_MAX_MAILBOXES - 1) +#define HECC_RX_HIGH_MBOX_MASK (~(BIT(HECC_RX_BUFFER_MBOX) - 1)) + +/* TI HECC module registers */ +#define HECC_CANME 0x0 /* Mailbox enable */ +#define HECC_CANMD 0x4 /* Mailbox direction */ +#define HECC_CANTRS 0x8 /* Transmit request set */ +#define HECC_CANTRR 0xC /* Transmit request */ +#define HECC_CANTA 0x10 /* Transmission acknowledge */ +#define HECC_CANAA 0x14 /* Abort acknowledge */ +#define HECC_CANRMP 0x18 /* Receive message pending */ +#define HECC_CANRML 0x1C /* Remote message lost */ +#define HECC_CANRFP 0x20 /* Remote frame pending */ +#define HECC_CANGAM 0x24 /* SECC only:Global acceptance mask */ +#define HECC_CANMC 0x28 /* Master control */ +#define HECC_CANBTC 0x2C /* Bit timing configuration */ +#define HECC_CANES 0x30 /* Error and status */ +#define HECC_CANTEC 0x34 /* Transmit error counter */ +#define HECC_CANREC 0x38 /* Receive error counter */ +#define HECC_CANGIF0 0x3C /* Global interrupt flag 0 */ +#define HECC_CANGIM 0x40 /* Global interrupt mask */ +#define HECC_CANGIF1 0x44 /* Global interrupt flag 1 */ +#define HECC_CANMIM 0x48 /* Mailbox interrupt mask */ +#define HECC_CANMIL 0x4C /* Mailbox interrupt level */ +#define HECC_CANOPC 0x50 /* Overwrite protection control */ +#define HECC_CANTIOC 0x54 /* Transmit I/O control */ +#define HECC_CANRIOC 0x58 /* Receive I/O control */ +#define HECC_CANLNT 0x5C /* HECC only: Local network time */ +#define HECC_CANTOC 0x60 /* HECC only: Time-out control */ +#define HECC_CANTOS 0x64 /* HECC only: Time-out status */ +#define HECC_CANTIOCE 0x68 /* SCC only:Enhanced TX I/O control */ +#define HECC_CANRIOCE 0x6C /* SCC only:Enhanced RX I/O control */ + +/* Mailbox registers */ +#define HECC_CANMID 0x0 +#define HECC_CANMCF 0x4 +#define HECC_CANMDL 0x8 +#define HECC_CANMDH 0xC + +#define HECC_SET_REG 0xFFFFFFFF +#define HECC_CANID_MASK 0x3FF /* 18 bits mask for extended id's */ +#define HECC_CCE_WAIT_COUNT 100 /* Wait for ~1 sec for CCE bit */ + +#define HECC_CANMC_SCM BIT(13) /* SCC compat mode */ +#define HECC_CANMC_CCR BIT(12) /* Change config request */ +#define HECC_CANMC_PDR BIT(11) /* Local Power down - for sleep mode */ +#define HECC_CANMC_ABO BIT(7) /* Auto Bus On */ +#define HECC_CANMC_STM BIT(6) /* Self test mode - loopback */ +#define HECC_CANMC_SRES BIT(5) /* Software reset */ + +#define HECC_CANTIOC_EN BIT(3) /* Enable CAN TX I/O pin */ +#define HECC_CANRIOC_EN BIT(3) /* Enable CAN RX I/O pin */ + +#define HECC_CANMID_IDE BIT(31) /* Extended frame format */ +#define HECC_CANMID_AME BIT(30) /* Acceptance mask enable */ +#define HECC_CANMID_AAM BIT(29) /* Auto answer mode */ + +#define HECC_CANES_FE BIT(24) /* form error */ +#define HECC_CANES_BE BIT(23) /* bit error */ +#define HECC_CANES_SA1 BIT(22) /* stuck at dominant error */ +#define HECC_CANES_CRCE BIT(21) /* CRC error */ +#define HECC_CANES_SE BIT(20) /* stuff bit error */ +#define HECC_CANES_ACKE BIT(19) /* ack error */ +#define HECC_CANES_BO BIT(18) /* Bus off status */ +#define HECC_CANES_EP BIT(17) /* Error passive status */ +#define HECC_CANES_EW BIT(16) /* Error warning status */ +#define HECC_CANES_SMA BIT(5) /* suspend mode ack */ +#define HECC_CANES_CCE BIT(4) /* Change config enabled */ +#define HECC_CANES_PDA BIT(3) /* Power down mode ack */ + +#define HECC_CANBTC_SAM BIT(7) /* sample points */ + +#define HECC_BUS_ERROR (HECC_CANES_FE | HECC_CANES_BE |\ + HECC_CANES_CRCE | HECC_CANES_SE |\ + HECC_CANES_ACKE) + +#define HECC_CANMCF_RTR BIT(4) /* Remote transmit request */ + +#define HECC_CANGIF_MAIF BIT(17) /* Message alarm interrupt */ +#define HECC_CANGIF_TCOIF BIT(16) /* Timer counter overflow int */ +#define HECC_CANGIF_GMIF BIT(15) /* Global mailbox interrupt */ +#define HECC_CANGIF_AAIF BIT(14) /* Abort ack interrupt */ +#define HECC_CANGIF_WDIF BIT(13) /* Write denied interrupt */ +#define HECC_CANGIF_WUIF BIT(12) /* Wake up interrupt */ +#define HECC_CANGIF_RMLIF BIT(11) /* Receive message lost interrupt */ +#define HECC_CANGIF_BOIF BIT(10) /* Bus off interrupt */ +#define HECC_CANGIF_EPIF BIT(9) /* Error passive interrupt */ +#define HECC_CANGIF_WLIF BIT(8) /* Warning level interrupt */ +#define HECC_CANGIF_MBOX_MASK 0x1F /* Mailbox number mask */ +#define HECC_CANGIM_I1EN BIT(1) /* Int line 1 enable */ +#define HECC_CANGIM_I0EN BIT(0) /* Int line 0 enable */ +#define HECC_CANGIM_DEF_MASK 0x700 /* only busoff/warning/passive */ +#define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */ + +/* CAN Bittiming constants as per HECC specs */ +static struct can_bittiming_const ti_hecc_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 = 256, + .brp_inc = 1, +}; + +struct ti_hecc_priv { + struct can_priv can; /* MUST be first member/field */ + struct napi_struct napi; + struct net_device *ndev; + struct clk *clk; + void __iomem *base; + u32 scc_ram_offset; + u32 hecc_ram_offset; + u32 mbx_offset; + u32 int_line; + spinlock_t mbx_lock; /* CANME register needs protection */ + u32 tx_head; + u32 tx_tail; + u32 rx_next; + void (*transceiver_switch)(int); +}; + +static inline int get_tx_head_mb(struct ti_hecc_priv *priv) +{ + return priv->tx_head & HECC_TX_MB_MASK; +} + +static inline int get_tx_tail_mb(struct ti_hecc_priv *priv) +{ + return priv->tx_tail & HECC_TX_MB_MASK; +} + +static inline int get_tx_head_prio(struct ti_hecc_priv *priv) +{ + return (priv->tx_head >> HECC_TX_PRIO_SHIFT) & MAX_TX_PRIO; +} + +static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val) +{ + __raw_writel(val, priv->base + priv->hecc_ram_offset + mbxno * 4); +} + +static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno, + u32 reg, u32 val) +{ + __raw_writel(val, priv->base + priv->mbx_offset + mbxno * 0x10 + + reg); +} + +static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg) +{ + return __raw_readl(priv->base + priv->mbx_offset + mbxno * 0x10 + + reg); +} + +static inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val) +{ + __raw_writel(val, priv->base + reg); +} + +static inline u32 hecc_read(struct ti_hecc_priv *priv, int reg) +{ + return __raw_readl(priv->base + reg); +} + +static inline void hecc_set_bit(struct ti_hecc_priv *priv, int reg, + u32 bit_mask) +{ + hecc_write(priv, reg, hecc_read(priv, reg) | bit_mask); +} + +static inline void hecc_clear_bit(struct ti_hecc_priv *priv, int reg, + u32 bit_mask) +{ + hecc_write(priv, reg, hecc_read(priv, reg) & ~bit_mask); +} + +static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) +{ + return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; +} + +static int ti_hecc_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + *state = priv->can.state; + return 0; +} + +static int ti_hecc_set_btc(struct ti_hecc_priv *priv) +{ + struct can_bittiming *bit_timing = &priv->can.bittiming; + u32 can_btc; + + can_btc = (bit_timing->phase_seg2 - 1) & 0x7; + can_btc |= ((bit_timing->phase_seg1 + bit_timing->prop_seg - 1) + & 0xF) << 3; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) { + if (bit_timing->brp > 4) + can_btc |= HECC_CANBTC_SAM; + else + netdev_warn(priv->ndev, "WARN: Triple" + "sampling not set due to h/w limitations"); + } + can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8; + can_btc |= ((bit_timing->brp - 1) & 0xFF) << 16; + + /* ERM being set to 0 by default meaning resync at falling edge */ + + hecc_write(priv, HECC_CANBTC, can_btc); + netdev_info(priv->ndev, "setting CANBTC=%#x\n", can_btc); + + return 0; +} + +static void ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, + int on) +{ + if (priv->transceiver_switch) + priv->transceiver_switch(on); +} + +static void ti_hecc_reset(struct net_device *ndev) +{ + u32 cnt; + struct ti_hecc_priv *priv = netdev_priv(ndev); + + netdev_dbg(ndev, "resetting hecc ...\n"); + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES); + + /* Set change control request and wait till enabled */ + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + + /* + * INFO: It has been observed that at times CCE bit may not be + * set and hw seems to be ok even if this bit is not set so + * timing out with a timing of 1ms to respect the specs + */ + cnt = HECC_CCE_WAIT_COUNT; + while (!hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { + --cnt; + udelay(10); + } + + /* + * Note: On HECC, BTC can be programmed only in initialization mode, so + * it is expected that the can bittiming parameters are set via ip + * utility before the device is opened + */ + ti_hecc_set_btc(priv); + + /* Clear CCR (and CANMC register) and wait for CCE = 0 enable */ + hecc_write(priv, HECC_CANMC, 0); + + /* + * INFO: CAN net stack handles bus off and hence disabling auto-bus-on + * hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_ABO); + */ + + /* + * INFO: It has been observed that at times CCE bit may not be + * set and hw seems to be ok even if this bit is not set so + */ + cnt = HECC_CCE_WAIT_COUNT; + while (hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { + --cnt; + udelay(10); + } + + /* Enable TX and RX I/O Control pins */ + hecc_write(priv, HECC_CANTIOC, HECC_CANTIOC_EN); + hecc_write(priv, HECC_CANRIOC, HECC_CANRIOC_EN); + + /* Clear registers for clean operation */ + hecc_write(priv, HECC_CANTA, HECC_SET_REG); + hecc_write(priv, HECC_CANRMP, HECC_SET_REG); + hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); + hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); + hecc_write(priv, HECC_CANME, 0); + hecc_write(priv, HECC_CANMD, 0); + + /* SCC compat mode NOT supported (and not needed too) */ + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SCM); +} + +static void ti_hecc_start(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + u32 cnt, mbxno, mbx_mask; + + /* put HECC in initialization mode and set btc */ + ti_hecc_reset(ndev); + + priv->tx_head = priv->tx_tail = HECC_TX_MASK; + priv->rx_next = HECC_RX_FIRST_MBOX; + + /* Enable local and global acceptance mask registers */ + hecc_write(priv, HECC_CANGAM, HECC_SET_REG); + + /* Prepare configured mailboxes to receive messages */ + for (cnt = 0; cnt < HECC_MAX_RX_MBOX; cnt++) { + mbxno = HECC_MAX_MAILBOXES - 1 - cnt; + mbx_mask = BIT(mbxno); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + hecc_write_mbx(priv, mbxno, HECC_CANMID, HECC_CANMID_AME); + hecc_write_lam(priv, mbxno, HECC_SET_REG); + hecc_set_bit(priv, HECC_CANMD, mbx_mask); + hecc_set_bit(priv, HECC_CANME, mbx_mask); + hecc_set_bit(priv, HECC_CANMIM, mbx_mask); + } + + /* Prevent message over-write & Enable interrupts */ + hecc_write(priv, HECC_CANOPC, HECC_SET_REG); + if (priv->int_line) { + hecc_write(priv, HECC_CANMIL, HECC_SET_REG); + hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK | + HECC_CANGIM_I1EN | HECC_CANGIM_SIL); + } else { + hecc_write(priv, HECC_CANMIL, 0); + hecc_write(priv, HECC_CANGIM, + HECC_CANGIM_DEF_MASK | HECC_CANGIM_I0EN); + } + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static void ti_hecc_stop(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + /* Disable interrupts and disable mailboxes */ + hecc_write(priv, HECC_CANGIM, 0); + hecc_write(priv, HECC_CANMIM, 0); + hecc_write(priv, HECC_CANME, 0); + priv->can.state = CAN_STATE_STOPPED; +} + +static int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + ti_hecc_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static int ti_hecc_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + bec->txerr = hecc_read(priv, HECC_CANTEC); + bec->rxerr = hecc_read(priv, HECC_CANREC); + + return 0; +} + +/* + * ti_hecc_xmit: HECC Transmit + * + * The transmit mailboxes start from 0 to HECC_MAX_TX_MBOX. In HECC the + * priority of the mailbox for tranmission is dependent upon priority setting + * field in mailbox registers. The mailbox with highest value in priority field + * is transmitted first. Only when two mailboxes have the same value in + * priority field the highest numbered mailbox is transmitted first. + * + * To utilize the HECC priority feature as described above we start with the + * highest numbered mailbox with highest priority level and move on to the next + * mailbox with the same priority level and so on. Once we loop through all the + * transmit mailboxes we choose the next priority level (lower) and so on + * until we reach the lowest priority level on the lowest numbered mailbox + * when we stop transmission until all mailboxes are transmitted and then + * restart at highest numbered mailbox with highest priority. + * + * Two counters (head and tail) are used to track the next mailbox to transmit + * and to track the echo buffer for already transmitted mailbox. The queue + * is stopped when all the mailboxes are busy or when there is a priority + * value roll-over happens. + */ +static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + u32 mbxno, mbx_mask, data; + unsigned long flags; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + mbxno = get_tx_head_mb(priv); + mbx_mask = BIT(mbxno); + spin_lock_irqsave(&priv->mbx_lock, flags); + if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) { + spin_unlock_irqrestore(&priv->mbx_lock, flags); + netif_stop_queue(ndev); + netdev_err(priv->ndev, + "BUG: TX mbx not ready tx_head=%08X, tx_tail=%08X\n", + priv->tx_head, priv->tx_tail); + return NETDEV_TX_BUSY; + } + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + /* Prepare mailbox for transmission */ + data = cf->can_dlc | (get_tx_head_prio(priv) << 8); + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + data |= HECC_CANMCF_RTR; + hecc_write_mbx(priv, mbxno, HECC_CANMCF, data); + + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + data = (cf->can_id & CAN_EFF_MASK) | HECC_CANMID_IDE; + else /* Standard frame format */ + data = (cf->can_id & CAN_SFF_MASK) << 18; + hecc_write_mbx(priv, mbxno, HECC_CANMID, data); + hecc_write_mbx(priv, mbxno, HECC_CANMDL, + be32_to_cpu(*(u32 *)(cf->data))); + if (cf->can_dlc > 4) + hecc_write_mbx(priv, mbxno, HECC_CANMDH, + be32_to_cpu(*(u32 *)(cf->data + 4))); + else + *(u32 *)(cf->data + 4) = 0; + can_put_echo_skb(skb, ndev, mbxno); + + spin_lock_irqsave(&priv->mbx_lock, flags); + --priv->tx_head; + if ((hecc_read(priv, HECC_CANME) & BIT(get_tx_head_mb(priv))) || + (priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK) { + netif_stop_queue(ndev); + } + hecc_set_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + hecc_clear_bit(priv, HECC_CANMD, mbx_mask); + hecc_set_bit(priv, HECC_CANMIM, mbx_mask); + hecc_write(priv, HECC_CANTRS, mbx_mask); + + return NETDEV_TX_OK; +} + +static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 data, mbx_mask; + unsigned long flags; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + netdev_err(priv->ndev, + "ti_hecc_rx_pkt: alloc_can_skb() failed\n"); + return -ENOMEM; + } + + mbx_mask = BIT(mbxno); + data = hecc_read_mbx(priv, mbxno, HECC_CANMID); + if (data & HECC_CANMID_IDE) + cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (data >> 18) & CAN_SFF_MASK; + data = hecc_read_mbx(priv, mbxno, HECC_CANMCF); + if (data & HECC_CANMCF_RTR) + cf->can_id |= CAN_RTR_FLAG; + cf->can_dlc = get_can_dlc(data & 0xF); + data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); + *(u32 *)(cf->data) = cpu_to_be32(data); + if (cf->can_dlc > 4) { + data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); + *(u32 *)(cf->data + 4) = cpu_to_be32(data); + } else { + *(u32 *)(cf->data + 4) = 0; + } + spin_lock_irqsave(&priv->mbx_lock, flags); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + hecc_write(priv, HECC_CANRMP, mbx_mask); + /* enable mailbox only if it is part of rx buffer mailboxes */ + if (priv->rx_next < HECC_RX_BUFFER_MBOX) + hecc_set_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + stats->rx_packets++; + + return 0; +} + +/* + * ti_hecc_rx_poll - HECC receive pkts + * + * The receive mailboxes start from highest numbered mailbox till last xmit + * mailbox. On CAN frame reception the hardware places the data into highest + * numbered mailbox that matches the CAN ID filter. Since all receive mailboxes + * have same filtering (ALL CAN frames) packets will arrive in the highest + * available RX mailbox and we need to ensure in-order packet reception. + * + * To ensure the packets are received in the right order we logically divide + * the RX mailboxes into main and buffer mailboxes. Packets are received as per + * mailbox priotity (higher to lower) in the main bank and once it is full we + * disable further reception into main mailboxes. While the main mailboxes are + * processed in NAPI, further packets are received in buffer mailboxes. + * + * We maintain a RX next mailbox counter to process packets and once all main + * mailboxe packets are passed to the upper stack we enable all of them but + * continue to process packets received in buffer mailboxes. With each packet + * received from buffer mailbox we enable it immediately so as to handle the + * overflow from higher mailboxes. + */ +static int ti_hecc_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ti_hecc_priv *priv = netdev_priv(ndev); + u32 num_pkts = 0; + u32 mbx_mask; + unsigned long pending_pkts, flags; + + if (!netif_running(ndev)) + return 0; + + while ((pending_pkts = hecc_read(priv, HECC_CANRMP)) && + num_pkts < quota) { + mbx_mask = BIT(priv->rx_next); /* next rx mailbox to process */ + if (mbx_mask & pending_pkts) { + if (ti_hecc_rx_pkt(priv, priv->rx_next) < 0) + return num_pkts; + ++num_pkts; + } else if (priv->rx_next > HECC_RX_BUFFER_MBOX) { + break; /* pkt not received yet */ + } + --priv->rx_next; + if (priv->rx_next == HECC_RX_BUFFER_MBOX) { + /* enable high bank mailboxes */ + spin_lock_irqsave(&priv->mbx_lock, flags); + mbx_mask = hecc_read(priv, HECC_CANME); + mbx_mask |= HECC_RX_HIGH_MBOX_MASK; + hecc_write(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + } else if (priv->rx_next == HECC_MAX_TX_MBOX - 1) { + priv->rx_next = HECC_RX_FIRST_MBOX; + break; + } + } + + /* Enable packet interrupt if all pkts are handled */ + if (hecc_read(priv, HECC_CANRMP) == 0) { + napi_complete(napi); + /* Re-enable RX mailbox interrupts */ + mbx_mask = hecc_read(priv, HECC_CANMIM); + mbx_mask |= HECC_TX_MBOX_MASK; + hecc_write(priv, HECC_CANMIM, mbx_mask); + } + + return num_pkts; +} + +static int ti_hecc_error(struct net_device *ndev, int int_status, + int err_status) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* propagate the error condition to the can stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + netdev_err(priv->ndev, + "ti_hecc_error: alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (int_status & HECC_CANGIF_WLIF) { /* warning level int */ + if ((int_status & HECC_CANGIF_BOIF) == 0) { + priv->can.state = CAN_STATE_ERROR_WARNING; + ++priv->can.can_stats.error_warning; + cf->can_id |= CAN_ERR_CRTL; + if (hecc_read(priv, HECC_CANTEC) > 96) + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; + if (hecc_read(priv, HECC_CANREC) > 96) + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; + } + hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW); + netdev_dbg(priv->ndev, "Error Warning interrupt\n"); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + } + + if (int_status & HECC_CANGIF_EPIF) { /* error passive int */ + if ((int_status & HECC_CANGIF_BOIF) == 0) { + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + if (hecc_read(priv, HECC_CANTEC) > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (hecc_read(priv, HECC_CANREC) > 127) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + } + hecc_set_bit(priv, HECC_CANES, HECC_CANES_EP); + netdev_dbg(priv->ndev, "Error passive interrupt\n"); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + } + + /* + * Need to check busoff condition in error status register too to + * ensure warning interrupts don't hog the system + */ + if ((int_status & HECC_CANGIF_BOIF) || (err_status & HECC_CANES_BO)) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + hecc_set_bit(priv, HECC_CANES, HECC_CANES_BO); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + /* Disable all interrupts in bus-off to avoid int hog */ + hecc_write(priv, HECC_CANGIM, 0); + can_bus_off(ndev); + } + + if (err_status & HECC_BUS_ERROR) { + ++priv->can.can_stats.bus_error; + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + if (err_status & HECC_CANES_FE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_FE); + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (err_status & HECC_CANES_BE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_BE); + cf->data[2] |= CAN_ERR_PROT_BIT; + } + if (err_status & HECC_CANES_SE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_SE); + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + if (err_status & HECC_CANES_CRCE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE); + cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + } + if (err_status & HECC_CANES_ACKE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); + cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL; + } + } + + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 mbxno, mbx_mask, int_status, err_status; + unsigned long ack, flags; + + int_status = hecc_read(priv, + (priv->int_line) ? HECC_CANGIF1 : HECC_CANGIF0); + + if (!int_status) + return IRQ_NONE; + + err_status = hecc_read(priv, HECC_CANES); + if (err_status & (HECC_BUS_ERROR | HECC_CANES_BO | + HECC_CANES_EP | HECC_CANES_EW)) + ti_hecc_error(ndev, int_status, err_status); + + if (int_status & HECC_CANGIF_GMIF) { + while (priv->tx_tail - priv->tx_head > 0) { + mbxno = get_tx_tail_mb(priv); + mbx_mask = BIT(mbxno); + if (!(mbx_mask & hecc_read(priv, HECC_CANTA))) + break; + hecc_clear_bit(priv, HECC_CANMIM, mbx_mask); + hecc_write(priv, HECC_CANTA, mbx_mask); + spin_lock_irqsave(&priv->mbx_lock, flags); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + stats->tx_bytes += hecc_read_mbx(priv, mbxno, + HECC_CANMCF) & 0xF; + stats->tx_packets++; + can_get_echo_skb(ndev, mbxno); + --priv->tx_tail; + } + + /* restart queue if wrap-up or if queue stalled on last pkt */ + if (((priv->tx_head == priv->tx_tail) && + ((priv->tx_head & HECC_TX_MASK) != HECC_TX_MASK)) || + (((priv->tx_tail & HECC_TX_MASK) == HECC_TX_MASK) && + ((priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK))) + netif_wake_queue(ndev); + + /* Disable RX mailbox interrupts and let NAPI reenable them */ + if (hecc_read(priv, HECC_CANRMP)) { + ack = hecc_read(priv, HECC_CANMIM); + ack &= BIT(HECC_MAX_TX_MBOX) - 1; + hecc_write(priv, HECC_CANMIM, ack); + napi_schedule(&priv->napi); + } + } + + /* clear all interrupt conditions - read back to avoid spurious ints */ + if (priv->int_line) { + hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); + int_status = hecc_read(priv, HECC_CANGIF1); + } else { + hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); + int_status = hecc_read(priv, HECC_CANGIF0); + } + + return IRQ_HANDLED; +} + +static int ti_hecc_open(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + int err; + + err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED, + ndev->name, ndev); + if (err) { + netdev_err(ndev, "error requesting interrupt\n"); + return err; + } + + ti_hecc_transceiver_switch(priv, 1); + + /* Open common can device */ + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed %d\n", err); + ti_hecc_transceiver_switch(priv, 0); + free_irq(ndev->irq, ndev); + return err; + } + + ti_hecc_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +} + +static int ti_hecc_close(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + ti_hecc_stop(ndev); + free_irq(ndev->irq, ndev); + close_candev(ndev); + ti_hecc_transceiver_switch(priv, 0); + + return 0; +} + +static const struct net_device_ops ti_hecc_netdev_ops = { + .ndo_open = ti_hecc_open, + .ndo_stop = ti_hecc_close, + .ndo_start_xmit = ti_hecc_xmit, +}; + +static int ti_hecc_probe(struct platform_device *pdev) +{ + struct net_device *ndev = (struct net_device *)0; + struct ti_hecc_priv *priv; + struct ti_hecc_platform_data *pdata; + struct resource *mem, *irq; + void __iomem *addr; + int err = -ENODEV; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + goto probe_exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No mem resources\n"); + goto probe_exit; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No irq resource\n"); + goto probe_exit; + } + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { + dev_err(&pdev->dev, "HECC region already claimed\n"); + err = -EBUSY; + goto probe_exit; + } + addr = ioremap(mem->start, resource_size(mem)); + if (!addr) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -ENOMEM; + goto probe_exit_free_region; + } + + ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit_iounmap; + } + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + priv->scc_ram_offset = pdata->scc_ram_offset; + priv->hecc_ram_offset = pdata->hecc_ram_offset; + priv->mbx_offset = pdata->mbx_offset; + priv->int_line = pdata->int_line; + priv->transceiver_switch = pdata->transceiver_switch; + + priv->can.bittiming_const = &ti_hecc_bittiming_const; + priv->can.do_set_mode = ti_hecc_do_set_mode; + priv->can.do_get_state = ti_hecc_get_state; + priv->can.do_get_berr_counter = ti_hecc_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + spin_lock_init(&priv->mbx_lock); + ndev->irq = irq->start; + ndev->flags |= IFF_ECHO; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &ti_hecc_netdev_ops; + + priv->clk = clk_get(&pdev->dev, "hecc_ck"); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "No clock available\n"); + err = PTR_ERR(priv->clk); + priv->clk = NULL; + goto probe_exit_candev; + } + priv->can.clock.freq = clk_get_rate(priv->clk); + netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, + HECC_DEF_NAPI_WEIGHT); + + clk_enable(priv->clk); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + goto probe_exit_clk; + } + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", + priv->base, (u32) ndev->irq); + + return 0; + +probe_exit_clk: + clk_put(priv->clk); +probe_exit_candev: + free_candev(ndev); +probe_exit_iounmap: + iounmap(addr); +probe_exit_free_region: + release_mem_region(mem->start, resource_size(mem)); +probe_exit: + return err; +} + +static int __devexit ti_hecc_remove(struct platform_device *pdev) +{ + struct resource *res; + struct net_device *ndev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(ndev); + + clk_disable(priv->clk); + clk_put(priv->clk); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(priv->base); + release_mem_region(res->start, resource_size(res)); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + + +#ifdef CONFIG_PM +static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + + return 0; +} + +static int ti_hecc_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + clk_enable(priv->clk); + + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#else +#define ti_hecc_suspend NULL +#define ti_hecc_resume NULL +#endif + +/* TI HECC netdevice driver: platform driver structure */ +static struct platform_driver ti_hecc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ti_hecc_probe, + .remove = __devexit_p(ti_hecc_remove), + .suspend = ti_hecc_suspend, + .resume = ti_hecc_resume, +}; + +module_platform_driver(ti_hecc_driver); + +MODULE_AUTHOR("Anant Gole <anantgole@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig new file mode 100644 index 00000000..0a687684 --- /dev/null +++ b/drivers/net/can/usb/Kconfig @@ -0,0 +1,22 @@ +menu "CAN USB interfaces" + depends on USB && CAN_DEV + +config CAN_EMS_USB + tristate "EMS CPC-USB/ARM7 CAN/USB interface" + ---help--- + This driver is for the one channel CPC-USB/ARM7 CAN/USB interface + from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). + +config CAN_ESD_USB2 + tristate "ESD USB/2 CAN/USB interface" + ---help--- + This driver supports the CAN-USB/2 interface + from esd electronic system design gmbh (http://www.esd.eu). + +config CAN_PEAK_USB + tristate "PEAK PCAN-USB/USB Pro interfaces" + ---help--- + This driver supports the PCAN-USB and PCAN-USB Pro adapters + from PEAK-System Technik (http://www.peak-system.com). + +endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile new file mode 100644 index 00000000..da6d1d3b --- /dev/null +++ b/drivers/net/can/usb/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Linux Controller Area Network USB drivers. +# + +obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o +obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c new file mode 100644 index 00000000..7ae65fc8 --- /dev/null +++ b/drivers/net/can/usb/ems_usb.c @@ -0,0 +1,1100 @@ +/* + * CAN driver for EMS Dr. Thomas Wuensche CPC-USB/ARM7 + * + * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche + * + * 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; version 2 of the License. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +MODULE_AUTHOR("Sebastian Haas <haas@ems-wuensche.com>"); +MODULE_DESCRIPTION("CAN driver for EMS Dr. Thomas Wuensche CAN/USB interfaces"); +MODULE_LICENSE("GPL v2"); + +/* Control-Values for CPC_Control() Command Subject Selection */ +#define CONTR_CAN_MESSAGE 0x04 +#define CONTR_CAN_STATE 0x0C +#define CONTR_BUS_ERROR 0x1C + +/* Control Command Actions */ +#define CONTR_CONT_OFF 0 +#define CONTR_CONT_ON 1 +#define CONTR_ONCE 2 + +/* Messages from CPC to PC */ +#define CPC_MSG_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_MSG_TYPE_RTR_FRAME 8 /* CAN remote frame */ +#define CPC_MSG_TYPE_CAN_PARAMS 12 /* Actual CAN parameters */ +#define CPC_MSG_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_MSG_TYPE_EXT_CAN_FRAME 16 /* Extended CAN data frame */ +#define CPC_MSG_TYPE_EXT_RTR_FRAME 17 /* Extended remote frame */ +#define CPC_MSG_TYPE_CONTROL 19 /* change interface behavior */ +#define CPC_MSG_TYPE_CONFIRM 20 /* command processed confirmation */ +#define CPC_MSG_TYPE_OVERRUN 21 /* overrun events */ +#define CPC_MSG_TYPE_CAN_FRAME_ERROR 23 /* detected bus errors */ +#define CPC_MSG_TYPE_ERR_COUNTER 25 /* RX/TX error counter */ + +/* Messages from the PC to the CPC interface */ +#define CPC_CMD_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_CMD_TYPE_CONTROL 3 /* control of interface behavior */ +#define CPC_CMD_TYPE_CAN_PARAMS 6 /* set CAN parameters */ +#define CPC_CMD_TYPE_RTR_FRAME 13 /* CAN remote frame */ +#define CPC_CMD_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_CMD_TYPE_EXT_CAN_FRAME 15 /* Extended CAN data frame */ +#define CPC_CMD_TYPE_EXT_RTR_FRAME 16 /* Extended CAN remote frame */ +#define CPC_CMD_TYPE_CAN_EXIT 200 /* exit the CAN */ + +#define CPC_CMD_TYPE_INQ_ERR_COUNTER 25 /* request the CAN error counters */ +#define CPC_CMD_TYPE_CLEAR_MSG_QUEUE 8 /* clear CPC_MSG queue */ +#define CPC_CMD_TYPE_CLEAR_CMD_QUEUE 28 /* clear CPC_CMD queue */ + +#define CPC_CC_TYPE_SJA1000 2 /* Philips basic CAN controller */ + +#define CPC_CAN_ECODE_ERRFRAME 0x01 /* Ecode type */ + +/* Overrun types */ +#define CPC_OVR_EVENT_CAN 0x01 +#define CPC_OVR_EVENT_CANSTATE 0x02 +#define CPC_OVR_EVENT_BUSERROR 0x04 + +/* + * If the CAN controller lost a message we indicate it with the highest bit + * set in the count field. + */ +#define CPC_OVR_HW 0x80 + +/* Size of the "struct ems_cpc_msg" without the union */ +#define CPC_MSG_HEADER_LEN 11 +#define CPC_CAN_MSG_MIN_SIZE 5 + +/* Define these values to match your devices */ +#define USB_CPCUSB_VENDOR_ID 0x12D6 + +#define USB_CPCUSB_ARM7_PRODUCT_ID 0x0444 + +/* Mode register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_MOD_NORMAL 0x00 +#define SJA1000_MOD_RM 0x01 + +/* ECC register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_ECC_SEG 0x1F +#define SJA1000_ECC_DIR 0x20 +#define SJA1000_ECC_ERR 0x06 +#define SJA1000_ECC_BIT 0x00 +#define SJA1000_ECC_FORM 0x40 +#define SJA1000_ECC_STUFF 0x80 +#define SJA1000_ECC_MASK 0xc0 + +/* Status register content */ +#define SJA1000_SR_BS 0x80 +#define SJA1000_SR_ES 0x40 + +#define SJA1000_DEFAULT_OUTPUT_CONTROL 0xDA + +/* + * The device actually uses a 16MHz clock to generate the CAN clock + * but it expects SJA1000 bit settings based on 8MHz (is internally + * converted). + */ +#define EMS_USB_ARM7_CLOCK 8000000 + +/* + * CAN-Message representation in a CPC_MSG. Message object type is + * CPC_MSG_TYPE_CAN_FRAME or CPC_MSG_TYPE_RTR_FRAME or + * CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME. + */ +struct cpc_can_msg { + u32 id; + u8 length; + u8 msg[8]; +}; + +/* Representation of the CAN parameters for the SJA1000 controller */ +struct cpc_sja1000_params { + u8 mode; + u8 acc_code0; + u8 acc_code1; + u8 acc_code2; + u8 acc_code3; + u8 acc_mask0; + u8 acc_mask1; + u8 acc_mask2; + u8 acc_mask3; + u8 btr0; + u8 btr1; + u8 outp_contr; +}; + +/* CAN params message representation */ +struct cpc_can_params { + u8 cc_type; + + /* Will support M16C CAN controller in the future */ + union { + struct cpc_sja1000_params sja1000; + } cc_params; +}; + +/* Structure for confirmed message handling */ +struct cpc_confirm { + u8 error; /* error code */ +}; + +/* Structure for overrun conditions */ +struct cpc_overrun { + u8 event; + u8 count; +}; + +/* SJA1000 CAN errors (compatible to NXP LPC2119) */ +struct cpc_sja1000_can_error { + u8 ecc; + u8 rxerr; + u8 txerr; +}; + +/* structure for CAN error conditions */ +struct cpc_can_error { + u8 ecode; + + struct { + u8 cc_type; + + /* Other controllers may also provide error code capture regs */ + union { + struct cpc_sja1000_can_error sja1000; + } regs; + } cc; +}; + +/* + * Structure containing RX/TX error counter. This structure is used to request + * the values of the CAN controllers TX and RX error counter. + */ +struct cpc_can_err_counter { + u8 rx; + u8 tx; +}; + +/* Main message type used between library and application */ +struct __packed ems_cpc_msg { + u8 type; /* type of message */ + u8 length; /* length of data within union 'msg' */ + u8 msgid; /* confirmation handle */ + u32 ts_sec; /* timestamp in seconds */ + u32 ts_nsec; /* timestamp in nano seconds */ + + union { + u8 generic[64]; + struct cpc_can_msg can_msg; + struct cpc_can_params can_params; + struct cpc_confirm confirmation; + struct cpc_overrun overrun; + struct cpc_can_error error; + struct cpc_can_err_counter err_counter; + u8 can_state; + } msg; +}; + +/* + * Table of devices that work with this driver + * NOTE: This driver supports only CPC-USB/ARM7 (LPC2119) yet. + */ +static struct usb_device_id ems_usb_table[] = { + {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_ARM7_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ems_usb_table); + +#define RX_BUFFER_SIZE 64 +#define CPC_HEADER_SIZE 4 +#define INTR_IN_BUFFER_SIZE 4 + +#define MAX_RX_URBS 10 +#define MAX_TX_URBS 10 + +struct ems_usb; + +struct ems_tx_urb_context { + struct ems_usb *dev; + + u32 echo_index; + u8 dlc; +}; + +struct ems_usb { + struct can_priv can; /* must be the first member */ + int open_time; + + struct sk_buff *echo_skb[MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct ems_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct usb_anchor rx_submitted; + + struct urb *intr_urb; + + u8 *tx_msg_buffer; + + u8 *intr_in_buffer; + unsigned int free_slots; /* remember number of available slots */ + + struct ems_cpc_msg active_params; /* active controller parameters */ +}; + +static void ems_usb_read_interrupt_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev = dev->netdev; + int err; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: + dev->free_slots = dev->intr_in_buffer[1]; + break; + + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx interrupt aborted %d\n", urb->status); + break; + } + + err = usb_submit_urb(urb, GFP_ATOMIC); + + if (err == -ENODEV) + netif_device_detach(netdev); + else if (err) + netdev_err(netdev, "failed resubmitting intr urb: %d\n", err); +} + +static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int i; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = alloc_can_skb(dev->netdev, &cf); + if (skb == NULL) + return; + + cf->can_id = le32_to_cpu(msg->msg.can_msg.id); + cf->can_dlc = get_can_dlc(msg->msg.can_msg.length & 0xF); + + if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->type == CPC_MSG_TYPE_RTR_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.can_msg.msg[i]; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = alloc_can_err_skb(dev->netdev, &cf); + if (skb == NULL) + return; + + if (msg->type == CPC_MSG_TYPE_CAN_STATE) { + u8 state = msg->msg.can_state; + + if (state & SJA1000_SR_BS) { + dev->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + + can_bus_off(dev->netdev); + } else if (state & SJA1000_SR_ES) { + dev->can.state = CAN_STATE_ERROR_WARNING; + dev->can.can_stats.error_warning++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + dev->can.can_stats.error_passive++; + } + } else if (msg->type == CPC_MSG_TYPE_CAN_FRAME_ERROR) { + u8 ecc = msg->msg.error.cc.regs.sja1000.ecc; + u8 txerr = msg->msg.error.cc.regs.sja1000.txerr; + u8 rxerr = msg->msg.error.cc.regs.sja1000.rxerr; + + /* bus error interrupt */ + dev->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SJA1000_ECC_MASK) { + case SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & SJA1000_ECC_SEG; + break; + } + + /* Error occurred during transmission? */ + if ((ecc & SJA1000_ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + + if (dev->can.state == CAN_STATE_ERROR_WARNING || + dev->can.state == CAN_STATE_ERROR_PASSIVE) { + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; + } + } else if (msg->type == CPC_MSG_TYPE_OVERRUN) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_over_errors++; + stats->rx_errors++; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_read_bulk_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev; + int retval; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + if (urb->actual_length > CPC_HEADER_SIZE) { + struct ems_cpc_msg *msg; + u8 *ibuf = urb->transfer_buffer; + u8 msg_count, again, start; + + msg_count = ibuf[0] & ~0x80; + again = ibuf[0] & 0x80; + + start = CPC_HEADER_SIZE; + + while (msg_count) { + msg = (struct ems_cpc_msg *)&ibuf[start]; + + switch (msg->type) { + case CPC_MSG_TYPE_CAN_STATE: + /* Process CAN state changes */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME: + case CPC_MSG_TYPE_EXT_CAN_FRAME: + case CPC_MSG_TYPE_RTR_FRAME: + case CPC_MSG_TYPE_EXT_RTR_FRAME: + ems_usb_rx_can_msg(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME_ERROR: + /* Process errorframe */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_OVERRUN: + /* Message lost while receiving */ + ems_usb_rx_err(dev, msg); + break; + } + + start += CPC_MSG_HEADER_LEN + msg->length; + msg_count--; + + if (start > urb->transfer_buffer_length) { + netdev_err(netdev, "format error\n"); + break; + } + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + urb->transfer_buffer, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + netdev_err(netdev, + "failed resubmitting read bulk urb: %d\n", retval); +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_write_bulk_callback(struct urb *urb) +{ + struct ems_tx_urb_context *context = urb->context; + struct ems_usb *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + netdev->trans_start = jiffies; + + /* transmission complete interrupt */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + can_get_echo_skb(netdev, context->echo_index); + + /* Release context */ + context->echo_index = MAX_TX_URBS; + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); +} + +/* + * Send the given CPC command synchronously + */ +static int ems_usb_command_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + int actual_length; + + /* Copy payload */ + memcpy(&dev->tx_msg_buffer[CPC_HEADER_SIZE], msg, + msg->length + CPC_MSG_HEADER_LEN); + + /* Clear header */ + memset(&dev->tx_msg_buffer[0], 0, CPC_HEADER_SIZE); + + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + &dev->tx_msg_buffer[0], + msg->length + CPC_MSG_HEADER_LEN + CPC_HEADER_SIZE, + &actual_length, 1000); +} + +/* + * Change CAN controllers' mode register + */ +static int ems_usb_write_mode(struct ems_usb *dev, u8 mode) +{ + dev->active_params.msg.can_params.cc_params.sja1000.mode = mode; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +/* + * Send a CPC_Control command to change behaviour when interface receives a CAN + * message, bus error or CAN state changed notifications. + */ +static int ems_usb_control_cmd(struct ems_usb *dev, u8 val) +{ + struct ems_cpc_msg cmd; + + cmd.type = CPC_CMD_TYPE_CONTROL; + cmd.length = CPC_MSG_HEADER_LEN + 1; + + cmd.msgid = 0; + + cmd.msg.generic[0] = val; + + return ems_usb_command_msg(dev, &cmd); +} + +/* + * Start interface + */ +static int ems_usb_start(struct ems_usb *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + dev->intr_in_buffer[0] = 0; + dev->free_slots = 15; /* initial size */ + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + buf, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + break; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + netdev_warn(netdev, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + /* Setup and start interrupt URB */ + usb_fill_int_urb(dev->intr_urb, dev->udev, + usb_rcvintpipe(dev->udev, 1), + dev->intr_in_buffer, + INTR_IN_BUFFER_SIZE, + ems_usb_read_interrupt_callback, dev, 1); + + err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); + if (err) { + netdev_warn(netdev, "intr URB submit failed: %d\n", err); + + return err; + } + + /* CPC-USB will transfer received message to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_MESSAGE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer CAN state changes to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_STATE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer bus errors to host */ + err = ems_usb_control_cmd(dev, CONTR_BUS_ERROR | CONTR_CONT_ON); + if (err) + goto failed; + + err = ems_usb_write_mode(dev, SJA1000_MOD_NORMAL); + if (err) + goto failed; + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + return err; +} + +static void unlink_all_urbs(struct ems_usb *dev) +{ + int i; + + usb_unlink_urb(dev->intr_urb); + + usb_kill_anchored_urbs(&dev->rx_submitted); + + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +static int ems_usb_open(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + int err; + + err = ems_usb_write_mode(dev, SJA1000_MOD_RM); + if (err) + return err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = ems_usb_start(dev); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", err); + + close_candev(netdev); + + return err; + } + + dev->open_time = jiffies; + + netif_start_queue(netdev); + + return 0; +} + +static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct ems_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct ems_cpc_msg *msg; + struct urb *urb; + u8 *buf; + int i, err; + size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN + + sizeof(struct cpc_can_msg); + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + goto nomem; + } + + buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + goto nomem; + } + + msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE]; + + msg->msg.can_msg.id = cf->can_id & CAN_ERR_MASK; + msg->msg.can_msg.length = cf->can_dlc; + + if (cf->can_id & CAN_RTR_FLAG) { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_RTR_FRAME : CPC_CMD_TYPE_RTR_FRAME; + + msg->length = CPC_CAN_MSG_MIN_SIZE; + } else { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME; + + for (i = 0; i < cf->can_dlc; i++) + msg->msg.can_msg.msg[i] = cf->data[i]; + + msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc; + } + + /* Respect byte order */ + msg->msg.can_msg.id = cpu_to_le32(msg->msg.can_msg.id); + + for (i = 0; i < MAX_TX_URBS; i++) { + if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &dev->tx_contexts[i]; + break; + } + } + + /* + * May never happen! When this happens we'd more URBs in flight as + * allowed (MAX_TX_URBS). + */ + if (!context) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + + netdev_warn(netdev, "couldn't find free context\n"); + + return NETDEV_TX_BUSY; + } + + context->dev = dev; + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, + size, ems_usb_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + dev_kfree_skb(skb); + + atomic_dec(&dev->active_tx_urbs); + + if (err == -ENODEV) { + netif_device_detach(netdev); + } else { + netdev_warn(netdev, "failed tx_urb %d\n", err); + + stats->tx_dropped++; + } + } else { + netdev->trans_start = jiffies; + + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS || + dev->free_slots < 5) { + netif_stop_queue(netdev); + } + } + + /* + * Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +nomem: + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static int ems_usb_close(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + + /* Stop polling */ + unlink_all_urbs(dev); + + netif_stop_queue(netdev); + + /* Set CAN controller to reset mode */ + if (ems_usb_write_mode(dev, SJA1000_MOD_RM)) + netdev_warn(netdev, "couldn't stop device"); + + close_candev(netdev); + + dev->open_time = 0; + + return 0; +} + +static const struct net_device_ops ems_usb_netdev_ops = { + .ndo_open = ems_usb_open, + .ndo_stop = ems_usb_close, + .ndo_start_xmit = ems_usb_start_xmit, +}; + +static struct can_bittiming_const ems_usb_bittiming_const = { + .name = "ems_usb", + .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 int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct ems_usb *dev = netdev_priv(netdev); + + if (!dev->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) + netdev_warn(netdev, "couldn't start device"); + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ems_usb_set_bittiming(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(netdev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + dev->active_params.msg.can_params.cc_params.sja1000.btr0 = btr0; + dev->active_params.msg.can_params.cc_params.sja1000.btr1 = btr1; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +static void init_params_sja1000(struct ems_cpc_msg *msg) +{ + struct cpc_sja1000_params *sja1000 = + &msg->msg.can_params.cc_params.sja1000; + + msg->type = CPC_CMD_TYPE_CAN_PARAMS; + msg->length = sizeof(struct cpc_can_params); + msg->msgid = 0; + + msg->msg.can_params.cc_type = CPC_CC_TYPE_SJA1000; + + /* Acceptance filter open */ + sja1000->acc_code0 = 0x00; + sja1000->acc_code1 = 0x00; + sja1000->acc_code2 = 0x00; + sja1000->acc_code3 = 0x00; + + /* Acceptance filter open */ + sja1000->acc_mask0 = 0xFF; + sja1000->acc_mask1 = 0xFF; + sja1000->acc_mask2 = 0xFF; + sja1000->acc_mask3 = 0xFF; + + sja1000->btr0 = 0; + sja1000->btr1 = 0; + + sja1000->outp_contr = SJA1000_DEFAULT_OUTPUT_CONTROL; + sja1000->mode = SJA1000_MOD_RM; +} + +/* + * probe function for new CPC-USB devices + */ +static int ems_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct ems_usb *dev; + int i, err = -ENOMEM; + + netdev = alloc_candev(sizeof(struct ems_usb), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "ems_usb: Couldn't alloc candev\n"); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + dev->udev = interface_to_usbdev(intf); + dev->netdev = netdev; + + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = EMS_USB_ARM7_CLOCK; + dev->can.bittiming_const = &ems_usb_bittiming_const; + dev->can.do_set_bittiming = ems_usb_set_bittiming; + dev->can.do_set_mode = ems_usb_set_mode; + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + netdev->netdev_ops = &ems_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; + + dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) { + dev_err(&intf->dev, "Couldn't alloc intr URB\n"); + goto cleanup_candev; + } + + dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL); + if (!dev->intr_in_buffer) { + dev_err(&intf->dev, "Couldn't alloc Intr buffer\n"); + goto cleanup_intr_urb; + } + + dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE + + sizeof(struct ems_cpc_msg), GFP_KERNEL); + if (!dev->tx_msg_buffer) { + dev_err(&intf->dev, "Couldn't alloc Tx buffer\n"); + goto cleanup_intr_in_buffer; + } + + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + + init_params_sja1000(&dev->active_params); + + err = ems_usb_command_msg(dev, &dev->active_params); + if (err) { + netdev_err(netdev, "couldn't initialize controller: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register CAN device: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + return 0; + +cleanup_tx_msg_buffer: + kfree(dev->tx_msg_buffer); + +cleanup_intr_in_buffer: + kfree(dev->intr_in_buffer); + +cleanup_intr_urb: + usb_free_urb(dev->intr_urb); + +cleanup_candev: + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void ems_usb_disconnect(struct usb_interface *intf) +{ + struct ems_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (dev) { + unregister_netdev(dev->netdev); + free_candev(dev->netdev); + + unlink_all_urbs(dev); + + usb_free_urb(dev->intr_urb); + + kfree(dev->intr_in_buffer); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver ems_usb_driver = { + .name = "ems_usb", + .probe = ems_usb_probe, + .disconnect = ems_usb_disconnect, + .id_table = ems_usb_table, +}; + +module_usb_driver(ems_usb_driver); diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c new file mode 100644 index 00000000..09b1da5b --- /dev/null +++ b/drivers/net/can/usb/esd_usb2.c @@ -0,0 +1,1108 @@ +/* + * CAN driver for esd CAN-USB/2 + * + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * + * 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; version 2 of the License. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd.eu>"); +MODULE_DESCRIPTION("CAN driver for esd CAN-USB/2 interfaces"); +MODULE_LICENSE("GPL v2"); + +/* Define these values to match your devices */ +#define USB_ESDGMBH_VENDOR_ID 0x0ab4 +#define USB_CANUSB2_PRODUCT_ID 0x0010 + +#define ESD_USB2_CAN_CLOCK 60000000 +#define ESD_USB2_MAX_NETS 2 + +/* USB2 commands */ +#define CMD_VERSION 1 /* also used for VERSION_REPLY */ +#define CMD_CAN_RX 2 /* device to host only */ +#define CMD_CAN_TX 3 /* also used for TX_DONE */ +#define CMD_SETBAUD 4 /* also used for SETBAUD_REPLY */ +#define CMD_TS 5 /* also used for TS_REPLY */ +#define CMD_IDADD 6 /* also used for IDADD_REPLY */ + +/* esd CAN message flags - dlc field */ +#define ESD_RTR 0x10 + +/* esd CAN message flags - id field */ +#define ESD_EXTID 0x20000000 +#define ESD_EVENT 0x40000000 +#define ESD_IDMASK 0x1fffffff + +/* esd CAN event ids used by this driver */ +#define ESD_EV_CAN_ERROR_EXT 2 + +/* baudrate message flags */ +#define ESD_USB2_UBR 0x80000000 +#define ESD_USB2_LOM 0x40000000 +#define ESD_USB2_NO_BAUDRATE 0x7fffffff +#define ESD_USB2_TSEG1_MIN 1 +#define ESD_USB2_TSEG1_MAX 16 +#define ESD_USB2_TSEG1_SHIFT 16 +#define ESD_USB2_TSEG2_MIN 1 +#define ESD_USB2_TSEG2_MAX 8 +#define ESD_USB2_TSEG2_SHIFT 20 +#define ESD_USB2_SJW_MAX 4 +#define ESD_USB2_SJW_SHIFT 14 +#define ESD_USB2_BRP_MIN 1 +#define ESD_USB2_BRP_MAX 1024 +#define ESD_USB2_BRP_INC 1 +#define ESD_USB2_3_SAMPLES 0x00800000 + +/* esd IDADD message */ +#define ESD_ID_ENABLE 0x80 +#define ESD_MAX_ID_SEGMENT 64 + +/* SJA1000 ECC register (emulated by usb2 firmware) */ +#define SJA1000_ECC_SEG 0x1F +#define SJA1000_ECC_DIR 0x20 +#define SJA1000_ECC_ERR 0x06 +#define SJA1000_ECC_BIT 0x00 +#define SJA1000_ECC_FORM 0x40 +#define SJA1000_ECC_STUFF 0x80 +#define SJA1000_ECC_MASK 0xc0 + +/* esd bus state event codes */ +#define ESD_BUSSTATE_MASK 0xc0 +#define ESD_BUSSTATE_WARN 0x40 +#define ESD_BUSSTATE_ERRPASSIVE 0x80 +#define ESD_BUSSTATE_BUSOFF 0xc0 + +#define RX_BUFFER_SIZE 1024 +#define MAX_RX_URBS 4 +#define MAX_TX_URBS 16 /* must be power of 2 */ + +struct header_msg { + u8 len; /* len is always the total message length in 32bit words */ + u8 cmd; + u8 rsvd[2]; +}; + +struct version_msg { + u8 len; + u8 cmd; + u8 rsvd; + u8 flags; + __le32 drv_version; +}; + +struct version_reply_msg { + u8 len; + u8 cmd; + u8 nets; + u8 features; + __le32 version; + u8 name[16]; + __le32 rsvd; + __le32 ts; +}; + +struct rx_msg { + u8 len; + u8 cmd; + u8 net; + u8 dlc; + __le32 ts; + __le32 id; /* upper 3 bits contain flags */ + u8 data[8]; +}; + +struct tx_msg { + u8 len; + u8 cmd; + u8 net; + u8 dlc; + __le32 hnd; + __le32 id; /* upper 3 bits contain flags */ + u8 data[8]; +}; + +struct tx_done_msg { + u8 len; + u8 cmd; + u8 net; + u8 status; + __le32 hnd; + __le32 ts; +}; + +struct id_filter_msg { + u8 len; + u8 cmd; + u8 net; + u8 option; + __le32 mask[ESD_MAX_ID_SEGMENT + 1]; +}; + +struct set_baudrate_msg { + u8 len; + u8 cmd; + u8 net; + u8 rsvd; + __le32 baud; +}; + +/* Main message type used between library and application */ +struct __attribute__ ((packed)) esd_usb2_msg { + union { + struct header_msg hdr; + struct version_msg version; + struct version_reply_msg version_reply; + struct rx_msg rx; + struct tx_msg tx; + struct tx_done_msg txdone; + struct set_baudrate_msg setbaud; + struct id_filter_msg filter; + } msg; +}; + +static struct usb_device_id esd_usb2_table[] = { + {USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB2_PRODUCT_ID)}, + {} +}; +MODULE_DEVICE_TABLE(usb, esd_usb2_table); + +struct esd_usb2_net_priv; + +struct esd_tx_urb_context { + struct esd_usb2_net_priv *priv; + u32 echo_index; + int dlc; +}; + +struct esd_usb2 { + struct usb_device *udev; + struct esd_usb2_net_priv *nets[ESD_USB2_MAX_NETS]; + + struct usb_anchor rx_submitted; + + int net_count; + u32 version; + int rxinitdone; +}; + +struct esd_usb2_net_priv { + struct can_priv can; /* must be the first member */ + + atomic_t active_tx_jobs; + struct usb_anchor tx_submitted; + struct esd_tx_urb_context tx_contexts[MAX_TX_URBS]; + + int open_time; + struct esd_usb2 *usb2; + struct net_device *netdev; + int index; + u8 old_state; + struct can_berr_counter bec; +}; + +static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv, + struct esd_usb2_msg *msg) +{ + struct net_device_stats *stats = &priv->netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 id = le32_to_cpu(msg->msg.rx.id) & ESD_IDMASK; + + if (id == ESD_EV_CAN_ERROR_EXT) { + u8 state = msg->msg.rx.data[0]; + u8 ecc = msg->msg.rx.data[1]; + u8 txerr = msg->msg.rx.data[2]; + u8 rxerr = msg->msg.rx.data[3]; + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (skb == NULL) { + stats->rx_dropped++; + return; + } + + if (state != priv->old_state) { + priv->old_state = state; + + switch (state & ESD_BUSSTATE_MASK) { + case ESD_BUSSTATE_BUSOFF: + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(priv->netdev); + break; + case ESD_BUSSTATE_WARN: + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + break; + case ESD_BUSSTATE_ERRPASSIVE: + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + break; + default: + priv->can.state = CAN_STATE_ERROR_ACTIVE; + break; + } + } else { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SJA1000_ECC_MASK) { + case SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & SJA1000_ECC_SEG; + break; + } + + /* Error occurred during transmission? */ + if (!(ecc & SJA1000_ECC_DIR)) + cf->data[2] |= CAN_ERR_PROT_TX; + + if (priv->can.state == CAN_STATE_ERROR_WARNING || + priv->can.state == CAN_STATE_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; + } + + netif_rx(skb); + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } +} + +static void esd_usb2_rx_can_msg(struct esd_usb2_net_priv *priv, + struct esd_usb2_msg *msg) +{ + struct net_device_stats *stats = &priv->netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + int i; + u32 id; + + if (!netif_device_present(priv->netdev)) + return; + + id = le32_to_cpu(msg->msg.rx.id); + + if (id & ESD_EVENT) { + esd_usb2_rx_event(priv, msg); + } else { + skb = alloc_can_skb(priv->netdev, &cf); + if (skb == NULL) { + stats->rx_dropped++; + return; + } + + cf->can_id = id & ESD_IDMASK; + cf->can_dlc = get_can_dlc(msg->msg.rx.dlc); + + if (id & ESD_EXTID) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->msg.rx.dlc & ESD_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.rx.data[i]; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } + + return; +} + +static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv, + struct esd_usb2_msg *msg) +{ + struct net_device_stats *stats = &priv->netdev->stats; + struct net_device *netdev = priv->netdev; + struct esd_tx_urb_context *context; + + if (!netif_device_present(netdev)) + return; + + context = &priv->tx_contexts[msg->msg.txdone.hnd & (MAX_TX_URBS - 1)]; + + if (!msg->msg.txdone.status) { + stats->tx_packets++; + stats->tx_bytes += context->dlc; + can_get_echo_skb(netdev, context->echo_index); + } else { + stats->tx_errors++; + can_free_echo_skb(netdev, context->echo_index); + } + + /* Release context */ + context->echo_index = MAX_TX_URBS; + atomic_dec(&priv->active_tx_jobs); + + netif_wake_queue(netdev); +} + +static void esd_usb2_read_bulk_callback(struct urb *urb) +{ + struct esd_usb2 *dev = urb->context; + int retval; + int pos = 0; + int i; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + dev_info(dev->udev->dev.parent, + "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct esd_usb2_msg *msg; + + msg = (struct esd_usb2_msg *)(urb->transfer_buffer + pos); + + switch (msg->msg.hdr.cmd) { + case CMD_CAN_RX: + esd_usb2_rx_can_msg(dev->nets[msg->msg.rx.net], msg); + break; + + case CMD_CAN_TX: + esd_usb2_tx_done_msg(dev->nets[msg->msg.txdone.net], + msg); + break; + } + + pos += msg->msg.hdr.len << 2; + + if (pos > urb->actual_length) { + dev_err(dev->udev->dev.parent, "format error\n"); + break; + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), + urb->transfer_buffer, RX_BUFFER_SIZE, + esd_usb2_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval == -ENODEV) { + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) + netif_device_detach(dev->nets[i]->netdev); + } + } else if (retval) { + dev_err(dev->udev->dev.parent, + "failed resubmitting read bulk urb: %d\n", retval); + } + + return; +} + +/* + * callback for bulk IN urb + */ +static void esd_usb2_write_bulk_callback(struct urb *urb) +{ + struct esd_tx_urb_context *context = urb->context; + struct esd_usb2_net_priv *priv; + struct esd_usb2 *dev; + struct net_device *netdev; + size_t size = sizeof(struct esd_usb2_msg); + + WARN_ON(!context); + + priv = context->priv; + netdev = priv->netdev; + dev = priv->usb2; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, size, + urb->transfer_buffer, urb->transfer_dma); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + netdev->trans_start = jiffies; +} + +static ssize_t show_firmware(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(d); + struct esd_usb2 *dev = usb_get_intfdata(intf); + + return sprintf(buf, "%d.%d.%d\n", + (dev->version >> 12) & 0xf, + (dev->version >> 8) & 0xf, + dev->version & 0xff); +} +static DEVICE_ATTR(firmware, S_IRUGO, show_firmware, NULL); + +static ssize_t show_hardware(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(d); + struct esd_usb2 *dev = usb_get_intfdata(intf); + + return sprintf(buf, "%d.%d.%d\n", + (dev->version >> 28) & 0xf, + (dev->version >> 24) & 0xf, + (dev->version >> 16) & 0xff); +} +static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL); + +static ssize_t show_nets(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(d); + struct esd_usb2 *dev = usb_get_intfdata(intf); + + return sprintf(buf, "%d", dev->net_count); +} +static DEVICE_ATTR(nets, S_IRUGO, show_nets, NULL); + +static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg) +{ + int actual_length; + + return usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, 2), + msg, + msg->msg.hdr.len << 2, + &actual_length, + 1000); +} + +static int esd_usb2_wait_msg(struct esd_usb2 *dev, + struct esd_usb2_msg *msg) +{ + int actual_length; + + return usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, 1), + msg, + sizeof(*msg), + &actual_length, + 1000); +} + +static int esd_usb2_setup_rx_urbs(struct esd_usb2 *dev) +{ + int i, err = 0; + + if (dev->rxinitdone) + return 0; + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_warn(dev->udev->dev.parent, + "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + dev_warn(dev->udev->dev.parent, + "No memory left for USB buffer\n"); + err = -ENOMEM; + goto freeurb; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, 1), + buf, RX_BUFFER_SIZE, + esd_usb2_read_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + } + +freeurb: + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + if (err) + break; + } + + /* Did we submit any URBs */ + if (i == 0) { + dev_err(dev->udev->dev.parent, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) { + dev_warn(dev->udev->dev.parent, + "rx performance may be slow\n"); + } + + dev->rxinitdone = 1; + return 0; +} + +/* + * Start interface + */ +static int esd_usb2_start(struct esd_usb2_net_priv *priv) +{ + struct esd_usb2 *dev = priv->usb2; + struct net_device *netdev = priv->netdev; + struct esd_usb2_msg msg; + int err, i; + + /* + * Enable all IDs + * The IDADD message takes up to 64 32 bit bitmasks (2048 bits). + * Each bit represents one 11 bit CAN identifier. A set bit + * enables reception of the corresponding CAN identifier. A cleared + * bit disabled this identifier. An additional bitmask value + * following the CAN 2.0A bits is used to enable reception of + * extended CAN frames. Only the LSB of this final mask is checked + * for the complete 29 bit ID range. The IDADD message also allows + * filter configuration for an ID subset. In this case you can add + * the number of the starting bitmask (0..64) to the filter.option + * field followed by only some bitmasks. + */ + msg.msg.hdr.cmd = CMD_IDADD; + msg.msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT; + msg.msg.filter.net = priv->index; + msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */ + for (i = 0; i < ESD_MAX_ID_SEGMENT; i++) + msg.msg.filter.mask[i] = cpu_to_le32(0xffffffff); + /* enable 29bit extended IDs */ + msg.msg.filter.mask[ESD_MAX_ID_SEGMENT] = cpu_to_le32(0x00000001); + + err = esd_usb2_send_msg(dev, &msg); + if (err) + goto failed; + + err = esd_usb2_setup_rx_urbs(dev); + if (err) + goto failed; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(netdev); + + netdev_err(netdev, "couldn't start device: %d\n", err); + + return err; +} + +static void unlink_all_urbs(struct esd_usb2 *dev) +{ + struct esd_usb2_net_priv *priv; + int i, j; + + usb_kill_anchored_urbs(&dev->rx_submitted); + for (i = 0; i < dev->net_count; i++) { + priv = dev->nets[i]; + if (priv) { + usb_kill_anchored_urbs(&priv->tx_submitted); + atomic_set(&priv->active_tx_jobs, 0); + + for (j = 0; j < MAX_TX_URBS; j++) + priv->tx_contexts[j].echo_index = MAX_TX_URBS; + } + } +} + +static int esd_usb2_open(struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = esd_usb2_start(priv); + if (err) { + netdev_warn(netdev, "couldn't start device: %d\n", err); + close_candev(netdev); + return err; + } + + priv->open_time = jiffies; + + netif_start_queue(netdev); + + return 0; +} + +static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + struct esd_usb2 *dev = priv->usb2; + struct esd_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct esd_usb2_msg *msg; + struct urb *urb; + u8 *buf; + int i, err; + int ret = NETDEV_TX_OK; + size_t size = sizeof(struct esd_usb2_msg); + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + stats->tx_dropped++; + dev_kfree_skb(skb); + goto nourbmem; + } + + buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + stats->tx_dropped++; + dev_kfree_skb(skb); + goto nobufmem; + } + + msg = (struct esd_usb2_msg *)buf; + + msg->msg.hdr.len = 3; /* minimal length */ + msg->msg.hdr.cmd = CMD_CAN_TX; + msg->msg.tx.net = priv->index; + msg->msg.tx.dlc = cf->can_dlc; + msg->msg.tx.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK); + + if (cf->can_id & CAN_RTR_FLAG) + msg->msg.tx.dlc |= ESD_RTR; + + if (cf->can_id & CAN_EFF_FLAG) + msg->msg.tx.id |= cpu_to_le32(ESD_EXTID); + + for (i = 0; i < cf->can_dlc; i++) + msg->msg.tx.data[i] = cf->data[i]; + + msg->msg.hdr.len += (cf->can_dlc + 3) >> 2; + + for (i = 0; i < MAX_TX_URBS; i++) { + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &priv->tx_contexts[i]; + break; + } + } + + /* + * This may never happen. + */ + if (!context) { + netdev_warn(netdev, "couldn't find free context\n"); + ret = NETDEV_TX_BUSY; + goto releasebuf; + } + + context->priv = priv; + context->echo_index = i; + context->dlc = cf->can_dlc; + + /* hnd must not be 0 - MSB is stripped in txdone handling */ + msg->msg.tx.hnd = 0x80000000 | i; /* returned in TX done message */ + + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, + msg->msg.hdr.len << 2, + esd_usb2_write_bulk_callback, context); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &priv->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&priv->active_tx_jobs); + + /* Slow down tx path */ + if (atomic_read(&priv->active_tx_jobs) >= MAX_TX_URBS) + netif_stop_queue(netdev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, context->echo_index); + + atomic_dec(&priv->active_tx_jobs); + usb_unanchor_urb(urb); + + stats->tx_dropped++; + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "failed tx_urb %d\n", err); + + goto releasebuf; + } + + netdev->trans_start = jiffies; + + /* + * Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +releasebuf: + usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + +nobufmem: + usb_free_urb(urb); + +nourbmem: + return ret; +} + +static int esd_usb2_close(struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + struct esd_usb2_msg msg; + int i; + + /* Disable all IDs (see esd_usb2_start()) */ + msg.msg.hdr.cmd = CMD_IDADD; + msg.msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT; + msg.msg.filter.net = priv->index; + msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */ + for (i = 0; i <= ESD_MAX_ID_SEGMENT; i++) + msg.msg.filter.mask[i] = 0; + if (esd_usb2_send_msg(priv->usb2, &msg) < 0) + netdev_err(netdev, "sending idadd message failed\n"); + + /* set CAN controller to reset mode */ + msg.msg.hdr.len = 2; + msg.msg.hdr.cmd = CMD_SETBAUD; + msg.msg.setbaud.net = priv->index; + msg.msg.setbaud.rsvd = 0; + msg.msg.setbaud.baud = cpu_to_le32(ESD_USB2_NO_BAUDRATE); + if (esd_usb2_send_msg(priv->usb2, &msg) < 0) + netdev_err(netdev, "sending setbaud message failed\n"); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + close_candev(netdev); + + priv->open_time = 0; + + return 0; +} + +static const struct net_device_ops esd_usb2_netdev_ops = { + .ndo_open = esd_usb2_open, + .ndo_stop = esd_usb2_close, + .ndo_start_xmit = esd_usb2_start_xmit, +}; + +static struct can_bittiming_const esd_usb2_bittiming_const = { + .name = "esd_usb2", + .tseg1_min = ESD_USB2_TSEG1_MIN, + .tseg1_max = ESD_USB2_TSEG1_MAX, + .tseg2_min = ESD_USB2_TSEG2_MIN, + .tseg2_max = ESD_USB2_TSEG2_MAX, + .sjw_max = ESD_USB2_SJW_MAX, + .brp_min = ESD_USB2_BRP_MIN, + .brp_max = ESD_USB2_BRP_MAX, + .brp_inc = ESD_USB2_BRP_INC, +}; + +static int esd_usb2_set_bittiming(struct net_device *netdev) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + struct can_bittiming *bt = &priv->can.bittiming; + struct esd_usb2_msg msg; + u32 canbtr; + + canbtr = ESD_USB2_UBR; + canbtr |= (bt->brp - 1) & (ESD_USB2_BRP_MAX - 1); + canbtr |= ((bt->sjw - 1) & (ESD_USB2_SJW_MAX - 1)) + << ESD_USB2_SJW_SHIFT; + canbtr |= ((bt->prop_seg + bt->phase_seg1 - 1) + & (ESD_USB2_TSEG1_MAX - 1)) + << ESD_USB2_TSEG1_SHIFT; + canbtr |= ((bt->phase_seg2 - 1) & (ESD_USB2_TSEG2_MAX - 1)) + << ESD_USB2_TSEG2_SHIFT; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + canbtr |= ESD_USB2_3_SAMPLES; + + msg.msg.hdr.len = 2; + msg.msg.hdr.cmd = CMD_SETBAUD; + msg.msg.setbaud.net = priv->index; + msg.msg.setbaud.rsvd = 0; + msg.msg.setbaud.baud = cpu_to_le32(canbtr); + + netdev_info(netdev, "setting BTR=%#x\n", canbtr); + + return esd_usb2_send_msg(priv->usb2, &msg); +} + +static int esd_usb2_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct esd_usb2_net_priv *priv = netdev_priv(netdev); + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + netif_wake_queue(netdev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int esd_usb2_probe_one_net(struct usb_interface *intf, int index) +{ + struct esd_usb2 *dev = usb_get_intfdata(intf); + struct net_device *netdev; + struct esd_usb2_net_priv *priv; + int err = 0; + int i; + + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "couldn't alloc candev\n"); + err = -ENOMEM; + goto done; + } + + priv = netdev_priv(netdev); + + init_usb_anchor(&priv->tx_submitted); + atomic_set(&priv->active_tx_jobs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; + + priv->usb2 = dev; + priv->netdev = netdev; + priv->index = index; + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = ESD_USB2_CAN_CLOCK; + priv->can.bittiming_const = &esd_usb2_bittiming_const; + priv->can.do_set_bittiming = esd_usb2_set_bittiming; + priv->can.do_set_mode = esd_usb2_set_mode; + priv->can.do_get_berr_counter = esd_usb2_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + netdev->netdev_ops = &esd_usb2_netdev_ops; + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + free_candev(netdev); + err = -ENOMEM; + goto done; + } + + dev->nets[index] = priv; + netdev_info(netdev, "device %s registered\n", netdev->name); + +done: + return err; +} + +/* + * probe function for new USB2 devices + * + * check version information and number of available + * CAN interfaces + */ +static int esd_usb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct esd_usb2 *dev; + struct esd_usb2_msg msg; + int i, err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + err = -ENOMEM; + goto done; + } + + dev->udev = interface_to_usbdev(intf); + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + + /* query number of CAN interfaces (nets) */ + msg.msg.hdr.cmd = CMD_VERSION; + msg.msg.hdr.len = 2; + msg.msg.version.rsvd = 0; + msg.msg.version.flags = 0; + msg.msg.version.drv_version = 0; + + err = esd_usb2_send_msg(dev, &msg); + if (err < 0) { + dev_err(&intf->dev, "sending version message failed\n"); + goto free_dev; + } + + err = esd_usb2_wait_msg(dev, &msg); + if (err < 0) { + dev_err(&intf->dev, "no version message answer\n"); + goto free_dev; + } + + dev->net_count = (int)msg.msg.version_reply.nets; + dev->version = le32_to_cpu(msg.msg.version_reply.version); + + if (device_create_file(&intf->dev, &dev_attr_firmware)) + dev_err(&intf->dev, + "Couldn't create device file for firmware\n"); + + if (device_create_file(&intf->dev, &dev_attr_hardware)) + dev_err(&intf->dev, + "Couldn't create device file for hardware\n"); + + if (device_create_file(&intf->dev, &dev_attr_nets)) + dev_err(&intf->dev, + "Couldn't create device file for nets\n"); + + /* do per device probing */ + for (i = 0; i < dev->net_count; i++) + esd_usb2_probe_one_net(intf, i); + + return 0; + +free_dev: + kfree(dev); +done: + return err; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void esd_usb2_disconnect(struct usb_interface *intf) +{ + struct esd_usb2 *dev = usb_get_intfdata(intf); + struct net_device *netdev; + int i; + + device_remove_file(&intf->dev, &dev_attr_firmware); + device_remove_file(&intf->dev, &dev_attr_hardware); + device_remove_file(&intf->dev, &dev_attr_nets); + + usb_set_intfdata(intf, NULL); + + if (dev) { + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) { + netdev = dev->nets[i]->netdev; + unregister_netdev(netdev); + free_candev(netdev); + } + } + unlink_all_urbs(dev); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver esd_usb2_driver = { + .name = "esd_usb2", + .probe = esd_usb2_probe, + .disconnect = esd_usb2_disconnect, + .id_table = esd_usb2_table, +}; + +module_usb_driver(esd_usb2_driver); diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile new file mode 100644 index 00000000..1aefbc88 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o +peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c new file mode 100644 index 00000000..86f26a1e --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -0,0 +1,899 @@ +/* + * CAN driver for PEAK System PCAN-USB adapter + * Derived from the PCAN project file driver/src/pcan_usb.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + * + * 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; version 2 of the License. + * + * 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. + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); + +/* PCAN-USB Endpoints */ +#define PCAN_USB_EP_CMDOUT 1 +#define PCAN_USB_EP_CMDIN (PCAN_USB_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USB_EP_MSGOUT 2 +#define PCAN_USB_EP_MSGIN (PCAN_USB_EP_MSGOUT | USB_DIR_IN) + +/* PCAN-USB command struct */ +#define PCAN_USB_CMD_FUNC 0 +#define PCAN_USB_CMD_NUM 1 +#define PCAN_USB_CMD_ARGS 2 +#define PCAN_USB_CMD_ARGS_LEN 14 +#define PCAN_USB_CMD_LEN (PCAN_USB_CMD_ARGS + \ + PCAN_USB_CMD_ARGS_LEN) + +/* PCAN-USB command timeout (ms.) */ +#define PCAN_USB_COMMAND_TIMEOUT 1000 + +/* PCAN-USB startup timeout (ms.) */ +#define PCAN_USB_STARTUP_TIMEOUT 10 + +/* PCAN-USB rx/tx buffers size */ +#define PCAN_USB_RX_BUFFER_SIZE 64 +#define PCAN_USB_TX_BUFFER_SIZE 64 + +#define PCAN_USB_MSG_HEADER_LEN 2 + +/* PCAN-USB adapter internal clock (MHz) */ +#define PCAN_USB_CRYSTAL_HZ 16000000 + +/* PCAN-USB USB message record status/len field */ +#define PCAN_USB_STATUSLEN_TIMESTAMP (1 << 7) +#define PCAN_USB_STATUSLEN_INTERNAL (1 << 6) +#define PCAN_USB_STATUSLEN_EXT_ID (1 << 5) +#define PCAN_USB_STATUSLEN_RTR (1 << 4) +#define PCAN_USB_STATUSLEN_DLC (0xf) + +/* PCAN-USB error flags */ +#define PCAN_USB_ERROR_TXFULL 0x01 +#define PCAN_USB_ERROR_RXQOVR 0x02 +#define PCAN_USB_ERROR_BUS_LIGHT 0x04 +#define PCAN_USB_ERROR_BUS_HEAVY 0x08 +#define PCAN_USB_ERROR_BUS_OFF 0x10 +#define PCAN_USB_ERROR_RXQEMPTY 0x20 +#define PCAN_USB_ERROR_QOVR 0x40 +#define PCAN_USB_ERROR_TXQFULL 0x80 + +/* SJA1000 modes */ +#define SJA1000_MODE_NORMAL 0x00 +#define SJA1000_MODE_INIT 0x01 + +/* + * tick duration = 42.666 us => + * (tick_number * 44739243) >> 20 ~ (tick_number * 42666) / 1000 + * accuracy = 10^-7 + */ +#define PCAN_USB_TS_DIV_SHIFTER 20 +#define PCAN_USB_TS_US_PER_TICK 44739243 + +/* PCAN-USB messages record types */ +#define PCAN_USB_REC_ERROR 1 +#define PCAN_USB_REC_ANALOG 2 +#define PCAN_USB_REC_BUSLOAD 3 +#define PCAN_USB_REC_TS 4 +#define PCAN_USB_REC_BUSEVT 5 + +/* private to PCAN-USB adapter */ +struct pcan_usb { + struct peak_usb_device dev; + struct peak_time_ref time_ref; + struct timer_list restart_timer; +}; + +/* incoming message context for decoding */ +struct pcan_usb_msg_context { + u16 ts16; + u8 prev_ts8; + u8 *ptr; + u8 *end; + u8 rec_cnt; + u8 rec_idx; + u8 rec_data_idx; + struct net_device *netdev; + struct pcan_usb *pdev; +}; + +/* + * send a command + */ +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + dev->cmd_buf[PCAN_USB_CMD_FUNC] = f; + dev->cmd_buf[PCAN_USB_CMD_NUM] = n; + + if (p) + memcpy(dev->cmd_buf + PCAN_USB_CMD_ARGS, + p, PCAN_USB_CMD_ARGS_LEN); + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "sending cmd f=0x%x n=0x%x failure: %d\n", + f, n, err); + return err; +} + +/* + * send a command then wait for its response + */ +static int pcan_usb_wait_rsp(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* first, send command */ + err = pcan_usb_send_cmd(dev, f, n, NULL); + if (err) + return err; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USB_EP_CMDIN), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "waiting rsp f=0x%x n=0x%x failure: %d\n", f, n, err); + else if (p) + memcpy(p, dev->cmd_buf + PCAN_USB_CMD_ARGS, + PCAN_USB_CMD_ARGS_LEN); + + return err; +} + +static int pcan_usb_set_sja1000(struct peak_usb_device *dev, u8 mode) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [1] = mode, + }; + + return pcan_usb_send_cmd(dev, 9, 2, args); +} + +static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 3, 2, args); +} + +static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 3, 3, args); +} + +static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 10, 2, args); +} + +/* + * set bittiming value to can + */ +static int pcan_usb_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(dev->netdev, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + args[0] = btr1; + args[1] = btr0; + + return pcan_usb_send_cmd(dev, 1, 2, args); +} + +/* + * init/reset can + */ +static int pcan_usb_write_mode(struct peak_usb_device *dev, u8 onoff) +{ + int err; + + err = pcan_usb_set_bus(dev, onoff); + if (err) + return err; + + if (!onoff) { + err = pcan_usb_set_sja1000(dev, SJA1000_MODE_INIT); + } else { + /* the PCAN-USB needs time to init */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + } + + return err; +} + +/* + * handle end of waiting for the device to reset + */ +static void pcan_usb_restart(unsigned long arg) +{ + /* notify candev and netdev */ + peak_usb_restart_complete((struct peak_usb_device *)arg); +} + +/* + * handle the submission of the restart urb + */ +static void pcan_usb_restart_pending(struct urb *urb) +{ + struct pcan_usb *pdev = urb->context; + + /* the PCAN-USB needs time to restart */ + mod_timer(&pdev->restart_timer, + jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + + /* can delete usb resources */ + peak_usb_async_complete(urb); +} + +/* + * handle asynchronous restart + */ +static int pcan_usb_restart_async(struct peak_usb_device *dev, struct urb *urb, + u8 *buf) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + if (timer_pending(&pdev->restart_timer)) + return -EBUSY; + + /* set bus on */ + buf[PCAN_USB_CMD_FUNC] = 3; + buf[PCAN_USB_CMD_NUM] = 2; + buf[PCAN_USB_CMD_ARGS] = 1; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + buf, PCAN_USB_CMD_LEN, + pcan_usb_restart_pending, pdev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * read serial number from device + */ +static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, 6, 1, args); + if (err) { + netdev_err(dev->netdev, "getting serial failure: %d\n", err); + } else if (serial_number) { + u32 tmp32; + + memcpy(&tmp32, args, 4); + *serial_number = le32_to_cpu(tmp32); + } + + return err; +} + +/* + * read device id from device + */ +static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, 4, 1, args); + if (err) + netdev_err(dev->netdev, "getting device id failure: %d\n", err); + else if (device_id) + *device_id = args[0]; + + return err; +} + +/* + * update current time ref with received timestamp + */ +static int pcan_usb_update_ts(struct pcan_usb_msg_context *mc) +{ + u16 tmp16; + + if ((mc->ptr+2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + + mc->ts16 = le16_to_cpu(tmp16); + + if (mc->rec_idx > 0) + peak_usb_update_ts_now(&mc->pdev->time_ref, mc->ts16); + else + peak_usb_set_ts_now(&mc->pdev->time_ref, mc->ts16); + + return 0; +} + +/* + * decode received timestamp + */ +static int pcan_usb_decode_ts(struct pcan_usb_msg_context *mc, u8 first_packet) +{ + /* only 1st packet supplies a word timestamp */ + if (first_packet) { + u16 tmp16; + + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + mc->ts16 = le16_to_cpu(tmp16); + mc->prev_ts8 = mc->ts16 & 0x00ff; + } else { + u8 ts8; + + if ((mc->ptr + 1) > mc->end) + return -EINVAL; + + ts8 = *mc->ptr++; + + if (ts8 < mc->prev_ts8) + mc->ts16 += 0x100; + + mc->ts16 &= 0xff00; + mc->ts16 |= ts8; + mc->prev_ts8 = ts8; + } + + return 0; +} + +static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, + u8 status_len) +{ + struct sk_buff *skb; + struct can_frame *cf; + struct timeval tv; + enum can_state new_state; + + /* ignore this error until 1st ts received */ + if (n == PCAN_USB_ERROR_QOVR) + if (!mc->pdev->time_ref.tick_count) + return 0; + + new_state = mc->pdev->dev.can.state; + + switch (mc->pdev->dev.can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + + case CAN_STATE_ERROR_WARNING: + if (n & PCAN_USB_ERROR_BUS_HEAVY) { + new_state = CAN_STATE_ERROR_PASSIVE; + break; + } + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + if ((n & PCAN_USB_ERROR_BUS_LIGHT) == 0) { + /* no error (back to active state) */ + mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + break; + + case CAN_STATE_ERROR_PASSIVE: + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + + if ((n & PCAN_USB_ERROR_BUS_HEAVY) == 0) { + /* no error (back to active state) */ + mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + break; + + default: + /* do nothing waiting for restart */ + return 0; + } + + /* donot post any error if current state didn't change */ + if (mc->pdev->dev.can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(mc->netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE | + CAN_ERR_CRTL_RX_PASSIVE; + mc->pdev->dev.can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING | + CAN_ERR_CRTL_RX_WARNING; + mc->pdev->dev.can.can_stats.error_warning++; + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + mc->netdev->stats.rx_over_errors++; + mc->netdev->stats.rx_errors++; + + new_state = mc->pdev->dev.can.state; + break; + } + + mc->pdev->dev.can.state = new_state; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); + skb->tstamp = timeval_to_ktime(tv); + } + + netif_rx(skb); + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + + return 0; +} + +/* + * decode non-data usb message + */ +static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc, + u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + u8 f, n; + int err; + + /* check whether function and number can be read */ + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + f = mc->ptr[PCAN_USB_CMD_FUNC]; + n = mc->ptr[PCAN_USB_CMD_NUM]; + mc->ptr += PCAN_USB_CMD_ARGS; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + int err = pcan_usb_decode_ts(mc, !mc->rec_idx); + + if (err) + return err; + } + + switch (f) { + case PCAN_USB_REC_ERROR: + err = pcan_usb_decode_error(mc, n, status_len); + if (err) + return err; + break; + + case PCAN_USB_REC_ANALOG: + /* analog values (ignored) */ + rec_len = 2; + break; + + case PCAN_USB_REC_BUSLOAD: + /* bus load (ignored) */ + rec_len = 1; + break; + + case PCAN_USB_REC_TS: + /* only timestamp */ + if (pcan_usb_update_ts(mc)) + return -EINVAL; + break; + + case PCAN_USB_REC_BUSEVT: + /* error frame/bus event */ + if (n & PCAN_USB_ERROR_TXQFULL) + netdev_dbg(mc->netdev, "device Tx queue full)\n"); + break; + default: + netdev_err(mc->netdev, "unexpected function %u\n", f); + break; + } + + if ((mc->ptr + rec_len) > mc->end) + return -EINVAL; + + mc->ptr += rec_len; + + return 0; +} + +/* + * decode data usb message + */ +static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + struct sk_buff *skb; + struct can_frame *cf; + struct timeval tv; + + skb = alloc_can_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + if (status_len & PCAN_USB_STATUSLEN_EXT_ID) { + u32 tmp32; + + if ((mc->ptr + 4) > mc->end) + goto decode_failed; + + memcpy(&tmp32, mc->ptr, 4); + mc->ptr += 4; + + cf->can_id = le32_to_cpu(tmp32 >> 3) | CAN_EFF_FLAG; + } else { + u16 tmp16; + + if ((mc->ptr + 2) > mc->end) + goto decode_failed; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + cf->can_id = le16_to_cpu(tmp16 >> 5); + } + + cf->can_dlc = get_can_dlc(rec_len); + + /* first data packet timestamp is a word */ + if (pcan_usb_decode_ts(mc, !mc->rec_data_idx)) + goto decode_failed; + + /* read data */ + memset(cf->data, 0x0, sizeof(cf->data)); + if (status_len & PCAN_USB_STATUSLEN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if ((mc->ptr + rec_len) > mc->end) + goto decode_failed; + + memcpy(cf->data, mc->ptr, rec_len); + mc->ptr += rec_len; + } + + /* convert timestamp into kernel time */ + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); + skb->tstamp = timeval_to_ktime(tv); + + /* push the skb */ + netif_rx(skb); + + /* update statistics */ + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + + return 0; + +decode_failed: + dev_kfree_skb(skb); + return -EINVAL; +} + +/* + * process incoming message + */ +static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf) +{ + struct pcan_usb_msg_context mc = { + .rec_cnt = ibuf[1], + .ptr = ibuf + PCAN_USB_MSG_HEADER_LEN, + .end = ibuf + lbuf, + .netdev = dev->netdev, + .pdev = container_of(dev, struct pcan_usb, dev), + }; + int err; + + for (err = 0; mc.rec_idx < mc.rec_cnt && !err; mc.rec_idx++) { + u8 sl = *mc.ptr++; + + /* handle status and error frames here */ + if (sl & PCAN_USB_STATUSLEN_INTERNAL) { + err = pcan_usb_decode_status(&mc, sl); + /* handle normal can frames here */ + } else { + err = pcan_usb_decode_data(&mc, sl); + mc.rec_data_idx++; + } + } + + return err; +} + +/* + * process any incoming buffer + */ +static int pcan_usb_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + int err = 0; + + if (urb->actual_length > PCAN_USB_MSG_HEADER_LEN) { + err = pcan_usb_decode_msg(dev, urb->transfer_buffer, + urb->actual_length); + + } else if (urb->actual_length > 0) { + netdev_err(dev->netdev, "usb message length error (%u)\n", + urb->actual_length); + err = -EINVAL; + } + + return err; +} + +/* + * process outgoing packet + */ +static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size) +{ + struct net_device *netdev = dev->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 *pc; + + obuf[0] = 2; + obuf[1] = 1; + + pc = obuf + PCAN_USB_MSG_HEADER_LEN; + + /* status/len byte */ + *pc = cf->can_dlc; + if (cf->can_id & CAN_RTR_FLAG) + *pc |= PCAN_USB_STATUSLEN_RTR; + + /* can id */ + if (cf->can_id & CAN_EFF_FLAG) { + __le32 tmp32 = cpu_to_le32((cf->can_id & CAN_ERR_MASK) << 3); + + *pc |= PCAN_USB_STATUSLEN_EXT_ID; + memcpy(++pc, &tmp32, 4); + pc += 4; + } else { + __le16 tmp16 = cpu_to_le16((cf->can_id & CAN_ERR_MASK) << 5); + + memcpy(++pc, &tmp16, 2); + pc += 2; + } + + /* can data */ + if (!(cf->can_id & CAN_RTR_FLAG)) { + memcpy(pc, cf->data, cf->can_dlc); + pc += cf->can_dlc; + } + + obuf[(*size)-1] = (u8)(stats->tx_packets & 0xff); + + return 0; +} + +/* + * start interface + */ +static int pcan_usb_start(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + /* number of bits used in timestamps read from adapter struct */ + peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb); + + /* if revision greater than 3, can put silent mode on/off */ + if (dev->device_rev > 3) { + int err; + + err = pcan_usb_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + } + + return pcan_usb_set_ext_vcc(dev, 0); +} + +static int pcan_usb_init(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + u32 serial_number; + int err; + + /* initialize a timer needed to wait for hardware restart */ + init_timer(&pdev->restart_timer); + pdev->restart_timer.function = pcan_usb_restart; + pdev->restart_timer.data = (unsigned long)dev; + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevice. + */ + err = pcan_usb_get_serial(dev, &serial_number); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s serial number (err %d)\n", + pcan_usb.name, err); + return err; + } + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s adapter hwrev %u serial %08X (%u channel)\n", + pcan_usb.name, dev->device_rev, serial_number, + pcan_usb.ctrl_count); + + return 0; +} + +/* + * probe function for new PCAN-USB usb interface + */ +static int pcan_usb_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + switch (ep->bEndpointAddress) { + case PCAN_USB_EP_CMDOUT: + case PCAN_USB_EP_CMDIN: + case PCAN_USB_EP_MSGOUT: + case PCAN_USB_EP_MSGIN: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB adapter + */ +struct peak_usb_adapter pcan_usb = { + .name = "PCAN-USB", + .device_id = PCAN_USB_PRODUCT_ID, + .ctrl_count = 1, + .clock = { + .freq = PCAN_USB_CRYSTAL_HZ / 2 , + }, + .bittiming_const = { + .name = "pcan_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb), + + /* timestamps usage */ + .ts_used_bits = 16, + .ts_period = 24575, /* calibration period in ts. */ + .us_per_ts_scale = PCAN_USB_TS_US_PER_TICK, /* us=(ts*scale) */ + .us_per_ts_shift = PCAN_USB_TS_DIV_SHIFTER, /* >> shift */ + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USB_EP_MSGIN, + .ep_msg_out = {PCAN_USB_EP_MSGOUT}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USB_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USB_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_probe, + .dev_init = pcan_usb_init, + .dev_set_bus = pcan_usb_write_mode, + .dev_set_bittiming = pcan_usb_set_bittiming, + .dev_get_device_id = pcan_usb_get_device_id, + .dev_decode_buf = pcan_usb_decode_buf, + .dev_encode_msg = pcan_usb_encode_msg, + .dev_start = pcan_usb_start, + .dev_restart_async = pcan_usb_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c new file mode 100644 index 00000000..d2f91f73 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -0,0 +1,951 @@ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + * + * 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; version 2 of the License. + * + * 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. + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters"); +MODULE_LICENSE("GPL v2"); + +/* Table of devices that work with this driver */ +static struct usb_device_id peak_usb_table[] = { + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, peak_usb_table); + +/* List of supported PCAN-USB adapters (NULL terminated list) */ +static struct peak_usb_adapter *peak_usb_adapters_list[] = { + &pcan_usb, + &pcan_usb_pro, + NULL, +}; + +/* + * dump memory + */ +#define DUMP_WIDTH 16 +void dump_mem(char *prompt, void *p, int l) +{ + pr_info("%s dumping %s (%d bytes):\n", + PCAN_USB_DRIVER_NAME, prompt ? prompt : "memory", l); + print_hex_dump(KERN_INFO, PCAN_USB_DRIVER_NAME " ", DUMP_PREFIX_NONE, + DUMP_WIDTH, 1, p, l, false); +} + +/* + * initialize a time_ref object with usb adapter own settings + */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + struct peak_usb_adapter *adapter) +{ + if (time_ref) { + memset(time_ref, 0, sizeof(struct peak_time_ref)); + time_ref->adapter = adapter; + } +} + +static void peak_usb_add_us(struct timeval *tv, u32 delta_us) +{ + /* number of s. to add to final time */ + u32 delta_s = delta_us / 1000000; + + delta_us -= delta_s * 1000000; + + tv->tv_usec += delta_us; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + delta_s++; + } + tv->tv_sec += delta_s; +} + +/* + * sometimes, another now may be more recent than current one... + */ +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + time_ref->ts_dev_2 = ts_now; + + /* should wait at least two passes before computing */ + if (time_ref->tv_host.tv_sec > 0) { + u32 delta_ts = time_ref->ts_dev_2 - time_ref->ts_dev_1; + + if (time_ref->ts_dev_2 < time_ref->ts_dev_1) + delta_ts &= (1 << time_ref->adapter->ts_used_bits) - 1; + + time_ref->ts_total += delta_ts; + } +} + +/* + * register device timestamp as now + */ +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + if (time_ref->tv_host_0.tv_sec == 0) { + /* use monotonic clock to correctly compute further deltas */ + time_ref->tv_host_0 = ktime_to_timeval(ktime_get()); + time_ref->tv_host.tv_sec = 0; + } else { + /* + * delta_us should not be >= 2^32 => delta_s should be < 4294 + * handle 32-bits wrapping here: if count of s. reaches 4200, + * reset counters and change time base + */ + if (time_ref->tv_host.tv_sec != 0) { + u32 delta_s = time_ref->tv_host.tv_sec + - time_ref->tv_host_0.tv_sec; + if (delta_s > 4200) { + time_ref->tv_host_0 = time_ref->tv_host; + time_ref->ts_total = 0; + } + } + + time_ref->tv_host = ktime_to_timeval(ktime_get()); + time_ref->tick_count++; + } + + time_ref->ts_dev_1 = time_ref->ts_dev_2; + peak_usb_update_ts_now(time_ref, ts_now); +} + +/* + * compute timeval according to current ts and time_ref data + */ +void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, + struct timeval *tv) +{ + /* protect from getting timeval before setting now */ + if (time_ref->tv_host.tv_sec > 0) { + u64 delta_us; + + delta_us = ts - time_ref->ts_dev_2; + if (ts < time_ref->ts_dev_2) + delta_us &= (1 << time_ref->adapter->ts_used_bits) - 1; + + delta_us += time_ref->ts_total; + + delta_us *= time_ref->adapter->us_per_ts_scale; + delta_us >>= time_ref->adapter->us_per_ts_shift; + + *tv = time_ref->tv_host_0; + peak_usb_add_us(tv, (u32)delta_us); + } else { + *tv = ktime_to_timeval(ktime_get()); + } +} + +/* + * callback for bulk Rx urb + */ +static void peak_usb_read_bulk_callback(struct urb *urb) +{ + struct peak_usb_device *dev = urb->context; + struct net_device *netdev; + int err; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + /* check reception status */ + switch (urb->status) { + case 0: + /* success */ + break; + + case -EILSEQ: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + + default: + if (net_ratelimit()) + netdev_err(netdev, + "Rx urb aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + /* protect from any incoming empty msgs */ + if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) { + /* handle these kinds of msgs only if _start callback called */ + if (dev->state & PCAN_USB_STATE_STARTED) { + err = dev->adapter->dev_decode_buf(dev, urb); + if (err) + dump_mem("received usb message", + urb->transfer_buffer, + urb->transfer_buffer_length); + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + urb->transfer_buffer, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + usb_anchor_urb(urb, &dev->rx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (!err) + return; + + usb_unanchor_urb(urb); + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_err(netdev, "failed resubmitting read bulk urb: %d\n", + err); +} + +/* + * callback for bulk Tx urb + */ +static void peak_usb_write_bulk_callback(struct urb *urb) +{ + struct peak_tx_urb_context *context = urb->context; + struct peak_usb_device *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + /* check tx status */ + switch (urb->status) { + case 0: + /* transmission complete */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + /* prevent tx timeout */ + netdev->trans_start = jiffies; + break; + + default: + if (net_ratelimit()) + netdev_err(netdev, "Tx urb aborted (%d)\n", + urb->status); + case -EPROTO: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + + break; + } + + /* should always release echo skb and corresponding context */ + can_get_echo_skb(netdev, context->echo_index); + context->echo_index = PCAN_USB_MAX_TX_URBS; + + /* do wakeup tx queue in case of success only */ + if (!urb->status) + netif_wake_queue(netdev); +} + +/* + * called by netdev to send one skb on the CAN interface. + */ +static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct peak_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct urb *urb; + u8 *obuf; + int i, err; + size_t size = dev->adapter->tx_buffer_size; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + if (dev->tx_contexts[i].echo_index == PCAN_USB_MAX_TX_URBS) { + context = dev->tx_contexts + i; + break; + } + + if (!context) { + /* should not occur except during restart */ + return NETDEV_TX_BUSY; + } + + urb = context->urb; + obuf = urb->transfer_buffer; + + err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size); + if (err) { + if (net_ratelimit()) + netdev_err(netdev, "packet dropped\n"); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; + } + + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + + /* this context is not used in fact */ + context->echo_index = PCAN_USB_MAX_TX_URBS; + + atomic_dec(&dev->active_tx_urbs); + + switch (err) { + case -ENODEV: + netif_device_detach(netdev); + break; + default: + netdev_warn(netdev, "tx urb submitting failed err=%d\n", + err); + case -ENOENT: + /* cable unplugged */ + stats->tx_dropped++; + } + } else { + netdev->trans_start = jiffies; + + /* slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= PCAN_USB_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + return NETDEV_TX_OK; +} + +/* + * start the CAN interface. + * Rx and Tx urbs are allocated here. Rx urbs are submitted here. + */ +static int peak_usb_start(struct peak_usb_device *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + for (i = 0; i < PCAN_USB_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + /* create a URB, and a buffer for it, to receive usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + buf, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + usb_unanchor_urb(urb); + kfree(buf); + usb_free_urb(urb); + break; + } + + /* drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* did we submit any URBs? Warn if we was not able to submit all urbs */ + if (i < PCAN_USB_MAX_RX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any rx URB\n"); + return err; + } + + netdev_warn(netdev, "rx performance may be slow\n"); + } + + /* pre-alloc tx buffers and corresponding urbs */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct peak_tx_urb_context *context; + struct urb *urb; + u8 *buf; + + /* create a URB and a buffer for it, to transmit usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + context = dev->tx_contexts + i; + context->dev = dev; + context->urb = urb; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->ep_msg_out), + buf, dev->adapter->tx_buffer_size, + peak_usb_write_bulk_callback, context); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + } + + /* warn if we were not able to allocate enough tx contexts */ + if (i < PCAN_USB_MAX_TX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any tx URB\n"); + return err; + } + + netdev_warn(netdev, "tx performance may be slow\n"); + } + + if (dev->adapter->dev_start) { + err = dev->adapter->dev_start(dev); + if (err) + goto failed; + } + + dev->state |= PCAN_USB_STATE_STARTED; + + /* can set bus on now */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 1); + if (err) + goto failed; + } + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + return err; +} + +/* + * called by netdev to open the corresponding CAN interface. + */ +static int peak_usb_ndo_open(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = peak_usb_start(dev); + if (err) { + netdev_err(netdev, "couldn't start device: %d\n", err); + close_candev(netdev); + return err; + } + + dev->open_time = jiffies; + netif_start_queue(netdev); + + return 0; +} + +/* + * unlink in-flight Rx and Tx urbs and free their memory. + */ +static void peak_usb_unlink_all_urbs(struct peak_usb_device *dev) +{ + int i; + + /* free all Rx (submitted) urbs */ + usb_kill_anchored_urbs(&dev->rx_submitted); + + /* free unsubmitted Tx urbs first */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct urb *urb = dev->tx_contexts[i].urb; + + if (!urb || + dev->tx_contexts[i].echo_index != PCAN_USB_MAX_TX_URBS) { + /* + * this urb is already released or always submitted, + * let usb core free by itself + */ + continue; + } + + usb_free_urb(urb); + dev->tx_contexts[i].urb = NULL; + } + + /* then free all submitted Tx urbs */ + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); +} + +/* + * called by netdev to close the corresponding CAN interface. + */ +static int peak_usb_ndo_stop(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + + dev->state &= ~PCAN_USB_STATE_STARTED; + netif_stop_queue(netdev); + + /* unlink all pending urbs and free used memory */ + peak_usb_unlink_all_urbs(dev); + + if (dev->adapter->dev_stop) + dev->adapter->dev_stop(dev); + + close_candev(netdev); + + dev->open_time = 0; + dev->can.state = CAN_STATE_STOPPED; + + /* can set bus off now */ + if (dev->adapter->dev_set_bus) { + int err = dev->adapter->dev_set_bus(dev, 0); + if (err) + return err; + } + + return 0; +} + +/* + * handle end of waiting for the device to reset + */ +void peak_usb_restart_complete(struct peak_usb_device *dev) +{ + /* finally MUST update can state */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + /* netdev queue can be awaken now */ + netif_wake_queue(dev->netdev); +} + +void peak_usb_async_complete(struct urb *urb) +{ + kfree(urb->transfer_buffer); + usb_free_urb(urb); +} + +/* + * device (auto-)restart mechanism runs in a timer context => + * MUST handle restart with asynchronous usb transfers + */ +static int peak_usb_restart(struct peak_usb_device *dev) +{ + struct urb *urb; + int err; + u8 *buf; + + /* + * if device doesn't define any asynchronous restart handler, simply + * wake the netdev queue up + */ + if (!dev->adapter->dev_restart_async) { + peak_usb_restart_complete(dev); + return 0; + } + + /* first allocate a urb to handle the asynchronous steps */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(dev->netdev, "no memory left for urb\n"); + return -ENOMEM; + } + + /* also allocate enough space for the commands to send */ + buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_ATOMIC); + if (!buf) { + netdev_err(dev->netdev, "no memory left for async cmd\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + /* call the device specific handler for the restart */ + err = dev->adapter->dev_restart_async(dev, urb, buf); + if (!err) + return 0; + + kfree(buf); + usb_free_urb(urb); + + return err; +} + +/* + * candev callback used to change CAN mode. + * Warning: this is called from a timer context! + */ +static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + int err = 0; + + if (!dev->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + err = peak_usb_restart(dev); + if (err) + netdev_err(netdev, "couldn't start device (err %d)\n", + err); + break; + + default: + return -EOPNOTSUPP; + } + + return err; +} + +/* + * candev callback used to set device bitrate. + */ +static int peak_usb_set_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + + if (dev->adapter->dev_set_bittiming) { + int err = dev->adapter->dev_set_bittiming(dev, bt); + + if (err) + netdev_info(netdev, "couldn't set bitrate (err %d)\n", + err); + return err; + } + + return 0; +} + +static const struct net_device_ops peak_usb_netdev_ops = { + .ndo_open = peak_usb_ndo_open, + .ndo_stop = peak_usb_ndo_stop, + .ndo_start_xmit = peak_usb_ndo_start_xmit, +}; + +/* + * create one device which is attached to CAN controller #ctrl_idx of the + * usb adapter. + */ +static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter, + struct usb_interface *intf, int ctrl_idx) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + int sizeof_candev = peak_usb_adapter->sizeof_dev_private; + struct peak_usb_device *dev; + struct net_device *netdev; + int i, err; + u16 tmp16; + + if (sizeof_candev < sizeof(struct peak_usb_device)) + sizeof_candev = sizeof(struct peak_usb_device); + + netdev = alloc_candev(sizeof_candev, PCAN_USB_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "%s: couldn't alloc candev\n", + PCAN_USB_DRIVER_NAME); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + /* allocate a buffer large enough to send commands */ + dev->cmd_buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL); + if (!dev->cmd_buf) { + dev_err(&intf->dev, "%s: couldn't alloc cmd buffer\n", + PCAN_USB_DRIVER_NAME); + err = -ENOMEM; + goto lbl_set_intf_data; + } + + dev->udev = usb_dev; + dev->netdev = netdev; + dev->adapter = peak_usb_adapter; + dev->ctrl_idx = ctrl_idx; + dev->state = PCAN_USB_STATE_CONNECTED; + + dev->ep_msg_in = peak_usb_adapter->ep_msg_in; + dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx]; + + dev->can.clock = peak_usb_adapter->clock; + dev->can.bittiming_const = &peak_usb_adapter->bittiming_const; + dev->can.do_set_bittiming = peak_usb_set_bittiming; + dev->can.do_set_mode = peak_usb_set_mode; + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LISTENONLY; + + netdev->netdev_ops = &peak_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = PCAN_USB_MAX_TX_URBS; + + dev->prev_siblings = usb_get_intfdata(intf); + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + goto lbl_free_cmd_buf; + } + + if (dev->prev_siblings) + (dev->prev_siblings)->next_siblings = dev; + + /* keep hw revision into the netdevice */ + tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice); + dev->device_rev = tmp16 >> 8; + + if (dev->adapter->dev_init) { + err = dev->adapter->dev_init(dev); + if (err) + goto lbl_free_cmd_buf; + } + + /* set bus off */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 0); + if (err) + goto lbl_free_cmd_buf; + } + + /* get device number early */ + if (dev->adapter->dev_get_device_id) + dev->adapter->dev_get_device_id(dev, &dev->device_number); + + netdev_info(netdev, "attached to %s channel %u (device %u)\n", + peak_usb_adapter->name, ctrl_idx, dev->device_number); + + return 0; + +lbl_free_cmd_buf: + kfree(dev->cmd_buf); + +lbl_set_intf_data: + usb_set_intfdata(intf, dev->prev_siblings); + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is unplugged from the system + */ +static void peak_usb_disconnect(struct usb_interface *intf) +{ + struct peak_usb_device *dev; + + /* unregister as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + char name[IFNAMSIZ]; + + dev->state &= ~PCAN_USB_STATE_CONNECTED; + strncpy(name, netdev->name, IFNAMSIZ); + + unregister_netdev(netdev); + free_candev(netdev); + + kfree(dev->cmd_buf); + dev->next_siblings = NULL; + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + + dev_info(&intf->dev, "%s removed\n", name); + } + + usb_set_intfdata(intf, NULL); +} + +/* + * probe function for new PEAK-System devices + */ +static int peak_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct peak_usb_adapter *peak_usb_adapter, **pp; + int i, err = -ENOMEM; + + usb_dev = interface_to_usbdev(intf); + + /* get corresponding PCAN-USB adapter */ + for (pp = peak_usb_adapters_list; *pp; pp++) + if ((*pp)->device_id == usb_dev->descriptor.idProduct) + break; + + peak_usb_adapter = *pp; + if (!peak_usb_adapter) { + /* should never come except device_id bad usage in this file */ + pr_err("%s: didn't find device id. 0x%x in devices list\n", + PCAN_USB_DRIVER_NAME, usb_dev->descriptor.idProduct); + return -ENODEV; + } + + /* got corresponding adapter: check if it handles current interface */ + if (peak_usb_adapter->intf_probe) { + err = peak_usb_adapter->intf_probe(intf); + if (err) + return err; + } + + for (i = 0; i < peak_usb_adapter->ctrl_count; i++) { + err = peak_usb_create_dev(peak_usb_adapter, intf, i); + if (err) { + /* deregister already created devices */ + peak_usb_disconnect(intf); + break; + } + } + + return err; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver peak_usb_driver = { + .name = PCAN_USB_DRIVER_NAME, + .disconnect = peak_usb_disconnect, + .probe = peak_usb_probe, + .id_table = peak_usb_table, +}; + +static int __init peak_usb_init(void) +{ + int err; + + /* register this driver with the USB subsystem */ + err = usb_register(&peak_usb_driver); + if (err) + pr_err("%s: usb_register failed (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + return err; +} + +static int peak_usb_do_device_exit(struct device *d, void *arg) +{ + struct usb_interface *intf = to_usb_interface(d); + struct peak_usb_device *dev; + + /* stop as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + + if (netif_device_present(netdev)) + if (dev->adapter->dev_exit) + dev->adapter->dev_exit(dev); + } + + return 0; +} + +static void __exit peak_usb_exit(void) +{ + int err; + + /* last chance do send any synchronous commands here */ + err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL, + NULL, peak_usb_do_device_exit); + if (err) + pr_err("%s: failed to stop all can devices (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + /* deregister this driver with the USB subsystem */ + usb_deregister(&peak_usb_driver); + + pr_info("%s: PCAN-USB interfaces driver unloaded\n", + PCAN_USB_DRIVER_NAME); +} + +module_init(peak_usb_init); +module_exit(peak_usb_exit); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h new file mode 100644 index 00000000..a948c5a8 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -0,0 +1,146 @@ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + * + * 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; version 2 of the License. + * + * 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. + */ +#ifndef PCAN_USB_CORE_H +#define PCAN_USB_CORE_H + +/* PEAK-System vendor id. */ +#define PCAN_USB_VENDOR_ID 0x0c72 + +/* supported device ids. */ +#define PCAN_USB_PRODUCT_ID 0x000c +#define PCAN_USBPRO_PRODUCT_ID 0x000d + +#define PCAN_USB_DRIVER_NAME "peak_usb" + +/* number of urbs that are submitted for rx/tx per channel */ +#define PCAN_USB_MAX_RX_URBS 4 +#define PCAN_USB_MAX_TX_URBS 10 + +/* usb adapters maximum channels per usb interface */ +#define PCAN_USB_MAX_CHANNEL 2 + +/* maximum length of the usb commands sent to/received from the devices */ +#define PCAN_USB_MAX_CMD_LEN 32 + +struct peak_usb_device; + +/* PEAK-System USB adapter descriptor */ +struct peak_usb_adapter { + char *name; + u32 device_id; + struct can_clock clock; + struct can_bittiming_const bittiming_const; + unsigned int ctrl_count; + + int (*intf_probe)(struct usb_interface *intf); + + int (*dev_init)(struct peak_usb_device *dev); + void (*dev_exit)(struct peak_usb_device *dev); + void (*dev_free)(struct peak_usb_device *dev); + int (*dev_open)(struct peak_usb_device *dev); + int (*dev_close)(struct peak_usb_device *dev); + int (*dev_set_bittiming)(struct peak_usb_device *dev, + struct can_bittiming *bt); + int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff); + int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id); + int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb); + int (*dev_encode_msg)(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size); + int (*dev_start)(struct peak_usb_device *dev); + int (*dev_stop)(struct peak_usb_device *dev); + int (*dev_restart_async)(struct peak_usb_device *dev, struct urb *urb, + u8 *buf); + u8 ep_msg_in; + u8 ep_msg_out[PCAN_USB_MAX_CHANNEL]; + u8 ts_used_bits; + u32 ts_period; + u8 us_per_ts_shift; + u32 us_per_ts_scale; + + int rx_buffer_size; + int tx_buffer_size; + int sizeof_dev_private; +}; + +extern struct peak_usb_adapter pcan_usb; +extern struct peak_usb_adapter pcan_usb_pro; + +struct peak_time_ref { + struct timeval tv_host_0, tv_host; + u32 ts_dev_1, ts_dev_2; + u64 ts_total; + u32 tick_count; + struct peak_usb_adapter *adapter; +}; + +struct peak_tx_urb_context { + struct peak_usb_device *dev; + u32 echo_index; + u8 dlc; + struct urb *urb; +}; + +#define PCAN_USB_STATE_CONNECTED 0x00000001 +#define PCAN_USB_STATE_STARTED 0x00000002 + +/* PEAK-System USB device */ +struct peak_usb_device { + struct can_priv can; + struct peak_usb_adapter *adapter; + unsigned int ctrl_idx; + int open_time; + u32 state; + + struct sk_buff *echo_skb[PCAN_USB_MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct peak_tx_urb_context tx_contexts[PCAN_USB_MAX_TX_URBS]; + + u8 *cmd_buf; + struct usb_anchor rx_submitted; + + u32 device_number; + u8 device_rev; + + u8 ep_msg_in; + u8 ep_msg_out; + + u16 bus_load; + + struct peak_usb_device *prev_siblings; + struct peak_usb_device *next_siblings; +}; + +void dump_mem(char *prompt, void *p, int l); + +/* common timestamp management */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + struct peak_usb_adapter *adapter); +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, + struct timeval *tv); + +void peak_usb_async_complete(struct urb *urb); +void peak_usb_restart_complete(struct peak_usb_device *dev); +#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c new file mode 100644 index 00000000..629c4ba5 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -0,0 +1,1038 @@ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro.c + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * 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; version 2 of the License. + * + * 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. + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro adapter"); + +/* PCAN-USB Pro Endpoints */ +#define PCAN_USBPRO_EP_CMDOUT 1 +#define PCAN_USBPRO_EP_CMDIN (PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_0 2 +#define PCAN_USBPRO_EP_MSGIN (PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_1 3 +#define PCAN_USBPRO_EP_UNUSED (PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN) + +#define PCAN_USBPRO_CHANNEL_COUNT 2 + +/* PCAN-USB Pro adapter internal clock (MHz) */ +#define PCAN_USBPRO_CRYSTAL_HZ 56000000 + +/* PCAN-USB Pro command timeout (ms.) */ +#define PCAN_USBPRO_COMMAND_TIMEOUT 1000 + +/* PCAN-USB Pro rx/tx buffers size */ +#define PCAN_USBPRO_RX_BUFFER_SIZE 1024 +#define PCAN_USBPRO_TX_BUFFER_SIZE 64 + +#define PCAN_USBPRO_MSG_HEADER_LEN 4 + +/* some commands responses need to be re-submitted */ +#define PCAN_USBPRO_RSP_SUBMIT_MAX 2 + +#define PCAN_USBPRO_RTR 0x01 +#define PCAN_USBPRO_EXT 0x02 + +#define PCAN_USBPRO_CMD_BUFFER_SIZE 512 + +/* handle device specific info used by the netdevices */ +struct pcan_usb_pro_interface { + struct peak_usb_device *dev[PCAN_USBPRO_CHANNEL_COUNT]; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_pro_device { + struct peak_usb_device dev; + struct pcan_usb_pro_interface *usb_if; + u32 cached_ccbt; +}; + +/* internal structure used to handle messages sent to bulk urb */ +struct pcan_usb_pro_msg { + u8 *rec_ptr; + int rec_buffer_size; + int rec_buffer_len; + union { + u16 *rec_cnt_rd; + u32 *rec_cnt; + u8 *rec_buffer; + } u; +}; + +/* records sizes table indexed on message id. (8-bits value) */ +static u16 pcan_usb_pro_sizeof_rec[256] = { + [PCAN_USBPRO_SETBTR] = sizeof(struct pcan_usb_pro_btr), + [PCAN_USBPRO_SETBUSACT] = sizeof(struct pcan_usb_pro_busact), + [PCAN_USBPRO_SETSILENT] = sizeof(struct pcan_usb_pro_silent), + [PCAN_USBPRO_SETFILTR] = sizeof(struct pcan_usb_pro_filter), + [PCAN_USBPRO_SETTS] = sizeof(struct pcan_usb_pro_setts), + [PCAN_USBPRO_GETDEVID] = sizeof(struct pcan_usb_pro_devid), + [PCAN_USBPRO_SETLED] = sizeof(struct pcan_usb_pro_setled), + [PCAN_USBPRO_RXMSG8] = sizeof(struct pcan_usb_pro_rxmsg), + [PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4, + [PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXRTR] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXSTATUS] = sizeof(struct pcan_usb_pro_rxstatus), + [PCAN_USBPRO_RXTS] = sizeof(struct pcan_usb_pro_rxts), + [PCAN_USBPRO_TXMSG8] = sizeof(struct pcan_usb_pro_txmsg), + [PCAN_USBPRO_TXMSG4] = sizeof(struct pcan_usb_pro_txmsg) - 4, + [PCAN_USBPRO_TXMSG0] = sizeof(struct pcan_usb_pro_txmsg) - 8, +}; + +/* + * initialize PCAN-USB Pro message data structure + */ +static u8 *pcan_msg_init(struct pcan_usb_pro_msg *pm, void *buffer_addr, + int buffer_size) +{ + if (buffer_size < PCAN_USBPRO_MSG_HEADER_LEN) + return NULL; + + pm->u.rec_buffer = (u8 *)buffer_addr; + pm->rec_buffer_size = pm->rec_buffer_len = buffer_size; + pm->rec_ptr = pm->u.rec_buffer + PCAN_USBPRO_MSG_HEADER_LEN; + + return pm->rec_ptr; +} + +static u8 *pcan_msg_init_empty(struct pcan_usb_pro_msg *pm, + void *buffer_addr, int buffer_size) +{ + u8 *pr = pcan_msg_init(pm, buffer_addr, buffer_size); + + if (pr) { + pm->rec_buffer_len = PCAN_USBPRO_MSG_HEADER_LEN; + *pm->u.rec_cnt = 0; + } + return pr; +} + +/* + * add one record to a message being built + */ +static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...) +{ + int len, i; + u8 *pc; + va_list ap; + + va_start(ap, id); + + pc = pm->rec_ptr + 1; + + i = 0; + switch (id) { + case PCAN_USBPRO_TXMSG8: + i += 4; + case PCAN_USBPRO_TXMSG4: + i += 4; + case PCAN_USBPRO_TXMSG0: + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + memcpy(pc, va_arg(ap, int *), i); + pc += i; + break; + + case PCAN_USBPRO_SETBTR: + case PCAN_USBPRO_GETDEVID: + *pc++ = va_arg(ap, int); + pc += 2; + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETFILTR: + case PCAN_USBPRO_SETBUSACT: + case PCAN_USBPRO_SETSILENT: + *pc++ = va_arg(ap, int); + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + case PCAN_USBPRO_SETLED: + *pc++ = va_arg(ap, int); + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETTS: + pc++; + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + default: + pr_err("%s: %s(): unknown data type %02Xh (%d)\n", + PCAN_USB_DRIVER_NAME, __func__, id, id); + pc--; + break; + } + + len = pc - pm->rec_ptr; + if (len > 0) { + *pm->u.rec_cnt = cpu_to_le32(*pm->u.rec_cnt+1); + *pm->rec_ptr = id; + + pm->rec_ptr = pc; + pm->rec_buffer_len += len; + } + + va_end(ap); + + return len; +} + +/* + * send PCAN-USB Pro command synchronously + */ +static int pcan_usb_pro_send_cmd(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + int actual_length; + int err; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, "sending command failure: %d\n", err); + + return err; +} + +/* + * wait for PCAN-USB Pro command response + */ +static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + u8 req_data_type, req_channel; + int actual_length; + int i, err = 0; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + req_data_type = pum->u.rec_buffer[4]; + req_channel = pum->u.rec_buffer[5]; + + *pum->u.rec_cnt = 0; + for (i = 0; !err && i < PCAN_USBPRO_RSP_SUBMIT_MAX; i++) { + struct pcan_usb_pro_msg rsp; + union pcan_usb_pro_rec *pr; + u32 r, rec_cnt; + u16 rec_len; + u8 *pc; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDIN), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) { + netdev_err(dev->netdev, "waiting rsp error %d\n", err); + break; + } + + if (actual_length == 0) + continue; + + err = -EBADMSG; + if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) { + netdev_err(dev->netdev, + "got abnormal too small rsp (len=%d)\n", + actual_length); + break; + } + + pc = pcan_msg_init(&rsp, pum->u.rec_buffer, + actual_length); + + rec_cnt = le32_to_cpu(*rsp.u.rec_cnt); + + /* loop on records stored into message */ + for (r = 0; r < rec_cnt; r++) { + pr = (union pcan_usb_pro_rec *)pc; + rec_len = pcan_usb_pro_sizeof_rec[pr->data_type]; + if (!rec_len) { + netdev_err(dev->netdev, + "got unprocessed record in msg\n"); + dump_mem("rcvd rsp msg", pum->u.rec_buffer, + actual_length); + break; + } + + /* check if response corresponds to request */ + if (pr->data_type != req_data_type) + netdev_err(dev->netdev, + "got unwanted rsp %xh: ignored\n", + pr->data_type); + + /* check if channel in response corresponds too */ + else if ((req_channel != 0xff) && \ + (pr->bus_act.channel != req_channel)) + netdev_err(dev->netdev, + "got rsp %xh but on chan%u: ignored\n", + req_data_type, pr->bus_act.channel); + + /* got the response */ + else + return 0; + + /* otherwise, go on with next record in message */ + pc += rec_len; + } + } + + return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err; +} + +static int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size) +{ + int err; + u8 req_type; + unsigned int p; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + memset(req_addr, '\0', req_size); + + req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER; + + switch (req_id) { + case PCAN_USBPRO_REQ_FCT: + p = usb_sndctrlpipe(dev->udev, 0); + break; + + default: + p = usb_rcvctrlpipe(dev->udev, 0); + req_type |= USB_DIR_IN; + break; + } + + err = usb_control_msg(dev->udev, p, req_id, req_type, req_value, 0, + req_addr, req_size, 2 * USB_CTRL_GET_TIMEOUT); + if (err < 0) { + netdev_info(dev->netdev, + "unable to request usb[type=%d value=%d] err=%d\n", + req_id, req_value, err); + return err; + } + + return 0; +} + +static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETTS, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bitrate(struct peak_usb_device *dev, u32 ccbt) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt); + + /* cache the CCBT value to reuse it before next buson */ + pdev->cached_ccbt = ccbt; + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + /* if bus=on, be sure the bitrate being set before! */ + if (onoff) { + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + pcan_usb_pro_set_bitrate(dev, pdev->cached_ccbt); + } + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETSILENT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_filter(struct peak_usb_device *dev, u16 filter_mode) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETFILTR, dev->ctrl_idx, filter_mode); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_led(struct peak_usb_device *dev, u8 mode, + u32 timeout) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETLED, dev->ctrl_idx, mode, timeout); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev, + u32 *device_id) +{ + struct pcan_usb_pro_devid *pdn; + struct pcan_usb_pro_msg um; + int err; + u8 *pc; + + pc = pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_GETDEVID, dev->ctrl_idx); + + err = pcan_usb_pro_send_cmd(dev, &um); + if (err) + return err; + + err = pcan_usb_pro_wait_rsp(dev, &um); + if (err) + return err; + + pdn = (struct pcan_usb_pro_devid *)pc; + if (device_id) + *device_id = le32_to_cpu(pdn->serial_num); + + return err; +} + +static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u32 ccbt; + + ccbt = (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 0x00800000 : 0; + ccbt |= (bt->sjw - 1) << 24; + ccbt |= (bt->phase_seg2 - 1) << 20; + ccbt |= (bt->prop_seg + bt->phase_seg1 - 1) << 16; /* = tseg1 */ + ccbt |= bt->brp - 1; + + netdev_info(dev->netdev, "setting ccbt=0x%08x\n", ccbt); + + return pcan_usb_pro_set_bitrate(dev, ccbt); +} + +static void pcan_usb_pro_restart_complete(struct urb *urb) +{ + /* can delete usb resources */ + peak_usb_async_complete(urb); + + /* notify candev and netdev */ + peak_usb_restart_complete(urb->context); +} + +/* + * handle restart but in asynchronously way + */ +static int pcan_usb_pro_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, 1); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, PCAN_USB_MAX_CMD_LEN, + pcan_usb_pro_restart_complete, dev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static void pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded) +{ + u8 buffer[16]; + + buffer[0] = 0; + buffer[1] = !!loaded; + + pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, buffer, sizeof(buffer)); +} + +static inline +struct pcan_usb_pro_interface *pcan_usb_pro_dev_if(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + return pdev->usb_if; +} + +static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxmsg *rx) +{ + const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + struct sk_buff *skb; + struct timeval tv; + + skb = alloc_can_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + can_frame->can_id = le32_to_cpu(rx->id); + can_frame->can_dlc = rx->len & 0x0f; + + if (rx->flags & PCAN_USBPRO_EXT) + can_frame->can_id |= CAN_EFF_FLAG; + + if (rx->flags & PCAN_USBPRO_RTR) + can_frame->can_id |= CAN_RTR_FLAG; + else + memcpy(can_frame->data, rx->data, can_frame->can_dlc); + + peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(rx->ts32), &tv); + skb->tstamp = timeval_to_ktime(tv); + + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + + return 0; +} + +static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxstatus *er) +{ + const u32 raw_status = le32_to_cpu(er->status); + const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + u8 err_mask = 0; + struct sk_buff *skb; + struct timeval tv; + + /* nothing should be sent while in BUS_OFF state */ + if (dev->can.state == CAN_STATE_BUS_OFF) + return 0; + + if (!raw_status) { + /* no error bit (back to active state) */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + + if (raw_status & (PCAN_USBPRO_STATUS_OVERRUN | + PCAN_USBPRO_STATUS_QOVERRUN)) { + /* trick to bypass next comparison and process other errors */ + new_state = CAN_STATE_MAX; + } + + if (raw_status & PCAN_USBPRO_STATUS_BUS) { + new_state = CAN_STATE_BUS_OFF; + } else if (raw_status & PCAN_USBPRO_STATUS_ERROR) { + u32 rx_err_cnt = (le32_to_cpu(er->err_frm) & 0x00ff0000) >> 16; + u32 tx_err_cnt = (le32_to_cpu(er->err_frm) & 0xff000000) >> 24; + + if (rx_err_cnt > 127) + err_mask |= CAN_ERR_CRTL_RX_PASSIVE; + else if (rx_err_cnt > 96) + err_mask |= CAN_ERR_CRTL_RX_WARNING; + + if (tx_err_cnt > 127) + err_mask |= CAN_ERR_CRTL_TX_PASSIVE; + else if (tx_err_cnt > 96) + err_mask |= CAN_ERR_CRTL_TX_WARNING; + + if (err_mask & (CAN_ERR_CRTL_RX_WARNING | + CAN_ERR_CRTL_TX_WARNING)) + new_state = CAN_STATE_ERROR_WARNING; + else if (err_mask & (CAN_ERR_CRTL_RX_PASSIVE | + CAN_ERR_CRTL_TX_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; + } + + /* donot post any error if current state didn't change */ + if (dev->can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + can_frame->can_id |= CAN_ERR_BUSOFF; + can_bus_off(netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_warning++; + break; + + case CAN_STATE_ERROR_ACTIVE: + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + if (raw_status & PCAN_USBPRO_STATUS_OVERRUN) { + can_frame->can_id |= CAN_ERR_PROT; + can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + if (raw_status & PCAN_USBPRO_STATUS_QOVERRUN) { + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + new_state = CAN_STATE_ERROR_ACTIVE; + break; + } + + dev->can.state = new_state; + + peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(er->ts32), &tv); + skb->tstamp = timeval_to_ktime(tv); + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + + return 0; +} + +static void pcan_usb_pro_handle_ts(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxts *ts) +{ + /* should wait until clock is stabilized */ + if (usb_if->cm_ignore_count > 0) + usb_if->cm_ignore_count--; + else + peak_usb_set_ts_now(&usb_if->time_ref, + le32_to_cpu(ts->ts64[1])); +} + +/* + * callback for bulk IN urb + */ +static int pcan_usb_pro_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_pro_interface *usb_if = pcan_usb_pro_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pcan_usb_pro_msg usb_msg; + u8 *rec_ptr, *msg_end; + u16 rec_cnt; + int err = 0; + + rec_ptr = pcan_msg_init(&usb_msg, urb->transfer_buffer, + urb->actual_length); + if (!rec_ptr) { + netdev_err(netdev, "bad msg hdr len %d\n", urb->actual_length); + return -EINVAL; + } + + /* loop reading all the records from the incoming message */ + msg_end = urb->transfer_buffer + urb->actual_length; + rec_cnt = le16_to_cpu(*usb_msg.u.rec_cnt_rd); + for (; rec_cnt > 0; rec_cnt--) { + union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr; + u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type]; + + if (!sizeof_rec) { + netdev_err(netdev, + "got unsupported rec in usb msg:\n"); + err = -ENOTSUPP; + break; + } + + /* check if the record goes out of current packet */ + if (rec_ptr + sizeof_rec > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf size\n"); + err = -EBADMSG; + break; + } + + switch (pr->data_type) { + case PCAN_USBPRO_RXMSG8: + case PCAN_USBPRO_RXMSG4: + case PCAN_USBPRO_RXMSG0: + case PCAN_USBPRO_RXRTR: + err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXSTATUS: + err = pcan_usb_pro_handle_error(usb_if, &pr->rx_status); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXTS: + pcan_usb_pro_handle_ts(usb_if, &pr->rx_ts); + break; + + default: + netdev_err(netdev, + "unhandled rec type 0x%02x (%d): ignored\n", + pr->data_type, pr->data_type); + break; + } + + rec_ptr += sizeof_rec; + } + +fail: + if (err) + dump_mem("received msg", + urb->transfer_buffer, urb->actual_length); + + return err; +} + +static int pcan_usb_pro_encode_msg(struct peak_usb_device *dev, + struct sk_buff *skb, u8 *obuf, size_t *size) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + u8 data_type, len, flags; + struct pcan_usb_pro_msg usb_msg; + + pcan_msg_init_empty(&usb_msg, obuf, *size); + + if ((cf->can_id & CAN_RTR_FLAG) || (cf->can_dlc == 0)) + data_type = PCAN_USBPRO_TXMSG0; + else if (cf->can_dlc <= 4) + data_type = PCAN_USBPRO_TXMSG4; + else + data_type = PCAN_USBPRO_TXMSG8; + + len = (dev->ctrl_idx << 4) | (cf->can_dlc & 0x0f); + + flags = 0; + if (cf->can_id & CAN_EFF_FLAG) + flags |= 0x02; + if (cf->can_id & CAN_RTR_FLAG) + flags |= 0x01; + + pcan_msg_add_rec(&usb_msg, data_type, 0, flags, len, cf->can_id, + cf->data); + + *size = usb_msg.rec_buffer_len; + + return 0; +} + +static int pcan_usb_pro_start(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + int err; + + err = pcan_usb_pro_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + + /* filter mode: 0-> All OFF; 1->bypass */ + err = pcan_usb_pro_set_filter(dev, 1); + if (err) + return err; + + /* opening first device: */ + if (pdev->usb_if->dev_opened_count == 0) { + /* reset time_ref */ + peak_usb_init_time_ref(&pdev->usb_if->time_ref, &pcan_usb_pro); + + /* ask device to send ts messages */ + err = pcan_usb_pro_set_ts(dev, 1); + } + + pdev->usb_if->dev_opened_count++; + + return err; +} + +/* + * stop interface + * (last chance before set bus off) + */ +static int pcan_usb_pro_stop(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* turn off ts msgs for that interface if no other dev opened */ + if (pdev->usb_if->dev_opened_count == 1) + pcan_usb_pro_set_ts(dev, 0); + + pdev->usb_if->dev_opened_count--; + + return 0; +} + +/* + * called when probing to initialize a device object. + */ +static int pcan_usb_pro_init(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_interface *usb_if; + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + struct pcan_usb_pro_fwinfo fi; + struct pcan_usb_pro_blinfo bi; + int err; + + /* allocate netdevices common structure attached to first one */ + usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface), + GFP_KERNEL); + if (!usb_if) + return -ENOMEM; + + /* number of ts msgs to ignore before taking one into account */ + usb_if->cm_ignore_count = 5; + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevices. + */ + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_FW, + &fi, sizeof(fi)); + if (err) { + kfree(usb_if); + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + pcan_usb_pro.name, err); + return err; + } + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_BL, + &bi, sizeof(bi)); + if (err) { + kfree(usb_if); + dev_err(dev->netdev->dev.parent, + "unable to read %s bootloader info (err %d)\n", + pcan_usb_pro.name, err); + return err; + } + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s hwrev %u serial %08X.%08X (%u channels)\n", + pcan_usb_pro.name, + bi.hw_rev, bi.serial_num_hi, bi.serial_num_lo, + pcan_usb_pro.ctrl_count); + + /* tell the device the can driver is running */ + pcan_usb_pro_drv_loaded(dev, 1); + } else { + usb_if = pcan_usb_pro_dev_if(dev->prev_siblings); + } + + pdev->usb_if = usb_if; + usb_if->dev[dev->ctrl_idx] = dev; + + /* set LED in default state (end of init phase) */ + pcan_usb_pro_set_led(dev, 0, 1); + + return 0; +} + +static void pcan_usb_pro_exit(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* + * when rmmod called before unplug and if down, should reset things + * before leaving + */ + if (dev->can.state != CAN_STATE_STOPPED) { + /* set bus off on the corresponding channel */ + pcan_usb_pro_set_bus(dev, 0); + } + + /* if channel #0 (only) */ + if (dev->ctrl_idx == 0) { + /* turn off calibration message if any device were opened */ + if (pdev->usb_if->dev_opened_count > 0) + pcan_usb_pro_set_ts(dev, 0); + + /* tell the PCAN-USB Pro device the driver is being unloaded */ + pcan_usb_pro_drv_loaded(dev, 0); + } +} + +/* + * called when PCAN-USB Pro adapter is unplugged + */ +static void pcan_usb_pro_free(struct peak_usb_device *dev) +{ + /* last device: can free pcan_usb_pro_interface object now */ + if (!dev->prev_siblings && !dev->next_siblings) + kfree(pcan_usb_pro_dev_if(dev)); +} + +/* + * probe function for new PCAN-USB Pro usb interface + */ +static int pcan_usb_pro_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + /* + * below is the list of valid ep addreses. Any other ep address + * is considered as not-CAN interface address => no dev created + */ + switch (ep->bEndpointAddress) { + case PCAN_USBPRO_EP_CMDOUT: + case PCAN_USBPRO_EP_CMDIN: + case PCAN_USBPRO_EP_MSGOUT_0: + case PCAN_USBPRO_EP_MSGOUT_1: + case PCAN_USBPRO_EP_MSGIN: + case PCAN_USBPRO_EP_UNUSED: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB Pro adapter + */ +struct peak_usb_adapter pcan_usb_pro = { + .name = "PCAN-USB Pro", + .device_id = PCAN_USBPRO_PRODUCT_ID, + .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT, + .clock = { + .freq = PCAN_USBPRO_CRYSTAL_HZ, + }, + .bittiming_const = { + .name = "pcan_usb_pro", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_pro_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USBPRO_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USBPRO_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, + .dev_init = pcan_usb_pro_init, + .dev_exit = pcan_usb_pro_exit, + .dev_free = pcan_usb_pro_free, + .dev_set_bus = pcan_usb_pro_set_bus, + .dev_set_bittiming = pcan_usb_pro_set_bittiming, + .dev_get_device_id = pcan_usb_pro_get_device_id, + .dev_decode_buf = pcan_usb_pro_decode_buf, + .dev_encode_msg = pcan_usb_pro_encode_msg, + .dev_start = pcan_usb_pro_start, + .dev_stop = pcan_usb_pro_stop, + .dev_restart_async = pcan_usb_pro_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h new file mode 100644 index 00000000..a869918c --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -0,0 +1,178 @@ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * 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; version 2 of the License. + * + * 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. + */ +#ifndef PCAN_USB_PRO_H +#define PCAN_USB_PRO_H + +/* + * USB Vendor request data types + */ +#define PCAN_USBPRO_REQ_INFO 0 +#define PCAN_USBPRO_REQ_FCT 2 + +/* Vendor Request value for XXX_INFO */ +#define PCAN_USBPRO_INFO_BL 0 +#define PCAN_USBPRO_INFO_FW 1 + +/* Vendor Request value for XXX_FCT */ +#define PCAN_USBPRO_FCT_DRVLD 5 /* tell device driver is loaded */ + +/* PCAN_USBPRO_INFO_BL vendor request record type */ +struct __packed pcan_usb_pro_blinfo { + u32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + u32 serial_num_hi; + u32 serial_num_lo; + u32 hw_type; + u32 hw_rev; +}; + +/* PCAN_USBPRO_INFO_FW vendor request record type */ +struct __packed pcan_usb_pro_fwinfo { + u32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + u32 fw_type; +}; + +/* + * USB Command record types + */ +#define PCAN_USBPRO_SETBTR 0x02 +#define PCAN_USBPRO_SETBUSACT 0x04 +#define PCAN_USBPRO_SETSILENT 0x05 +#define PCAN_USBPRO_SETFILTR 0x0a +#define PCAN_USBPRO_SETTS 0x10 +#define PCAN_USBPRO_GETDEVID 0x12 +#define PCAN_USBPRO_SETLED 0x1C +#define PCAN_USBPRO_RXMSG8 0x80 +#define PCAN_USBPRO_RXMSG4 0x81 +#define PCAN_USBPRO_RXMSG0 0x82 +#define PCAN_USBPRO_RXRTR 0x83 +#define PCAN_USBPRO_RXSTATUS 0x84 +#define PCAN_USBPRO_RXTS 0x85 +#define PCAN_USBPRO_TXMSG8 0x41 +#define PCAN_USBPRO_TXMSG4 0x42 +#define PCAN_USBPRO_TXMSG0 0x43 + +/* record structures */ +struct __packed pcan_usb_pro_btr { + u8 data_type; + u8 channel; + u16 dummy; + u32 CCBT; +}; + +struct __packed pcan_usb_pro_busact { + u8 data_type; + u8 channel; + u16 onoff; +}; + +struct __packed pcan_usb_pro_silent { + u8 data_type; + u8 channel; + u16 onoff; +}; + +struct __packed pcan_usb_pro_filter { + u8 data_type; + u8 dummy; + u16 filter_mode; +}; + +struct __packed pcan_usb_pro_setts { + u8 data_type; + u8 dummy; + u16 mode; +}; + +struct __packed pcan_usb_pro_devid { + u8 data_type; + u8 channel; + u16 dummy; + u32 serial_num; +}; + +struct __packed pcan_usb_pro_setled { + u8 data_type; + u8 channel; + u16 mode; + u32 timeout; +}; + +struct __packed pcan_usb_pro_rxmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + u32 ts32; + u32 id; + + u8 data[8]; +}; + +#define PCAN_USBPRO_STATUS_ERROR 0x0001 +#define PCAN_USBPRO_STATUS_BUS 0x0002 +#define PCAN_USBPRO_STATUS_OVERRUN 0x0004 +#define PCAN_USBPRO_STATUS_QOVERRUN 0x0008 + +struct __packed pcan_usb_pro_rxstatus { + u8 data_type; + u8 channel; + u16 status; + u32 ts32; + u32 err_frm; +}; + +struct __packed pcan_usb_pro_rxts { + u8 data_type; + u8 dummy[3]; + u32 ts64[2]; +}; + +struct __packed pcan_usb_pro_txmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + u32 id; + u8 data[8]; +}; + +union pcan_usb_pro_rec { + u8 data_type; + struct pcan_usb_pro_btr btr; + struct pcan_usb_pro_busact bus_act; + struct pcan_usb_pro_silent silent_mode; + struct pcan_usb_pro_filter filter_mode; + struct pcan_usb_pro_setts ts; + struct pcan_usb_pro_devid dev_id; + struct pcan_usb_pro_setled set_led; + struct pcan_usb_pro_rxmsg rx_msg; + struct pcan_usb_pro_rxstatus rx_status; + struct pcan_usb_pro_rxts rx_ts; + struct pcan_usb_pro_txmsg tx_msg; +}; + +#endif diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c new file mode 100644 index 00000000..ea2d9428 --- /dev/null +++ b/drivers/net/can/vcan.c @@ -0,0 +1,178 @@ +/* + * vcan.c - Virtual CAN interface + * + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/slab.h> +#include <net/rtnetlink.h> + +static __initdata const char banner[] = + KERN_INFO "vcan: Virtual CAN interface driver\n"; + +MODULE_DESCRIPTION("virtual CAN interface"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>"); + + +/* + * CAN test feature: + * Enable the echo on driver level for testing the CAN core echo modes. + * See Documentation/networking/can.txt for details. + */ + +static bool echo; /* echo testing. Default: 0 (Off) */ +module_param(echo, bool, S_IRUGO); +MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)"); + + +static void vcan_rx(struct sk_buff *skb, struct net_device *dev) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + struct net_device_stats *stats = &dev->stats; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->dev = dev; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx_ni(skb); +} + +static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + struct net_device_stats *stats = &dev->stats; + int loop; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + stats->tx_packets++; + stats->tx_bytes += cf->can_dlc; + + /* set flag whether this packet has to be looped back */ + loop = skb->pkt_type == PACKET_LOOPBACK; + + if (!echo) { + /* no echo handling available inside this driver */ + + if (loop) { + /* + * only count the packets here, because the + * CAN core already did the echo for us + */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } + kfree_skb(skb); + return NETDEV_TX_OK; + } + + /* perform standard echo handling for CAN network interfaces */ + + if (loop) { + struct sock *srcsk = skb->sk; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NETDEV_TX_OK; + + /* receive with packet counting */ + skb->sk = srcsk; + vcan_rx(skb, dev); + } else { + /* no looped packets => no counting */ + kfree_skb(skb); + } + return NETDEV_TX_OK; +} + +static const struct net_device_ops vcan_netdev_ops = { + .ndo_start_xmit = vcan_tx, +}; + +static void vcan_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = sizeof(struct can_frame); + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 0; + dev->flags = IFF_NOARP; + + /* set flags according to driver capabilities */ + if (echo) + dev->flags |= IFF_ECHO; + + dev->netdev_ops = &vcan_netdev_ops; + dev->destructor = free_netdev; +} + +static struct rtnl_link_ops vcan_link_ops __read_mostly = { + .kind = "vcan", + .setup = vcan_setup, +}; + +static __init int vcan_init_module(void) +{ + printk(banner); + + if (echo) + printk(KERN_INFO "vcan: enabled echo on driver level.\n"); + + return rtnl_link_register(&vcan_link_ops); +} + +static __exit void vcan_cleanup_module(void) +{ + rtnl_link_unregister(&vcan_link_ops); +} + +module_init(vcan_init_module); +module_exit(vcan_cleanup_module); |