diff options
Diffstat (limited to 'drivers/net/can')
92 files changed, 54022 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig new file mode 100644 index 000000000..7cdd0cead --- /dev/null +++ b/drivers/net/can/Kconfig @@ -0,0 +1,177 @@ +menu "CAN Device Drivers" + +config CAN_VCAN + tristate "Virtual Local CAN Interface (vcan)" + ---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_VXCAN + tristate "Virtual CAN Tunnel (vxcan)" + ---help--- + Similar to the virtual ethernet driver veth, vxcan implements a + local CAN traffic tunnel between two virtual CAN network devices. + When creating a vxcan, two vxcan devices are created as pair. + When one end receives the packet it appears on its pair and vice + versa. The vxcan can be used for cross namespace communication. + + In opposite to vcan loopback devices the vxcan only forwards CAN + frames to its pair and does *not* provide a local echo of sent + CAN frames. To disable a potential echo in af_can.c the vxcan driver + announces IFF_ECHO in the interface flags. To have a clean start + in each namespace the CAN GW hop counter is set to zero. + + This driver can also be built as a module. If so, the module + will be called vxcan. + +config CAN_SLCAN + tristate "Serial / USB serial CAN Adaptors (slcan)" + depends on TTY + ---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" + 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. + +if CAN_DEV + +config CAN_CALC_BITTIMING + bool "CAN bit-timing calculation" + 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_LEDS + bool "Enable LED triggers for Netlink based drivers" + depends on LEDS_CLASS + # The netdev trigger (LEDS_TRIGGER_NETDEV) should be able to do + # everything that this driver is doing. This is marked as broken + # because it uses stuff that is intended to be changed or removed. + # Please consider switching to the netdev trigger and confirm it + # fulfills your needs instead of fixing this driver. + depends on BROKEN + select LEDS_TRIGGERS + ---help--- + This option adds two LED triggers for packet receive and transmit + events on each supported CAN device. + + Say Y here if you are working on a system with led-class supported + LEDs and you want to use them as canbus activity indicators. + +config CAN_AT91 + tristate "Atmel AT91 onchip CAN controller" + depends on (ARCH_AT91 || COMPILE_TEST) && HAS_IOMEM + ---help--- + This is a driver for the SoC CAN controller in Atmel's AT91SAM9263 + and AT91SAM9X5 processors. + +config CAN_FLEXCAN + tristate "Support for Freescale FLEXCAN based chips" + depends on ARM || PPC + ---help--- + Say Y here if you want to support for Freescale FlexCAN. + +config CAN_GRCAN + tristate "Aeroflex Gaisler GRCAN and GRHCAN CAN devices" + depends on OF && HAS_DMA + ---help--- + Say Y here if you want to use Aeroflex Gaisler GRCAN or GRHCAN. + Note that the driver supports little endian, even though little + endian syntheses of the cores would need some modifications on + the hardware level to work. + +config CAN_JANZ_ICAN3 + tristate "Janz VMOD-ICAN3 Intelligent CAN controller" + depends on 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 CAN_SUN4I + tristate "Allwinner A10 CAN controller" + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + ---help--- + Say Y here if you want to use CAN controller found on Allwinner + A10/A20 SoCs. + + To compile this driver as a module, choose M here: the module will + be called sun4i_can. + +config CAN_TI_HECC + depends on ARM + 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_XILINXCAN + tristate "Xilinx CAN" + depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST + depends on COMMON_CLK && HAS_IOMEM + ---help--- + Xilinx CAN driver. This driver supports both soft AXI CAN IP and + Zynq CANPS IP. + +config PCH_CAN + tristate "Intel EG20T PCH CAN controller" + depends on PCI && (X86_32 || COMPILE_TEST) + ---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/c_can/Kconfig" +source "drivers/net/can/cc770/Kconfig" +source "drivers/net/can/ifi_canfd/Kconfig" +source "drivers/net/can/m_can/Kconfig" +source "drivers/net/can/mscan/Kconfig" +source "drivers/net/can/peak_canfd/Kconfig" +source "drivers/net/can/rcar/Kconfig" +source "drivers/net/can/sja1000/Kconfig" +source "drivers/net/can/softing/Kconfig" +source "drivers/net/can/spi/Kconfig" +source "drivers/net/can/usb/Kconfig" + +endif + +config CAN_DEBUG_DEVICES + bool "CAN devices debugging messages" + ---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 000000000..44922bf29 --- /dev/null +++ b/drivers/net/can/Makefile @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux Controller Area Network drivers. +# + +obj-$(CONFIG_CAN_VCAN) += vcan.o +obj-$(CONFIG_CAN_VXCAN) += vxcan.o +obj-$(CONFIG_CAN_SLCAN) += slcan.o + +obj-$(CONFIG_CAN_DEV) += can-dev.o +can-dev-y += dev.o +can-dev-y += rx-offload.o + +can-dev-$(CONFIG_CAN_LEDS) += led.o + +obj-y += rcar/ +obj-y += spi/ +obj-y += usb/ +obj-y += softing/ + +obj-$(CONFIG_CAN_AT91) += at91_can.o +obj-$(CONFIG_CAN_CC770) += cc770/ +obj-$(CONFIG_CAN_C_CAN) += c_can/ +obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o +obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ +obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o +obj-$(CONFIG_CAN_MSCAN) += mscan/ +obj-$(CONFIG_CAN_M_CAN) += m_can/ +obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ +obj-$(CONFIG_CAN_SJA1000) += sja1000/ +obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o +obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o +obj-$(CONFIG_PCH_CAN) += pch_can.o + +subdir-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 000000000..d98c69045 --- /dev/null +++ b/drivers/net/can/at91_can.c @@ -0,0 +1,1417 @@ +/* + * 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. + * + */ + +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.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 <linux/can/led.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 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_at91sam9263_data = { + .rx_first = 1, + .rx_split = 8, + .rx_last = 11, + .tx_shift = 2, + .type = AT91_DEVTYPE_SAM9263, +}; + +static const struct at91_devtype_data at91_at91sam9x5_data = { + .rx_first = 0, + .rx_split = 4, + .rx_last = 5, + .tx_shift = 1, + .type = AT91_DEVTYPE_SAM9X5, +}; + +static const 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 readl_relaxed(priv->reg_base + reg); +} + +static inline void at91_write(const struct at91_priv *priv, enum at91_reg reg, + u32 value) +{ + writel_relaxed(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; +} + +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); + + /* enable chip */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg_mr = AT91_MR_CANEN | AT91_MR_ABM; + else + reg_mr = AT91_MR_CANEN; + at91_write(priv, AT91_MR, reg_mr); + + 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); + + 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; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); +} + +/** + * 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); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + can_led_event(dev, CAN_LED_EVENT_RX); +} + +/** + * 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) && + mb > get_mb_rx_last(priv)) { + priv->rx_next = get_mb_rx_first(priv); + if (quota > 0) + 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); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + 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_done(napi, work_done); + 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++; + can_led_event(dev, CAN_LED_EVENT_TX); + } + } + + /* + * 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); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + netif_rx(skb); + + 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; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + /* 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; + } + + can_led_event(dev, CAN_LED_EVENT_OPEN); + + /* 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_unprepare(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_unprepare(priv->clk); + + close_candev(dev); + + can_led_event(dev, CAN_LED_EVENT_STOP); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +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 = kstrtoul(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, 0644, at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id); + +static struct attribute *at91_sysfs_attrs[] = { + &dev_attr_mb0_id.attr, + NULL, +}; + +static const struct attribute_group at91_sysfs_attr_group = { + .attrs = at91_sysfs_attrs, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id at91_can_dt_ids[] = { + { + .compatible = "atmel,at91sam9x5-can", + .data = &at91_at91sam9x5_data, + }, { + .compatible = "atmel,at91sam9263-can", + .data = &at91_at91sam9263_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, at91_can_dt_ids); +#endif + +static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(at91_can_dt_ids, pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "no matching node found in dtb\n"); + return NULL; + } + return (const struct at91_devtype_data *)match->data; + } + return (const struct at91_devtype_data *) + platform_get_device_id(pdev)->driver_data; +} + +static int at91_can_probe(struct platform_device *pdev) +{ + const struct at91_devtype_data *devtype_data; + struct net_device *dev; + struct at91_priv *priv; + struct resource *res; + struct clk *clk; + void __iomem *addr; + int err, irq; + + devtype_data = at91_can_get_driver_data(pdev); + if (!devtype_data) { + dev_err(&pdev->dev, "no driver data\n"); + err = -ENODEV; + goto exit; + } + + 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 | + CAN_CTRLMODE_LISTENONLY; + priv->reg_base = addr; + priv->devtype_data = *devtype_data; + priv->clk = clk; + priv->pdata = dev_get_platdata(&pdev->dev); + 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; + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering netdev failed\n"); + goto exit_free; + } + + devm_can_led_init(dev); + + 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 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); + + 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 = "at91sam9x5_can", + .driver_data = (kernel_ulong_t)&at91_at91sam9x5_data, + }, { + .name = "at91_can", + .driver_data = (kernel_ulong_t)&at91_at91sam9263_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, at91_can_id_table); + +static struct platform_driver at91_can_driver = { + .probe = at91_can_probe, + .remove = at91_can_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(at91_can_dt_ids), + }, + .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/c_can/Kconfig b/drivers/net/can/c_can/Kconfig new file mode 100644 index 000000000..61ffc12d8 --- /dev/null +++ b/drivers/net/can/c_can/Kconfig @@ -0,0 +1,23 @@ +menuconfig CAN_C_CAN + tristate "Bosch C_CAN/D_CAN devices" + depends on HAS_IOMEM + +if CAN_C_CAN + +config CAN_C_CAN_PLATFORM + tristate "Generic Platform Bus based C_CAN/D_CAN driver" + ---help--- + This driver adds support for the C_CAN/D_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 & TI (www.ti.com) + boards like am335x, dm814x, dm813x and dm811x. + +config CAN_C_CAN_PCI + tristate "Generic PCI Bus based C_CAN/D_CAN driver" + depends on PCI + ---help--- + This driver adds support for the C_CAN/D_CAN chips connected + to the PCI bus. +endif diff --git a/drivers/net/can/c_can/Makefile b/drivers/net/can/c_can/Makefile new file mode 100644 index 000000000..9fdc678b5 --- /dev/null +++ b/drivers/net/can/c_can/Makefile @@ -0,0 +1,7 @@ +# +# 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 +obj-$(CONFIG_CAN_C_CAN_PCI) += c_can_pci.o 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 000000000..2278c5fff --- /dev/null +++ b/drivers/net/can/c_can/c_can.c @@ -0,0 +1,1336 @@ +/* + * 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/pm_runtime.h> +#include <linux/pinctrl/consumer.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> + +#include "c_can.h" + +/* Number of interface registers */ +#define IF_ENUM_REG_LEN 11 +#define C_CAN_IFACE(reg, iface) (C_CAN_IF1_##reg + (iface) * IF_ENUM_REG_LEN) + +/* control extension register D_CAN specific */ +#define CONTROL_EX_PDR BIT(8) + +/* control register */ +#define CONTROL_SWR BIT(15) +#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) + +#define CONTROL_IRQMSK (CONTROL_EIE | CONTROL_IE | CONTROL_SIE) + +/* 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_PDA BIT(10) +#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) + +/* interrupt register */ +#define INT_STS_PENDING 0x8000 + +/* 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_CLR_NEWDAT IF_COMM_TXRQST +#define IF_COMM_DATAA BIT(1) +#define IF_COMM_DATAB BIT(0) + +/* TX buffer setup */ +#define IF_COMM_TX (IF_COMM_ARB | IF_COMM_CONTROL | \ + IF_COMM_TXRQST | \ + IF_COMM_DATAA | IF_COMM_DATAB) + +/* For the low buffers we clear the interrupt bit, but keep newdat */ +#define IF_COMM_RCV_LOW (IF_COMM_MASK | IF_COMM_ARB | \ + IF_COMM_CONTROL | IF_COMM_CLR_INT_PND | \ + IF_COMM_DATAA | IF_COMM_DATAB) + +/* For the high buffers we clear the interrupt bit and newdat */ +#define IF_COMM_RCV_HIGH (IF_COMM_RCV_LOW | IF_COMM_CLR_NEWDAT) + + +/* Receive setup of message objects */ +#define IF_COMM_RCV_SETUP (IF_COMM_MASK | IF_COMM_ARB | IF_COMM_CONTROL) + +/* Invalidation of message objects */ +#define IF_COMM_INVAL (IF_COMM_ARB | IF_COMM_CONTROL) + +/* IFx arbitration */ +#define IF_ARB_MSGVAL BIT(31) +#define IF_ARB_MSGXTD BIT(30) +#define IF_ARB_TRANSMIT BIT(29) + +/* IFx message control */ +#define IF_MCONT_NEWDAT BIT(15) +#define IF_MCONT_MSGLST BIT(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 + +#define IF_MCONT_RCV (IF_MCONT_RXIE | IF_MCONT_UMASK) +#define IF_MCONT_RCV_EOB (IF_MCONT_RCV | IF_MCONT_EOB) + +#define IF_MCONT_TX (IF_MCONT_TXIE | IF_MCONT_EOB) + +/* + * Use IF1 for RX and IF2 for TX + */ +#define IF_RX 0 +#define IF_TX 1 + +/* minimum timeout for checking BUSY status */ +#define MIN_TIMEOUT_VALUE 6 + +/* Wait for ~1 sec for INIT bit */ +#define INIT_WAIT_MS 1000 + +/* 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, + LEC_MASK = 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 const 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 void c_can_pm_runtime_get_sync(const struct c_can_priv *priv) +{ + if (priv->device) + pm_runtime_get_sync(priv->device); +} + +static inline void c_can_pm_runtime_put_sync(const struct c_can_priv *priv) +{ + if (priv->device) + pm_runtime_put_sync(priv->device); +} + +static inline void c_can_reset_ram(const struct c_can_priv *priv, bool enable) +{ + if (priv->raminit) + priv->raminit(priv, enable); +} + +static void c_can_irq_control(struct c_can_priv *priv, bool enable) +{ + u32 ctrl = priv->read_reg(priv, C_CAN_CTRL_REG) & ~CONTROL_IRQMSK; + + if (enable) + ctrl |= CONTROL_IRQMSK; + + priv->write_reg(priv, C_CAN_CTRL_REG, ctrl); +} + +static void c_can_obj_update(struct net_device *dev, int iface, u32 cmd, u32 obj) +{ + struct c_can_priv *priv = netdev_priv(dev); + int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface); + + priv->write_reg32(priv, reg, (cmd << 16) | obj); + + for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) { + if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY)) + return; + udelay(1); + } + netdev_err(dev, "Updating object timed out\n"); + +} + +static inline void c_can_object_get(struct net_device *dev, int iface, + u32 obj, u32 cmd) +{ + c_can_obj_update(dev, iface, cmd, obj); +} + +static inline void c_can_object_put(struct net_device *dev, int iface, + u32 obj, u32 cmd) +{ + c_can_obj_update(dev, iface, cmd | IF_COMM_WR, obj); +} + +/* + * Note: According to documentation clearing TXIE while MSGVAL is set + * is not allowed, but works nicely on C/DCAN. And that lowers the I/O + * load significantly. + */ +static void c_can_inval_tx_object(struct net_device *dev, int iface, int obj) +{ + struct c_can_priv *priv = netdev_priv(dev); + + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0); + c_can_object_put(dev, iface, obj, IF_COMM_INVAL); +} + +static void c_can_inval_msg_object(struct net_device *dev, int iface, int obj) +{ + struct c_can_priv *priv = netdev_priv(dev); + + priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), 0); + priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), 0); + c_can_inval_tx_object(dev, iface, obj); +} + +static void c_can_setup_tx_object(struct net_device *dev, int iface, + struct can_frame *frame, int idx) +{ + struct c_can_priv *priv = netdev_priv(dev); + u16 ctrl = IF_MCONT_TX | frame->can_dlc; + bool rtr = frame->can_id & CAN_RTR_FLAG; + u32 arb = IF_ARB_MSGVAL; + int i; + + if (frame->can_id & CAN_EFF_FLAG) { + arb |= frame->can_id & CAN_EFF_MASK; + arb |= IF_ARB_MSGXTD; + } else { + arb |= (frame->can_id & CAN_SFF_MASK) << 18; + } + + if (!rtr) + arb |= IF_ARB_TRANSMIT; + + /* + * If we change the DIR bit, we need to invalidate the buffer + * first, i.e. clear the MSGVAL flag in the arbiter. + */ + if (rtr != (bool)test_bit(idx, &priv->tx_dir)) { + u32 obj = idx + C_CAN_MSG_OBJ_TX_FIRST; + + c_can_inval_msg_object(dev, iface, obj); + change_bit(idx, &priv->tx_dir); + } + + priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb); + + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl); + + if (priv->type == BOSCH_D_CAN) { + u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface); + + for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) { + data = (u32)frame->data[i]; + data |= (u32)frame->data[i + 1] << 8; + data |= (u32)frame->data[i + 2] << 16; + data |= (u32)frame->data[i + 3] << 24; + priv->write_reg32(priv, dreg, data); + } + } else { + for (i = 0; i < frame->can_dlc; i += 2) { + priv->write_reg(priv, + C_CAN_IFACE(DATA1_REG, iface) + i / 2, + frame->data[i] | + (frame->data[i + 1] << 8)); + } + } +} + +static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev, + int iface) +{ + int i; + + for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++) + c_can_object_get(dev, iface, i, IF_COMM_CLR_NEWDAT); +} + +static int c_can_handle_lost_msg_obj(struct net_device *dev, + int iface, int objno, u32 ctrl) +{ + struct net_device_stats *stats = &dev->stats; + struct c_can_priv *priv = netdev_priv(dev); + struct can_frame *frame; + struct sk_buff *skb; + + ctrl &= ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT); + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl); + c_can_object_put(dev, iface, objno, IF_COMM_CONTROL); + + stats->rx_errors++; + stats->rx_over_errors++; + + /* create an error msg */ + skb = alloc_can_err_skb(dev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + netif_receive_skb(skb); + return 1; +} + +static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl) +{ + struct net_device_stats *stats = &dev->stats; + struct c_can_priv *priv = netdev_priv(dev); + struct can_frame *frame; + struct sk_buff *skb; + u32 arb, data; + + skb = alloc_can_skb(dev, &frame); + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + frame->can_dlc = get_can_dlc(ctrl & 0x0F); + + arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface)); + + if (arb & IF_ARB_MSGXTD) + frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + frame->can_id = (arb >> 18) & CAN_SFF_MASK; + + if (arb & IF_ARB_TRANSMIT) { + frame->can_id |= CAN_RTR_FLAG; + } else { + int i, dreg = C_CAN_IFACE(DATA1_REG, iface); + + if (priv->type == BOSCH_D_CAN) { + for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) { + data = priv->read_reg32(priv, dreg); + frame->data[i] = data; + frame->data[i + 1] = data >> 8; + frame->data[i + 2] = data >> 16; + frame->data[i + 3] = data >> 24; + } + } else { + for (i = 0; i < frame->can_dlc; i += 2, dreg++) { + data = priv->read_reg(priv, dreg); + frame->data[i] = data; + frame->data[i + 1] = data >> 8; + } + } + } + + stats->rx_packets++; + stats->rx_bytes += frame->can_dlc; + + netif_receive_skb(skb); + return 0; +} + +static void c_can_setup_receive_object(struct net_device *dev, int iface, + u32 obj, u32 mask, u32 id, u32 mcont) +{ + struct c_can_priv *priv = netdev_priv(dev); + + mask |= BIT(29); + priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask); + + id |= IF_ARB_MSGVAL; + priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id); + + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont); + c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP); +} + +static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct can_frame *frame = (struct can_frame *)skb->data; + struct c_can_priv *priv = netdev_priv(dev); + u32 idx, obj; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + /* + * This is not a FIFO. C/D_CAN sends out the buffers + * prioritized. The lowest buffer number wins. + */ + idx = fls(atomic_read(&priv->tx_active)); + obj = idx + C_CAN_MSG_OBJ_TX_FIRST; + + /* If this is the last buffer, stop the xmit queue */ + if (idx == C_CAN_MSG_OBJ_TX_NUM - 1) + netif_stop_queue(dev); + /* + * Store the message in the interface so we can call + * can_put_echo_skb(). We must do this before we enable + * transmit as we might race against do_tx(). + */ + c_can_setup_tx_object(dev, IF_TX, frame, idx); + priv->dlc[idx] = frame->can_dlc; + can_put_echo_skb(skb, dev, idx); + + /* Update the active bits */ + atomic_add((1 << idx), &priv->tx_active); + /* Start transmission */ + c_can_object_put(dev, IF_TX, obj, IF_COMM_TX); + + return NETDEV_TX_OK; +} + +static int c_can_wait_for_ctrl_init(struct net_device *dev, + struct c_can_priv *priv, u32 init) +{ + int retry = 0; + + while (init != (priv->read_reg(priv, C_CAN_CTRL_REG) & CONTROL_INIT)) { + udelay(10); + if (retry++ > 1000) { + netdev_err(dev, "CCTRL: set CONTROL_INIT failed\n"); + return -EIO; + } + } + return 0; +} + +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; + int res; + + /* 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, C_CAN_CTRL_REG); + ctrl_save &= ~CONTROL_INIT; + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_CCE | CONTROL_INIT); + res = c_can_wait_for_ctrl_init(dev, priv, CONTROL_INIT); + if (res) + return res; + + priv->write_reg(priv, C_CAN_BTR_REG, reg_btr); + priv->write_reg(priv, C_CAN_BRPEXT_REG, reg_brpe); + priv->write_reg(priv, C_CAN_CTRL_REG, ctrl_save); + + return c_can_wait_for_ctrl_init(dev, priv, 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, IF_RX, 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, IF_RX, i, 0, 0, IF_MCONT_RCV); + + c_can_setup_receive_object(dev, IF_RX, C_CAN_MSG_OBJ_RX_LAST, 0, 0, + IF_MCONT_RCV_EOB); +} + +static int c_can_software_reset(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + int retry = 0; + + if (priv->type != BOSCH_D_CAN) + return 0; + + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_SWR | CONTROL_INIT); + while (priv->read_reg(priv, C_CAN_CTRL_REG) & CONTROL_SWR) { + msleep(20); + if (retry++ > 100) { + netdev_err(dev, "CCTRL: software reset failed\n"); + return -EIO; + } + } + + return 0; +} + +/* + * Configure C_CAN chip: + * - enable/disable auto-retransmission + * - set operating mode + * - configure message objects + */ +static int c_can_chip_config(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + int err; + + err = c_can_software_reset(dev); + if (err) + return err; + + /* enable automatic retransmission */ + priv->write_reg(priv, C_CAN_CTRL_REG, 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, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT); + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + /* loopback mode : useful for self-test function */ + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK); + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + /* silent mode : bus-monitoring mode */ + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT); + } + + /* 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, C_CAN_STS_REG, LEC_UNUSED); + + /* Clear all internal status */ + atomic_set(&priv->tx_active, 0); + priv->rxmasked = 0; + priv->tx_dir = 0; + + /* set bittiming params */ + return c_can_set_bittiming(dev); +} + +static int c_can_start(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + int err; + struct pinctrl *p; + + /* basic c_can configuration */ + err = c_can_chip_config(dev); + if (err) + return err; + + /* Setup the command for new messages */ + priv->comm_rcv_high = priv->type != BOSCH_D_CAN ? + IF_COMM_RCV_LOW : IF_COMM_RCV_HIGH; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Attempt to use "active" if available else use "default" */ + p = pinctrl_get_select(priv->device, "active"); + if (!IS_ERR(p)) + pinctrl_put(p); + else + pinctrl_pm_select_default_state(priv->device); + + return 0; +} + +static void c_can_stop(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + c_can_irq_control(priv, false); + + /* put ctrl to init on stop to end ongoing transmission */ + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_INIT); + + /* deactivate pins */ + pinctrl_pm_select_sleep_state(dev->dev.parent); + priv->can.state = CAN_STATE_STOPPED; +} + +static int c_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct c_can_priv *priv = netdev_priv(dev); + int err; + + switch (mode) { + case CAN_MODE_START: + err = c_can_start(dev); + if (err) + return err; + netif_wake_queue(dev); + c_can_irq_control(priv, true); + 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, C_CAN_ERR_CNT_REG); + bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> + ERR_CNT_REC_SHIFT; + bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; + + return 0; +} + +static int c_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct c_can_priv *priv = netdev_priv(dev); + int err; + + c_can_pm_runtime_get_sync(priv); + err = __c_can_get_berr_counter(dev, bec); + c_can_pm_runtime_put_sync(priv); + + return err; +} + +static void c_can_do_tx(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + u32 idx, obj, pkts = 0, bytes = 0, pend, clr; + + clr = pend = priv->read_reg(priv, C_CAN_INTPND2_REG); + + while ((idx = ffs(pend))) { + idx--; + pend &= ~(1 << idx); + obj = idx + C_CAN_MSG_OBJ_TX_FIRST; + c_can_inval_tx_object(dev, IF_RX, obj); + can_get_echo_skb(dev, idx); + bytes += priv->dlc[idx]; + pkts++; + } + + /* Clear the bits in the tx_active mask */ + atomic_sub(clr, &priv->tx_active); + + if (clr & (1 << (C_CAN_MSG_OBJ_TX_NUM - 1))) + netif_wake_queue(dev); + + if (pkts) { + stats->tx_bytes += bytes; + stats->tx_packets += pkts; + can_led_event(dev, CAN_LED_EVENT_TX); + } +} + +/* + * If we have a gap in the pending bits, that means we either + * raced with the hardware or failed to readout all upper + * objects in the last run due to quota limit. + */ +static u32 c_can_adjust_pending(u32 pend) +{ + u32 weight, lasts; + + if (pend == RECEIVE_OBJECT_BITS) + return pend; + + /* + * If the last set bit is larger than the number of pending + * bits we have a gap. + */ + weight = hweight32(pend); + lasts = fls(pend); + + /* If the bits are linear, nothing to do */ + if (lasts == weight) + return pend; + + /* + * Find the first set bit after the gap. We walk backwards + * from the last set bit. + */ + for (lasts--; pend & (1 << (lasts - 1)); lasts--); + + return pend & ~((1 << lasts) - 1); +} + +static inline void c_can_rx_object_get(struct net_device *dev, + struct c_can_priv *priv, u32 obj) +{ + c_can_object_get(dev, IF_RX, obj, priv->comm_rcv_high); +} + +static inline void c_can_rx_finalize(struct net_device *dev, + struct c_can_priv *priv, u32 obj) +{ + if (priv->type != BOSCH_D_CAN) + c_can_object_get(dev, IF_RX, obj, IF_COMM_CLR_NEWDAT); +} + +static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv, + u32 pend, int quota) +{ + u32 pkts = 0, ctrl, obj; + + while ((obj = ffs(pend)) && quota > 0) { + pend &= ~BIT(obj - 1); + + c_can_rx_object_get(dev, priv, obj); + ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_RX)); + + if (ctrl & IF_MCONT_MSGLST) { + int n = c_can_handle_lost_msg_obj(dev, IF_RX, obj, ctrl); + + pkts += n; + quota -= n; + continue; + } + + /* + * This really should not happen, but this covers some + * odd HW behaviour. Do not remove that unless you + * want to brick your machine. + */ + if (!(ctrl & IF_MCONT_NEWDAT)) + continue; + + /* read the data from the message object */ + c_can_read_msg_object(dev, IF_RX, ctrl); + + c_can_rx_finalize(dev, priv, obj); + + pkts++; + quota--; + } + + return pkts; +} + +static inline u32 c_can_get_pending(struct c_can_priv *priv) +{ + u32 pend = priv->read_reg(priv, C_CAN_NEWDAT1_REG); + + return pend; +} + +/* + * 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. + * + * We clear the newdat bit right away. + * + * This can result in packet reordering when the readout is slow. + */ +static int c_can_do_rx_poll(struct net_device *dev, int quota) +{ + struct c_can_priv *priv = netdev_priv(dev); + u32 pkts = 0, pend = 0, toread, n; + + /* + * It is faster to read only one 16bit register. This is only possible + * for a maximum number of 16 objects. + */ + BUILD_BUG_ON_MSG(C_CAN_MSG_OBJ_RX_LAST > 16, + "Implementation does not support more message objects than 16"); + + while (quota > 0) { + if (!pend) { + pend = c_can_get_pending(priv); + if (!pend) + break; + /* + * If the pending field has a gap, handle the + * bits above the gap first. + */ + toread = c_can_adjust_pending(pend); + } else { + toread = pend; + } + /* Remove the bits from pend */ + pend &= ~toread; + /* Read the objects */ + n = c_can_read_objects(dev, priv, toread, quota); + pkts += n; + quota -= n; + } + + if (pkts) + can_led_event(dev, CAN_LED_EVENT_RX); + + return pkts; +} + +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; + + switch (error_type) { + case C_CAN_ERROR_WARNING: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + break; + case C_CAN_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + break; + case C_CAN_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + break; + default: + break; + } + + /* 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, C_CAN_ERR_CNT_REG); + 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 */ + 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 */ + 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 */ + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + 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; + + if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) + return 0; + + /* common for all type of bus errors */ + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + /* 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 + */ + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + 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[3] = CAN_ERR_PROT_LOC_ACK; + 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[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + return 1; +} + +static int c_can_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + struct c_can_priv *priv = netdev_priv(dev); + u16 curr, last = priv->last_status; + int work_done = 0; + + /* Only read the status register if a status interrupt was pending */ + if (atomic_xchg(&priv->sie_pending, 0)) { + priv->last_status = curr = priv->read_reg(priv, C_CAN_STS_REG); + /* Ack status on C_CAN. D_CAN is self clearing */ + if (priv->type != BOSCH_D_CAN) + priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED); + } else { + /* no change detected ... */ + curr = last; + } + + /* handle state changes */ + if ((curr & STATUS_EWARN) && (!(last & STATUS_EWARN))) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += c_can_handle_state_change(dev, C_CAN_ERROR_WARNING); + } + + if ((curr & STATUS_EPASS) && (!(last & STATUS_EPASS))) { + netdev_dbg(dev, "entered error passive state\n"); + work_done += c_can_handle_state_change(dev, C_CAN_ERROR_PASSIVE); + } + + if ((curr & STATUS_BOFF) && (!(last & STATUS_BOFF))) { + netdev_dbg(dev, "entered bus off state\n"); + work_done += c_can_handle_state_change(dev, C_CAN_BUS_OFF); + goto end; + } + + /* handle bus recovery events */ + if ((!(curr & STATUS_BOFF)) && (last & STATUS_BOFF)) { + netdev_dbg(dev, "left bus off state\n"); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + if ((!(curr & STATUS_EPASS)) && (last & STATUS_EPASS)) { + netdev_dbg(dev, "left error passive state\n"); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + /* handle lec errors on the bus */ + work_done += c_can_handle_bus_err(dev, curr & LEC_MASK); + + /* Handle Tx/Rx events. We do this unconditionally */ + work_done += c_can_do_rx_poll(dev, (quota - work_done)); + c_can_do_tx(dev); + +end: + if (work_done < quota) { + napi_complete_done(napi, work_done); + /* enable all IRQs if we are not in bus off state */ + if (priv->can.state != CAN_STATE_BUS_OFF) + c_can_irq_control(priv, true); + } + + 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); + int reg_int; + + reg_int = priv->read_reg(priv, C_CAN_INT_REG); + if (!reg_int) + return IRQ_NONE; + + /* save for later use */ + if (reg_int & INT_STS_PENDING) + atomic_set(&priv->sie_pending, 1); + + /* disable all interrupts and schedule the NAPI */ + c_can_irq_control(priv, false); + 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); + + c_can_pm_runtime_get_sync(priv); + c_can_reset_ram(priv, true); + + /* open the can device */ + err = open_candev(dev); + if (err) { + netdev_err(dev, "failed to open can device\n"); + goto exit_open_fail; + } + + /* 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; + } + + /* start the c_can controller */ + err = c_can_start(dev); + if (err) + goto exit_start_fail; + + can_led_event(dev, CAN_LED_EVENT_OPEN); + + napi_enable(&priv->napi); + /* enable status change, error and module interrupts */ + c_can_irq_control(priv, true); + netif_start_queue(dev); + + return 0; + +exit_start_fail: + free_irq(dev->irq, dev); +exit_irq_fail: + close_candev(dev); +exit_open_fail: + c_can_reset_ram(priv, false); + c_can_pm_runtime_put_sync(priv); + 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); + + c_can_reset_ram(priv, false); + c_can_pm_runtime_put_sync(priv); + + can_led_event(dev, CAN_LED_EVENT_STOP); + + 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); + +#ifdef CONFIG_PM +int c_can_power_down(struct net_device *dev) +{ + u32 val; + unsigned long time_out; + struct c_can_priv *priv = netdev_priv(dev); + + if (!(dev->flags & IFF_UP)) + return 0; + + WARN_ON(priv->type != BOSCH_D_CAN); + + /* set PDR value so the device goes to power down mode */ + val = priv->read_reg(priv, C_CAN_CTRL_EX_REG); + val |= CONTROL_EX_PDR; + priv->write_reg(priv, C_CAN_CTRL_EX_REG, val); + + /* Wait for the PDA bit to get set */ + time_out = jiffies + msecs_to_jiffies(INIT_WAIT_MS); + while (!(priv->read_reg(priv, C_CAN_STS_REG) & STATUS_PDA) && + time_after(time_out, jiffies)) + cpu_relax(); + + if (time_after(jiffies, time_out)) + return -ETIMEDOUT; + + c_can_stop(dev); + + c_can_reset_ram(priv, false); + c_can_pm_runtime_put_sync(priv); + + return 0; +} +EXPORT_SYMBOL_GPL(c_can_power_down); + +int c_can_power_up(struct net_device *dev) +{ + u32 val; + unsigned long time_out; + struct c_can_priv *priv = netdev_priv(dev); + int ret; + + if (!(dev->flags & IFF_UP)) + return 0; + + WARN_ON(priv->type != BOSCH_D_CAN); + + c_can_pm_runtime_get_sync(priv); + c_can_reset_ram(priv, true); + + /* Clear PDR and INIT bits */ + val = priv->read_reg(priv, C_CAN_CTRL_EX_REG); + val &= ~CONTROL_EX_PDR; + priv->write_reg(priv, C_CAN_CTRL_EX_REG, val); + val = priv->read_reg(priv, C_CAN_CTRL_REG); + val &= ~CONTROL_INIT; + priv->write_reg(priv, C_CAN_CTRL_REG, val); + + /* Wait for the PDA bit to get clear */ + time_out = jiffies + msecs_to_jiffies(INIT_WAIT_MS); + while ((priv->read_reg(priv, C_CAN_STS_REG) & STATUS_PDA) && + time_after(time_out, jiffies)) + cpu_relax(); + + if (time_after(jiffies, time_out)) + return -ETIMEDOUT; + + ret = c_can_start(dev); + if (!ret) + c_can_irq_control(priv, true); + + return ret; +} +EXPORT_SYMBOL_GPL(c_can_power_up); +#endif + +void free_c_can_dev(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + netif_napi_del(&priv->napi); + 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, + .ndo_change_mtu = can_change_mtu, +}; + +int register_c_can_dev(struct net_device *dev) +{ + int err; + + /* Deactivate pins to prevent DRA7 DCAN IP from being + * stuck in transition when module is disabled. + * Pins are activated in c_can_start() and deactivated + * in c_can_stop() + */ + pinctrl_pm_select_sleep_state(dev->dev.parent); + + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &c_can_netdev_ops; + + err = register_candev(dev); + if (!err) + devm_can_led_init(dev); + return err; +} +EXPORT_SYMBOL_GPL(register_c_can_dev); + +void unregister_c_can_dev(struct net_device *dev) +{ + 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 000000000..d5567a7c1 --- /dev/null +++ b/drivers/net/can/c_can/c_can.h @@ -0,0 +1,229 @@ +/* + * 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 + +/* 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 RECEIVE_OBJECT_BITS 0x0000ffff + +enum reg { + C_CAN_CTRL_REG = 0, + C_CAN_CTRL_EX_REG, + C_CAN_STS_REG, + C_CAN_ERR_CNT_REG, + C_CAN_BTR_REG, + C_CAN_INT_REG, + C_CAN_TEST_REG, + C_CAN_BRPEXT_REG, + C_CAN_IF1_COMREQ_REG, + C_CAN_IF1_COMMSK_REG, + C_CAN_IF1_MASK1_REG, + C_CAN_IF1_MASK2_REG, + C_CAN_IF1_ARB1_REG, + C_CAN_IF1_ARB2_REG, + C_CAN_IF1_MSGCTRL_REG, + C_CAN_IF1_DATA1_REG, + C_CAN_IF1_DATA2_REG, + C_CAN_IF1_DATA3_REG, + C_CAN_IF1_DATA4_REG, + C_CAN_IF2_COMREQ_REG, + C_CAN_IF2_COMMSK_REG, + C_CAN_IF2_MASK1_REG, + C_CAN_IF2_MASK2_REG, + C_CAN_IF2_ARB1_REG, + C_CAN_IF2_ARB2_REG, + C_CAN_IF2_MSGCTRL_REG, + C_CAN_IF2_DATA1_REG, + C_CAN_IF2_DATA2_REG, + C_CAN_IF2_DATA3_REG, + C_CAN_IF2_DATA4_REG, + C_CAN_TXRQST1_REG, + C_CAN_TXRQST2_REG, + C_CAN_NEWDAT1_REG, + C_CAN_NEWDAT2_REG, + C_CAN_INTPND1_REG, + C_CAN_INTPND2_REG, + C_CAN_MSGVAL1_REG, + C_CAN_MSGVAL2_REG, + C_CAN_FUNCTION_REG, +}; + +static const u16 reg_map_c_can[] = { + [C_CAN_CTRL_REG] = 0x00, + [C_CAN_STS_REG] = 0x02, + [C_CAN_ERR_CNT_REG] = 0x04, + [C_CAN_BTR_REG] = 0x06, + [C_CAN_INT_REG] = 0x08, + [C_CAN_TEST_REG] = 0x0A, + [C_CAN_BRPEXT_REG] = 0x0C, + [C_CAN_IF1_COMREQ_REG] = 0x10, + [C_CAN_IF1_COMMSK_REG] = 0x12, + [C_CAN_IF1_MASK1_REG] = 0x14, + [C_CAN_IF1_MASK2_REG] = 0x16, + [C_CAN_IF1_ARB1_REG] = 0x18, + [C_CAN_IF1_ARB2_REG] = 0x1A, + [C_CAN_IF1_MSGCTRL_REG] = 0x1C, + [C_CAN_IF1_DATA1_REG] = 0x1E, + [C_CAN_IF1_DATA2_REG] = 0x20, + [C_CAN_IF1_DATA3_REG] = 0x22, + [C_CAN_IF1_DATA4_REG] = 0x24, + [C_CAN_IF2_COMREQ_REG] = 0x40, + [C_CAN_IF2_COMMSK_REG] = 0x42, + [C_CAN_IF2_MASK1_REG] = 0x44, + [C_CAN_IF2_MASK2_REG] = 0x46, + [C_CAN_IF2_ARB1_REG] = 0x48, + [C_CAN_IF2_ARB2_REG] = 0x4A, + [C_CAN_IF2_MSGCTRL_REG] = 0x4C, + [C_CAN_IF2_DATA1_REG] = 0x4E, + [C_CAN_IF2_DATA2_REG] = 0x50, + [C_CAN_IF2_DATA3_REG] = 0x52, + [C_CAN_IF2_DATA4_REG] = 0x54, + [C_CAN_TXRQST1_REG] = 0x80, + [C_CAN_TXRQST2_REG] = 0x82, + [C_CAN_NEWDAT1_REG] = 0x90, + [C_CAN_NEWDAT2_REG] = 0x92, + [C_CAN_INTPND1_REG] = 0xA0, + [C_CAN_INTPND2_REG] = 0xA2, + [C_CAN_MSGVAL1_REG] = 0xB0, + [C_CAN_MSGVAL2_REG] = 0xB2, +}; + +static const u16 reg_map_d_can[] = { + [C_CAN_CTRL_REG] = 0x00, + [C_CAN_CTRL_EX_REG] = 0x02, + [C_CAN_STS_REG] = 0x04, + [C_CAN_ERR_CNT_REG] = 0x08, + [C_CAN_BTR_REG] = 0x0C, + [C_CAN_BRPEXT_REG] = 0x0E, + [C_CAN_INT_REG] = 0x10, + [C_CAN_TEST_REG] = 0x14, + [C_CAN_FUNCTION_REG] = 0x18, + [C_CAN_TXRQST1_REG] = 0x88, + [C_CAN_TXRQST2_REG] = 0x8A, + [C_CAN_NEWDAT1_REG] = 0x9C, + [C_CAN_NEWDAT2_REG] = 0x9E, + [C_CAN_INTPND1_REG] = 0xB0, + [C_CAN_INTPND2_REG] = 0xB2, + [C_CAN_MSGVAL1_REG] = 0xC4, + [C_CAN_MSGVAL2_REG] = 0xC6, + [C_CAN_IF1_COMREQ_REG] = 0x100, + [C_CAN_IF1_COMMSK_REG] = 0x102, + [C_CAN_IF1_MASK1_REG] = 0x104, + [C_CAN_IF1_MASK2_REG] = 0x106, + [C_CAN_IF1_ARB1_REG] = 0x108, + [C_CAN_IF1_ARB2_REG] = 0x10A, + [C_CAN_IF1_MSGCTRL_REG] = 0x10C, + [C_CAN_IF1_DATA1_REG] = 0x110, + [C_CAN_IF1_DATA2_REG] = 0x112, + [C_CAN_IF1_DATA3_REG] = 0x114, + [C_CAN_IF1_DATA4_REG] = 0x116, + [C_CAN_IF2_COMREQ_REG] = 0x120, + [C_CAN_IF2_COMMSK_REG] = 0x122, + [C_CAN_IF2_MASK1_REG] = 0x124, + [C_CAN_IF2_MASK2_REG] = 0x126, + [C_CAN_IF2_ARB1_REG] = 0x128, + [C_CAN_IF2_ARB2_REG] = 0x12A, + [C_CAN_IF2_MSGCTRL_REG] = 0x12C, + [C_CAN_IF2_DATA1_REG] = 0x130, + [C_CAN_IF2_DATA2_REG] = 0x132, + [C_CAN_IF2_DATA3_REG] = 0x134, + [C_CAN_IF2_DATA4_REG] = 0x136, +}; + +enum c_can_dev_id { + BOSCH_C_CAN_PLATFORM, + BOSCH_C_CAN, + BOSCH_D_CAN, +}; + +struct raminit_bits { + u8 start; + u8 done; +}; + +struct c_can_driver_data { + enum c_can_dev_id id; + + /* RAMINIT register description. Optional. */ + const struct raminit_bits *raminit_bits; /* Array of START/DONE bit positions */ + u8 raminit_num; /* Number of CAN instances on the SoC */ + bool raminit_pulse; /* If set, sets and clears START bit (pulse) */ +}; + +/* Out of band RAMINIT register access via syscon regmap */ +struct c_can_raminit { + struct regmap *syscon; /* for raminit ctrl. reg. access */ + unsigned int reg; /* register index within syscon */ + struct raminit_bits bits; + bool needs_pulse; +}; + +/* 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; + struct device *device; + atomic_t tx_active; + atomic_t sie_pending; + unsigned long tx_dir; + int last_status; + u16 (*read_reg) (const struct c_can_priv *priv, enum reg index); + void (*write_reg) (const struct c_can_priv *priv, enum reg index, u16 val); + u32 (*read_reg32) (const struct c_can_priv *priv, enum reg index); + void (*write_reg32) (const struct c_can_priv *priv, enum reg index, u32 val); + void __iomem *base; + const u16 *regs; + void *priv; /* for board-specific data */ + enum c_can_dev_id type; + struct c_can_raminit raminit_sys; /* RAMINIT via syscon regmap */ + void (*raminit) (const struct c_can_priv *priv, bool enable); + u32 comm_rcv_high; + u32 rxmasked; + u32 dlc[C_CAN_MSG_OBJ_TX_NUM]; +}; + +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); + +#ifdef CONFIG_PM +int c_can_power_up(struct net_device *dev); +int c_can_power_down(struct net_device *dev); +#endif + +#endif /* C_CAN_H */ diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c new file mode 100644 index 000000000..7efb60b50 --- /dev/null +++ b/drivers/net/can/c_can/c_can_pci.c @@ -0,0 +1,294 @@ +/* + * PCI bus driver for Bosch C_CAN/D_CAN controller + * + * Copyright (C) 2012 Federico Vaga <federico.vaga@gmail.com> + * + * Borrowed from c_can_platform.c + * + * 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/netdevice.h> +#include <linux/pci.h> + +#include <linux/can/dev.h> + +#include "c_can.h" + +#define PCI_DEVICE_ID_PCH_CAN 0x8818 +#define PCH_PCI_SOFT_RESET 0x01fc + +enum c_can_pci_reg_align { + C_CAN_REG_ALIGN_16, + C_CAN_REG_ALIGN_32, + C_CAN_REG_32, +}; + +struct c_can_pci_data { + /* Specify if is C_CAN or D_CAN */ + enum c_can_dev_id type; + /* Set the register alignment in the memory */ + enum c_can_pci_reg_align reg_align; + /* Set the frequency */ + unsigned int freq; + /* PCI bar number */ + int bar; + /* Callback for reset */ + void (*init)(const struct c_can_priv *priv, bool enable); +}; + +/* + * 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_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv, + enum reg index) +{ + return readw(priv->base + priv->regs[index]); +} + +static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv, + enum reg index, u16 val) +{ + writew(val, priv->base + priv->regs[index]); +} + +static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv, + enum reg index) +{ + return readw(priv->base + 2 * priv->regs[index]); +} + +static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv, + enum reg index, u16 val) +{ + writew(val, priv->base + 2 * priv->regs[index]); +} + +static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv, + enum reg index) +{ + return (u16)ioread32(priv->base + 2 * priv->regs[index]); +} + +static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv, + enum reg index, u16 val) +{ + iowrite32((u32)val, priv->base + 2 * priv->regs[index]); +} + +static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index) +{ + u32 val; + + val = priv->read_reg(priv, index); + val |= ((u32) priv->read_reg(priv, index + 1)) << 16; + + return val; +} + +static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index, + u32 val) +{ + priv->write_reg(priv, index + 1, val >> 16); + priv->write_reg(priv, index, val); +} + +static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable) +{ + if (enable) { + u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET; + + /* write to sw reset register */ + iowrite32(1, addr); + iowrite32(0, addr); + } +} + +static int c_can_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct c_can_pci_data *c_can_pci_data = (void *)ent->driver_data; + struct c_can_priv *priv; + struct net_device *dev; + void __iomem *addr; + int ret; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_device FAILED\n"); + goto out; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "pci_request_regions FAILED\n"); + goto out_disable_device; + } + + ret = pci_enable_msi(pdev); + if (!ret) { + dev_info(&pdev->dev, "MSI enabled\n"); + pci_set_master(pdev); + } + + addr = pci_iomap(pdev, c_can_pci_data->bar, + pci_resource_len(pdev, c_can_pci_data->bar)); + if (!addr) { + dev_err(&pdev->dev, + "device has no PCI memory resources, " + "failing adapter\n"); + ret = -ENOMEM; + goto out_release_regions; + } + + /* allocate the c_can device */ + dev = alloc_c_can_dev(); + if (!dev) { + ret = -ENOMEM; + goto out_iounmap; + } + + priv = netdev_priv(dev); + pci_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + dev->irq = pdev->irq; + priv->base = addr; + priv->device = &pdev->dev; + + if (!c_can_pci_data->freq) { + dev_err(&pdev->dev, "no clock frequency defined\n"); + ret = -ENODEV; + goto out_free_c_can; + } else { + priv->can.clock.freq = c_can_pci_data->freq; + } + + /* Configure CAN type */ + switch (c_can_pci_data->type) { + case BOSCH_C_CAN: + priv->regs = reg_map_c_can; + break; + case BOSCH_D_CAN: + priv->regs = reg_map_d_can; + break; + default: + ret = -EINVAL; + goto out_free_c_can; + } + + priv->type = c_can_pci_data->type; + + /* Configure access to registers */ + switch (c_can_pci_data->reg_align) { + case C_CAN_REG_ALIGN_32: + priv->read_reg = c_can_pci_read_reg_aligned_to_32bit; + priv->write_reg = c_can_pci_write_reg_aligned_to_32bit; + break; + case C_CAN_REG_ALIGN_16: + priv->read_reg = c_can_pci_read_reg_aligned_to_16bit; + priv->write_reg = c_can_pci_write_reg_aligned_to_16bit; + break; + case C_CAN_REG_32: + priv->read_reg = c_can_pci_read_reg_32bit; + priv->write_reg = c_can_pci_write_reg_32bit; + break; + default: + ret = -EINVAL; + goto out_free_c_can; + } + priv->read_reg32 = c_can_pci_read_reg32; + priv->write_reg32 = c_can_pci_write_reg32; + + priv->raminit = c_can_pci_data->init; + + ret = register_c_can_dev(dev); + if (ret) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + KBUILD_MODNAME, ret); + goto out_free_c_can; + } + + dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", + KBUILD_MODNAME, priv->regs, dev->irq); + + return 0; + +out_free_c_can: + free_c_can_dev(dev); +out_iounmap: + pci_iounmap(pdev, addr); +out_release_regions: + pci_disable_msi(pdev); + pci_clear_master(pdev); + pci_release_regions(pdev); +out_disable_device: + pci_disable_device(pdev); +out: + return ret; +} + +static void c_can_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct c_can_priv *priv = netdev_priv(dev); + void __iomem *addr = priv->base; + + unregister_c_can_dev(dev); + + free_c_can_dev(dev); + + pci_iounmap(pdev, addr); + pci_disable_msi(pdev); + pci_clear_master(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct c_can_pci_data c_can_sta2x11= { + .type = BOSCH_C_CAN, + .reg_align = C_CAN_REG_ALIGN_32, + .freq = 52000000, /* 52 Mhz */ + .bar = 0, +}; + +static const struct c_can_pci_data c_can_pch = { + .type = BOSCH_C_CAN, + .reg_align = C_CAN_REG_32, + .freq = 50000000, /* 50 MHz */ + .init = c_can_pci_reset_pch, + .bar = 1, +}; + +#define C_CAN_ID(_vend, _dev, _driverdata) { \ + PCI_DEVICE(_vend, _dev), \ + .driver_data = (unsigned long)&_driverdata, \ +} + +static const struct pci_device_id c_can_pci_tbl[] = { + C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN, + c_can_sta2x11), + C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN, + c_can_pch), + {}, +}; +static struct pci_driver c_can_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = c_can_pci_tbl, + .probe = c_can_pci_probe, + .remove = c_can_pci_remove, +}; + +module_pci_driver(c_can_pci_driver); + +MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller"); +MODULE_DEVICE_TABLE(pci, c_can_pci_tbl); 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 000000000..f2b0408ce --- /dev/null +++ b/drivers/net/can/c_can/c_can_platform.c @@ -0,0 +1,497 @@ +/* + * 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/pm_runtime.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include <linux/can/dev.h> + +#include "c_can.h" + +#define DCAN_RAM_INIT_BIT (1 << 3) +static DEFINE_SPINLOCK(raminit_lock); +/* + * 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(const struct c_can_priv *priv, + enum reg index) +{ + return readw(priv->base + priv->regs[index]); +} + +static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv *priv, + enum reg index, u16 val) +{ + writew(val, priv->base + priv->regs[index]); +} + +static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv *priv, + enum reg index) +{ + return readw(priv->base + 2 * priv->regs[index]); +} + +static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv *priv, + enum reg index, u16 val) +{ + writew(val, priv->base + 2 * priv->regs[index]); +} + +static void c_can_hw_raminit_wait_syscon(const struct c_can_priv *priv, + u32 mask, u32 val) +{ + const struct c_can_raminit *raminit = &priv->raminit_sys; + int timeout = 0; + u32 ctrl = 0; + + /* We look only at the bits of our instance. */ + val &= mask; + do { + udelay(1); + timeout++; + + regmap_read(raminit->syscon, raminit->reg, &ctrl); + if (timeout == 1000) { + dev_err(&priv->dev->dev, "%s: time out\n", __func__); + break; + } + } while ((ctrl & mask) != val); +} + +static void c_can_hw_raminit_syscon(const struct c_can_priv *priv, bool enable) +{ + const struct c_can_raminit *raminit = &priv->raminit_sys; + u32 ctrl = 0; + u32 mask; + + spin_lock(&raminit_lock); + + mask = 1 << raminit->bits.start | 1 << raminit->bits.done; + regmap_read(raminit->syscon, raminit->reg, &ctrl); + + /* We clear the start bit first. The start bit is + * looking at the 0 -> transition, but is not self clearing; + * NOTE: DONE must be written with 1 to clear it. + * We can't clear the DONE bit here using regmap_update_bits() + * as it will bypass the write if initial condition is START:0 DONE:1 + * e.g. on DRA7 which needs START pulse. + */ + ctrl &= ~mask; /* START = 0, DONE = 0 */ + regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl); + + /* check if START bit is 0. Ignore DONE bit for now + * as it can be either 0 or 1. + */ + c_can_hw_raminit_wait_syscon(priv, 1 << raminit->bits.start, ctrl); + + if (enable) { + /* Clear DONE bit & set START bit. */ + ctrl |= 1 << raminit->bits.start; + /* DONE must be written with 1 to clear it */ + ctrl |= 1 << raminit->bits.done; + regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl); + /* prevent further clearing of DONE bit */ + ctrl &= ~(1 << raminit->bits.done); + /* clear START bit if start pulse is needed */ + if (raminit->needs_pulse) { + ctrl &= ~(1 << raminit->bits.start); + regmap_update_bits(raminit->syscon, raminit->reg, + mask, ctrl); + } + + ctrl |= 1 << raminit->bits.done; + c_can_hw_raminit_wait_syscon(priv, mask, ctrl); + } + spin_unlock(&raminit_lock); +} + +static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index) +{ + u32 val; + + val = priv->read_reg(priv, index); + val |= ((u32) priv->read_reg(priv, index + 1)) << 16; + + return val; +} + +static void c_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index, + u32 val) +{ + priv->write_reg(priv, index + 1, val >> 16); + priv->write_reg(priv, index, val); +} + +static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index) +{ + return readl(priv->base + priv->regs[index]); +} + +static void d_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index, + u32 val) +{ + writel(val, priv->base + priv->regs[index]); +} + +static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask) +{ + while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask) + udelay(1); +} + +static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable) +{ + u32 ctrl; + + ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG); + ctrl &= ~DCAN_RAM_INIT_BIT; + priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl); + c_can_hw_raminit_wait(priv, ctrl); + + if (enable) { + ctrl |= DCAN_RAM_INIT_BIT; + priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl); + c_can_hw_raminit_wait(priv, ctrl); + } +} + +static const struct c_can_driver_data c_can_drvdata = { + .id = BOSCH_C_CAN, +}; + +static const struct c_can_driver_data d_can_drvdata = { + .id = BOSCH_D_CAN, +}; + +static const struct raminit_bits dra7_raminit_bits[] = { + [0] = { .start = 3, .done = 1, }, + [1] = { .start = 5, .done = 2, }, +}; + +static const struct c_can_driver_data dra7_dcan_drvdata = { + .id = BOSCH_D_CAN, + .raminit_num = ARRAY_SIZE(dra7_raminit_bits), + .raminit_bits = dra7_raminit_bits, + .raminit_pulse = true, +}; + +static const struct raminit_bits am3352_raminit_bits[] = { + [0] = { .start = 0, .done = 8, }, + [1] = { .start = 1, .done = 9, }, +}; + +static const struct c_can_driver_data am3352_dcan_drvdata = { + .id = BOSCH_D_CAN, + .raminit_num = ARRAY_SIZE(am3352_raminit_bits), + .raminit_bits = am3352_raminit_bits, +}; + +static const struct platform_device_id c_can_id_table[] = { + { + .name = KBUILD_MODNAME, + .driver_data = (kernel_ulong_t)&c_can_drvdata, + }, + { + .name = "c_can", + .driver_data = (kernel_ulong_t)&c_can_drvdata, + }, + { + .name = "d_can", + .driver_data = (kernel_ulong_t)&d_can_drvdata, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, c_can_id_table); + +static const struct of_device_id c_can_of_table[] = { + { .compatible = "bosch,c_can", .data = &c_can_drvdata }, + { .compatible = "bosch,d_can", .data = &d_can_drvdata }, + { .compatible = "ti,dra7-d_can", .data = &dra7_dcan_drvdata }, + { .compatible = "ti,am3352-d_can", .data = &am3352_dcan_drvdata }, + { .compatible = "ti,am4372-d_can", .data = &am3352_dcan_drvdata }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, c_can_of_table); + +static int c_can_plat_probe(struct platform_device *pdev) +{ + int ret; + void __iomem *addr; + struct net_device *dev; + struct c_can_priv *priv; + const struct of_device_id *match; + struct resource *mem; + int irq; + struct clk *clk; + const struct c_can_driver_data *drvdata; + struct device_node *np = pdev->dev.of_node; + + match = of_match_device(c_can_of_table, &pdev->dev); + if (match) { + drvdata = match->data; + } else if (pdev->id_entry->driver_data) { + drvdata = (struct c_can_driver_data *) + platform_get_device_id(pdev)->driver_data; + } else { + return -ENODEV; + } + + /* get the appropriate clk */ + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto exit; + } + + /* get the platform data */ + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + ret = -ENODEV; + goto exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + ret = PTR_ERR(addr); + goto exit; + } + + /* allocate the c_can device */ + dev = alloc_c_can_dev(); + if (!dev) { + ret = -ENOMEM; + goto exit; + } + + priv = netdev_priv(dev); + switch (drvdata->id) { + case BOSCH_C_CAN: + priv->regs = reg_map_c_can; + 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; + priv->read_reg32 = c_can_plat_read_reg32; + priv->write_reg32 = c_can_plat_write_reg32; + 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; + priv->read_reg32 = c_can_plat_read_reg32; + priv->write_reg32 = c_can_plat_write_reg32; + break; + } + break; + case BOSCH_D_CAN: + priv->regs = reg_map_d_can; + priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; + priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + priv->read_reg32 = d_can_plat_read_reg32; + priv->write_reg32 = d_can_plat_write_reg32; + + /* Check if we need custom RAMINIT via syscon. Mostly for TI + * platforms. Only supported with DT boot. + */ + if (np && of_property_read_bool(np, "syscon-raminit")) { + u32 id; + struct c_can_raminit *raminit = &priv->raminit_sys; + + ret = -EINVAL; + raminit->syscon = syscon_regmap_lookup_by_phandle(np, + "syscon-raminit"); + if (IS_ERR(raminit->syscon)) { + /* can fail with -EPROBE_DEFER */ + ret = PTR_ERR(raminit->syscon); + free_c_can_dev(dev); + return ret; + } + + if (of_property_read_u32_index(np, "syscon-raminit", 1, + &raminit->reg)) { + dev_err(&pdev->dev, + "couldn't get the RAMINIT reg. offset!\n"); + goto exit_free_device; + } + + if (of_property_read_u32_index(np, "syscon-raminit", 2, + &id)) { + dev_err(&pdev->dev, + "couldn't get the CAN instance ID\n"); + goto exit_free_device; + } + + if (id >= drvdata->raminit_num) { + dev_err(&pdev->dev, + "Invalid CAN instance ID\n"); + goto exit_free_device; + } + + raminit->bits = drvdata->raminit_bits[id]; + raminit->needs_pulse = drvdata->raminit_pulse; + + priv->raminit = c_can_hw_raminit_syscon; + } else { + priv->raminit = c_can_hw_raminit; + } + break; + default: + ret = -EINVAL; + goto exit_free_device; + } + + dev->irq = irq; + priv->base = addr; + priv->device = &pdev->dev; + priv->can.clock.freq = clk_get_rate(clk); + priv->priv = clk; + priv->type = drvdata->id; + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + pm_runtime_enable(priv->device); + 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->base, dev->irq); + return 0; + +exit_free_device: + pm_runtime_disable(priv->device); + free_c_can_dev(dev); +exit: + dev_err(&pdev->dev, "probe failed\n"); + + return ret; +} + +static int c_can_plat_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct c_can_priv *priv = netdev_priv(dev); + + unregister_c_can_dev(dev); + pm_runtime_disable(priv->device); + free_c_can_dev(dev); + + return 0; +} + +#ifdef CONFIG_PM +static int c_can_suspend(struct platform_device *pdev, pm_message_t state) +{ + int ret; + struct net_device *ndev = platform_get_drvdata(pdev); + struct c_can_priv *priv = netdev_priv(ndev); + + if (priv->type != BOSCH_D_CAN) { + dev_warn(&pdev->dev, "Not supported\n"); + return 0; + } + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + } + + ret = c_can_power_down(ndev); + if (ret) { + netdev_err(ndev, "failed to enter power down mode\n"); + return ret; + } + + priv->can.state = CAN_STATE_SLEEPING; + + return 0; +} + +static int c_can_resume(struct platform_device *pdev) +{ + int ret; + struct net_device *ndev = platform_get_drvdata(pdev); + struct c_can_priv *priv = netdev_priv(ndev); + + if (priv->type != BOSCH_D_CAN) { + dev_warn(&pdev->dev, "Not supported\n"); + return 0; + } + + ret = c_can_power_up(ndev); + if (ret) { + netdev_err(ndev, "Still in power down mode\n"); + return ret; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} +#else +#define c_can_suspend NULL +#define c_can_resume NULL +#endif + +static struct platform_driver c_can_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = c_can_of_table, + }, + .probe = c_can_plat_probe, + .remove = c_can_plat_remove, + .suspend = c_can_suspend, + .resume = c_can_resume, + .id_table = c_can_id_table, +}; + +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 000000000..6a9a5ba79 --- /dev/null +++ b/drivers/net/can/cc770/Kconfig @@ -0,0 +1,21 @@ +menuconfig CAN_CC770 + tristate "Bosch CC770 and Intel AN82527 devices" + depends on 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 000000000..8657f879a --- /dev/null +++ b/drivers/net/can/cc770/Makefile @@ -0,0 +1,7 @@ +# +# 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 diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c new file mode 100644 index 000000000..da636a22c --- /dev/null +++ b/drivers/net/can/cc770/cc770.c @@ -0,0 +1,906 @@ +/* + * 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 reception 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, 0444); +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, 0444); +MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 compatibility 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 const 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 void cc770_tx(struct net_device *dev, int mo) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct can_frame *cf = (struct can_frame *)priv->tx_skb->data; + u8 dlc, rtr; + u32 id; + int i; + + dlc = cf->can_dlc; + id = cf->can_id; + rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR; + + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_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]); + + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC); +} + +static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + unsigned int mo = obj2msgobj(CC770_OBJ_TX); + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + 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; + } + + priv->tx_skb = skb; + cc770_tx(dev, mo); + + 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]); + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +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; + priv->can.can_stats.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; + } + } + } + + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + 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); + struct can_frame *cf; + u8 ctrl1; + + ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); + + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_RES | MSGLST_RES | NEWDAT_RES); + + if (unlikely(!priv->tx_skb)) { + netdev_err(dev, "missing tx skb in tx interrupt\n"); + return; + } + + if (unlikely(ctrl1 & MSGLST_SET)) { + stats->rx_over_errors++; + stats->rx_errors++; + } + + /* When the CC770 is sending an RTR message and it receives a regular + * message that matches the id of the RTR message, it will overwrite the + * outgoing message in the TX register. When this happens we must + * process the received message and try to transmit the outgoing skb + * again. + */ + if (unlikely(ctrl1 & NEWDAT_SET)) { + cc770_rx(dev, mo, ctrl1); + cc770_tx(dev, mo); + return; + } + + cf = (struct can_frame *)priv->tx_skb->data; + stats->tx_bytes += cf->can_dlc; + stats->tx_packets++; + + can_put_echo_skb(priv->tx_skb, dev, 0); + can_get_echo_skb(dev, 0); + priv->tx_skb = NULL; + + netif_wake_queue(dev); +} + +static 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; + priv->tx_skb = NULL; + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +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 000000000..95752e1d1 --- /dev/null +++ b/drivers/net/can/cc770/cc770.h @@ -0,0 +1,205 @@ +/* + * 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 sk_buff *tx_skb; +}; + +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 000000000..fcd346980 --- /dev/null +++ b/drivers/net/can/cc770/cc770_isa.c @@ -0,0 +1,379 @@ +/* + * 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 irq[MAXDEV]; +static int clk[MAXDEV]; +static u8 cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; + +module_param_hw_array(port, ulong, ioport, NULL, 0444); +MODULE_PARM_DESC(port, "I/O port number"); + +module_param_hw_array(mem, ulong, iomem, NULL, 0444); +MODULE_PARM_DESC(mem, "I/O memory address"); + +module_param_hw_array(indirect, int, ioport, NULL, 0444); +MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); + +module_param_hw_array(irq, int, irq, NULL, 0444); +MODULE_PARM_DESC(irq, "IRQ number"); + +module_param_array(clk, int, NULL, 0444); +MODULE_PARM_DESC(clk, "External oscillator clock frequency " + "(default=16000000 [16 MHz])"); + +module_param_array(cir, byte, NULL, 0444); +MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])"); + +module_param_array(cor, byte, NULL, 0444); +MODULE_PARM_DESC(cor, "Clockout register (default=0x00)"); + +module_param_array(bcr, byte, NULL, 0444); +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 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; + + platform_set_drvdata(pdev, 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 cc770_isa_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct cc770_priv *priv = netdev_priv(dev); + int idx = pdev->id; + + unregister_cc770dev(dev); + + 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 = cc770_isa_remove, + .driver = { + .name = KBUILD_MODNAME, + }, +}; + +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 000000000..866e5e12f --- /dev/null +++ b/drivers/net/can/cc770/cc770_platform.c @@ -0,0 +1,273 @@ +/* + * 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"); +MODULE_ALIAS("platform:" DRV_NAME); + +#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 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 cc770_get_platform_data(struct platform_device *pdev, + struct cc770_priv *priv) +{ + + struct cc770_platform_data *pdata = dev_get_platdata(&pdev->dev); + + 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 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 (dev_get_platdata(&pdev->dev)) + 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); + + platform_set_drvdata(pdev, 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 cc770_platform_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + 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 const struct of_device_id cc770_platform_table[] = { + {.compatible = "bosch,cc770"}, /* CC770 from Bosch */ + {.compatible = "intc,82527"}, /* AN82527 from Intel CP */ + {}, +}; +MODULE_DEVICE_TABLE(of, cc770_platform_table); + +static struct platform_driver cc770_platform_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = cc770_platform_table, + }, + .probe = cc770_platform_probe, + .remove = 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 000000000..8738d37f7 --- /dev/null +++ b/drivers/net/can/dev.c @@ -0,0 +1,1324 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/workqueue.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/skb.h> +#include <linux/can/netlink.h> +#include <linux/can/led.h> +#include <linux/of.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>"); + +/* CAN DLC to real data length conversion helpers */ + +static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 12, 16, 20, 24, 32, 48, 64}; + +/* get data length from can_dlc with sanitized can_dlc */ +u8 can_dlc2len(u8 can_dlc) +{ + return dlc2len[can_dlc & 0x0F]; +} +EXPORT_SYMBOL_GPL(can_dlc2len); + +static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ + 9, 9, 9, 9, /* 9 - 12 */ + 10, 10, 10, 10, /* 13 - 16 */ + 11, 11, 11, 11, /* 17 - 20 */ + 12, 12, 12, 12, /* 21 - 24 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */ + 15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */ + +/* map the sanitized data length to an appropriate data length code */ +u8 can_len2dlc(u8 len) +{ + if (unlikely(len > 64)) + return 0xF; + + return len2dlc[len]; +} +EXPORT_SYMBOL_GPL(can_len2dlc); + +#ifdef CONFIG_CAN_CALC_BITTIMING +#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ +#define CAN_CALC_SYNC_SEG 1 + +/* + * 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_sample_point(const struct can_bittiming_const *btc, + unsigned int sample_point_nominal, unsigned int tseg, + unsigned int *tseg1_ptr, unsigned int *tseg2_ptr, + unsigned int *sample_point_error_ptr) +{ + unsigned int sample_point_error, best_sample_point_error = UINT_MAX; + unsigned int sample_point, best_sample_point = 0; + unsigned int tseg1, tseg2; + int i; + + for (i = 0; i <= 1; i++) { + tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal * (tseg + CAN_CALC_SYNC_SEG)) / 1000 - i; + tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max); + tseg1 = tseg - tseg2; + if (tseg1 > btc->tseg1_max) { + tseg1 = btc->tseg1_max; + tseg2 = tseg - tseg1; + } + + sample_point = 1000 * (tseg + CAN_CALC_SYNC_SEG - tseg2) / (tseg + CAN_CALC_SYNC_SEG); + sample_point_error = abs(sample_point_nominal - sample_point); + + if ((sample_point <= sample_point_nominal) && (sample_point_error < best_sample_point_error)) { + best_sample_point = sample_point; + best_sample_point_error = sample_point_error; + *tseg1_ptr = tseg1; + *tseg2_ptr = tseg2; + } + } + + if (sample_point_error_ptr) + *sample_point_error_ptr = best_sample_point_error; + + return best_sample_point; +} + +static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) +{ + struct can_priv *priv = netdev_priv(dev); + unsigned int bitrate; /* current bitrate */ + unsigned int bitrate_error; /* difference between current and nominal value */ + unsigned int best_bitrate_error = UINT_MAX; + unsigned int sample_point_error; /* difference between current and nominal value */ + unsigned int best_sample_point_error = UINT_MAX; + unsigned int sample_point_nominal; /* nominal sample point */ + unsigned int best_tseg = 0; /* current best value for tseg */ + unsigned int best_brp = 0; /* current best value for brp */ + unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0; + u64 v64; + + /* Use CiA recommended sample points */ + if (bt->sample_point) { + sample_point_nominal = bt->sample_point; + } else { + if (bt->bitrate > 800000) + sample_point_nominal = 750; + else if (bt->bitrate > 500000) + sample_point_nominal = 800; + else + sample_point_nominal = 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 = CAN_CALC_SYNC_SEG + tseg / 2; + + /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ + brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2; + + /* choose 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; + + bitrate = priv->clock.freq / (brp * tsegall); + bitrate_error = abs(bt->bitrate - bitrate); + + /* tseg brp biterror */ + if (bitrate_error > best_bitrate_error) + continue; + + /* reset sample point error if we have a better bitrate */ + if (bitrate_error < best_bitrate_error) + best_sample_point_error = UINT_MAX; + + can_update_sample_point(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error); + if (sample_point_error > best_sample_point_error) + continue; + + best_sample_point_error = sample_point_error; + best_bitrate_error = bitrate_error; + best_tseg = tseg / 2; + best_brp = brp; + + if (bitrate_error == 0 && sample_point_error == 0) + break; + } + + if (best_bitrate_error) { + /* Error in one-tenth of a percent */ + v64 = (u64)best_bitrate_error * 1000; + do_div(v64, bt->bitrate); + bitrate_error = (u32)v64; + if (bitrate_error > CAN_CALC_MAX_ERROR) { + netdev_err(dev, + "bitrate error %d.%d%% too high\n", + bitrate_error / 10, bitrate_error % 10); + return -EDOM; + } + netdev_warn(dev, "bitrate error %d.%d%%\n", + bitrate_error / 10, bitrate_error % 10); + } + + /* real sample point */ + bt->sample_point = can_update_sample_point(btc, sample_point_nominal, best_tseg, + &tseg1, &tseg2, NULL); + + v64 = (u64)best_brp * 1000 * 1000 * 1000; + 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 bitrate */ + bt->bitrate = priv->clock.freq / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2)); + + return 0; +} +#else /* !CONFIG_CAN_CALC_BITTIMING */ +static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) +{ + 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, + const struct can_bittiming_const *btc) +{ + struct can_priv *priv = netdev_priv(dev); + int tseg1, alltseg; + u64 brp64; + + 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; +} + +/* Checks the validity of predefined bitrate settings */ +static int can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt, + const u32 *bitrate_const, + const unsigned int bitrate_const_cnt) +{ + struct can_priv *priv = netdev_priv(dev); + unsigned int i; + + for (i = 0; i < bitrate_const_cnt; i++) { + if (bt->bitrate == bitrate_const[i]) + break; + } + + if (i >= priv->bitrate_const_cnt) + return -EINVAL; + + return 0; +} + +static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc, + const u32 *bitrate_const, + const unsigned int bitrate_const_cnt) +{ + int err; + + /* + * Depending on the given can_bittiming parameter structure the CAN + * timing parameters are calculated based on the provided bitrate OR + * alternatively the CAN timing parameters (tq, prop_seg, etc.) are + * provided directly which are then checked and fixed up. + */ + if (!bt->tq && bt->bitrate && btc) + err = can_calc_bittiming(dev, bt, btc); + else if (bt->tq && !bt->bitrate && btc) + err = can_fixup_bittiming(dev, bt, btc); + else if (!bt->tq && bt->bitrate && bitrate_const) + err = can_validate_bitrate(dev, bt, bitrate_const, + bitrate_const_cnt); + else + err = -EINVAL; + + return err; +} + +static void can_update_state_error_stats(struct net_device *dev, + enum can_state new_state) +{ + struct can_priv *priv = netdev_priv(dev); + + if (new_state <= priv->state) + return; + + switch (new_state) { + case CAN_STATE_ERROR_WARNING: + priv->can_stats.error_warning++; + break; + case CAN_STATE_ERROR_PASSIVE: + priv->can_stats.error_passive++; + break; + case CAN_STATE_BUS_OFF: + priv->can_stats.bus_off++; + break; + default: + break; + } +} + +static int can_tx_state_to_frame(struct net_device *dev, enum can_state state) +{ + switch (state) { + case CAN_STATE_ERROR_ACTIVE: + return CAN_ERR_CRTL_ACTIVE; + case CAN_STATE_ERROR_WARNING: + return CAN_ERR_CRTL_TX_WARNING; + case CAN_STATE_ERROR_PASSIVE: + return CAN_ERR_CRTL_TX_PASSIVE; + default: + return 0; + } +} + +static int can_rx_state_to_frame(struct net_device *dev, enum can_state state) +{ + switch (state) { + case CAN_STATE_ERROR_ACTIVE: + return CAN_ERR_CRTL_ACTIVE; + case CAN_STATE_ERROR_WARNING: + return CAN_ERR_CRTL_RX_WARNING; + case CAN_STATE_ERROR_PASSIVE: + return CAN_ERR_CRTL_RX_PASSIVE; + default: + return 0; + } +} + +void can_change_state(struct net_device *dev, struct can_frame *cf, + enum can_state tx_state, enum can_state rx_state) +{ + struct can_priv *priv = netdev_priv(dev); + enum can_state new_state = max(tx_state, rx_state); + + if (unlikely(new_state == priv->state)) { + netdev_warn(dev, "%s: oops, state did not change", __func__); + return; + } + + netdev_dbg(dev, "New error state: %d\n", new_state); + + can_update_state_error_stats(dev, new_state); + priv->state = new_state; + + if (!cf) + return; + + if (unlikely(new_state == CAN_STATE_BUS_OFF)) { + cf->can_id |= CAN_ERR_BUSOFF; + return; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= tx_state >= rx_state ? + can_tx_state_to_frame(dev, tx_state) : 0; + cf->data[1] |= tx_state <= rx_state ? + can_rx_state_to_frame(dev, rx_state) : 0; +} +EXPORT_SYMBOL_GPL(can_change_state); + +/* + * Local echo of CAN messages + * + * CAN network devices *should* support a local echo functionality + * (see Documentation/networking/can.rst). 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 || + (skb->protocol != htons(ETH_P_CAN) && + skb->protocol != htons(ETH_P_CANFD))) { + kfree_skb(skb); + return; + } + + if (!priv->echo_skb[idx]) { + + skb = can_create_echo_skb(skb); + if (!skb) + return; + + /* make settings for echo to reduce code in irq context */ + 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); + +struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr) +{ + struct can_priv *priv = netdev_priv(dev); + + if (idx >= priv->echo_skb_max) { + netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n", + __func__, idx, priv->echo_skb_max); + return NULL; + } + + if (priv->echo_skb[idx]) { + /* Using "struct canfd_frame::len" for the frame + * length is supported on both CAN and CANFD frames. + */ + struct sk_buff *skb = priv->echo_skb[idx]; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + + /* get the real payload length for netdev statistics */ + if (cf->can_id & CAN_RTR_FLAG) + *len_ptr = 0; + else + *len_ptr = cf->len; + + priv->echo_skb[idx] = NULL; + + return skb; + } + + return NULL; +} + +/* + * 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 sk_buff *skb; + u8 len; + + skb = __can_get_echo_skb(dev, idx, &len); + if (!skb) + return 0; + + skb_get(skb); + if (netif_rx(skb) == NET_RX_SUCCESS) + dev_consume_skb_any(skb); + else + dev_kfree_skb_any(skb); + + return len; +} +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]) { + dev_kfree_skb_any(priv->echo_skb[idx]); + priv->echo_skb[idx] = NULL; + } +} +EXPORT_SYMBOL_GPL(can_free_echo_skb); + +/* + * CAN device restart for bus-off recovery + */ +static void can_restart(struct net_device *dev) +{ + 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; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + netif_rx_ni(skb); + +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); +} + +static void can_restart_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct can_priv *priv = container_of(dwork, struct can_priv, restart_work); + + can_restart(priv->dev); +} + +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; + + cancel_delayed_work_sync(&priv->restart_work); + can_restart(dev); + + 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_info(dev, "bus-off\n"); + + netif_carrier_off(dev); + + if (priv->restart_ms) + schedule_delayed_work(&priv->restart_work, + msecs_to_jiffies(priv->restart_ms)); +} +EXPORT_SYMBOL_GPL(can_bus_off); + +static void can_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = CAN_MTU; + 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_skb_priv) + + 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; + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + *cf = skb_put_zero(skb, sizeof(struct can_frame)); + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_skb); + +struct sk_buff *alloc_canfd_skb(struct net_device *dev, + struct canfd_frame **cfd) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + + sizeof(struct canfd_frame)); + if (unlikely(!skb)) + return NULL; + + skb->protocol = htons(ETH_P_CANFD); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + *cfd = skb_put_zero(skb, sizeof(struct canfd_frame)); + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_canfd_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_mqs(int sizeof_priv, unsigned int echo_skb_max, + unsigned int txqs, unsigned int rxqs) +{ + 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_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup, + txqs, rxqs); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + priv->dev = 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_DELAYED_WORK(&priv->restart_work, can_restart_work); + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_candev_mqs); + +/* + * Free space of the CAN network device + */ +void free_candev(struct net_device *dev) +{ + free_netdev(dev); +} +EXPORT_SYMBOL_GPL(free_candev); + +/* + * changing MTU and control mode for CAN/CANFD devices + */ +int can_change_mtu(struct net_device *dev, int new_mtu) +{ + struct can_priv *priv = netdev_priv(dev); + + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* allow change of MTU according to the CANFD ability of the device */ + switch (new_mtu) { + case CAN_MTU: + /* 'CANFD-only' controllers can not switch to CAN_MTU */ + if (priv->ctrlmode_static & CAN_CTRLMODE_FD) + return -EINVAL; + + priv->ctrlmode &= ~CAN_CTRLMODE_FD; + break; + + case CANFD_MTU: + /* check for potential CANFD ability */ + if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) && + !(priv->ctrlmode_static & CAN_CTRLMODE_FD)) + return -EINVAL; + + priv->ctrlmode |= CAN_CTRLMODE_FD; + break; + + default: + return -EINVAL; + } + + dev->mtu = new_mtu; + return 0; +} +EXPORT_SYMBOL_GPL(can_change_mtu); + +/* + * 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.bitrate) { + netdev_err(dev, "bit-timing not yet defined\n"); + return -EINVAL; + } + + /* For CAN FD the data bitrate has to be >= the arbitration bitrate */ + if ((priv->ctrlmode & CAN_CTRLMODE_FD) && + (!priv->data_bittiming.bitrate || + (priv->data_bittiming.bitrate < priv->bittiming.bitrate))) { + netdev_err(dev, "incorrect/missing data bit-timing\n"); + return -EINVAL; + } + + /* Switch carrier on if device was stopped while in bus-off state */ + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(open_candev); + +#ifdef CONFIG_OF +/* Common function that can be used to understand the limitation of + * a transceiver when it provides no means to determine these limitations + * at runtime. + */ +void of_can_transceiver(struct net_device *dev) +{ + struct device_node *dn; + struct can_priv *priv = netdev_priv(dev); + struct device_node *np = dev->dev.parent->of_node; + int ret; + + dn = of_get_child_by_name(np, "can-transceiver"); + if (!dn) + return; + + ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max); + of_node_put(dn); + if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max)) + netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n"); +} +EXPORT_SYMBOL_GPL(of_can_transceiver); +#endif + +/* + * 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); + + cancel_delayed_work_sync(&priv->restart_work); + 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) }, + [IFLA_CAN_DATA_BITTIMING] + = { .len = sizeof(struct can_bittiming) }, + [IFLA_CAN_DATA_BITTIMING_CONST] + = { .len = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_TERMINATION] = { .type = NLA_U16 }, +}; + +static int can_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + bool is_can_fd = false; + + /* Make sure that valid CAN FD configurations always consist of + * - nominal/arbitration bittiming + * - data bittiming + * - control mode with CAN_CTRLMODE_FD set + */ + + if (!data) + return 0; + + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); + + is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; + } + + if (is_can_fd) { + if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING]) + return -EOPNOTSUPP; + } + + if (data[IFLA_CAN_DATA_BITTIMING]) { + if (!is_can_fd || !data[IFLA_CAN_BITTIMING]) + return -EOPNOTSUPP; + } + + return 0; +} + +static int can_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct can_priv *priv = netdev_priv(dev); + int err; + + /* We need synchronization with dev->stop() */ + ASSERT_RTNL(); + + if (data[IFLA_CAN_BITTIMING]) { + struct can_bittiming bt; + + /* Do not allow changing bittiming while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* Calculate bittiming parameters based on + * bittiming_const if set, otherwise pass bitrate + * directly via do_set_bitrate(). Bail out if neither + * is given. + */ + if (!priv->bittiming_const && !priv->do_set_bittiming) + return -EOPNOTSUPP; + + memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); + err = can_get_bittiming(dev, &bt, + priv->bittiming_const, + priv->bitrate_const, + priv->bitrate_const_cnt); + if (err) + return err; + + if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) { + netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n", + priv->bitrate_max); + return -EINVAL; + } + + 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_CTRLMODE]) { + struct can_ctrlmode *cm; + u32 ctrlstatic; + u32 maskedflags; + + /* Do not allow changing controller mode while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + cm = nla_data(data[IFLA_CAN_CTRLMODE]); + ctrlstatic = priv->ctrlmode_static; + maskedflags = cm->flags & cm->mask; + + /* check whether provided bits are allowed to be passed */ + if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic)) + return -EOPNOTSUPP; + + /* do not check for static fd-non-iso if 'fd' is disabled */ + if (!(maskedflags & CAN_CTRLMODE_FD)) + ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO; + + /* make sure static options are provided by configuration */ + if ((maskedflags & ctrlstatic) != ctrlstatic) + return -EOPNOTSUPP; + + /* clear bits to be modified and copy the flag values */ + priv->ctrlmode &= ~cm->mask; + priv->ctrlmode |= maskedflags; + + /* CAN_CTRLMODE_FD can only be set when driver supports FD */ + if (priv->ctrlmode & CAN_CTRLMODE_FD) + dev->mtu = CANFD_MTU; + else + dev->mtu = CAN_MTU; + } + + 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; + } + + if (data[IFLA_CAN_DATA_BITTIMING]) { + struct can_bittiming dbt; + + /* Do not allow changing bittiming while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* Calculate bittiming parameters based on + * data_bittiming_const if set, otherwise pass bitrate + * directly via do_set_bitrate(). Bail out if neither + * is given. + */ + if (!priv->data_bittiming_const && !priv->do_set_data_bittiming) + return -EOPNOTSUPP; + + memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), + sizeof(dbt)); + err = can_get_bittiming(dev, &dbt, + priv->data_bittiming_const, + priv->data_bitrate_const, + priv->data_bitrate_const_cnt); + if (err) + return err; + + if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) { + netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n", + priv->bitrate_max); + return -EINVAL; + } + + memcpy(&priv->data_bittiming, &dbt, sizeof(dbt)); + + if (priv->do_set_data_bittiming) { + /* Finally, set the bit-timing registers */ + err = priv->do_set_data_bittiming(dev); + if (err) + return err; + } + } + + if (data[IFLA_CAN_TERMINATION]) { + const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]); + const unsigned int num_term = priv->termination_const_cnt; + unsigned int i; + + if (!priv->do_set_termination) + return -EOPNOTSUPP; + + /* check whether given value is supported by the interface */ + for (i = 0; i < num_term; i++) { + if (termval == priv->termination_const[i]) + break; + } + if (i >= num_term) + return -EINVAL; + + /* Finally, set the termination value */ + err = priv->do_set_termination(dev, termval); + if (err) + return err; + + priv->termination = termval; + } + + return 0; +} + +static size_t can_get_size(const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + size_t size = 0; + + if (priv->bittiming.bitrate) /* IFLA_CAN_BITTIMING */ + size += nla_total_size(sizeof(struct can_bittiming)); + if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ + size += nla_total_size(sizeof(struct can_bittiming_const)); + size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ + size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ + if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ + size += nla_total_size(sizeof(struct can_berr_counter)); + if (priv->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */ + size += nla_total_size(sizeof(struct can_bittiming)); + if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */ + size += nla_total_size(sizeof(struct can_bittiming_const)); + if (priv->termination_const) { + size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */ + size += nla_total_size(sizeof(*priv->termination_const) * /* IFLA_CAN_TERMINATION_CONST */ + priv->termination_const_cnt); + } + if (priv->bitrate_const) /* IFLA_CAN_BITRATE_CONST */ + size += nla_total_size(sizeof(*priv->bitrate_const) * + priv->bitrate_const_cnt); + if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */ + size += nla_total_size(sizeof(*priv->data_bitrate_const) * + priv->data_bitrate_const_cnt); + size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */ + + 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); + + if ((priv->bittiming.bitrate && + nla_put(skb, IFLA_CAN_BITTIMING, + sizeof(priv->bittiming), &priv->bittiming)) || + + (priv->bittiming_const && + nla_put(skb, IFLA_CAN_BITTIMING_CONST, + sizeof(*priv->bittiming_const), priv->bittiming_const)) || + + nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) || + 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) || + + (priv->do_get_berr_counter && + !priv->do_get_berr_counter(dev, &bec) && + nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) || + + (priv->data_bittiming.bitrate && + nla_put(skb, IFLA_CAN_DATA_BITTIMING, + sizeof(priv->data_bittiming), &priv->data_bittiming)) || + + (priv->data_bittiming_const && + nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST, + sizeof(*priv->data_bittiming_const), + priv->data_bittiming_const)) || + + (priv->termination_const && + (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) || + nla_put(skb, IFLA_CAN_TERMINATION_CONST, + sizeof(*priv->termination_const) * + priv->termination_const_cnt, + priv->termination_const))) || + + (priv->bitrate_const && + nla_put(skb, IFLA_CAN_BITRATE_CONST, + sizeof(*priv->bitrate_const) * + priv->bitrate_const_cnt, + priv->bitrate_const)) || + + (priv->data_bitrate_const && + nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST, + sizeof(*priv->data_bitrate_const) * + priv->data_bitrate_const_cnt, + priv->data_bitrate_const)) || + + (nla_put(skb, IFLA_CAN_BITRATE_MAX, + sizeof(priv->bitrate_max), + &priv->bitrate_max)) + ) + + return -EMSGSIZE; + + return 0; +} + +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); + + if (nla_put(skb, IFLA_INFO_XSTATS, + sizeof(priv->can_stats), &priv->can_stats)) + goto nla_put_failure; + 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[], + struct netlink_ext_ack *extack) +{ + return -EOPNOTSUPP; +} + +static void can_dellink(struct net_device *dev, struct list_head *head) +{ + return; +} + +static struct rtnl_link_ops can_link_ops __read_mostly = { + .kind = "can", + .netns_refund = true, + .maxtype = IFLA_CAN_MAX, + .policy = can_policy, + .setup = can_setup, + .validate = can_validate, + .newlink = can_newlink, + .changelink = can_changelink, + .dellink = can_dellink, + .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) +{ + struct can_priv *priv = netdev_priv(dev); + + /* Ensure termination_const, termination_const_cnt and + * do_set_termination consistency. All must be either set or + * unset. + */ + if ((!priv->termination_const != !priv->termination_const_cnt) || + (!priv->termination_const != !priv->do_set_termination)) + return -EINVAL; + + if (!priv->bitrate_const != !priv->bitrate_const_cnt) + return -EINVAL; + + if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt) + return -EINVAL; + + dev->rtnl_link_ops = &can_link_ops; + netif_carrier_off(dev); + + 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); + +/* + * Test if a network device is a candev based device + * and return the can_priv* if so. + */ +struct can_priv *safe_candev_priv(struct net_device *dev) +{ + if ((dev->type != ARPHRD_CAN) || (dev->rtnl_link_ops != &can_link_ops)) + return NULL; + + return netdev_priv(dev); +} +EXPORT_SYMBOL_GPL(safe_candev_priv); + +static __init int can_dev_init(void) +{ + int err; + + can_led_notifier_init(); + + 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); + + can_led_notifier_exit(); +} +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 000000000..d4dfa0247 --- /dev/null +++ b/drivers/net/can/flexcan.c @@ -0,0 +1,1525 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// flexcan.c - FLEXCAN CAN controller driver +// +// Copyright (c) 2005-2006 Varma Electronics Oy +// Copyright (c) 2009 Sascha Hauer, Pengutronix +// Copyright (c) 2010-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de> +// Copyright (c) 2014 David Jander, Protonic Holland +// +// Based on code originally by Andrey Volkov <avolkov@varma-el.com> + +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/can/rx-offload.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.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_IRMQ BIT(16) +#define FLEXCAN_MCR_LPRIO_EN BIT(13) +#define FLEXCAN_MCR_AEN BIT(12) +/* MCR_MAXMB: maximum used MBs is MAXMB + 1 */ +#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f) +#define FLEXCAN_MCR_IDAM_A (0x0 << 8) +#define FLEXCAN_MCR_IDAM_B (0x1 << 8) +#define FLEXCAN_MCR_IDAM_C (0x2 << 8) +#define FLEXCAN_MCR_IDAM_D (0x3 << 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 control register 2 (CTRL2) bits */ +#define FLEXCAN_CTRL2_ECRWRE BIT(29) +#define FLEXCAN_CTRL2_WRMFRZ BIT(28) +#define FLEXCAN_CTRL2_RFFN(x) (((x) & 0x0f) << 24) +#define FLEXCAN_CTRL2_TASD(x) (((x) & 0x1f) << 19) +#define FLEXCAN_CTRL2_MRP BIT(18) +#define FLEXCAN_CTRL2_RRS BIT(17) +#define FLEXCAN_CTRL2_EACEN BIT(16) + +/* FLEXCAN memory error control register (MECR) bits */ +#define FLEXCAN_MECR_ECRWRDIS BIT(31) +#define FLEXCAN_MECR_HANCEI_MSK BIT(19) +#define FLEXCAN_MECR_FANCEI_MSK BIT(18) +#define FLEXCAN_MECR_CEI_MSK BIT(16) +#define FLEXCAN_MECR_HAERRIE BIT(15) +#define FLEXCAN_MECR_FAERRIE BIT(14) +#define FLEXCAN_MECR_EXTERRIE BIT(13) +#define FLEXCAN_MECR_RERRDIS BIT(9) +#define FLEXCAN_MECR_ECCDIS BIT(8) +#define FLEXCAN_MECR_NCEFAFRZ BIT(7) + +/* 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 */ +/* Errata ERR005829 step7: Reserve first valid MB */ +#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8 +#define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP 0 +#define FLEXCAN_TX_MB 63 +#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST (FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP + 1) +#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST (FLEXCAN_TX_MB - 1) +#define FLEXCAN_IFLAG_MB(x) BIT((x) & 0x1f) +#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7) +#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6) +#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5) + +/* FLEXCAN message buffers */ +#define FLEXCAN_MB_CODE_MASK (0xf << 24) +#define FLEXCAN_MB_CODE_RX_BUSY_BIT (0x1 << 24) +#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24) +#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24) +#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24) +#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24) +#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24) + +#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24) +#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24) +#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24) +#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 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_TIMEOUT_US (250) + +/* FLEXCAN hardware feature flags + * + * Below is some version info we got: + * SOC Version IP-Version Glitch- [TR]WRN_INT IRQ Err Memory err RTR re- + * Filter? connected? Passive detection ception in MB + * MX25 FlexCAN2 03.00.00.00 no no no no no + * MX28 FlexCAN2 03.00.04.00 yes yes no no no + * MX35 FlexCAN2 03.00.00.00 no no no no no + * MX53 FlexCAN2 03.00.00.00 yes no no no no + * MX6s FlexCAN3 10.00.12.00 yes yes no no yes + * VF610 FlexCAN3 ? no yes no yes yes? + * LS1021A FlexCAN2 03.00.04.00 no yes no no yes + * + * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected. + */ +#define FLEXCAN_QUIRK_BROKEN_WERR_STATE BIT(1) /* [TR]WRN_INT not connected */ +#define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */ +#define FLEXCAN_QUIRK_ENABLE_EACEN_RRS BIT(3) /* Enable EACEN and RRS bit in ctrl2 */ +#define FLEXCAN_QUIRK_DISABLE_MECR BIT(4) /* Disable Memory error detection */ +#define FLEXCAN_QUIRK_USE_OFF_TIMESTAMP BIT(5) /* Use timestamp based offloading */ +#define FLEXCAN_QUIRK_BROKEN_PERR_STATE BIT(6) /* No interrupt for error passive */ +#define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN BIT(7) /* default to BE register access */ + +/* 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 */ + union { /* 0x34 */ + u32 gfwr_mx28; /* MX28, MX53 */ + u32 ctrl2; /* MX6, VF610 */ + }; + u32 esr2; /* 0x38 */ + u32 imeur; /* 0x3c */ + u32 lrfr; /* 0x40 */ + u32 crcr; /* 0x44 */ + u32 rxfgmask; /* 0x48 */ + u32 rxfir; /* 0x4c */ + u32 _reserved3[12]; /* 0x50 */ + struct flexcan_mb mb[64]; /* 0x80 */ + /* FIFO-mode: + * MB + * 0x080...0x08f 0 RX message buffer + * 0x090...0x0df 1-5 reserverd + * 0x0e0...0x0ff 6-7 8 entry ID table + * (mx25, mx28, mx35, mx53) + * 0x0e0...0x2df 6-7..37 8..128 entry ID table + * size conf'ed via ctrl2::RFFN + * (mx6, vf610) + */ + u32 _reserved4[256]; /* 0x480 */ + u32 rximr[64]; /* 0x880 */ + u32 _reserved5[24]; /* 0x980 */ + u32 gfwr_mx6; /* 0x9e0 - MX6 */ + u32 _reserved6[63]; /* 0x9e4 */ + u32 mecr; /* 0xae0 */ + u32 erriar; /* 0xae4 */ + u32 erridpr; /* 0xae8 */ + u32 errippr; /* 0xaec */ + u32 rerrar; /* 0xaf0 */ + u32 rerrdr; /* 0xaf4 */ + u32 rerrsynr; /* 0xaf8 */ + u32 errsr; /* 0xafc */ +}; + +struct flexcan_devtype_data { + u32 quirks; /* quirks needed for different IP cores */ +}; + +struct flexcan_priv { + struct can_priv can; + struct can_rx_offload offload; + + struct flexcan_regs __iomem *regs; + struct flexcan_mb __iomem *tx_mb_reserved; + u32 reg_ctrl_default; + u32 reg_imask1_default; + u32 reg_imask2_default; + + struct clk *clk_ipg; + struct clk *clk_per; + const struct flexcan_devtype_data *devtype_data; + struct regulator *reg_xceiver; + + /* Read and Write APIs */ + u32 (*read)(void __iomem *addr); + void (*write)(u32 val, void __iomem *addr); +}; + +static const struct flexcan_devtype_data fsl_p1010_devtype_data = { + .quirks = FLEXCAN_QUIRK_BROKEN_WERR_STATE | + FLEXCAN_QUIRK_BROKEN_PERR_STATE | + FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN, +}; + +static const struct flexcan_devtype_data fsl_imx25_devtype_data = { + .quirks = FLEXCAN_QUIRK_BROKEN_WERR_STATE | + FLEXCAN_QUIRK_BROKEN_PERR_STATE, +}; + +static const struct flexcan_devtype_data fsl_imx28_devtype_data = { + .quirks = FLEXCAN_QUIRK_BROKEN_PERR_STATE, +}; + +static const struct flexcan_devtype_data fsl_imx6q_devtype_data = { + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | + FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE, +}; + +static const struct flexcan_devtype_data fsl_vf610_devtype_data = { + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | + FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | + FLEXCAN_QUIRK_BROKEN_PERR_STATE, +}; + +static const struct flexcan_devtype_data fsl_ls1021a_r2_devtype_data = { + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | + FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP, +}; + +static const 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, +}; + +/* FlexCAN module is essentially modelled as a little-endian IP in most + * SoCs, i.e the registers as well as the message buffer areas are + * implemented in a little-endian fashion. + * + * However there are some SoCs (e.g. LS1021A) which implement the FlexCAN + * module in a big-endian fashion (i.e the registers as well as the + * message buffer areas are implemented in a big-endian way). + * + * In addition, the FlexCAN module can be found on SoCs having ARM or + * PPC cores. So, we need to abstract off the register read/write + * functions, ensuring that these cater to all the combinations of module + * endianness and underlying CPU endianness. + */ +static inline u32 flexcan_read_be(void __iomem *addr) +{ + return ioread32be(addr); +} + +static inline void flexcan_write_be(u32 val, void __iomem *addr) +{ + iowrite32be(val, addr); +} + +static inline u32 flexcan_read_le(void __iomem *addr) +{ + return ioread32(addr); +} + +static inline void flexcan_write_le(u32 val, void __iomem *addr) +{ + iowrite32(val, addr); +} + +static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + u32 reg_ctrl = (priv->reg_ctrl_default | FLEXCAN_CTRL_ERR_MSK); + + priv->write(reg_ctrl, ®s->ctrl); +} + +static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + u32 reg_ctrl = (priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_MSK); + + priv->write(reg_ctrl, ®s->ctrl); +} + +static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv) +{ + if (!priv->reg_xceiver) + return 0; + + return regulator_enable(priv->reg_xceiver); +} + +static inline int flexcan_transceiver_disable(const struct flexcan_priv *priv) +{ + if (!priv->reg_xceiver) + return 0; + + return regulator_disable(priv->reg_xceiver); +} + +static int flexcan_chip_enable(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + u32 reg; + + reg = priv->read(®s->mcr); + reg &= ~FLEXCAN_MCR_MDIS; + priv->write(reg, ®s->mcr); + + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + udelay(10); + + if (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_disable(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + u32 reg; + + reg = priv->read(®s->mcr); + reg |= FLEXCAN_MCR_MDIS; + priv->write(reg, ®s->mcr); + + while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + udelay(10); + + if (!(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_freeze(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout; + u32 bitrate = priv->can.bittiming.bitrate; + u32 reg; + + if (bitrate) + timeout = 1000 * 1000 * 10 / bitrate; + else + timeout = FLEXCAN_TIMEOUT_US / 10; + + reg = priv->read(®s->mcr); + reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT; + priv->write(reg, ®s->mcr); + + while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + udelay(100); + + if (!(priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_unfreeze(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + u32 reg; + + reg = priv->read(®s->mcr); + reg &= ~FLEXCAN_MCR_HALT; + priv->write(reg, ®s->mcr); + + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + udelay(10); + + if (priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_softreset(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + + priv->write(FLEXCAN_MCR_SOFTRST, ®s->mcr); + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_SOFTRST)) + udelay(10); + + if (priv->read(®s->mcr) & FLEXCAN_MCR_SOFTRST) + return -ETIMEDOUT; + + return 0; +} + +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->regs; + u32 reg = priv->read(®s->ecr); + + bec->txerr = (reg >> 0) & 0xff; + bec->rxerr = (reg >> 8) & 0xff; + + return 0; +} + +static int flexcan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + int err; + + err = clk_prepare_enable(priv->clk_ipg); + if (err) + return err; + + err = clk_prepare_enable(priv->clk_per); + if (err) + goto out_disable_ipg; + + err = __flexcan_get_berr_counter(dev, bec); + + clk_disable_unprepare(priv->clk_per); + out_disable_ipg: + clk_disable_unprepare(priv->clk_ipg); + + return err; +} + +static netdev_tx_t 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->regs; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 can_id; + u32 data; + u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (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) { + data = be32_to_cpup((__be32 *)&cf->data[0]); + priv->write(data, ®s->mb[FLEXCAN_TX_MB].data[0]); + } + if (cf->can_dlc > 4) { + data = be32_to_cpup((__be32 *)&cf->data[4]); + priv->write(data, ®s->mb[FLEXCAN_TX_MB].data[1]); + } + + can_put_echo_skb(skb, dev, 0); + + priv->write(can_id, ®s->mb[FLEXCAN_TX_MB].can_id); + priv->write(ctrl, ®s->mb[FLEXCAN_TX_MB].can_ctrl); + + /* Errata ERR005829 step8: + * Write twice INACTIVE(0x8) code to first MB. + */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + &priv->tx_mb_reserved->can_ctrl); + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + &priv->tx_mb_reserved->can_ctrl); + + return NETDEV_TX_OK; +} + +static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; + struct sk_buff *skb; + struct can_frame *cf; + bool rx_errors = false, tx_errors = false; + u32 timestamp; + int err; + + timestamp = priv->read(®s->timer) << 16; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return; + + 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 = true; + } + if (reg_esr & FLEXCAN_ESR_BIT0_ERR) { + netdev_dbg(dev, "BIT0_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_BIT0; + tx_errors = true; + } + 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 = true; + } + 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 = true; + } + if (reg_esr & FLEXCAN_ESR_FRM_ERR) { + netdev_dbg(dev, "FRM_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_FORM; + rx_errors = true; + } + if (reg_esr & FLEXCAN_ESR_STF_ERR) { + netdev_dbg(dev, "STF_ERR irq\n"); + cf->data[2] |= CAN_ERR_PROT_STUFF; + rx_errors = true; + } + + priv->can.can_stats.bus_error++; + if (rx_errors) + dev->stats.rx_errors++; + if (tx_errors) + dev->stats.tx_errors++; + + err = can_rx_offload_queue_sorted(&priv->offload, skb, timestamp); + if (err) + dev->stats.rx_fifo_errors++; +} + +static void flexcan_irq_state(struct net_device *dev, u32 reg_esr) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; + struct sk_buff *skb; + struct can_frame *cf; + enum can_state new_state, rx_state, tx_state; + int flt; + struct can_berr_counter bec; + u32 timestamp; + int err; + + timestamp = priv->read(®s->timer) << 16; + + flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK; + if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) { + tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ? + CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; + rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ? + CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; + new_state = max(tx_state, rx_state); + } else { + __flexcan_get_berr_counter(dev, &bec); + new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ? + CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF; + rx_state = bec.rxerr >= bec.txerr ? new_state : 0; + tx_state = bec.rxerr <= bec.txerr ? new_state : 0; + } + + /* state hasn't changed */ + if (likely(new_state == priv->can.state)) + return; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return; + + can_change_state(dev, cf, tx_state, rx_state); + + if (unlikely(new_state == CAN_STATE_BUS_OFF)) + can_bus_off(dev); + + err = can_rx_offload_queue_sorted(&priv->offload, skb, timestamp); + if (err) + dev->stats.rx_fifo_errors++; +} + +static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload) +{ + return container_of(offload, struct flexcan_priv, offload); +} + +static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload, + struct can_frame *cf, + u32 *timestamp, unsigned int n) +{ + struct flexcan_priv *priv = rx_offload_to_priv(offload); + struct flexcan_regs __iomem *regs = priv->regs; + struct flexcan_mb __iomem *mb = ®s->mb[n]; + u32 reg_ctrl, reg_id, reg_iflag1; + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { + u32 code; + + do { + reg_ctrl = priv->read(&mb->can_ctrl); + } while (reg_ctrl & FLEXCAN_MB_CODE_RX_BUSY_BIT); + + /* is this MB empty? */ + code = reg_ctrl & FLEXCAN_MB_CODE_MASK; + if ((code != FLEXCAN_MB_CODE_RX_FULL) && + (code != FLEXCAN_MB_CODE_RX_OVERRUN)) + return 0; + + if (code == FLEXCAN_MB_CODE_RX_OVERRUN) { + /* This MB was overrun, we lost data */ + offload->dev->stats.rx_over_errors++; + offload->dev->stats.rx_errors++; + } + } else { + reg_iflag1 = priv->read(®s->iflag1); + if (!(reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) + return 0; + + reg_ctrl = priv->read(&mb->can_ctrl); + } + + /* increase timstamp to full 32 bit */ + *timestamp = reg_ctrl << 16; + + reg_id = priv->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(priv->read(&mb->data[0])); + *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1])); + + /* mark as read */ + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { + /* Clear IRQ */ + if (n < 32) + priv->write(BIT(n), ®s->iflag1); + else + priv->write(BIT(n - 32), ®s->iflag2); + } else { + priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); + } + + /* Read the Free Running Timer. It is optional but recommended + * to unlock Mailbox as soon as possible and make it available + * for reception. + */ + priv->read(®s->timer); + + return 1; +} + + +static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + u32 iflag1, iflag2; + + iflag2 = priv->read(®s->iflag2) & priv->reg_imask2_default & + ~FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB); + iflag1 = priv->read(®s->iflag1) & priv->reg_imask1_default; + + return (u64)iflag2 << 32 | iflag1; +} + +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->regs; + irqreturn_t handled = IRQ_NONE; + u32 reg_iflag2, reg_esr; + enum can_state last_state = priv->can.state; + + /* reception interrupt */ + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { + u64 reg_iflag; + int ret; + + while ((reg_iflag = flexcan_read_reg_iflag_rx(priv))) { + handled = IRQ_HANDLED; + ret = can_rx_offload_irq_offload_timestamp(&priv->offload, + reg_iflag); + if (!ret) + break; + } + } else { + u32 reg_iflag1; + + reg_iflag1 = priv->read(®s->iflag1); + if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) { + handled = IRQ_HANDLED; + can_rx_offload_irq_offload_fifo(&priv->offload); + } + + /* FIFO overflow interrupt */ + if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { + handled = IRQ_HANDLED; + priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, + ®s->iflag1); + dev->stats.rx_over_errors++; + dev->stats.rx_errors++; + } + } + + reg_iflag2 = priv->read(®s->iflag2); + + /* transmission complete interrupt */ + if (reg_iflag2 & FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB)) { + u32 reg_ctrl = priv->read(®s->mb[FLEXCAN_TX_MB].can_ctrl); + + handled = IRQ_HANDLED; + stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload, + 0, reg_ctrl << 16); + stats->tx_packets++; + can_led_event(dev, CAN_LED_EVENT_TX); + + /* after sending a RTR frame MB is in RX mode */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + ®s->mb[FLEXCAN_TX_MB].can_ctrl); + priv->write(FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB), ®s->iflag2); + netif_wake_queue(dev); + } + + reg_esr = priv->read(®s->esr); + + /* ACK all bus error and state change IRQ sources */ + if (reg_esr & FLEXCAN_ESR_ALL_INT) { + handled = IRQ_HANDLED; + priv->write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); + } + + /* state change interrupt or broken error state quirk fix is enabled */ + if ((reg_esr & FLEXCAN_ESR_ERR_STATE) || + (priv->devtype_data->quirks & (FLEXCAN_QUIRK_BROKEN_WERR_STATE | + FLEXCAN_QUIRK_BROKEN_PERR_STATE))) + flexcan_irq_state(dev, reg_esr); + + /* bus error IRQ - handle if bus error reporting is activated */ + if ((reg_esr & FLEXCAN_ESR_ERR_BUS) && + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) + flexcan_irq_bus_err(dev, reg_esr); + + /* availability of error interrupt among state transitions in case + * bus error reporting is de-activated and + * FLEXCAN_QUIRK_BROKEN_PERR_STATE is enabled: + * +--------------------------------------------------------------+ + * | +----------------------------------------------+ [stopped / | + * | | | sleeping] -+ + * +-+-> active <-> warning <-> passive -> bus off -+ + * ___________^^^^^^^^^^^^_______________________________ + * disabled(1) enabled disabled + * + * (1): enabled if FLEXCAN_QUIRK_BROKEN_WERR_STATE is enabled + */ + if ((last_state != priv->can.state) && + (priv->devtype_data->quirks & FLEXCAN_QUIRK_BROKEN_PERR_STATE) && + !(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) { + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (priv->devtype_data->quirks & + FLEXCAN_QUIRK_BROKEN_WERR_STATE) + flexcan_error_irq_enable(priv); + else + flexcan_error_irq_disable(priv); + break; + + case CAN_STATE_ERROR_WARNING: + flexcan_error_irq_enable(priv); + break; + + case CAN_STATE_ERROR_PASSIVE: + case CAN_STATE_BUS_OFF: + flexcan_error_irq_disable(priv); + break; + + default: + break; + } + } + + return 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->regs; + u32 reg; + + reg = priv->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_dbg(dev, "writing ctrl=0x%08x\n", reg); + priv->write(reg, ®s->ctrl); + + /* print chip status */ + netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, + priv->read(®s->mcr), priv->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->regs; + u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr; + int err, i; + + /* enable module */ + err = flexcan_chip_enable(priv); + if (err) + return err; + + /* soft reset */ + err = flexcan_chip_softreset(priv); + if (err) + goto out_chip_disable; + + flexcan_set_bittiming(dev); + + /* MCR + * + * enable freeze + * enable fifo + * halt now + * only supervisor access + * enable warning int + * disable local echo + * enable individual RX masking + * choose format C + * set max mailbox number + */ + reg_mcr = priv->read(®s->mcr); + reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); + reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV | + FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ | + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_MB); + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) + reg_mcr &= ~FLEXCAN_MCR_FEN; + else + reg_mcr |= FLEXCAN_MCR_FEN; + + netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); + priv->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) + */ + reg_ctrl = priv->read(®s->ctrl); + reg_ctrl &= ~FLEXCAN_CTRL_TSYN; + reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | + FLEXCAN_CTRL_ERR_STATE; + + /* enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK), + * on most Flexcan cores, too. Otherwise we don't get + * any error warning or passive interrupts. + */ + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_BROKEN_WERR_STATE || + priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + reg_ctrl |= FLEXCAN_CTRL_ERR_MSK; + else + reg_ctrl &= ~FLEXCAN_CTRL_ERR_MSK; + + /* save for later use */ + priv->reg_ctrl_default = reg_ctrl; + /* leave interrupts disabled for now */ + reg_ctrl &= ~FLEXCAN_CTRL_ERR_ALL; + netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); + priv->write(reg_ctrl, ®s->ctrl); + + if ((priv->devtype_data->quirks & FLEXCAN_QUIRK_ENABLE_EACEN_RRS)) { + reg_ctrl2 = priv->read(®s->ctrl2); + reg_ctrl2 |= FLEXCAN_CTRL2_EACEN | FLEXCAN_CTRL2_RRS; + priv->write(reg_ctrl2, ®s->ctrl2); + } + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { + for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) { + priv->write(FLEXCAN_MB_CODE_RX_EMPTY, + ®s->mb[i].can_ctrl); + } + } else { + /* clear and invalidate unused mailboxes first */ + for (i = FLEXCAN_TX_MB_RESERVED_OFF_FIFO; i < ARRAY_SIZE(regs->mb); i++) { + priv->write(FLEXCAN_MB_CODE_RX_INACTIVE, + ®s->mb[i].can_ctrl); + } + } + + /* Errata ERR005829: mark first TX mailbox as INACTIVE */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + &priv->tx_mb_reserved->can_ctrl); + + /* mark TX mailbox as INACTIVE */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + ®s->mb[FLEXCAN_TX_MB].can_ctrl); + + /* acceptance mask/acceptance code (accept everything) */ + priv->write(0x0, ®s->rxgmask); + priv->write(0x0, ®s->rx14mask); + priv->write(0x0, ®s->rx15mask); + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) + priv->write(0x0, ®s->rxfgmask); + + /* clear acceptance filters */ + for (i = 0; i < ARRAY_SIZE(regs->mb); i++) + priv->write(0, ®s->rximr[i]); + + /* On Vybrid, disable memory error detection interrupts + * and freeze mode. + * This also works around errata e5295 which generates + * false positive memory errors and put the device in + * freeze mode. + */ + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_MECR) { + /* Follow the protocol as described in "Detection + * and Correction of Memory Errors" to write to + * MECR register + */ + reg_ctrl2 = priv->read(®s->ctrl2); + reg_ctrl2 |= FLEXCAN_CTRL2_ECRWRE; + priv->write(reg_ctrl2, ®s->ctrl2); + + reg_mecr = priv->read(®s->mecr); + reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS; + priv->write(reg_mecr, ®s->mecr); + reg_mecr |= FLEXCAN_MECR_ECCDIS; + reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK | + FLEXCAN_MECR_FANCEI_MSK); + priv->write(reg_mecr, ®s->mecr); + } + + err = flexcan_transceiver_enable(priv); + if (err) + goto out_chip_disable; + + /* synchronize with the can bus */ + err = flexcan_chip_unfreeze(priv); + if (err) + goto out_transceiver_disable; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* enable interrupts atomically */ + disable_irq(dev->irq); + priv->write(priv->reg_ctrl_default, ®s->ctrl); + priv->write(priv->reg_imask1_default, ®s->imask1); + priv->write(priv->reg_imask2_default, ®s->imask2); + enable_irq(dev->irq); + + /* print chip status */ + netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, + priv->read(®s->mcr), priv->read(®s->ctrl)); + + return 0; + + out_transceiver_disable: + flexcan_transceiver_disable(priv); + out_chip_disable: + flexcan_chip_disable(priv); + return err; +} + +/* __flexcan_chip_stop + * + * this function is entered with clocks enabled + */ +static int __flexcan_chip_stop(struct net_device *dev, bool disable_on_error) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; + int err; + + /* freeze + disable module */ + err = flexcan_chip_freeze(priv); + if (err && !disable_on_error) + return err; + err = flexcan_chip_disable(priv); + if (err && !disable_on_error) + goto out_chip_unfreeze; + + /* Disable all interrupts */ + priv->write(0, ®s->imask2); + priv->write(0, ®s->imask1); + priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + ®s->ctrl); + + flexcan_transceiver_disable(priv); + priv->can.state = CAN_STATE_STOPPED; + + return 0; + + out_chip_unfreeze: + flexcan_chip_unfreeze(priv); + + return err; +} + +static inline int flexcan_chip_stop_disable_on_error(struct net_device *dev) +{ + return __flexcan_chip_stop(dev, true); +} + +static inline int flexcan_chip_stop(struct net_device *dev) +{ + return __flexcan_chip_stop(dev, false); +} + +static int flexcan_open(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + int err; + + err = clk_prepare_enable(priv->clk_ipg); + if (err) + return err; + + err = clk_prepare_enable(priv->clk_per); + if (err) + goto out_disable_ipg; + + err = open_candev(dev); + if (err) + goto out_disable_per; + + 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_free_irq; + + can_led_event(dev, CAN_LED_EVENT_OPEN); + + can_rx_offload_enable(&priv->offload); + netif_start_queue(dev); + + return 0; + + out_free_irq: + free_irq(dev->irq, dev); + out_close: + close_candev(dev); + out_disable_per: + clk_disable_unprepare(priv->clk_per); + out_disable_ipg: + clk_disable_unprepare(priv->clk_ipg); + + return err; +} + +static int flexcan_close(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + can_rx_offload_disable(&priv->offload); + flexcan_chip_stop_disable_on_error(dev); + + free_irq(dev->irq, dev); + clk_disable_unprepare(priv->clk_per); + clk_disable_unprepare(priv->clk_ipg); + + close_candev(dev); + + can_led_event(dev, CAN_LED_EVENT_STOP); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +static int register_flexcandev(struct net_device *dev) +{ + struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; + u32 reg, err; + + err = clk_prepare_enable(priv->clk_ipg); + if (err) + return err; + + err = clk_prepare_enable(priv->clk_per); + if (err) + goto out_disable_ipg; + + /* select "bus clock", chip must be disabled */ + err = flexcan_chip_disable(priv); + if (err) + goto out_disable_per; + reg = priv->read(®s->ctrl); + reg |= FLEXCAN_CTRL_CLK_SRC; + priv->write(reg, ®s->ctrl); + + err = flexcan_chip_enable(priv); + if (err) + goto out_chip_disable; + + /* set freeze, halt */ + err = flexcan_chip_freeze(priv); + if (err) + goto out_chip_disable; + + /* activate FIFO, restrict register access */ + reg = priv->read(®s->mcr); + reg |= FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; + priv->write(reg, ®s->mcr); + + /* Currently we only support newer versions of this core + * featuring a RX hardware FIFO (although this driver doesn't + * make use of it on some cores). Older cores, found on some + * Coldfire derivates are not tested. + */ + reg = priv->read(®s->mcr); + if (!(reg & FLEXCAN_MCR_FEN)) { + netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); + err = -ENODEV; + goto out_chip_disable; + } + + err = register_candev(dev); + + /* disable core and turn off clocks */ + out_chip_disable: + flexcan_chip_disable(priv); + out_disable_per: + clk_disable_unprepare(priv->clk_per); + out_disable_ipg: + clk_disable_unprepare(priv->clk_ipg); + + return err; +} + +static void unregister_flexcandev(struct net_device *dev) +{ + unregister_candev(dev); +} + +static const struct of_device_id flexcan_of_match[] = { + { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, + { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, }, + { .compatible = "fsl,imx53-flexcan", .data = &fsl_imx25_devtype_data, }, + { .compatible = "fsl,imx35-flexcan", .data = &fsl_imx25_devtype_data, }, + { .compatible = "fsl,imx25-flexcan", .data = &fsl_imx25_devtype_data, }, + { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, }, + { .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, }, + { .compatible = "fsl,ls1021ar2-flexcan", .data = &fsl_ls1021a_r2_devtype_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, flexcan_of_match); + +static const struct platform_device_id flexcan_id_table[] = { + { .name = "flexcan", .driver_data = (kernel_ulong_t)&fsl_p1010_devtype_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, flexcan_id_table); + +static int flexcan_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + const struct flexcan_devtype_data *devtype_data; + struct net_device *dev; + struct flexcan_priv *priv; + struct regulator *reg_xceiver; + struct resource *mem; + struct clk *clk_ipg = NULL, *clk_per = NULL; + struct flexcan_regs __iomem *regs; + int err, irq; + u32 clock_freq = 0; + + reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (IS_ERR(reg_xceiver)) + reg_xceiver = NULL; + + if (pdev->dev.of_node) + of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &clock_freq); + + if (!clock_freq) { + clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(clk_ipg)) { + dev_err(&pdev->dev, "no ipg clock defined\n"); + return PTR_ERR(clk_ipg); + } + + clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(clk_per)) { + dev_err(&pdev->dev, "no per clock defined\n"); + return PTR_ERR(clk_per); + } + clock_freq = clk_get_rate(clk_per); + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENODEV; + + regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + of_id = of_match_device(flexcan_of_match, &pdev->dev); + if (of_id) { + devtype_data = of_id->data; + } else if (platform_get_device_id(pdev)->driver_data) { + devtype_data = (struct flexcan_devtype_data *) + platform_get_device_id(pdev)->driver_data; + } else { + return -ENODEV; + } + + dev = alloc_candev(sizeof(struct flexcan_priv), 1); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + dev->netdev_ops = &flexcan_netdev_ops; + dev->irq = irq; + dev->flags |= IFF_ECHO; + + priv = netdev_priv(dev); + + if (of_property_read_bool(pdev->dev.of_node, "big-endian") || + devtype_data->quirks & FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN) { + priv->read = flexcan_read_be; + priv->write = flexcan_write_be; + } else { + priv->read = flexcan_read_le; + priv->write = flexcan_write_le; + } + + 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->regs = regs; + priv->clk_ipg = clk_ipg; + priv->clk_per = clk_per; + priv->devtype_data = devtype_data; + priv->reg_xceiver = reg_xceiver; + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) + priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP]; + else + priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO]; + + priv->reg_imask1_default = 0; + priv->reg_imask2_default = FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB); + + priv->offload.mailbox_read = flexcan_mailbox_read; + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { + u64 imask; + + priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST; + priv->offload.mb_last = FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST; + + imask = GENMASK_ULL(priv->offload.mb_last, priv->offload.mb_first); + priv->reg_imask1_default |= imask; + priv->reg_imask2_default |= imask >> 32; + + err = can_rx_offload_add_timestamp(dev, &priv->offload); + } else { + priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | + FLEXCAN_IFLAG_RX_FIFO_AVAILABLE; + err = can_rx_offload_add_fifo(dev, &priv->offload, FLEXCAN_NAPI_WEIGHT); + } + if (err) + goto failed_offload; + + err = register_flexcandev(dev); + if (err) { + dev_err(&pdev->dev, "registering netdev failed\n"); + goto failed_register; + } + + devm_can_led_init(dev); + + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", + priv->regs, dev->irq); + + return 0; + + failed_offload: + failed_register: + free_candev(dev); + return err; +} + +static int flexcan_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv = netdev_priv(dev); + + unregister_flexcandev(dev); + can_rx_offload_del(&priv->offload); + free_candev(dev); + + return 0; +} + +static int __maybe_unused flexcan_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct flexcan_priv *priv = netdev_priv(dev); + int err; + + if (netif_running(dev)) { + err = flexcan_chip_disable(priv); + if (err) + return err; + netif_stop_queue(dev); + netif_device_detach(dev); + } + priv->can.state = CAN_STATE_SLEEPING; + + return 0; +} + +static int __maybe_unused flexcan_resume(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct flexcan_priv *priv = netdev_priv(dev); + int err; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + err = flexcan_chip_enable(priv); + if (err) + return err; + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume); + +static struct platform_driver flexcan_driver = { + .driver = { + .name = DRV_NAME, + .pm = &flexcan_pm_ops, + .of_match_table = flexcan_of_match, + }, + .probe = flexcan_probe, + .remove = flexcan_remove, + .id_table = flexcan_id_table, +}; + +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/grcan.c b/drivers/net/can/grcan.c new file mode 100644 index 000000000..f07a6ff66 --- /dev/null +++ b/drivers/net/can/grcan.c @@ -0,0 +1,1749 @@ +/* + * Socket CAN driver for Aeroflex Gaisler GRCAN and GRHCAN. + * + * 2012 (c) Aeroflex Gaisler AB + * + * This driver supports GRCAN and GRHCAN CAN controllers available in the GRLIB + * VHDL IP core library. + * + * Full documentation of the GRCAN core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * See "Documentation/devicetree/bindings/net/can/grcan.txt" for information on + * open firmware properties. + * + * See "Documentation/ABI/testing/sysfs-class-net-grcan" for information on the + * sysfs interface. + * + * See "Documentation/admin-guide/kernel-parameters.rst" for information on the module + * parameters. + * + * 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. + * + * Contributors: Andreas Larsson <andreas@gaisler.com> + */ + +#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/spinlock.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> + +#include <linux/dma-mapping.h> + +#define DRV_NAME "grcan" + +#define GRCAN_NAPI_WEIGHT 32 + +#define GRCAN_RESERVE_SIZE(slot1, slot2) (((slot2) - (slot1)) / 4 - 1) + +struct grcan_registers { + u32 conf; /* 0x00 */ + u32 stat; /* 0x04 */ + u32 ctrl; /* 0x08 */ + u32 __reserved1[GRCAN_RESERVE_SIZE(0x08, 0x18)]; + u32 smask; /* 0x18 - CanMASK */ + u32 scode; /* 0x1c - CanCODE */ + u32 __reserved2[GRCAN_RESERVE_SIZE(0x1c, 0x100)]; + u32 pimsr; /* 0x100 */ + u32 pimr; /* 0x104 */ + u32 pisr; /* 0x108 */ + u32 pir; /* 0x10C */ + u32 imr; /* 0x110 */ + u32 picr; /* 0x114 */ + u32 __reserved3[GRCAN_RESERVE_SIZE(0x114, 0x200)]; + u32 txctrl; /* 0x200 */ + u32 txaddr; /* 0x204 */ + u32 txsize; /* 0x208 */ + u32 txwr; /* 0x20C */ + u32 txrd; /* 0x210 */ + u32 txirq; /* 0x214 */ + u32 __reserved4[GRCAN_RESERVE_SIZE(0x214, 0x300)]; + u32 rxctrl; /* 0x300 */ + u32 rxaddr; /* 0x304 */ + u32 rxsize; /* 0x308 */ + u32 rxwr; /* 0x30C */ + u32 rxrd; /* 0x310 */ + u32 rxirq; /* 0x314 */ + u32 rxmask; /* 0x318 */ + u32 rxcode; /* 0x31C */ +}; + +#define GRCAN_CONF_ABORT 0x00000001 +#define GRCAN_CONF_ENABLE0 0x00000002 +#define GRCAN_CONF_ENABLE1 0x00000004 +#define GRCAN_CONF_SELECT 0x00000008 +#define GRCAN_CONF_SILENT 0x00000010 +#define GRCAN_CONF_SAM 0x00000020 /* Available in some hardware */ +#define GRCAN_CONF_BPR 0x00000300 /* Note: not BRP */ +#define GRCAN_CONF_RSJ 0x00007000 +#define GRCAN_CONF_PS1 0x00f00000 +#define GRCAN_CONF_PS2 0x000f0000 +#define GRCAN_CONF_SCALER 0xff000000 +#define GRCAN_CONF_OPERATION \ + (GRCAN_CONF_ABORT | GRCAN_CONF_ENABLE0 | GRCAN_CONF_ENABLE1 \ + | GRCAN_CONF_SELECT | GRCAN_CONF_SILENT | GRCAN_CONF_SAM) +#define GRCAN_CONF_TIMING \ + (GRCAN_CONF_BPR | GRCAN_CONF_RSJ | GRCAN_CONF_PS1 \ + | GRCAN_CONF_PS2 | GRCAN_CONF_SCALER) + +#define GRCAN_CONF_RSJ_MIN 1 +#define GRCAN_CONF_RSJ_MAX 4 +#define GRCAN_CONF_PS1_MIN 1 +#define GRCAN_CONF_PS1_MAX 15 +#define GRCAN_CONF_PS2_MIN 2 +#define GRCAN_CONF_PS2_MAX 8 +#define GRCAN_CONF_SCALER_MIN 0 +#define GRCAN_CONF_SCALER_MAX 255 +#define GRCAN_CONF_SCALER_INC 1 + +#define GRCAN_CONF_BPR_BIT 8 +#define GRCAN_CONF_RSJ_BIT 12 +#define GRCAN_CONF_PS1_BIT 20 +#define GRCAN_CONF_PS2_BIT 16 +#define GRCAN_CONF_SCALER_BIT 24 + +#define GRCAN_STAT_PASS 0x000001 +#define GRCAN_STAT_OFF 0x000002 +#define GRCAN_STAT_OR 0x000004 +#define GRCAN_STAT_AHBERR 0x000008 +#define GRCAN_STAT_ACTIVE 0x000010 +#define GRCAN_STAT_RXERRCNT 0x00ff00 +#define GRCAN_STAT_TXERRCNT 0xff0000 + +#define GRCAN_STAT_ERRCTR_RELATED (GRCAN_STAT_PASS | GRCAN_STAT_OFF) + +#define GRCAN_STAT_RXERRCNT_BIT 8 +#define GRCAN_STAT_TXERRCNT_BIT 16 + +#define GRCAN_STAT_ERRCNT_WARNING_LIMIT 96 +#define GRCAN_STAT_ERRCNT_PASSIVE_LIMIT 127 + +#define GRCAN_CTRL_RESET 0x2 +#define GRCAN_CTRL_ENABLE 0x1 + +#define GRCAN_TXCTRL_ENABLE 0x1 +#define GRCAN_TXCTRL_ONGOING 0x2 +#define GRCAN_TXCTRL_SINGLE 0x4 + +#define GRCAN_RXCTRL_ENABLE 0x1 +#define GRCAN_RXCTRL_ONGOING 0x2 + +/* Relative offset of IRQ sources to AMBA Plug&Play */ +#define GRCAN_IRQIX_IRQ 0 +#define GRCAN_IRQIX_TXSYNC 1 +#define GRCAN_IRQIX_RXSYNC 2 + +#define GRCAN_IRQ_PASS 0x00001 +#define GRCAN_IRQ_OFF 0x00002 +#define GRCAN_IRQ_OR 0x00004 +#define GRCAN_IRQ_RXAHBERR 0x00008 +#define GRCAN_IRQ_TXAHBERR 0x00010 +#define GRCAN_IRQ_RXIRQ 0x00020 +#define GRCAN_IRQ_TXIRQ 0x00040 +#define GRCAN_IRQ_RXFULL 0x00080 +#define GRCAN_IRQ_TXEMPTY 0x00100 +#define GRCAN_IRQ_RX 0x00200 +#define GRCAN_IRQ_TX 0x00400 +#define GRCAN_IRQ_RXSYNC 0x00800 +#define GRCAN_IRQ_TXSYNC 0x01000 +#define GRCAN_IRQ_RXERRCTR 0x02000 +#define GRCAN_IRQ_TXERRCTR 0x04000 +#define GRCAN_IRQ_RXMISS 0x08000 +#define GRCAN_IRQ_TXLOSS 0x10000 + +#define GRCAN_IRQ_NONE 0 +#define GRCAN_IRQ_ALL \ + (GRCAN_IRQ_PASS | GRCAN_IRQ_OFF | GRCAN_IRQ_OR \ + | GRCAN_IRQ_RXAHBERR | GRCAN_IRQ_TXAHBERR \ + | GRCAN_IRQ_RXIRQ | GRCAN_IRQ_TXIRQ \ + | GRCAN_IRQ_RXFULL | GRCAN_IRQ_TXEMPTY \ + | GRCAN_IRQ_RX | GRCAN_IRQ_TX | GRCAN_IRQ_RXSYNC \ + | GRCAN_IRQ_TXSYNC | GRCAN_IRQ_RXERRCTR \ + | GRCAN_IRQ_TXERRCTR | GRCAN_IRQ_RXMISS \ + | GRCAN_IRQ_TXLOSS) + +#define GRCAN_IRQ_ERRCTR_RELATED (GRCAN_IRQ_RXERRCTR | GRCAN_IRQ_TXERRCTR \ + | GRCAN_IRQ_PASS | GRCAN_IRQ_OFF) +#define GRCAN_IRQ_ERRORS (GRCAN_IRQ_ERRCTR_RELATED | GRCAN_IRQ_OR \ + | GRCAN_IRQ_TXAHBERR | GRCAN_IRQ_RXAHBERR \ + | GRCAN_IRQ_TXLOSS) +#define GRCAN_IRQ_DEFAULT (GRCAN_IRQ_RX | GRCAN_IRQ_TX | GRCAN_IRQ_ERRORS) + +#define GRCAN_MSG_SIZE 16 + +#define GRCAN_MSG_IDE 0x80000000 +#define GRCAN_MSG_RTR 0x40000000 +#define GRCAN_MSG_BID 0x1ffc0000 +#define GRCAN_MSG_EID 0x1fffffff +#define GRCAN_MSG_IDE_BIT 31 +#define GRCAN_MSG_RTR_BIT 30 +#define GRCAN_MSG_BID_BIT 18 +#define GRCAN_MSG_EID_BIT 0 + +#define GRCAN_MSG_DLC 0xf0000000 +#define GRCAN_MSG_TXERRC 0x00ff0000 +#define GRCAN_MSG_RXERRC 0x0000ff00 +#define GRCAN_MSG_DLC_BIT 28 +#define GRCAN_MSG_TXERRC_BIT 16 +#define GRCAN_MSG_RXERRC_BIT 8 +#define GRCAN_MSG_AHBERR 0x00000008 +#define GRCAN_MSG_OR 0x00000004 +#define GRCAN_MSG_OFF 0x00000002 +#define GRCAN_MSG_PASS 0x00000001 + +#define GRCAN_MSG_DATA_SLOT_INDEX(i) (2 + (i) / 4) +#define GRCAN_MSG_DATA_SHIFT(i) ((3 - (i) % 4) * 8) + +#define GRCAN_BUFFER_ALIGNMENT 1024 +#define GRCAN_DEFAULT_BUFFER_SIZE 1024 +#define GRCAN_VALID_TR_SIZE_MASK 0x001fffc0 + +#define GRCAN_INVALID_BUFFER_SIZE(s) \ + ((s) == 0 || ((s) & ~GRCAN_VALID_TR_SIZE_MASK)) + +#if GRCAN_INVALID_BUFFER_SIZE(GRCAN_DEFAULT_BUFFER_SIZE) +#error "Invalid default buffer size" +#endif + +struct grcan_dma_buffer { + size_t size; + void *buf; + dma_addr_t handle; +}; + +struct grcan_dma { + size_t base_size; + void *base_buf; + dma_addr_t base_handle; + struct grcan_dma_buffer tx; + struct grcan_dma_buffer rx; +}; + +/* GRCAN configuration parameters */ +struct grcan_device_config { + unsigned short enable0; + unsigned short enable1; + unsigned short select; + unsigned int txsize; + unsigned int rxsize; +}; + +#define GRCAN_DEFAULT_DEVICE_CONFIG { \ + .enable0 = 0, \ + .enable1 = 0, \ + .select = 0, \ + .txsize = GRCAN_DEFAULT_BUFFER_SIZE, \ + .rxsize = GRCAN_DEFAULT_BUFFER_SIZE, \ + } + +#define GRCAN_TXBUG_SAFE_GRLIB_VERSION 4100 +#define GRLIB_VERSION_MASK 0xffff + +/* GRCAN private data structure */ +struct grcan_priv { + struct can_priv can; /* must be the first member */ + struct net_device *dev; + struct device *ofdev_dev; + struct napi_struct napi; + + struct grcan_registers __iomem *regs; /* ioremap'ed registers */ + struct grcan_device_config config; + struct grcan_dma dma; + + struct sk_buff **echo_skb; /* We allocate this on our own */ + u8 *txdlc; /* Length of queued frames */ + + /* The echo skb pointer, pointing into echo_skb and indicating which + * frames can be echoed back. See the "Notes on the tx cyclic buffer + * handling"-comment for grcan_start_xmit for more details. + */ + u32 eskbp; + + /* Lock for controlling changes to the netif tx queue state, accesses to + * the echo_skb pointer eskbp and for making sure that a running reset + * and/or a close of the interface is done without interference from + * other parts of the code. + * + * The echo_skb pointer, eskbp, should only be accessed under this lock + * as it can be changed in several places and together with decisions on + * whether to wake up the tx queue. + * + * The tx queue must never be woken up if there is a running reset or + * close in progress. + * + * A running reset (see below on need_txbug_workaround) should never be + * done if the interface is closing down and several running resets + * should never be scheduled simultaneously. + */ + spinlock_t lock; + + /* Whether a workaround is needed due to a bug in older hardware. In + * this case, the driver both tries to prevent the bug from being + * triggered and recovers, if the bug nevertheless happens, by doing a + * running reset. A running reset, resets the device and continues from + * where it were without being noticeable from outside the driver (apart + * from slight delays). + */ + bool need_txbug_workaround; + + /* To trigger initization of running reset and to trigger running reset + * respectively in the case of a hanged device due to a txbug. + */ + struct timer_list hang_timer; + struct timer_list rr_timer; + + /* To avoid waking up the netif queue and restarting timers + * when a reset is scheduled or when closing of the device is + * undergoing + */ + bool resetting; + bool closing; +}; + +/* Wait time for a short wait for ongoing to clear */ +#define GRCAN_SHORTWAIT_USECS 10 + +/* Limit on the number of transmitted bits of an eff frame according to the CAN + * specification: 1 bit start of frame, 32 bits arbitration field, 6 bits + * control field, 8 bytes data field, 16 bits crc field, 2 bits ACK field and 7 + * bits end of frame + */ +#define GRCAN_EFF_FRAME_MAX_BITS (1+32+6+8*8+16+2+7) + +#if defined(__BIG_ENDIAN) +static inline u32 grcan_read_reg(u32 __iomem *reg) +{ + return ioread32be(reg); +} + +static inline void grcan_write_reg(u32 __iomem *reg, u32 val) +{ + iowrite32be(val, reg); +} +#else +static inline u32 grcan_read_reg(u32 __iomem *reg) +{ + return ioread32(reg); +} + +static inline void grcan_write_reg(u32 __iomem *reg, u32 val) +{ + iowrite32(val, reg); +} +#endif + +static inline void grcan_clear_bits(u32 __iomem *reg, u32 mask) +{ + grcan_write_reg(reg, grcan_read_reg(reg) & ~mask); +} + +static inline void grcan_set_bits(u32 __iomem *reg, u32 mask) +{ + grcan_write_reg(reg, grcan_read_reg(reg) | mask); +} + +static inline u32 grcan_read_bits(u32 __iomem *reg, u32 mask) +{ + return grcan_read_reg(reg) & mask; +} + +static inline void grcan_write_bits(u32 __iomem *reg, u32 value, u32 mask) +{ + u32 old = grcan_read_reg(reg); + + grcan_write_reg(reg, (old & ~mask) | (value & mask)); +} + +/* a and b should both be in [0,size] and a == b == size should not hold */ +static inline u32 grcan_ring_add(u32 a, u32 b, u32 size) +{ + u32 sum = a + b; + + if (sum < size) + return sum; + else + return sum - size; +} + +/* a and b should both be in [0,size) */ +static inline u32 grcan_ring_sub(u32 a, u32 b, u32 size) +{ + return grcan_ring_add(a, size - b, size); +} + +/* Available slots for new transmissions */ +static inline u32 grcan_txspace(size_t txsize, u32 txwr, u32 eskbp) +{ + u32 slots = txsize / GRCAN_MSG_SIZE - 1; + u32 used = grcan_ring_sub(txwr, eskbp, txsize) / GRCAN_MSG_SIZE; + + return slots - used; +} + +/* Configuration parameters that can be set via module parameters */ +static struct grcan_device_config grcan_module_config = + GRCAN_DEFAULT_DEVICE_CONFIG; + +static const struct can_bittiming_const grcan_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = GRCAN_CONF_PS1_MIN + 1, + .tseg1_max = GRCAN_CONF_PS1_MAX + 1, + .tseg2_min = GRCAN_CONF_PS2_MIN, + .tseg2_max = GRCAN_CONF_PS2_MAX, + .sjw_max = GRCAN_CONF_RSJ_MAX, + .brp_min = GRCAN_CONF_SCALER_MIN + 1, + .brp_max = GRCAN_CONF_SCALER_MAX + 1, + .brp_inc = GRCAN_CONF_SCALER_INC, +}; + +static int grcan_set_bittiming(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct can_bittiming *bt = &priv->can.bittiming; + u32 timing = 0; + int bpr, rsj, ps1, ps2, scaler; + + /* Should never happen - function will not be called when + * device is up + */ + if (grcan_read_bits(®s->ctrl, GRCAN_CTRL_ENABLE)) + return -EBUSY; + + bpr = 0; /* Note bpr and brp are different concepts */ + rsj = bt->sjw; + ps1 = (bt->prop_seg + bt->phase_seg1) - 1; /* tseg1 - 1 */ + ps2 = bt->phase_seg2; + scaler = (bt->brp - 1); + netdev_dbg(dev, "Request for BPR=%d, RSJ=%d, PS1=%d, PS2=%d, SCALER=%d", + bpr, rsj, ps1, ps2, scaler); + if (!(ps1 > ps2)) { + netdev_err(dev, "PS1 > PS2 must hold: PS1=%d, PS2=%d\n", + ps1, ps2); + return -EINVAL; + } + if (!(ps2 >= rsj)) { + netdev_err(dev, "PS2 >= RSJ must hold: PS2=%d, RSJ=%d\n", + ps2, rsj); + return -EINVAL; + } + + timing |= (bpr << GRCAN_CONF_BPR_BIT) & GRCAN_CONF_BPR; + timing |= (rsj << GRCAN_CONF_RSJ_BIT) & GRCAN_CONF_RSJ; + timing |= (ps1 << GRCAN_CONF_PS1_BIT) & GRCAN_CONF_PS1; + timing |= (ps2 << GRCAN_CONF_PS2_BIT) & GRCAN_CONF_PS2; + timing |= (scaler << GRCAN_CONF_SCALER_BIT) & GRCAN_CONF_SCALER; + netdev_info(dev, "setting timing=0x%x\n", timing); + grcan_write_bits(®s->conf, timing, GRCAN_CONF_TIMING); + + return 0; +} + +static int grcan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 status = grcan_read_reg(®s->stat); + + bec->txerr = (status & GRCAN_STAT_TXERRCNT) >> GRCAN_STAT_TXERRCNT_BIT; + bec->rxerr = (status & GRCAN_STAT_RXERRCNT) >> GRCAN_STAT_RXERRCNT_BIT; + return 0; +} + +static int grcan_poll(struct napi_struct *napi, int budget); + +/* Reset device, but keep configuration information */ +static void grcan_reset(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 config = grcan_read_reg(®s->conf); + + grcan_set_bits(®s->ctrl, GRCAN_CTRL_RESET); + grcan_write_reg(®s->conf, config); + + priv->eskbp = grcan_read_reg(®s->txrd); + priv->can.state = CAN_STATE_STOPPED; + + /* Turn off hardware filtering - regs->rxcode set to 0 by reset */ + grcan_write_reg(®s->rxmask, 0); +} + +/* stop device without changing any configurations */ +static void grcan_stop_hardware(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + + grcan_write_reg(®s->imr, GRCAN_IRQ_NONE); + grcan_clear_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE); + grcan_clear_bits(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_clear_bits(®s->ctrl, GRCAN_CTRL_ENABLE); +} + +/* Let priv->eskbp catch up to regs->txrd and echo back the skbs if echo + * is true and free them otherwise. + * + * If budget is >= 0, stop after handling at most budget skbs. Otherwise, + * continue until priv->eskbp catches up to regs->txrd. + * + * priv->lock *must* be held when calling this function + */ +static int catch_up_echo_skb(struct net_device *dev, int budget, bool echo) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct net_device_stats *stats = &dev->stats; + int i, work_done; + + /* Updates to priv->eskbp and wake-ups of the queue needs to + * be atomic towards the reads of priv->eskbp and shut-downs + * of the queue in grcan_start_xmit. + */ + u32 txrd = grcan_read_reg(®s->txrd); + + for (work_done = 0; work_done < budget || budget < 0; work_done++) { + if (priv->eskbp == txrd) + break; + i = priv->eskbp / GRCAN_MSG_SIZE; + if (echo) { + /* Normal echo of messages */ + stats->tx_packets++; + stats->tx_bytes += priv->txdlc[i]; + priv->txdlc[i] = 0; + can_get_echo_skb(dev, i); + } else { + /* For cleanup of untransmitted messages */ + can_free_echo_skb(dev, i); + } + + priv->eskbp = grcan_ring_add(priv->eskbp, GRCAN_MSG_SIZE, + dma->tx.size); + txrd = grcan_read_reg(®s->txrd); + } + return work_done; +} + +static void grcan_lost_one_shot_frame(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + u32 txrd; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + catch_up_echo_skb(dev, -1, true); + + if (unlikely(grcan_read_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE))) { + /* Should never happen */ + netdev_err(dev, "TXCTRL enabled at TXLOSS in one shot mode\n"); + } else { + /* By the time an GRCAN_IRQ_TXLOSS is generated in + * one-shot mode there is no problem in writing + * to TXRD even in versions of the hardware in + * which GRCAN_TXCTRL_ONGOING is not cleared properly + * in one-shot mode. + */ + + /* Skip message and discard echo-skb */ + txrd = grcan_read_reg(®s->txrd); + txrd = grcan_ring_add(txrd, GRCAN_MSG_SIZE, dma->tx.size); + grcan_write_reg(®s->txrd, txrd); + catch_up_echo_skb(dev, -1, false); + + if (!priv->resetting && !priv->closing && + !(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) { + netif_wake_queue(dev); + grcan_set_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE); + } + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void grcan_err(struct net_device *dev, u32 sources, u32 status) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct net_device_stats *stats = &dev->stats; + struct can_frame cf; + + /* Zero potential error_frame */ + memset(&cf, 0, sizeof(cf)); + + /* Message lost interrupt. This might be due to arbitration error, but + * is also triggered when there is no one else on the can bus or when + * there is a problem with the hardware interface or the bus itself. As + * arbitration errors can not be singled out, no error frames are + * generated reporting this event as an arbitration error. + */ + if (sources & GRCAN_IRQ_TXLOSS) { + /* Take care of failed one-shot transmit */ + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + grcan_lost_one_shot_frame(dev); + + /* Stop printing as soon as error passive or bus off is in + * effect to limit the amount of txloss debug printouts. + */ + if (!(status & GRCAN_STAT_ERRCTR_RELATED)) { + netdev_dbg(dev, "tx message lost\n"); + stats->tx_errors++; + } + } + + /* Conditions dealing with the error counters. There is no interrupt for + * error warning, but there are interrupts for increases of the error + * counters. + */ + if ((sources & GRCAN_IRQ_ERRCTR_RELATED) || + (status & GRCAN_STAT_ERRCTR_RELATED)) { + enum can_state state = priv->can.state; + enum can_state oldstate = state; + u32 txerr = (status & GRCAN_STAT_TXERRCNT) + >> GRCAN_STAT_TXERRCNT_BIT; + u32 rxerr = (status & GRCAN_STAT_RXERRCNT) + >> GRCAN_STAT_RXERRCNT_BIT; + + /* Figure out current state */ + if (status & GRCAN_STAT_OFF) { + state = CAN_STATE_BUS_OFF; + } else if (status & GRCAN_STAT_PASS) { + state = CAN_STATE_ERROR_PASSIVE; + } else if (txerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT || + rxerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT) { + state = CAN_STATE_ERROR_WARNING; + } else { + state = CAN_STATE_ERROR_ACTIVE; + } + + /* Handle and report state changes */ + if (state != oldstate) { + switch (state) { + case CAN_STATE_BUS_OFF: + netdev_dbg(dev, "bus-off\n"); + netif_carrier_off(dev); + priv->can.can_stats.bus_off++; + + /* Prevent the hardware from recovering from bus + * off on its own if restart is disabled. + */ + if (!priv->can.restart_ms) + grcan_stop_hardware(dev); + + cf.can_id |= CAN_ERR_BUSOFF; + break; + + case CAN_STATE_ERROR_PASSIVE: + netdev_dbg(dev, "Error passive condition\n"); + priv->can.can_stats.error_passive++; + + cf.can_id |= CAN_ERR_CRTL; + if (txerr >= GRCAN_STAT_ERRCNT_PASSIVE_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (rxerr >= GRCAN_STAT_ERRCNT_PASSIVE_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + break; + + case CAN_STATE_ERROR_WARNING: + netdev_dbg(dev, "Error warning condition\n"); + priv->can.can_stats.error_warning++; + + cf.can_id |= CAN_ERR_CRTL; + if (txerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_TX_WARNING; + if (rxerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_RX_WARNING; + break; + + case CAN_STATE_ERROR_ACTIVE: + netdev_dbg(dev, "Error active condition\n"); + cf.can_id |= CAN_ERR_CRTL; + break; + + default: + /* There are no others at this point */ + break; + } + cf.data[6] = txerr; + cf.data[7] = rxerr; + priv->can.state = state; + } + + /* Report automatic restarts */ + if (priv->can.restart_ms && oldstate == CAN_STATE_BUS_OFF) { + unsigned long flags; + + cf.can_id |= CAN_ERR_RESTARTED; + netdev_dbg(dev, "restarted\n"); + priv->can.can_stats.restarts++; + netif_carrier_on(dev); + + spin_lock_irqsave(&priv->lock, flags); + + if (!priv->resetting && !priv->closing) { + u32 txwr = grcan_read_reg(®s->txwr); + + if (grcan_txspace(dma->tx.size, txwr, + priv->eskbp)) + netif_wake_queue(dev); + } + + spin_unlock_irqrestore(&priv->lock, flags); + } + } + + /* Data overrun interrupt */ + if ((sources & GRCAN_IRQ_OR) || (status & GRCAN_STAT_OR)) { + netdev_dbg(dev, "got data overrun interrupt\n"); + stats->rx_over_errors++; + stats->rx_errors++; + + cf.can_id |= CAN_ERR_CRTL; + cf.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + + /* AHB bus error interrupts (not CAN bus errors) - shut down the + * device. + */ + if (sources & (GRCAN_IRQ_TXAHBERR | GRCAN_IRQ_RXAHBERR) || + (status & GRCAN_STAT_AHBERR)) { + char *txrx = ""; + unsigned long flags; + + if (sources & GRCAN_IRQ_TXAHBERR) { + txrx = "on tx "; + stats->tx_errors++; + } else if (sources & GRCAN_IRQ_RXAHBERR) { + txrx = "on rx "; + stats->rx_errors++; + } + netdev_err(dev, "Fatal AHB buss error %s- halting device\n", + txrx); + + spin_lock_irqsave(&priv->lock, flags); + + /* Prevent anything to be enabled again and halt device */ + priv->closing = true; + netif_stop_queue(dev); + grcan_stop_hardware(dev); + priv->can.state = CAN_STATE_STOPPED; + + spin_unlock_irqrestore(&priv->lock, flags); + } + + /* Pass on error frame if something to report, + * i.e. id contains some information + */ + if (cf.can_id) { + struct can_frame *skb_cf; + struct sk_buff *skb = alloc_can_err_skb(dev, &skb_cf); + + if (skb == NULL) { + netdev_dbg(dev, "could not allocate error frame\n"); + return; + } + skb_cf->can_id |= cf.can_id; + memcpy(skb_cf->data, cf.data, sizeof(cf.data)); + + netif_rx(skb); + } +} + +static irqreturn_t grcan_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 sources, status; + + /* Find out the source */ + sources = grcan_read_reg(®s->pimsr); + if (!sources) + return IRQ_NONE; + grcan_write_reg(®s->picr, sources); + status = grcan_read_reg(®s->stat); + + /* If we got TX progress, the device has not hanged, + * so disable the hang timer + */ + if (priv->need_txbug_workaround && + (sources & (GRCAN_IRQ_TX | GRCAN_IRQ_TXLOSS))) { + del_timer(&priv->hang_timer); + } + + /* Frame(s) received or transmitted */ + if (sources & (GRCAN_IRQ_TX | GRCAN_IRQ_RX)) { + /* Disable tx/rx interrupts and schedule poll(). No need for + * locking as interference from a running reset at worst leads + * to an extra interrupt. + */ + grcan_clear_bits(®s->imr, GRCAN_IRQ_TX | GRCAN_IRQ_RX); + napi_schedule(&priv->napi); + } + + /* (Potential) error conditions to take care of */ + if (sources & GRCAN_IRQ_ERRORS) + grcan_err(dev, sources, status); + + return IRQ_HANDLED; +} + +/* Reset device and restart operations from where they were. + * + * This assumes that RXCTRL & RXCTRL is properly disabled and that RX + * is not ONGOING (TX might be stuck in ONGOING due to a harwrware bug + * for single shot) + */ +static void grcan_running_reset(struct timer_list *t) +{ + struct grcan_priv *priv = from_timer(priv, t, rr_timer); + struct net_device *dev = priv->dev; + struct grcan_registers __iomem *regs = priv->regs; + unsigned long flags; + + /* This temporarily messes with eskbp, so we need to lock + * priv->lock + */ + spin_lock_irqsave(&priv->lock, flags); + + priv->resetting = false; + del_timer(&priv->hang_timer); + del_timer(&priv->rr_timer); + + if (!priv->closing) { + /* Save and reset - config register preserved by grcan_reset */ + u32 imr = grcan_read_reg(®s->imr); + + u32 txaddr = grcan_read_reg(®s->txaddr); + u32 txsize = grcan_read_reg(®s->txsize); + u32 txwr = grcan_read_reg(®s->txwr); + u32 txrd = grcan_read_reg(®s->txrd); + u32 eskbp = priv->eskbp; + + u32 rxaddr = grcan_read_reg(®s->rxaddr); + u32 rxsize = grcan_read_reg(®s->rxsize); + u32 rxwr = grcan_read_reg(®s->rxwr); + u32 rxrd = grcan_read_reg(®s->rxrd); + + grcan_reset(dev); + + /* Restore */ + grcan_write_reg(®s->txaddr, txaddr); + grcan_write_reg(®s->txsize, txsize); + grcan_write_reg(®s->txwr, txwr); + grcan_write_reg(®s->txrd, txrd); + priv->eskbp = eskbp; + + grcan_write_reg(®s->rxaddr, rxaddr); + grcan_write_reg(®s->rxsize, rxsize); + grcan_write_reg(®s->rxwr, rxwr); + grcan_write_reg(®s->rxrd, rxrd); + + /* Turn on device again */ + grcan_write_reg(®s->imr, imr); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + grcan_write_reg(®s->txctrl, GRCAN_TXCTRL_ENABLE + | (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT + ? GRCAN_TXCTRL_SINGLE : 0)); + grcan_write_reg(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_write_reg(®s->ctrl, GRCAN_CTRL_ENABLE); + + /* Start queue if there is size and listen-onle mode is not + * enabled + */ + if (grcan_txspace(priv->dma.tx.size, txwr, priv->eskbp) && + !(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_wake_queue(dev); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + netdev_err(dev, "Device reset and restored\n"); +} + +/* Waiting time in usecs corresponding to the transmission of three maximum + * sized can frames in the given bitrate (in bits/sec). Waiting for this amount + * of time makes sure that the can controller have time to finish sending or + * receiving a frame with a good margin. + * + * usecs/sec * number of frames * bits/frame / bits/sec + */ +static inline u32 grcan_ongoing_wait_usecs(__u32 bitrate) +{ + return 1000000 * 3 * GRCAN_EFF_FRAME_MAX_BITS / bitrate; +} + +/* Set timer so that it will not fire until after a period in which the can + * controller have a good margin to finish transmitting a frame unless it has + * hanged + */ +static inline void grcan_reset_timer(struct timer_list *timer, __u32 bitrate) +{ + u32 wait_jiffies = usecs_to_jiffies(grcan_ongoing_wait_usecs(bitrate)); + + mod_timer(timer, jiffies + wait_jiffies); +} + +/* Disable channels and schedule a running reset */ +static void grcan_initiate_running_reset(struct timer_list *t) +{ + struct grcan_priv *priv = from_timer(priv, t, hang_timer); + struct net_device *dev = priv->dev; + struct grcan_registers __iomem *regs = priv->regs; + unsigned long flags; + + netdev_err(dev, "Device seems hanged - reset scheduled\n"); + + spin_lock_irqsave(&priv->lock, flags); + + /* The main body of this function must never be executed again + * until after an execution of grcan_running_reset + */ + if (!priv->resetting && !priv->closing) { + priv->resetting = true; + netif_stop_queue(dev); + grcan_clear_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE); + grcan_clear_bits(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_reset_timer(&priv->rr_timer, priv->can.bittiming.bitrate); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void grcan_free_dma_buffers(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_dma *dma = &priv->dma; + + dma_free_coherent(priv->ofdev_dev, dma->base_size, dma->base_buf, + dma->base_handle); + memset(dma, 0, sizeof(*dma)); +} + +static int grcan_allocate_dma_buffers(struct net_device *dev, + size_t tsize, size_t rsize) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_dma *dma = &priv->dma; + struct grcan_dma_buffer *large = rsize > tsize ? &dma->rx : &dma->tx; + struct grcan_dma_buffer *small = rsize > tsize ? &dma->tx : &dma->rx; + size_t shift; + + /* Need a whole number of GRCAN_BUFFER_ALIGNMENT for the large, + * i.e. first buffer + */ + size_t maxs = max(tsize, rsize); + size_t lsize = ALIGN(maxs, GRCAN_BUFFER_ALIGNMENT); + + /* Put the small buffer after that */ + size_t ssize = min(tsize, rsize); + + /* Extra GRCAN_BUFFER_ALIGNMENT to allow for alignment */ + dma->base_size = lsize + ssize + GRCAN_BUFFER_ALIGNMENT; + dma->base_buf = dma_alloc_coherent(priv->ofdev_dev, + dma->base_size, + &dma->base_handle, + GFP_KERNEL); + + if (!dma->base_buf) + return -ENOMEM; + + dma->tx.size = tsize; + dma->rx.size = rsize; + + large->handle = ALIGN(dma->base_handle, GRCAN_BUFFER_ALIGNMENT); + small->handle = large->handle + lsize; + shift = large->handle - dma->base_handle; + + large->buf = dma->base_buf + shift; + small->buf = large->buf + lsize; + + return 0; +} + +/* priv->lock *must* be held when calling this function */ +static int grcan_start(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 confop, txctrl; + + grcan_reset(dev); + + grcan_write_reg(®s->txaddr, priv->dma.tx.handle); + grcan_write_reg(®s->txsize, priv->dma.tx.size); + /* regs->txwr, regs->txrd and priv->eskbp already set to 0 by reset */ + + grcan_write_reg(®s->rxaddr, priv->dma.rx.handle); + grcan_write_reg(®s->rxsize, priv->dma.rx.size); + /* regs->rxwr and regs->rxrd already set to 0 by reset */ + + /* Enable interrupts */ + grcan_read_reg(®s->pir); + grcan_write_reg(®s->imr, GRCAN_IRQ_DEFAULT); + + /* Enable interfaces, channels and device */ + confop = GRCAN_CONF_ABORT + | (priv->config.enable0 ? GRCAN_CONF_ENABLE0 : 0) + | (priv->config.enable1 ? GRCAN_CONF_ENABLE1 : 0) + | (priv->config.select ? GRCAN_CONF_SELECT : 0) + | (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY ? + GRCAN_CONF_SILENT : 0) + | (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ? + GRCAN_CONF_SAM : 0); + grcan_write_bits(®s->conf, confop, GRCAN_CONF_OPERATION); + txctrl = GRCAN_TXCTRL_ENABLE + | (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT + ? GRCAN_TXCTRL_SINGLE : 0); + grcan_write_reg(®s->txctrl, txctrl); + grcan_write_reg(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_write_reg(®s->ctrl, GRCAN_CTRL_ENABLE); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static int grcan_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct grcan_priv *priv = netdev_priv(dev); + unsigned long flags; + int err = 0; + + if (mode == CAN_MODE_START) { + /* This might be called to restart the device to recover from + * bus off errors + */ + spin_lock_irqsave(&priv->lock, flags); + if (priv->closing || priv->resetting) { + err = -EBUSY; + } else { + netdev_info(dev, "Restarting device\n"); + grcan_start(dev); + if (!(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_wake_queue(dev); + } + spin_unlock_irqrestore(&priv->lock, flags); + return err; + } + return -EOPNOTSUPP; +} + +static int grcan_open(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_dma *dma = &priv->dma; + unsigned long flags; + int err; + + /* Allocate memory */ + err = grcan_allocate_dma_buffers(dev, priv->config.txsize, + priv->config.rxsize); + if (err) { + netdev_err(dev, "could not allocate DMA buffers\n"); + return err; + } + + priv->echo_skb = kcalloc(dma->tx.size, sizeof(*priv->echo_skb), + GFP_KERNEL); + if (!priv->echo_skb) { + err = -ENOMEM; + goto exit_free_dma_buffers; + } + priv->can.echo_skb_max = dma->tx.size; + priv->can.echo_skb = priv->echo_skb; + + priv->txdlc = kcalloc(dma->tx.size, sizeof(*priv->txdlc), GFP_KERNEL); + if (!priv->txdlc) { + err = -ENOMEM; + goto exit_free_echo_skb; + } + + /* Get can device up */ + err = open_candev(dev); + if (err) + goto exit_free_txdlc; + + err = request_irq(dev->irq, grcan_interrupt, IRQF_SHARED, + dev->name, dev); + if (err) + goto exit_close_candev; + + spin_lock_irqsave(&priv->lock, flags); + + napi_enable(&priv->napi); + grcan_start(dev); + if (!(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_start_queue(dev); + priv->resetting = false; + priv->closing = false; + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + +exit_close_candev: + close_candev(dev); +exit_free_txdlc: + kfree(priv->txdlc); +exit_free_echo_skb: + kfree(priv->echo_skb); +exit_free_dma_buffers: + grcan_free_dma_buffers(dev); + return err; +} + +static int grcan_close(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + unsigned long flags; + + napi_disable(&priv->napi); + + spin_lock_irqsave(&priv->lock, flags); + + priv->closing = true; + if (priv->need_txbug_workaround) { + spin_unlock_irqrestore(&priv->lock, flags); + del_timer_sync(&priv->hang_timer); + del_timer_sync(&priv->rr_timer); + spin_lock_irqsave(&priv->lock, flags); + } + netif_stop_queue(dev); + grcan_stop_hardware(dev); + priv->can.state = CAN_STATE_STOPPED; + + spin_unlock_irqrestore(&priv->lock, flags); + + free_irq(dev->irq, dev); + close_candev(dev); + + grcan_free_dma_buffers(dev); + priv->can.echo_skb_max = 0; + priv->can.echo_skb = NULL; + kfree(priv->echo_skb); + kfree(priv->txdlc); + + return 0; +} + +static void grcan_transmit_catch_up(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + unsigned long flags; + int work_done; + + spin_lock_irqsave(&priv->lock, flags); + + work_done = catch_up_echo_skb(dev, -1, true); + if (work_done) { + if (!priv->resetting && !priv->closing && + !(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_wake_queue(dev); + + /* With napi we don't get TX interrupts for a while, + * so prevent a running reset while catching up + */ + if (priv->need_txbug_workaround) + del_timer(&priv->hang_timer); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int grcan_receive(struct net_device *dev, int budget) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 wr, rd, startrd; + u32 *slot; + u32 i, rtr, eff, j, shift; + int work_done = 0; + + rd = grcan_read_reg(®s->rxrd); + startrd = rd; + for (work_done = 0; work_done < budget; work_done++) { + /* Check for packet to receive */ + wr = grcan_read_reg(®s->rxwr); + if (rd == wr) + break; + + /* Take care of packet */ + skb = alloc_can_skb(dev, &cf); + if (skb == NULL) { + netdev_err(dev, + "dropping frame: skb allocation failed\n"); + stats->rx_dropped++; + continue; + } + + slot = dma->rx.buf + rd; + eff = slot[0] & GRCAN_MSG_IDE; + rtr = slot[0] & GRCAN_MSG_RTR; + if (eff) { + cf->can_id = ((slot[0] & GRCAN_MSG_EID) + >> GRCAN_MSG_EID_BIT); + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id = ((slot[0] & GRCAN_MSG_BID) + >> GRCAN_MSG_BID_BIT); + } + cf->can_dlc = get_can_dlc((slot[1] & GRCAN_MSG_DLC) + >> GRCAN_MSG_DLC_BIT); + if (rtr) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) { + j = GRCAN_MSG_DATA_SLOT_INDEX(i); + shift = GRCAN_MSG_DATA_SHIFT(i); + cf->data[i] = (u8)(slot[j] >> shift); + } + } + + /* Update statistics and read pointer */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size); + } + + /* Make sure everything is read before allowing hardware to + * use the memory + */ + mb(); + + /* Update read pointer - no need to check for ongoing */ + if (likely(rd != startrd)) + grcan_write_reg(®s->rxrd, rd); + + return work_done; +} + +static int grcan_poll(struct napi_struct *napi, int budget) +{ + struct grcan_priv *priv = container_of(napi, struct grcan_priv, napi); + struct net_device *dev = priv->dev; + struct grcan_registers __iomem *regs = priv->regs; + unsigned long flags; + int work_done; + + work_done = grcan_receive(dev, budget); + + grcan_transmit_catch_up(dev); + + if (work_done < budget) { + napi_complete(napi); + + /* Guarantee no interference with a running reset that otherwise + * could turn off interrupts. + */ + spin_lock_irqsave(&priv->lock, flags); + + /* Enable tx and rx interrupts again. No need to check + * priv->closing as napi_disable in grcan_close is waiting for + * scheduled napi calls to finish. + */ + grcan_set_bits(®s->imr, GRCAN_IRQ_TX | GRCAN_IRQ_RX); + + spin_unlock_irqrestore(&priv->lock, flags); + } + + return work_done; +} + +/* Work tx bug by waiting while for the risky situation to clear. If that fails, + * drop a frame in one-shot mode or indicate a busy device otherwise. + * + * Returns 0 on successful wait. Otherwise it sets *netdev_tx_status to the + * value that should be returned by grcan_start_xmit when aborting the xmit. + */ +static int grcan_txbug_workaround(struct net_device *dev, struct sk_buff *skb, + u32 txwr, u32 oneshotmode, + netdev_tx_t *netdev_tx_status) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + int i; + unsigned long flags; + + /* Wait a while for ongoing to be cleared or read pointer to catch up to + * write pointer. The latter is needed due to a bug in older versions of + * GRCAN in which ONGOING is not cleared properly one-shot mode when a + * transmission fails. + */ + for (i = 0; i < GRCAN_SHORTWAIT_USECS; i++) { + udelay(1); + if (!grcan_read_bits(®s->txctrl, GRCAN_TXCTRL_ONGOING) || + grcan_read_reg(®s->txrd) == txwr) { + return 0; + } + } + + /* Clean up, in case the situation was not resolved */ + spin_lock_irqsave(&priv->lock, flags); + if (!priv->resetting && !priv->closing) { + /* Queue might have been stopped earlier in grcan_start_xmit */ + if (grcan_txspace(dma->tx.size, txwr, priv->eskbp)) + netif_wake_queue(dev); + /* Set a timer to resolve a hanged tx controller */ + if (!timer_pending(&priv->hang_timer)) + grcan_reset_timer(&priv->hang_timer, + priv->can.bittiming.bitrate); + } + spin_unlock_irqrestore(&priv->lock, flags); + + if (oneshotmode) { + /* In one-shot mode we should never end up here because + * then the interrupt handler increases txrd on TXLOSS, + * but it is consistent with one-shot mode to drop the + * frame in this case. + */ + kfree_skb(skb); + *netdev_tx_status = NETDEV_TX_OK; + } else { + /* In normal mode the socket-can transmission queue get + * to keep the frame so that it can be retransmitted + * later + */ + *netdev_tx_status = NETDEV_TX_BUSY; + } + return -EBUSY; +} + +/* Notes on the tx cyclic buffer handling: + * + * regs->txwr - the next slot for the driver to put data to be sent + * regs->txrd - the next slot for the device to read data + * priv->eskbp - the next slot for the driver to call can_put_echo_skb for + * + * grcan_start_xmit can enter more messages as long as regs->txwr does + * not reach priv->eskbp (within 1 message gap) + * + * The device sends messages until regs->txrd reaches regs->txwr + * + * The interrupt calls handler calls can_put_echo_skb until + * priv->eskbp reaches regs->txrd + */ +static netdev_tx_t grcan_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 id, txwr, txrd, space, txctrl; + int slotindex; + u32 *slot; + u32 i, rtr, eff, dlc, tmp, err; + int j, shift; + unsigned long flags; + u32 oneshotmode = priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + /* Trying to transmit in silent mode will generate error interrupts, but + * this should never happen - the queue should not have been started. + */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + return NETDEV_TX_BUSY; + + /* Reads of priv->eskbp and shut-downs of the queue needs to + * be atomic towards the updates to priv->eskbp and wake-ups + * of the queue in the interrupt handler. + */ + spin_lock_irqsave(&priv->lock, flags); + + txwr = grcan_read_reg(®s->txwr); + space = grcan_txspace(dma->tx.size, txwr, priv->eskbp); + + slotindex = txwr / GRCAN_MSG_SIZE; + slot = dma->tx.buf + txwr; + + if (unlikely(space == 1)) + netif_stop_queue(dev); + + spin_unlock_irqrestore(&priv->lock, flags); + /* End of critical section*/ + + /* This should never happen. If circular buffer is full, the + * netif_stop_queue should have been stopped already. + */ + if (unlikely(!space)) { + netdev_err(dev, "No buffer space, but queue is non-stopped.\n"); + return NETDEV_TX_BUSY; + } + + /* Convert and write CAN message to DMA buffer */ + eff = cf->can_id & CAN_EFF_FLAG; + rtr = cf->can_id & CAN_RTR_FLAG; + id = cf->can_id & (eff ? CAN_EFF_MASK : CAN_SFF_MASK); + dlc = cf->can_dlc; + if (eff) + tmp = (id << GRCAN_MSG_EID_BIT) & GRCAN_MSG_EID; + else + tmp = (id << GRCAN_MSG_BID_BIT) & GRCAN_MSG_BID; + slot[0] = (eff ? GRCAN_MSG_IDE : 0) | (rtr ? GRCAN_MSG_RTR : 0) | tmp; + + slot[1] = ((dlc << GRCAN_MSG_DLC_BIT) & GRCAN_MSG_DLC); + slot[2] = 0; + slot[3] = 0; + for (i = 0; i < dlc; i++) { + j = GRCAN_MSG_DATA_SLOT_INDEX(i); + shift = GRCAN_MSG_DATA_SHIFT(i); + slot[j] |= cf->data[i] << shift; + } + + /* Checking that channel has not been disabled. These cases + * should never happen + */ + txctrl = grcan_read_reg(®s->txctrl); + if (!(txctrl & GRCAN_TXCTRL_ENABLE)) + netdev_err(dev, "tx channel spuriously disabled\n"); + + if (oneshotmode && !(txctrl & GRCAN_TXCTRL_SINGLE)) + netdev_err(dev, "one-shot mode spuriously disabled\n"); + + /* Bug workaround for old version of grcan where updating txwr + * in the same clock cycle as the controller updates txrd to + * the current txwr could hang the can controller + */ + if (priv->need_txbug_workaround) { + txrd = grcan_read_reg(®s->txrd); + if (unlikely(grcan_ring_sub(txwr, txrd, dma->tx.size) == 1)) { + netdev_tx_t txstatus; + + err = grcan_txbug_workaround(dev, skb, txwr, + oneshotmode, &txstatus); + if (err) + return txstatus; + } + } + + /* Prepare skb for echoing. This must be after the bug workaround above + * as ownership of the skb is passed on by calling can_put_echo_skb. + * Returning NETDEV_TX_BUSY or accessing skb or cf after a call to + * can_put_echo_skb would be an error unless other measures are + * taken. + */ + priv->txdlc[slotindex] = cf->can_dlc; /* Store dlc for statistics */ + can_put_echo_skb(skb, dev, slotindex); + + /* Make sure everything is written before allowing hardware to + * read from the memory + */ + wmb(); + + /* Update write pointer to start transmission */ + grcan_write_reg(®s->txwr, + grcan_ring_add(txwr, GRCAN_MSG_SIZE, dma->tx.size)); + + return NETDEV_TX_OK; +} + +/* ========== Setting up sysfs interface and module parameters ========== */ + +#define GRCAN_NOT_BOOL(unsigned_val) ((unsigned_val) > 1) + +#define GRCAN_MODULE_PARAM(name, mtype, valcheckf, desc) \ + static void grcan_sanitize_##name(struct platform_device *pd) \ + { \ + struct grcan_device_config grcan_default_config \ + = GRCAN_DEFAULT_DEVICE_CONFIG; \ + if (valcheckf(grcan_module_config.name)) { \ + dev_err(&pd->dev, \ + "Invalid module parameter value for " \ + #name " - setting default\n"); \ + grcan_module_config.name = \ + grcan_default_config.name; \ + } \ + } \ + module_param_named(name, grcan_module_config.name, \ + mtype, 0444); \ + MODULE_PARM_DESC(name, desc) + +#define GRCAN_CONFIG_ATTR(name, desc) \ + static ssize_t grcan_store_##name(struct device *sdev, \ + struct device_attribute *att, \ + const char *buf, \ + size_t count) \ + { \ + struct net_device *dev = to_net_dev(sdev); \ + struct grcan_priv *priv = netdev_priv(dev); \ + u8 val; \ + int ret; \ + if (dev->flags & IFF_UP) \ + return -EBUSY; \ + ret = kstrtou8(buf, 0, &val); \ + if (ret < 0 || val > 1) \ + return -EINVAL; \ + priv->config.name = val; \ + return count; \ + } \ + static ssize_t grcan_show_##name(struct device *sdev, \ + struct device_attribute *att, \ + char *buf) \ + { \ + struct net_device *dev = to_net_dev(sdev); \ + struct grcan_priv *priv = netdev_priv(dev); \ + return sprintf(buf, "%d\n", priv->config.name); \ + } \ + static DEVICE_ATTR(name, 0644, \ + grcan_show_##name, \ + grcan_store_##name); \ + GRCAN_MODULE_PARAM(name, ushort, GRCAN_NOT_BOOL, desc) + +/* The following configuration options are made available both via module + * parameters and writable sysfs files. See the chapter about GRCAN in the + * documentation for the GRLIB VHDL library for further details. + */ +GRCAN_CONFIG_ATTR(enable0, + "Configuration of physical interface 0. Determines\n" \ + "the \"Enable 0\" bit of the configuration register.\n" \ + "Format: 0 | 1\nDefault: 0\n"); + +GRCAN_CONFIG_ATTR(enable1, + "Configuration of physical interface 1. Determines\n" \ + "the \"Enable 1\" bit of the configuration register.\n" \ + "Format: 0 | 1\nDefault: 0\n"); + +GRCAN_CONFIG_ATTR(select, + "Select which physical interface to use.\n" \ + "Format: 0 | 1\nDefault: 0\n"); + +/* The tx and rx buffer size configuration options are only available via module + * parameters. + */ +GRCAN_MODULE_PARAM(txsize, uint, GRCAN_INVALID_BUFFER_SIZE, + "Sets the size of the tx buffer.\n" \ + "Format: <unsigned int> where (txsize & ~0x1fffc0) == 0\n" \ + "Default: 1024\n"); +GRCAN_MODULE_PARAM(rxsize, uint, GRCAN_INVALID_BUFFER_SIZE, + "Sets the size of the rx buffer.\n" \ + "Format: <unsigned int> where (size & ~0x1fffc0) == 0\n" \ + "Default: 1024\n"); + +/* Function that makes sure that configuration done using + * module parameters are set to valid values + */ +static void grcan_sanitize_module_config(struct platform_device *ofdev) +{ + grcan_sanitize_enable0(ofdev); + grcan_sanitize_enable1(ofdev); + grcan_sanitize_select(ofdev); + grcan_sanitize_txsize(ofdev); + grcan_sanitize_rxsize(ofdev); +} + +static const struct attribute *const sysfs_grcan_attrs[] = { + /* Config attrs */ + &dev_attr_enable0.attr, + &dev_attr_enable1.attr, + &dev_attr_select.attr, + NULL, +}; + +static const struct attribute_group sysfs_grcan_group = { + .name = "grcan", + .attrs = (struct attribute **)sysfs_grcan_attrs, +}; + +/* ========== Setting up the driver ========== */ + +static const struct net_device_ops grcan_netdev_ops = { + .ndo_open = grcan_open, + .ndo_stop = grcan_close, + .ndo_start_xmit = grcan_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int grcan_setup_netdev(struct platform_device *ofdev, + void __iomem *base, + int irq, u32 ambafreq, bool txbug) +{ + struct net_device *dev; + struct grcan_priv *priv; + struct grcan_registers __iomem *regs; + int err; + + dev = alloc_candev(sizeof(struct grcan_priv), 0); + if (!dev) + return -ENOMEM; + + dev->irq = irq; + dev->flags |= IFF_ECHO; + dev->netdev_ops = &grcan_netdev_ops; + dev->sysfs_groups[0] = &sysfs_grcan_group; + + priv = netdev_priv(dev); + memcpy(&priv->config, &grcan_module_config, + sizeof(struct grcan_device_config)); + priv->dev = dev; + priv->ofdev_dev = &ofdev->dev; + priv->regs = base; + priv->can.bittiming_const = &grcan_bittiming_const; + priv->can.do_set_bittiming = grcan_set_bittiming; + priv->can.do_set_mode = grcan_set_mode; + priv->can.do_get_berr_counter = grcan_get_berr_counter; + priv->can.clock.freq = ambafreq; + priv->can.ctrlmode_supported = + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_ONE_SHOT; + priv->need_txbug_workaround = txbug; + + /* Discover if triple sampling is supported by hardware */ + regs = priv->regs; + grcan_set_bits(®s->ctrl, GRCAN_CTRL_RESET); + grcan_set_bits(®s->conf, GRCAN_CONF_SAM); + if (grcan_read_bits(®s->conf, GRCAN_CONF_SAM)) { + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + dev_dbg(&ofdev->dev, "Hardware supports triple-sampling\n"); + } + + spin_lock_init(&priv->lock); + + if (priv->need_txbug_workaround) { + timer_setup(&priv->rr_timer, grcan_running_reset, 0); + timer_setup(&priv->hang_timer, grcan_initiate_running_reset, 0); + } + + netif_napi_add(dev, &priv->napi, grcan_poll, GRCAN_NAPI_WEIGHT); + + SET_NETDEV_DEV(dev, &ofdev->dev); + dev_info(&ofdev->dev, "regs=0x%p, irq=%d, clock=%d\n", + priv->regs, dev->irq, priv->can.clock.freq); + + err = register_candev(dev); + if (err) + goto exit_free_candev; + + platform_set_drvdata(ofdev, dev); + + /* Reset device to allow bit-timing to be set. No need to call + * grcan_reset at this stage. That is done in grcan_open. + */ + grcan_write_reg(®s->ctrl, GRCAN_CTRL_RESET); + + return 0; +exit_free_candev: + free_candev(dev); + return err; +} + +static int grcan_probe(struct platform_device *ofdev) +{ + struct device_node *np = ofdev->dev.of_node; + struct device_node *sysid_parent; + struct resource *res; + u32 sysid, ambafreq; + int irq, err; + void __iomem *base; + bool txbug = true; + + /* Compare GRLIB version number with the first that does not + * have the tx bug (see start_xmit) + */ + sysid_parent = of_find_node_by_path("/ambapp0"); + if (sysid_parent) { + of_node_get(sysid_parent); + err = of_property_read_u32(sysid_parent, "systemid", &sysid); + if (!err && ((sysid & GRLIB_VERSION_MASK) >= + GRCAN_TXBUG_SAFE_GRLIB_VERSION)) + txbug = false; + of_node_put(sysid_parent); + } + + err = of_property_read_u32(np, "freq", &ambafreq); + if (err) { + dev_err(&ofdev->dev, "unable to fetch \"freq\" property\n"); + goto exit_error; + } + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(base)) { + err = PTR_ERR(base); + goto exit_error; + } + + irq = irq_of_parse_and_map(np, GRCAN_IRQIX_IRQ); + if (!irq) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_error; + } + + grcan_sanitize_module_config(ofdev); + + err = grcan_setup_netdev(ofdev, base, irq, ambafreq, txbug); + if (err) + goto exit_dispose_irq; + + return 0; + +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_error: + dev_err(&ofdev->dev, + "%s socket CAN driver initialization failed with error %d\n", + DRV_NAME, err); + return err; +} + +static int grcan_remove(struct platform_device *ofdev) +{ + struct net_device *dev = platform_get_drvdata(ofdev); + struct grcan_priv *priv = netdev_priv(dev); + + unregister_candev(dev); /* Will in turn call grcan_close */ + + irq_dispose_mapping(dev->irq); + netif_napi_del(&priv->napi); + free_candev(dev); + + return 0; +} + +static const struct of_device_id grcan_match[] = { + {.name = "GAISLER_GRCAN"}, + {.name = "01_03d"}, + {.name = "GAISLER_GRHCAN"}, + {.name = "01_034"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, grcan_match); + +static struct platform_driver grcan_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = grcan_match, + }, + .probe = grcan_probe, + .remove = grcan_remove, +}; + +module_platform_driver(grcan_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION("Socket CAN driver for Aeroflex Gaisler GRCAN"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/can/ifi_canfd/Kconfig b/drivers/net/can/ifi_canfd/Kconfig new file mode 100644 index 000000000..9e8934ff6 --- /dev/null +++ b/drivers/net/can/ifi_canfd/Kconfig @@ -0,0 +1,8 @@ +config CAN_IFI_CANFD + depends on HAS_IOMEM + tristate "IFI CAN_FD IP" + ---help--- + This driver adds support for the I/F/I CAN_FD soft IP block + connected to the "platform bus" (Linux abstraction for directly + to the processor attached devices). The CAN_FD is most often + synthesised into an FPGA or CPLD. diff --git a/drivers/net/can/ifi_canfd/Makefile b/drivers/net/can/ifi_canfd/Makefile new file mode 100644 index 000000000..b229960cd --- /dev/null +++ b/drivers/net/can/ifi_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the IFI CANFD controller driver. +# + +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd.o diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c new file mode 100644 index 000000000..fedd927ba --- /dev/null +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -0,0 +1,1053 @@ +/* + * CAN bus driver for IFI CANFD controller + * + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * Details about this controller can be found at + * http://www.ifi-pld.de/IP/CANFD/canfd.html + * + * 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/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <linux/can/dev.h> + +#define IFI_CANFD_STCMD 0x0 +#define IFI_CANFD_STCMD_HARDRESET 0xDEADCAFD +#define IFI_CANFD_STCMD_ENABLE BIT(0) +#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) +#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) +#define IFI_CANFD_STCMD_BUSOFF BIT(4) +#define IFI_CANFD_STCMD_ERROR_WARNING BIT(5) +#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) +#define IFI_CANFD_STCMD_LOOPBACK BIT(18) +#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) +#define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) +#define IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING BIT(26) +#define IFI_CANFD_STCMD_NORMAL_MODE ((u32)BIT(31)) + +#define IFI_CANFD_RXSTCMD 0x4 +#define IFI_CANFD_RXSTCMD_REMOVE_MSG BIT(0) +#define IFI_CANFD_RXSTCMD_RESET BIT(7) +#define IFI_CANFD_RXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_RXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_TXSTCMD 0x8 +#define IFI_CANFD_TXSTCMD_ADD_MSG BIT(0) +#define IFI_CANFD_TXSTCMD_HIGH_PRIO BIT(1) +#define IFI_CANFD_TXSTCMD_RESET BIT(7) +#define IFI_CANFD_TXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_TXSTCMD_FULL BIT(12) +#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_INTERRUPT 0xc +#define IFI_CANFD_INTERRUPT_ERROR_BUSOFF BIT(0) +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) +#define IFI_CANFD_INTERRUPT_ERROR_STATE_CHG BIT(2) +#define IFI_CANFD_INTERRUPT_ERROR_REC_TEC_INC BIT(3) +#define IFI_CANFD_INTERRUPT_ERROR_COUNTER BIT(10) +#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER BIT(25) +#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31)) + +#define IFI_CANFD_IRQMASK 0x10 +#define IFI_CANFD_IRQMASK_ERROR_BUSOFF BIT(0) +#define IFI_CANFD_IRQMASK_ERROR_WARNING BIT(1) +#define IFI_CANFD_IRQMASK_ERROR_STATE_CHG BIT(2) +#define IFI_CANFD_IRQMASK_ERROR_REC_TEC_INC BIT(3) +#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) +#define IFI_CANFD_IRQMASK_SET_TS BIT(15) +#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_IRQMASK_SET_TX BIT(23) +#define IFI_CANFD_IRQMASK_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_IRQMASK_SET_RX ((u32)BIT(31)) + +#define IFI_CANFD_TIME 0x14 +#define IFI_CANFD_FTIME 0x18 +#define IFI_CANFD_TIME_TIMEB_OFF 0 +#define IFI_CANFD_TIME_TIMEA_OFF 8 +#define IFI_CANFD_TIME_PRESCALE_OFF 16 +#define IFI_CANFD_TIME_SJW_OFF_7_9_8_8 25 +#define IFI_CANFD_TIME_SJW_OFF_4_12_6_6 28 +#define IFI_CANFD_TIME_SET_SJW_4_12_6_6 BIT(6) +#define IFI_CANFD_TIME_SET_TIMEB_4_12_6_6 BIT(7) +#define IFI_CANFD_TIME_SET_PRESC_4_12_6_6 BIT(14) +#define IFI_CANFD_TIME_SET_TIMEA_4_12_6_6 BIT(15) + +#define IFI_CANFD_TDELAY 0x1c +#define IFI_CANFD_TDELAY_DEFAULT 0xb +#define IFI_CANFD_TDELAY_MASK 0x3fff +#define IFI_CANFD_TDELAY_ABS BIT(14) +#define IFI_CANFD_TDELAY_EN BIT(15) + +#define IFI_CANFD_ERROR 0x20 +#define IFI_CANFD_ERROR_TX_OFFSET 0 +#define IFI_CANFD_ERROR_TX_MASK 0xff +#define IFI_CANFD_ERROR_RX_OFFSET 16 +#define IFI_CANFD_ERROR_RX_MASK 0xff + +#define IFI_CANFD_ERRCNT 0x24 + +#define IFI_CANFD_SUSPEND 0x28 + +#define IFI_CANFD_REPEAT 0x2c + +#define IFI_CANFD_TRAFFIC 0x30 + +#define IFI_CANFD_TSCONTROL 0x34 + +#define IFI_CANFD_TSC 0x38 + +#define IFI_CANFD_TST 0x3c + +#define IFI_CANFD_RES1 0x40 + +#define IFI_CANFD_ERROR_CTR 0x44 +#define IFI_CANFD_ERROR_CTR_UNLOCK_MAGIC 0x21302899 +#define IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST BIT(0) +#define IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST BIT(1) +#define IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST BIT(2) +#define IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST BIT(3) +#define IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST BIT(4) +#define IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST BIT(5) +#define IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST BIT(6) +#define IFI_CANFD_ERROR_CTR_OVERLOAD_ALL BIT(8) +#define IFI_CANFD_ERROR_CTR_ACK_ERROR_ALL BIT(9) +#define IFI_CANFD_ERROR_CTR_BIT0_ERROR_ALL BIT(10) +#define IFI_CANFD_ERROR_CTR_BIT1_ERROR_ALL BIT(11) +#define IFI_CANFD_ERROR_CTR_STUFF_ERROR_ALL BIT(12) +#define IFI_CANFD_ERROR_CTR_CRC_ERROR_ALL BIT(13) +#define IFI_CANFD_ERROR_CTR_FORM_ERROR_ALL BIT(14) +#define IFI_CANFD_ERROR_CTR_BITPOSITION_OFFSET 16 +#define IFI_CANFD_ERROR_CTR_BITPOSITION_MASK 0xff +#define IFI_CANFD_ERROR_CTR_ER_RESET BIT(30) +#define IFI_CANFD_ERROR_CTR_ER_ENABLE ((u32)BIT(31)) + +#define IFI_CANFD_PAR 0x48 + +#define IFI_CANFD_CANCLOCK 0x4c + +#define IFI_CANFD_SYSCLOCK 0x50 + +#define IFI_CANFD_VER 0x54 +#define IFI_CANFD_VER_REV_MASK 0xff +#define IFI_CANFD_VER_REV_MIN_SUPPORTED 0x15 + +#define IFI_CANFD_IP_ID 0x58 +#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD + +#define IFI_CANFD_TEST 0x5c + +#define IFI_CANFD_RXFIFO_TS_63_32 0x60 + +#define IFI_CANFD_RXFIFO_TS_31_0 0x64 + +#define IFI_CANFD_RXFIFO_DLC 0x68 +#define IFI_CANFD_RXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_RXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_RXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_RXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_RXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_RXFIFO_DLC_ESI BIT(7) +#define IFI_CANFD_RXFIFO_DLC_OBJ_OFFSET 8 +#define IFI_CANFD_RXFIFO_DLC_OBJ_MASK 0x1ff +#define IFI_CANFD_RXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_RXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_RXFIFO_ID 0x6c +#define IFI_CANFD_RXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_RXFIFO_ID_ID_STD_MASK CAN_SFF_MASK +#define IFI_CANFD_RXFIFO_ID_ID_STD_OFFSET 0 +#define IFI_CANFD_RXFIFO_ID_ID_STD_WIDTH 10 +#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK CAN_EFF_MASK +#define IFI_CANFD_RXFIFO_ID_ID_XTD_OFFSET 11 +#define IFI_CANFD_RXFIFO_ID_ID_XTD_WIDTH 18 +#define IFI_CANFD_RXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_RXFIFO_DATA 0x70 /* 0x70..0xac */ + +#define IFI_CANFD_TXFIFO_SUSPEND_US 0xb0 + +#define IFI_CANFD_TXFIFO_REPEATCOUNT 0xb4 + +#define IFI_CANFD_TXFIFO_DLC 0xb8 +#define IFI_CANFD_TXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_TXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_TXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_TXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_TXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_TXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_TXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_TXFIFO_ID 0xbc +#define IFI_CANFD_TXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_TXFIFO_ID_ID_STD_MASK CAN_SFF_MASK +#define IFI_CANFD_TXFIFO_ID_ID_STD_OFFSET 0 +#define IFI_CANFD_TXFIFO_ID_ID_STD_WIDTH 10 +#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK CAN_EFF_MASK +#define IFI_CANFD_TXFIFO_ID_ID_XTD_OFFSET 11 +#define IFI_CANFD_TXFIFO_ID_ID_XTD_WIDTH 18 +#define IFI_CANFD_TXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_TXFIFO_DATA 0xc0 /* 0xb0..0xfc */ + +#define IFI_CANFD_FILTER_MASK(n) (0x800 + ((n) * 8) + 0) +#define IFI_CANFD_FILTER_MASK_EXT BIT(29) +#define IFI_CANFD_FILTER_MASK_EDL BIT(30) +#define IFI_CANFD_FILTER_MASK_VALID ((u32)BIT(31)) + +#define IFI_CANFD_FILTER_IDENT(n) (0x800 + ((n) * 8) + 4) +#define IFI_CANFD_FILTER_IDENT_IDE BIT(29) +#define IFI_CANFD_FILTER_IDENT_CANFD BIT(30) +#define IFI_CANFD_FILTER_IDENT_VALID ((u32)BIT(31)) + +/* IFI CANFD private data structure */ +struct ifi_canfd_priv { + struct can_priv can; /* must be the first member */ + struct napi_struct napi; + struct net_device *ndev; + void __iomem *base; +}; + +static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 enirq = 0; + + if (enable) { + enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | + IFI_CANFD_IRQMASK_RXFIFO_NEMPTY | + IFI_CANFD_IRQMASK_ERROR_STATE_CHG | + IFI_CANFD_IRQMASK_ERROR_WARNING | + IFI_CANFD_IRQMASK_ERROR_BUSOFF; + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER; + } + + writel(IFI_CANFD_IRQMASK_SET_ERR | + IFI_CANFD_IRQMASK_SET_TS | + IFI_CANFD_IRQMASK_SET_TX | + IFI_CANFD_IRQMASK_SET_RX | enirq, + priv->base + IFI_CANFD_IRQMASK); +} + +static void ifi_canfd_read_fifo(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf; + struct sk_buff *skb; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + u32 rxdlc, rxid; + u32 dlc, id; + int i; + + rxdlc = readl(priv->base + IFI_CANFD_RXFIFO_DLC); + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + + if (!skb) { + stats->rx_dropped++; + return; + } + + dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) & + IFI_CANFD_RXFIFO_DLC_DLC_MASK; + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + cf->len = can_dlc2len(dlc); + else + cf->len = get_can_dlc(dlc); + + rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID); + id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET); + if (id & IFI_CANFD_RXFIFO_ID_IDE) { + id &= IFI_CANFD_RXFIFO_ID_ID_XTD_MASK; + /* + * In case the Extended ID frame is received, the standard + * and extended part of the ID are swapped in the register, + * so swap them back to obtain the correct ID. + */ + id = (id >> IFI_CANFD_RXFIFO_ID_ID_XTD_OFFSET) | + ((id & IFI_CANFD_RXFIFO_ID_ID_STD_MASK) << + IFI_CANFD_RXFIFO_ID_ID_XTD_WIDTH); + id |= CAN_EFF_FLAG; + } else { + id &= IFI_CANFD_RXFIFO_ID_ID_STD_MASK; + } + cf->can_id = id; + + if (rxdlc & IFI_CANFD_RXFIFO_DLC_ESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(ndev, "ESI Error\n"); + } + + if (!(rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) && + (rxdlc & IFI_CANFD_RXFIFO_DLC_RTR)) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if (rxdlc & IFI_CANFD_RXFIFO_DLC_BRS) + cf->flags |= CANFD_BRS; + + for (i = 0; i < cf->len; i += 4) { + *(u32 *)(cf->data + i) = + readl(priv->base + IFI_CANFD_RXFIFO_DATA + i); + } + } + + /* Remove the packet from FIFO */ + writel(IFI_CANFD_RXSTCMD_REMOVE_MSG, priv->base + IFI_CANFD_RXSTCMD); + writel(rx_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_receive_skb(skb); +} + +static int ifi_canfd_do_rx_poll(struct net_device *ndev, int quota) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 pkts = 0; + u32 rxst; + + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) { + netdev_dbg(ndev, "No messages in RX FIFO\n"); + return 0; + } + + for (;;) { + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) + break; + if (quota <= 0) + break; + + ifi_canfd_read_fifo(ndev); + quota--; + pkts++; + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + } + + if (pkts) + can_led_event(ndev, CAN_LED_EVENT_RX); + + return pkts; +} + +static int ifi_canfd_handle_lost_msg(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(ndev, "RX FIFO overflow, message(s) lost.\n"); + + stats->rx_errors++; + stats->rx_over_errors++; + + skb = alloc_can_err_skb(ndev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_handle_lec_err(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR); + const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST | + IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST; + + if (!(errctr & errmask)) /* No error happened. */ + return 0; + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + /* Propagate the error condition to the CAN stack. */ + skb = alloc_can_err_skb(ndev, &cf); + if (unlikely(!skb)) + return 0; + + /* Read the error counter register and check for new errors. */ + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + if (errctr & IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST) + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + + if (errctr & IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST) + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + + if (errctr & IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_BIT0; + + if (errctr & IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_BIT1; + + if (errctr & IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_STUFF; + + if (errctr & IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST) + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + + if (errctr & IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_FORM; + + /* Reset the error counter, ack the IRQ and re-enable the counter. */ + writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); + writel(IFI_CANFD_INTERRUPT_ERROR_COUNTER, + priv->base + IFI_CANFD_INTERRUPT); + writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 err; + + err = readl(priv->base + IFI_CANFD_ERROR); + bec->rxerr = (err >> IFI_CANFD_ERROR_RX_OFFSET) & + IFI_CANFD_ERROR_RX_MASK; + bec->txerr = (err >> IFI_CANFD_ERROR_TX_OFFSET) & + IFI_CANFD_ERROR_TX_MASK; + + return 0; +} + +static int ifi_canfd_handle_state_change(struct net_device *ndev, + enum can_state new_state) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error active state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + break; + case CAN_STATE_ERROR_WARNING: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + ifi_canfd_irq_enable(ndev, 0); + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + break; + default: + break; + } + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (unlikely(!skb)) + return 0; + + ifi_canfd_get_berr_counter(ndev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_WARNING: + /* error warning state */ + 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 CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + 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 CAN_STATE_BUS_OFF: + /* bus-off state */ + cf->can_id |= CAN_ERR_BUSOFF; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_handle_state_errors(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 stcmd = readl(priv->base + IFI_CANFD_STCMD); + int work_done = 0; + + if ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) && + (priv->can.state != CAN_STATE_ERROR_ACTIVE)) { + netdev_dbg(ndev, "Error, entered active state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_ACTIVE); + } + + if ((stcmd & IFI_CANFD_STCMD_ERROR_WARNING) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + netdev_dbg(ndev, "Error, entered warning state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_WARNING); + } + + if ((stcmd & IFI_CANFD_STCMD_ERROR_PASSIVE) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(ndev, "Error, entered passive state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((stcmd & IFI_CANFD_STCMD_BUSOFF) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(ndev, "Error, entered bus-off state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_BUS_OFF); + } + + return work_done; +} + +static int ifi_canfd_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD); + int work_done = 0; + + /* Handle bus state changes */ + work_done += ifi_canfd_handle_state_errors(ndev); + + /* Handle lost messages on RX */ + if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) + work_done += ifi_canfd_handle_lost_msg(ndev); + + /* Handle lec errors on the bus */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + work_done += ifi_canfd_handle_lec_err(ndev); + + /* Handle normal messages on RX */ + if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) + work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); + + if (work_done < quota) { + napi_complete_done(napi, work_done); + ifi_canfd_irq_enable(ndev, 1); + } + + return work_done; +} + +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER | + IFI_CANFD_INTERRUPT_ERROR_COUNTER | + IFI_CANFD_INTERRUPT_ERROR_STATE_CHG | + IFI_CANFD_INTERRUPT_ERROR_WARNING | + IFI_CANFD_INTERRUPT_ERROR_BUSOFF; + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; + const u32 clr_irq_mask = ~((u32)IFI_CANFD_INTERRUPT_SET_IRQ); + u32 isr; + + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + + /* No interrupt */ + if (isr == 0) + return IRQ_NONE; + + /* Clear all pending interrupts but ErrWarn */ + writel(clr_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + /* RX IRQ or bus warning, start NAPI */ + if (isr & rx_irq_mask) { + ifi_canfd_irq_enable(ndev, 0); + napi_schedule(&priv->napi); + } + + /* TX IRQ */ + if (isr & IFI_CANFD_INTERRUPT_TXFIFO_REMOVE) { + stats->tx_bytes += can_get_echo_skb(ndev, 0); + stats->tx_packets++; + can_led_event(ndev, CAN_LED_EVENT_TX); + } + + if (isr & tx_irq_mask) + netif_wake_queue(ndev); + + return IRQ_HANDLED; +} + +static const struct can_bittiming_const ifi_canfd_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 256, + .tseg2_min = 2, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 256, + .sjw_max = 128, + .brp_min = 2, + .brp_max = 512, + .brp_inc = 1, +}; + +static void ifi_canfd_set_bittiming(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2, tdc; + + /* Configure bit timing */ + brp = bt->brp - 2; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 2; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << IFI_CANFD_TIME_SJW_OFF_7_9_8_8), + priv->base + IFI_CANFD_TIME); + + /* Configure data bit timing */ + brp = dbt->brp - 2; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 2; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << IFI_CANFD_TIME_SJW_OFF_7_9_8_8), + priv->base + IFI_CANFD_FTIME); + + /* Configure transmitter delay */ + tdc = dbt->brp * (dbt->prop_seg + dbt->phase_seg1); + tdc &= IFI_CANFD_TDELAY_MASK; + writel(IFI_CANFD_TDELAY_EN | tdc, priv->base + IFI_CANFD_TDELAY); +} + +static void ifi_canfd_set_filter(struct net_device *ndev, const u32 id, + const u32 mask, const u32 ident) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + writel(mask, priv->base + IFI_CANFD_FILTER_MASK(id)); + writel(ident, priv->base + IFI_CANFD_FILTER_IDENT(id)); +} + +static void ifi_canfd_set_filters(struct net_device *ndev) +{ + /* Receive all CAN frames (standard ID) */ + ifi_canfd_set_filter(ndev, 0, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID); + + /* Receive all CAN frames (extended ID) */ + ifi_canfd_set_filter(ndev, 1, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_IDE); + + /* Receive all CANFD frames */ + ifi_canfd_set_filter(ndev, 2, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EDL | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_CANFD | + IFI_CANFD_FILTER_IDENT_IDE); +} + +static void ifi_canfd_start(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 stcmd; + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + writel(IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING, + priv->base + IFI_CANFD_STCMD); + + ifi_canfd_set_bittiming(ndev); + ifi_canfd_set_filters(ndev); + + /* Reset FIFOs */ + writel(IFI_CANFD_RXSTCMD_RESET, priv->base + IFI_CANFD_RXSTCMD); + writel(0, priv->base + IFI_CANFD_RXSTCMD); + writel(IFI_CANFD_TXSTCMD_RESET, priv->base + IFI_CANFD_TXSTCMD); + writel(0, priv->base + IFI_CANFD_TXSTCMD); + + /* Repeat transmission until successful */ + writel(0, priv->base + IFI_CANFD_REPEAT); + writel(0, priv->base + IFI_CANFD_SUSPEND); + + /* Clear all pending interrupts */ + writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), + priv->base + IFI_CANFD_INTERRUPT); + + stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE | + IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + stcmd |= IFI_CANFD_STCMD_BUSMONITOR; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + stcmd |= IFI_CANFD_STCMD_LOOPBACK; + + if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && + !(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) + stcmd |= IFI_CANFD_STCMD_ENABLE_ISO; + + if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD)) + stcmd |= IFI_CANFD_STCMD_DISABLE_CANFD; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + ifi_canfd_irq_enable(ndev, 1); + + /* Unlock, reset and enable the error counter. */ + writel(IFI_CANFD_ERROR_CTR_UNLOCK_MAGIC, + priv->base + IFI_CANFD_ERROR_CTR); + writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); + writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); + + /* Enable controller */ + writel(stcmd, priv->base + IFI_CANFD_STCMD); +} + +static void ifi_canfd_stop(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + /* Reset and disable the error counter. */ + writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); + writel(0, priv->base + IFI_CANFD_ERROR_CTR); + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + + /* Mask all interrupts */ + writel(~0, priv->base + IFI_CANFD_IRQMASK); + + /* Clear all pending interrupts */ + writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), + priv->base + IFI_CANFD_INTERRUPT); + + /* Set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int ifi_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + ifi_canfd_start(ndev); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ifi_canfd_open(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int ret; + + ret = open_candev(ndev); + if (ret) { + netdev_err(ndev, "Failed to open CAN device\n"); + return ret; + } + + /* Register interrupt handler */ + ret = request_irq(ndev->irq, ifi_canfd_isr, IRQF_SHARED, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "Failed to request interrupt\n"); + goto err_irq; + } + + ifi_canfd_start(ndev); + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +err_irq: + close_candev(ndev); + return ret; +} + +static int ifi_canfd_close(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + + ifi_canfd_stop(ndev); + + free_irq(ndev->irq, ndev); + + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + return 0; +} + +static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 txst, txid, txdlc; + int i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + /* Check if the TX buffer is full */ + txst = readl(priv->base + IFI_CANFD_TXSTCMD); + if (txst & IFI_CANFD_TXSTCMD_FULL) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG! TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(ndev); + + if (cf->can_id & CAN_EFF_FLAG) { + txid = cf->can_id & CAN_EFF_MASK; + /* + * In case the Extended ID frame is transmitted, the + * standard and extended part of the ID are swapped + * in the register, so swap them back to send the + * correct ID. + */ + txid = (txid >> IFI_CANFD_TXFIFO_ID_ID_XTD_WIDTH) | + ((txid & IFI_CANFD_TXFIFO_ID_ID_XTD_MASK) << + IFI_CANFD_TXFIFO_ID_ID_XTD_OFFSET); + txid |= IFI_CANFD_TXFIFO_ID_IDE; + } else { + txid = cf->can_id & CAN_SFF_MASK; + } + + txdlc = can_len2dlc(cf->len); + if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) { + txdlc |= IFI_CANFD_TXFIFO_DLC_EDL; + if (cf->flags & CANFD_BRS) + txdlc |= IFI_CANFD_TXFIFO_DLC_BRS; + } + + if (cf->can_id & CAN_RTR_FLAG) + txdlc |= IFI_CANFD_TXFIFO_DLC_RTR; + + /* message ram configuration */ + writel(txid, priv->base + IFI_CANFD_TXFIFO_ID); + writel(txdlc, priv->base + IFI_CANFD_TXFIFO_DLC); + + for (i = 0; i < cf->len; i += 4) { + writel(*(u32 *)(cf->data + i), + priv->base + IFI_CANFD_TXFIFO_DATA + i); + } + + writel(0, priv->base + IFI_CANFD_TXFIFO_REPEATCOUNT); + writel(0, priv->base + IFI_CANFD_TXFIFO_SUSPEND_US); + + can_put_echo_skb(skb, ndev, 0); + + /* Start the transmission */ + writel(IFI_CANFD_TXSTCMD_ADD_MSG, priv->base + IFI_CANFD_TXSTCMD); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops ifi_canfd_netdev_ops = { + .ndo_open = ifi_canfd_open, + .ndo_stop = ifi_canfd_close, + .ndo_start_xmit = ifi_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int ifi_canfd_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct ifi_canfd_priv *priv; + struct resource *res; + void __iomem *addr; + int irq, ret; + u32 id, rev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(dev, res); + irq = platform_get_irq(pdev, 0); + if (IS_ERR(addr) || irq < 0) + return -EINVAL; + + id = readl(addr + IFI_CANFD_IP_ID); + if (id != IFI_CANFD_IP_ID_VALUE) { + dev_err(dev, "This block is not IFI CANFD, id=%08x\n", id); + return -EINVAL; + } + + rev = readl(addr + IFI_CANFD_VER) & IFI_CANFD_VER_REV_MASK; + if (rev < IFI_CANFD_VER_REV_MIN_SUPPORTED) { + dev_err(dev, "This block is too old (rev %i), minimum supported is rev %i\n", + rev, IFI_CANFD_VER_REV_MIN_SUPPORTED); + return -EINVAL; + } + + ndev = alloc_candev(sizeof(*priv), 1); + if (!ndev) + return -ENOMEM; + + ndev->irq = irq; + ndev->flags |= IFF_ECHO; /* we support local echo */ + ndev->netdev_ops = &ifi_canfd_netdev_ops; + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + + netif_napi_add(ndev, &priv->napi, ifi_canfd_poll, 64); + + priv->can.state = CAN_STATE_STOPPED; + + priv->can.clock.freq = readl(addr + IFI_CANFD_CANCLOCK); + + priv->can.bittiming_const = &ifi_canfd_bittiming_const; + priv->can.data_bittiming_const = &ifi_canfd_bittiming_const; + priv->can.do_set_mode = ifi_canfd_set_mode; + priv->can.do_get_berr_counter = ifi_canfd_get_berr_counter; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode = CAN_CTRLMODE_FD; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_BERR_REPORTING; + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, dev); + + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "Failed to register (ret=%d)\n", ret); + goto err_reg; + } + + devm_can_led_init(ndev); + + dev_info(dev, "Driver registered: regs=%p, irq=%d, clock=%d\n", + priv->base, ndev->irq, priv->can.clock.freq); + + return 0; + +err_reg: + free_candev(ndev); + return ret; +} + +static int ifi_canfd_plat_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_candev(ndev); + platform_set_drvdata(pdev, NULL); + free_candev(ndev); + + return 0; +} + +static const struct of_device_id ifi_canfd_of_table[] = { + { .compatible = "ifi,canfd-1.0", .data = NULL }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ifi_canfd_of_table); + +static struct platform_driver ifi_canfd_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ifi_canfd_of_table, + }, + .probe = ifi_canfd_plat_probe, + .remove = ifi_canfd_plat_remove, +}; + +module_platform_driver(ifi_canfd_plat_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for IFI CANFD controller"); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c new file mode 100644 index 000000000..02042cb09 --- /dev/null +++ b/drivers/net/can/janz-ican3.c @@ -0,0 +1,2058 @@ +/* + * 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/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/skb.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 FIRMWARE_STAMP 0x60 /* big endian firmware stamp */ + +#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 +#define MSG_NMTS 0xb0 +#define MSG_LMTS 0xb4 + +/* + * 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_SNGL 0x02 +#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 + +/* Janz ICAN3 NMTS subtypes */ +#define NMTS_CREATE_NODE_REQ 0x0 +#define NMTS_SLAVE_STATE_IND 0x8 +#define NMTS_SLAVE_EVENT_IND 0x9 + +/* Janz ICAN3 LMTS subtypes */ +#define LMTS_BUSON_REQ 0x0 +#define LMTS_BUSOFF_REQ 0x1 +#define LMTS_CAN_CONF_REQ 0x2 + +/* Janz ICAN3 NMTS Event indications */ +#define NE_LOCAL_OCCURRED 0x3 +#define NE_LOCAL_RESOLVED 0x2 +#define NE_REMOTE_OCCURRED 0xc +#define NE_REMOTE_RESOLVED 0x8 + +/* + * 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 + +/* Janz ICAN3 firmware types */ +enum ican3_fwtype { + ICAN3_FWTYPE_ICANOS, + ICAN3_FWTYPE_CAL_CANOPEN, +}; + +/* 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; + + /* 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; + + /* firmware type */ + enum ican3_fwtype fwtype; + char fwinfo[32]; + + /* old and new style host interface */ + unsigned int iftype; + + /* queue for echo packets */ + struct sk_buff_head echoq; + + /* + * 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_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) { + netdev_dbg(mod->ndev, "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) { + netdev_err(mod->ndev, "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 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 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; + + /* 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)) { + netdev_dbg(mod->ndev, "%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)) { + netdev_dbg(mod->ndev, "%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 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 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 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 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 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 can_bittiming *bt = &mod->can.bittiming; + struct ican3_msg msg; + u8 btr0, btr1; + int res; + + /* This algorithm 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 */ + 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; + + if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { + if (on) { + /* set bittiming */ + 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; + + res = ican3_send_msg(mod, &msg); + if (res) + return res; + } + + /* can-on/off request */ + memset(&msg, 0, sizeof(msg)); + msg.spec = on ? MSG_CONREQ : MSG_COFFREQ; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); + + } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { + /* bittiming + can-on/off request */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_LMTS; + if (on) { + msg.len = cpu_to_le16(4); + msg.data[0] = LMTS_BUSON_REQ; + msg.data[1] = 0; + msg.data[2] = btr0; + msg.data[3] = btr1; + } else { + msg.len = cpu_to_le16(2); + msg.data[0] = LMTS_BUSOFF_REQ; + msg.data[1] = 0; + } + res = ican3_send_msg(mod, &msg); + if (res) + return res; + + if (on) { + /* create NMT Slave Node for error processing + * class 2 (with error capability, see CiA/DS203-1) + * id 1 + * name locnod1 (must be exactly 7 bytes) + */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_NMTS; + msg.len = cpu_to_le16(11); + msg.data[0] = NMTS_CREATE_NODE_REQ; + msg.data[1] = 0; + msg.data[2] = 2; /* node class */ + msg.data[3] = 1; /* node id */ + strcpy(msg.data + 4, "locnod1"); /* node name */ + return ican3_send_msg(mod, &msg); + } + return 0; + } + return -ENOTSUPP; +} + +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; + + if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CCONFREQ; + msg.len = cpu_to_le16(2); + msg.data[0] = 0x00; + msg.data[1] = quota; + } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_LMTS; + msg.len = cpu_to_le16(4); + msg.data[0] = LMTS_CAN_CONF_REQ; + msg.data[1] = 0x00; + msg.data[2] = 0x00; + msg.data[3] = quota; + } else { + return -ENOTSUPP; + } + 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 = get_can_dlc(desc->data[1] & ICAN3_CAN_DLC_MASK); + memcpy(cf->data, &desc->data[2], cf->can_dlc); + } else { + cf->can_dlc = get_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], cf->can_dlc); + } +} + +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; + + /* support single transmission (no retries) mode */ + if (mod->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + desc->data[1] |= ICAN3_SNGL; + + 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, cf->can_dlc); +} + +/* + * 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) +{ + netdev_dbg(mod->ndev, "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) { + netdev_err(mod->ndev, "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_over_errors++; + stats->rx_errors++; + 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 isrc, ecc, status, rxerr, txerr; + struct can_frame *cf; + struct sk_buff *skb; + + /* we can only handle the SJA1000 part */ + if (msg->data[1] != CEVTIND_CHIP_SJA1000) { + netdev_err(mod->ndev, "unable to handle errors on non-SJA1000\n"); + return -ENODEV; + } + + /* check the message length for sanity */ + if (le16_to_cpu(msg->len) < 6) { + netdev_err(mod->ndev, "error message too short\n"); + return -EINVAL; + } + + isrc = msg->data[0]; + ecc = msg->data[2]; + status = msg->data[3]; + rxerr = msg->data[4]; + txerr = msg->data[5]; + + /* + * This hardware lacks any support other than bus error messages to + * determine if packet transmission has failed. + * + * When TX errors happen, one echo skb needs to be dropped from the + * front of the queue. + * + * A small bit of code is duplicated here and below, to avoid error + * skb allocation when it will just be freed immediately. + */ + if (isrc == CEVTIND_BEI) { + int ret; + netdev_dbg(mod->ndev, "bus error interrupt\n"); + + /* TX error */ + if (!(ecc & ECC_DIR)) { + kfree_skb(skb_dequeue(&mod->echoq)); + stats->tx_errors++; + } else { + stats->rx_errors++; + } + + /* + * The controller automatically disables bus-error interrupts + * and therefore we must re-enable them. + */ + ret = ican3_set_buserror(mod, 1); + if (ret) { + netdev_err(mod->ndev, "unable to re-enable bus-error\n"); + return ret; + } + + /* bus error reporting is off, return immediately */ + if (!(mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) + return 0; + } + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + /* data overrun interrupt */ + if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) { + netdev_dbg(mod->ndev, "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) { + netdev_dbg(mod->ndev, "error warning + passive interrupt\n"); + if (status & SR_BS) { + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + mod->can.can_stats.bus_off++; + 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) { + mod->can.can_stats.bus_error++; + 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[3] = ecc & ECC_SEG; + break; + } + + if (!(ecc & ECC_DIR)) + 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; + 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: + netdev_err(mod->ndev, "received an unknown inquiry response\n"); + break; + } +} + +/* Handle NMTS Slave Event Indication Messages from the firmware */ +static void ican3_handle_nmtsind(struct ican3_dev *mod, struct ican3_msg *msg) +{ + u16 subspec; + + subspec = msg->data[0] + msg->data[1] * 0x100; + if (subspec == NMTS_SLAVE_EVENT_IND) { + switch (msg->data[2]) { + case NE_LOCAL_OCCURRED: + case NE_LOCAL_RESOLVED: + /* now follows the same message as Raw ICANOS CEVTIND + * shift the data at the same place and call this method + */ + le16_add_cpu(&msg->len, -3); + memmove(msg->data, msg->data + 3, le16_to_cpu(msg->len)); + ican3_handle_cevtind(mod, msg); + break; + case NE_REMOTE_OCCURRED: + case NE_REMOTE_RESOLVED: + /* should not occurre, ignore */ + break; + default: + netdev_warn(mod->ndev, "unknown NMTS event indication %x\n", + msg->data[2]); + break; + } + } else if (subspec == NMTS_SLAVE_STATE_IND) { + /* ignore state indications */ + } else { + netdev_warn(mod->ndev, "unhandled NMTS indication %x\n", + subspec); + return; + } +} + +static void ican3_handle_unknown_message(struct ican3_dev *mod, + struct ican3_msg *msg) +{ + netdev_warn(mod->ndev, "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) +{ + netdev_dbg(mod->ndev, "%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; + case MSG_NMTS: + ican3_handle_nmtsind(mod, msg); + break; + default: + ican3_handle_unknown_message(mod, msg); + break; + } +} + +/* + * The ican3 needs to store all echo skbs, and therefore cannot + * use the generic infrastructure for this. + */ +static void ican3_put_echo_skb(struct ican3_dev *mod, struct sk_buff *skb) +{ + skb = can_create_echo_skb(skb); + if (!skb) + return; + + /* save this skb for tx interrupt echo handling */ + skb_queue_tail(&mod->echoq, skb); +} + +static unsigned int ican3_get_echo_skb(struct ican3_dev *mod) +{ + struct sk_buff *skb = skb_dequeue(&mod->echoq); + struct can_frame *cf; + u8 dlc; + + /* this should never trigger unless there is a driver bug */ + if (!skb) { + netdev_err(mod->ndev, "BUG: echo skb not occupied\n"); + return 0; + } + + cf = (struct can_frame *)skb->data; + dlc = cf->can_dlc; + + /* check flag whether this packet has to be looped back */ + if (skb->pkt_type != PACKET_LOOPBACK) { + kfree_skb(skb); + return dlc; + } + + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->dev = mod->ndev; + netif_receive_skb(skb); + return dlc; +} + +/* + * Compare an skb with an existing echo skb + * + * This function will be used on devices which have a hardware loopback. + * On these devices, this function can be used to compare a received skb + * with the saved echo skbs so that the hardware echo skb can be dropped. + * + * Returns true if the skb's are identical, false otherwise. + */ +static bool ican3_echo_skb_matches(struct ican3_dev *mod, struct sk_buff *skb) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + struct sk_buff *echo_skb = skb_peek(&mod->echoq); + struct can_frame *echo_cf; + + if (!echo_skb) + return false; + + echo_cf = (struct can_frame *)echo_skb->data; + if (cf->can_id != echo_cf->can_id) + return false; + + if (cf->can_dlc != echo_cf->can_dlc) + return false; + + return memcmp(cf->data, echo_cf->data, cf->can_dlc) == 0; +} + +/* + * 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; + + /* check that we have echo queue space */ + if (skb_queue_len(&mod->echoq) >= ICAN3_TX_BUFFERS) + return false; + + /* 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); + + /* + * If this is an ECHO frame received from the hardware loopback + * feature, use the skb saved in the ECHO stack instead. This allows + * the Linux CAN core to support CAN_RAW_RECV_OWN_MSGS correctly. + * + * Since this is a confirmation of a successfully transmitted packet + * sent from this host, update the transmit statistics. + * + * Also, the netdevice queue needs to be allowed to send packets again. + */ + if (ican3_echo_skb_matches(mod, skb)) { + stats->tx_packets++; + stats->tx_bytes += ican3_get_echo_skb(mod); + kfree_skb(skb); + goto err_noalloc; + } + + /* update statistics, receive the skb */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + +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); + unsigned long flags; + int received = 0; + int ret; + + /* process all communication messages */ + while (true) { + struct ican3_msg uninitialized_var(msg); + 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_done(napi, received); + + 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) +{ + 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(0x00, &mod->dpmctrl->hwreset); + + /* 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 / 2)); + + netdev_err(mod->ndev, "failed to reset CAN module\n"); + return -ETIMEDOUT; +} + +static void 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 ican3_startup_module(struct ican3_dev *mod) +{ + int ret; + + ret = ican3_reset_module(mod); + if (ret) { + netdev_err(mod->ndev, "unable to reset module\n"); + return ret; + } + + /* detect firmware */ + memcpy_fromio(mod->fwinfo, mod->dpm + FIRMWARE_STAMP, sizeof(mod->fwinfo) - 1); + if (strncmp(mod->fwinfo, "JANZ-ICAN3", 10)) { + netdev_err(mod->ndev, "ICAN3 not detected (found %s)\n", mod->fwinfo); + return -ENODEV; + } + if (strstr(mod->fwinfo, "CAL/CANopen")) + mod->fwtype = ICAN3_FWTYPE_CAL_CANOPEN; + else + mod->fwtype = ICAN3_FWTYPE_ICANOS; + + /* re-enable interrupts so we can send messages */ + iowrite8(1 << mod->num, &mod->ctrl->int_enable); + + ret = ican3_msg_connect(mod); + if (ret) { + netdev_err(mod->ndev, "unable to connect to module\n"); + return ret; + } + + ican3_init_new_host_interface(mod); + ret = ican3_msg_newhostif(mod); + if (ret) { + netdev_err(mod->ndev, "unable to switch to new-style interface\n"); + return ret; + } + + /* default to "termination on" */ + ret = ican3_set_termination(mod, true); + if (ret) { + netdev_err(mod->ndev, "unable to enable termination\n"); + return ret; + } + + /* default to "bus errors enabled" */ + ret = ican3_set_buserror(mod, 1); + if (ret) { + netdev_err(mod->ndev, "unable to set bus-error\n"); + return ret; + } + + ican3_init_fast_host_interface(mod); + ret = ican3_msg_fasthostif(mod); + if (ret) { + netdev_err(mod->ndev, "unable to switch to fast host interface\n"); + return ret; + } + + ret = ican3_set_id_filter(mod, true); + if (ret) { + netdev_err(mod->ndev, "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); + int ret; + + /* open the CAN layer */ + ret = open_candev(ndev); + if (ret) { + netdev_err(mod->ndev, "unable to start CAN layer\n"); + return ret; + } + + /* bring the bus online */ + ret = ican3_set_bus_state(mod, true); + if (ret) { + netdev_err(mod->ndev, "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) { + netdev_err(mod->ndev, "unable to set bus-off\n"); + return ret; + } + + /* drop all outstanding echo skbs */ + skb_queue_purge(&mod->echoq); + + /* close the CAN layer */ + close_candev(ndev); + return 0; +} + +static netdev_tx_t ican3_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct ican3_fast_desc desc; + void __iomem *desc_addr; + unsigned long flags; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + spin_lock_irqsave(&mod->lock, flags); + + /* check that we can actually transmit */ + if (!ican3_txok(mod)) { + netdev_err(mod->ndev, "BUG: no free descriptors\n"); + 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); + + /* + * This hardware doesn't have TX-done notifications, so we'll try and + * emulate it the best we can using ECHO skbs. Add the skb to the ECHO + * stack. Upon packet reception, check if the ECHO skb and received + * skb match, and use that to wake the queue. + */ + ican3_put_echo_skb(mod, skb); + + /* + * 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); + + /* if there is no free descriptor space, stop the transmit queue */ + 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, + .ndo_change_mtu = can_change_mtu, +}; + +/* + * Low-level CAN Device + */ + +/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */ +static const 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, +}; + +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) { + netdev_err(ndev, "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; + + if (!wait_for_completion_timeout(&mod->buserror_comp, HZ)) { + netdev_info(mod->ndev, "%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; + + if (!wait_for_completion_timeout(&mod->termination_comp, HZ)) { + netdev_info(mod->ndev, "%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 (kstrtoul(buf, 0, &enable)) + return -EINVAL; + + ret = ican3_set_termination(mod, enable); + if (ret) + return ret; + + return count; +} + +static ssize_t ican3_sysfs_show_fwinfo(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); + + return scnprintf(buf, PAGE_SIZE, "%s\n", mod->fwinfo); +} + +static DEVICE_ATTR(termination, 0644, ican3_sysfs_show_term, + ican3_sysfs_set_term); +static DEVICE_ATTR(fwinfo, 0444, ican3_sysfs_show_fwinfo, NULL); + +static struct attribute *ican3_sysfs_attrs[] = { + &dev_attr_termination.attr, + &dev_attr_fwinfo.attr, + NULL, +}; + +static const struct attribute_group ican3_sysfs_attr_group = { + .attrs = ican3_sysfs_attrs, +}; + +/* + * PCI Subsystem + */ + +static int 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 = dev_get_platdata(&pdev->dev); + 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->num = pdata->modno; + netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS); + skb_queue_head_init(&mod->echoq); + 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_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 + | CAN_CTRLMODE_ONE_SHOT; + + /* 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; + } + + netdev_info(mod->ndev, "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 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, + }, + .probe = ican3_probe, + .remove = 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/led.c b/drivers/net/can/led.c new file mode 100644 index 000000000..c1b667675 --- /dev/null +++ b/drivers/net/can/led.c @@ -0,0 +1,143 @@ +/* + * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com> + * Copyright 2012, Kurt Van Dijck <kurt.van.dijck@eia.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/can/dev.h> + +#include <linux/can/led.h> + +static unsigned long led_delay = 50; +module_param(led_delay, ulong, 0644); +MODULE_PARM_DESC(led_delay, + "blink delay time for activity leds (msecs, default: 50)."); + +/* Trigger a LED event in response to a CAN device event */ +void can_led_event(struct net_device *netdev, enum can_led_event event) +{ + struct can_priv *priv = netdev_priv(netdev); + + switch (event) { + case CAN_LED_EVENT_OPEN: + led_trigger_event(priv->tx_led_trig, LED_FULL); + led_trigger_event(priv->rx_led_trig, LED_FULL); + led_trigger_event(priv->rxtx_led_trig, LED_FULL); + break; + case CAN_LED_EVENT_STOP: + led_trigger_event(priv->tx_led_trig, LED_OFF); + led_trigger_event(priv->rx_led_trig, LED_OFF); + led_trigger_event(priv->rxtx_led_trig, LED_OFF); + break; + case CAN_LED_EVENT_TX: + if (led_delay) { + led_trigger_blink_oneshot(priv->tx_led_trig, + &led_delay, &led_delay, 1); + led_trigger_blink_oneshot(priv->rxtx_led_trig, + &led_delay, &led_delay, 1); + } + break; + case CAN_LED_EVENT_RX: + if (led_delay) { + led_trigger_blink_oneshot(priv->rx_led_trig, + &led_delay, &led_delay, 1); + led_trigger_blink_oneshot(priv->rxtx_led_trig, + &led_delay, &led_delay, 1); + } + break; + } +} +EXPORT_SYMBOL_GPL(can_led_event); + +static void can_led_release(struct device *gendev, void *res) +{ + struct can_priv *priv = netdev_priv(to_net_dev(gendev)); + + led_trigger_unregister_simple(priv->tx_led_trig); + led_trigger_unregister_simple(priv->rx_led_trig); + led_trigger_unregister_simple(priv->rxtx_led_trig); +} + +/* Register CAN LED triggers for a CAN device + * + * This is normally called from a driver's probe function + */ +void devm_can_led_init(struct net_device *netdev) +{ + struct can_priv *priv = netdev_priv(netdev); + void *res; + + res = devres_alloc(can_led_release, 0, GFP_KERNEL); + if (!res) { + netdev_err(netdev, "cannot register LED triggers\n"); + return; + } + + snprintf(priv->tx_led_trig_name, sizeof(priv->tx_led_trig_name), + "%s-tx", netdev->name); + snprintf(priv->rx_led_trig_name, sizeof(priv->rx_led_trig_name), + "%s-rx", netdev->name); + snprintf(priv->rxtx_led_trig_name, sizeof(priv->rxtx_led_trig_name), + "%s-rxtx", netdev->name); + + led_trigger_register_simple(priv->tx_led_trig_name, + &priv->tx_led_trig); + led_trigger_register_simple(priv->rx_led_trig_name, + &priv->rx_led_trig); + led_trigger_register_simple(priv->rxtx_led_trig_name, + &priv->rxtx_led_trig); + + devres_add(&netdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_can_led_init); + +/* NETDEV rename notifier to rename the associated led triggers too */ +static int can_led_notifier(struct notifier_block *nb, unsigned long msg, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct can_priv *priv = safe_candev_priv(netdev); + char name[CAN_LED_NAME_SZ]; + + if (!priv) + return NOTIFY_DONE; + + if (!priv->tx_led_trig || !priv->rx_led_trig || !priv->rxtx_led_trig) + return NOTIFY_DONE; + + if (msg == NETDEV_CHANGENAME) { + snprintf(name, sizeof(name), "%s-tx", netdev->name); + led_trigger_rename_static(name, priv->tx_led_trig); + + snprintf(name, sizeof(name), "%s-rx", netdev->name); + led_trigger_rename_static(name, priv->rx_led_trig); + + snprintf(name, sizeof(name), "%s-rxtx", netdev->name); + led_trigger_rename_static(name, priv->rxtx_led_trig); + } + + return NOTIFY_DONE; +} + +/* notifier block for netdevice event */ +static struct notifier_block can_netdev_notifier __read_mostly = { + .notifier_call = can_led_notifier, +}; + +int __init can_led_notifier_init(void) +{ + return register_netdevice_notifier(&can_netdev_notifier); +} + +void __exit can_led_notifier_exit(void) +{ + unregister_netdevice_notifier(&can_netdev_notifier); +} diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig new file mode 100644 index 000000000..04f20dd39 --- /dev/null +++ b/drivers/net/can/m_can/Kconfig @@ -0,0 +1,5 @@ +config CAN_M_CAN + depends on HAS_IOMEM + tristate "Bosch M_CAN devices" + ---help--- + Say Y here if you want to support for Bosch M_CAN controller. diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile new file mode 100644 index 000000000..8bbd7f24f --- /dev/null +++ b/drivers/net/can/m_can/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Bosch M_CAN controller driver. +# + +obj-$(CONFIG_CAN_M_CAN) += m_can.o diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c new file mode 100644 index 000000000..f5f1367d4 --- /dev/null +++ b/drivers/net/can/m_can/m_can.c @@ -0,0 +1,1827 @@ +/* + * CAN bus driver for Bosch M_CAN controller + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * Dong Aisheng <b29396@freescale.com> + * + * Bosch M_CAN user manual can be obtained from: + * http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/ + * mcan_users_manual_v302.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/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/iopoll.h> +#include <linux/can/dev.h> +#include <linux/pinctrl/consumer.h> + +/* napi related */ +#define M_CAN_NAPI_WEIGHT 64 + +/* message ram configuration data length */ +#define MRAM_CFG_LEN 8 + +/* registers definition */ +enum m_can_reg { + M_CAN_CREL = 0x0, + M_CAN_ENDN = 0x4, + M_CAN_CUST = 0x8, + M_CAN_DBTP = 0xc, + M_CAN_TEST = 0x10, + M_CAN_RWD = 0x14, + M_CAN_CCCR = 0x18, + M_CAN_NBTP = 0x1c, + M_CAN_TSCC = 0x20, + M_CAN_TSCV = 0x24, + M_CAN_TOCC = 0x28, + M_CAN_TOCV = 0x2c, + M_CAN_ECR = 0x40, + M_CAN_PSR = 0x44, +/* TDCR Register only available for version >=3.1.x */ + M_CAN_TDCR = 0x48, + M_CAN_IR = 0x50, + M_CAN_IE = 0x54, + M_CAN_ILS = 0x58, + M_CAN_ILE = 0x5c, + M_CAN_GFC = 0x80, + M_CAN_SIDFC = 0x84, + M_CAN_XIDFC = 0x88, + M_CAN_XIDAM = 0x90, + M_CAN_HPMS = 0x94, + M_CAN_NDAT1 = 0x98, + M_CAN_NDAT2 = 0x9c, + M_CAN_RXF0C = 0xa0, + M_CAN_RXF0S = 0xa4, + M_CAN_RXF0A = 0xa8, + M_CAN_RXBC = 0xac, + M_CAN_RXF1C = 0xb0, + M_CAN_RXF1S = 0xb4, + M_CAN_RXF1A = 0xb8, + M_CAN_RXESC = 0xbc, + M_CAN_TXBC = 0xc0, + M_CAN_TXFQS = 0xc4, + M_CAN_TXESC = 0xc8, + M_CAN_TXBRP = 0xcc, + M_CAN_TXBAR = 0xd0, + M_CAN_TXBCR = 0xd4, + M_CAN_TXBTO = 0xd8, + M_CAN_TXBCF = 0xdc, + M_CAN_TXBTIE = 0xe0, + M_CAN_TXBCIE = 0xe4, + M_CAN_TXEFC = 0xf0, + M_CAN_TXEFS = 0xf4, + M_CAN_TXEFA = 0xf8, +}; + +/* m_can lec values */ +enum m_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, +}; + +enum m_can_mram_cfg { + MRAM_SIDF = 0, + MRAM_XIDF, + MRAM_RXF0, + MRAM_RXF1, + MRAM_RXB, + MRAM_TXE, + MRAM_TXB, + MRAM_CFG_NUM, +}; + +/* Core Release Register (CREL) */ +#define CREL_REL_SHIFT 28 +#define CREL_REL_MASK (0xF << CREL_REL_SHIFT) +#define CREL_STEP_SHIFT 24 +#define CREL_STEP_MASK (0xF << CREL_STEP_SHIFT) +#define CREL_SUBSTEP_SHIFT 20 +#define CREL_SUBSTEP_MASK (0xF << CREL_SUBSTEP_SHIFT) + +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_TDC BIT(23) +#define DBTP_DBRP_SHIFT 16 +#define DBTP_DBRP_MASK (0x1f << DBTP_DBRP_SHIFT) +#define DBTP_DTSEG1_SHIFT 8 +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT) +#define DBTP_DTSEG2_SHIFT 4 +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT) +#define DBTP_DSJW_SHIFT 0 +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT) + +/* Transmitter Delay Compensation Register (TDCR) */ +#define TDCR_TDCO_SHIFT 8 +#define TDCR_TDCO_MASK (0x7F << TDCR_TDCO_SHIFT) +#define TDCR_TDCF_SHIFT 0 +#define TDCR_TDCF_MASK (0x7F << TDCR_TDCF_SHIFT) + +/* Test Register (TEST) */ +#define TEST_LBCK BIT(4) + +/* CC Control Register(CCCR) */ +#define CCCR_CMR_MASK 0x3 +#define CCCR_CMR_SHIFT 10 +#define CCCR_CMR_CANFD 0x1 +#define CCCR_CMR_CANFD_BRS 0x2 +#define CCCR_CMR_CAN 0x3 +#define CCCR_CME_MASK 0x3 +#define CCCR_CME_SHIFT 8 +#define CCCR_CME_CAN 0 +#define CCCR_CME_CANFD 0x1 +#define CCCR_CME_CANFD_BRS 0x2 +#define CCCR_TXP BIT(14) +#define CCCR_TEST BIT(7) +#define CCCR_MON BIT(5) +#define CCCR_CSR BIT(4) +#define CCCR_CSA BIT(3) +#define CCCR_ASM BIT(2) +#define CCCR_CCE BIT(1) +#define CCCR_INIT BIT(0) +#define CCCR_CANFD 0x10 +/* for version >=3.1.x */ +#define CCCR_EFBI BIT(13) +#define CCCR_PXHD BIT(12) +#define CCCR_BRSE BIT(9) +#define CCCR_FDOE BIT(8) +/* only for version >=3.2.x */ +#define CCCR_NISO BIT(15) + +/* Nominal Bit Timing & Prescaler Register (NBTP) */ +#define NBTP_NSJW_SHIFT 25 +#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT) +#define NBTP_NBRP_SHIFT 16 +#define NBTP_NBRP_MASK (0x1ff << NBTP_NBRP_SHIFT) +#define NBTP_NTSEG1_SHIFT 8 +#define NBTP_NTSEG1_MASK (0xff << NBTP_NTSEG1_SHIFT) +#define NBTP_NTSEG2_SHIFT 0 +#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT) + +/* Error Counter Register(ECR) */ +#define ECR_RP BIT(15) +#define ECR_REC_SHIFT 8 +#define ECR_REC_MASK (0x7f << ECR_REC_SHIFT) +#define ECR_TEC_SHIFT 0 +#define ECR_TEC_MASK 0xff + +/* Protocol Status Register(PSR) */ +#define PSR_BO BIT(7) +#define PSR_EW BIT(6) +#define PSR_EP BIT(5) +#define PSR_LEC_MASK 0x7 + +/* Interrupt Register(IR) */ +#define IR_ALL_INT 0xffffffff + +/* Renamed bits for versions > 3.1.x */ +#define IR_ARA BIT(29) +#define IR_PED BIT(28) +#define IR_PEA BIT(27) + +/* Bits for version 3.0.x */ +#define IR_STE BIT(31) +#define IR_FOE BIT(30) +#define IR_ACKE BIT(29) +#define IR_BE BIT(28) +#define IR_CRCE BIT(27) +#define IR_WDI BIT(26) +#define IR_BO BIT(25) +#define IR_EW BIT(24) +#define IR_EP BIT(23) +#define IR_ELO BIT(22) +#define IR_BEU BIT(21) +#define IR_BEC BIT(20) +#define IR_DRX BIT(19) +#define IR_TOO BIT(18) +#define IR_MRAF BIT(17) +#define IR_TSW BIT(16) +#define IR_TEFL BIT(15) +#define IR_TEFF BIT(14) +#define IR_TEFW BIT(13) +#define IR_TEFN BIT(12) +#define IR_TFE BIT(11) +#define IR_TCF BIT(10) +#define IR_TC BIT(9) +#define IR_HPM BIT(8) +#define IR_RF1L BIT(7) +#define IR_RF1F BIT(6) +#define IR_RF1W BIT(5) +#define IR_RF1N BIT(4) +#define IR_RF0L BIT(3) +#define IR_RF0F BIT(2) +#define IR_RF0W BIT(1) +#define IR_RF0N BIT(0) +#define IR_ERR_STATE (IR_BO | IR_EW | IR_EP) + +/* Interrupts for version 3.0.x */ +#define IR_ERR_LEC_30X (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE) +#define IR_ERR_BUS_30X (IR_ERR_LEC_30X | IR_WDI | IR_BEU | IR_BEC | \ + IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \ + IR_RF0L) +#define IR_ERR_ALL_30X (IR_ERR_STATE | IR_ERR_BUS_30X) +/* Interrupts for version >= 3.1.x */ +#define IR_ERR_LEC_31X (IR_PED | IR_PEA) +#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \ + IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \ + IR_RF0L) +#define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X) + +/* Interrupt Line Select (ILS) */ +#define ILS_ALL_INT0 0x0 +#define ILS_ALL_INT1 0xFFFFFFFF + +/* Interrupt Line Enable (ILE) */ +#define ILE_EINT1 BIT(1) +#define ILE_EINT0 BIT(0) + +/* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */ +#define RXFC_FWM_SHIFT 24 +#define RXFC_FWM_MASK (0x7f << RXFC_FWM_SHIFT) +#define RXFC_FS_SHIFT 16 +#define RXFC_FS_MASK (0x7f << RXFC_FS_SHIFT) + +/* Rx FIFO 0/1 Status (RXF0S/RXF1S) */ +#define RXFS_RFL BIT(25) +#define RXFS_FF BIT(24) +#define RXFS_FPI_SHIFT 16 +#define RXFS_FPI_MASK 0x3f0000 +#define RXFS_FGI_SHIFT 8 +#define RXFS_FGI_MASK 0x3f00 +#define RXFS_FFL_MASK 0x7f + +/* Rx Buffer / FIFO Element Size Configuration (RXESC) */ +#define M_CAN_RXESC_8BYTES 0x0 +#define M_CAN_RXESC_64BYTES 0x777 + +/* Tx Buffer Configuration(TXBC) */ +#define TXBC_NDTB_SHIFT 16 +#define TXBC_NDTB_MASK (0x3f << TXBC_NDTB_SHIFT) +#define TXBC_TFQS_SHIFT 24 +#define TXBC_TFQS_MASK (0x3f << TXBC_TFQS_SHIFT) + +/* Tx FIFO/Queue Status (TXFQS) */ +#define TXFQS_TFQF BIT(21) +#define TXFQS_TFQPI_SHIFT 16 +#define TXFQS_TFQPI_MASK (0x1f << TXFQS_TFQPI_SHIFT) +#define TXFQS_TFGI_SHIFT 8 +#define TXFQS_TFGI_MASK (0x1f << TXFQS_TFGI_SHIFT) +#define TXFQS_TFFL_SHIFT 0 +#define TXFQS_TFFL_MASK (0x3f << TXFQS_TFFL_SHIFT) + +/* Tx Buffer Element Size Configuration(TXESC) */ +#define TXESC_TBDS_8BYTES 0x0 +#define TXESC_TBDS_64BYTES 0x7 + +/* Tx Event FIFO Configuration (TXEFC) */ +#define TXEFC_EFS_SHIFT 16 +#define TXEFC_EFS_MASK (0x3f << TXEFC_EFS_SHIFT) + +/* Tx Event FIFO Status (TXEFS) */ +#define TXEFS_TEFL BIT(25) +#define TXEFS_EFF BIT(24) +#define TXEFS_EFGI_SHIFT 8 +#define TXEFS_EFGI_MASK (0x1f << TXEFS_EFGI_SHIFT) +#define TXEFS_EFFL_SHIFT 0 +#define TXEFS_EFFL_MASK (0x3f << TXEFS_EFFL_SHIFT) + +/* Tx Event FIFO Acknowledge (TXEFA) */ +#define TXEFA_EFAI_SHIFT 0 +#define TXEFA_EFAI_MASK (0x1f << TXEFA_EFAI_SHIFT) + +/* Message RAM Configuration (in bytes) */ +#define SIDF_ELEMENT_SIZE 4 +#define XIDF_ELEMENT_SIZE 8 +#define RXF0_ELEMENT_SIZE 72 +#define RXF1_ELEMENT_SIZE 72 +#define RXB_ELEMENT_SIZE 72 +#define TXE_ELEMENT_SIZE 8 +#define TXB_ELEMENT_SIZE 72 + +/* Message RAM Elements */ +#define M_CAN_FIFO_ID 0x0 +#define M_CAN_FIFO_DLC 0x4 +#define M_CAN_FIFO_DATA(n) (0x8 + ((n) << 2)) + +/* Rx Buffer Element */ +/* R0 */ +#define RX_BUF_ESI BIT(31) +#define RX_BUF_XTD BIT(30) +#define RX_BUF_RTR BIT(29) +/* R1 */ +#define RX_BUF_ANMF BIT(31) +#define RX_BUF_FDF BIT(21) +#define RX_BUF_BRS BIT(20) + +/* Tx Buffer Element */ +/* T0 */ +#define TX_BUF_ESI BIT(31) +#define TX_BUF_XTD BIT(30) +#define TX_BUF_RTR BIT(29) +/* T1 */ +#define TX_BUF_EFC BIT(23) +#define TX_BUF_FDF BIT(21) +#define TX_BUF_BRS BIT(20) +#define TX_BUF_MM_SHIFT 24 +#define TX_BUF_MM_MASK (0xff << TX_BUF_MM_SHIFT) + +/* Tx event FIFO Element */ +/* E1 */ +#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT +#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT) + +/* address offset and element number for each FIFO/Buffer in the Message RAM */ +struct mram_cfg { + u16 off; + u8 num; +}; + +/* m_can private data structure */ +struct m_can_priv { + struct can_priv can; /* must be the first member */ + struct napi_struct napi; + struct net_device *dev; + struct device *device; + struct clk *hclk; + struct clk *cclk; + void __iomem *base; + u32 irqstatus; + int version; + + /* message ram configuration */ + void __iomem *mram_base; + struct mram_cfg mcfg[MRAM_CFG_NUM]; +}; + +static inline u32 m_can_read(const struct m_can_priv *priv, enum m_can_reg reg) +{ + return readl(priv->base + reg); +} + +static inline void m_can_write(const struct m_can_priv *priv, + enum m_can_reg reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static inline u32 m_can_fifo_read(const struct m_can_priv *priv, + u32 fgi, unsigned int offset) +{ + return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off + + fgi * RXF0_ELEMENT_SIZE + offset); +} + +static inline void m_can_fifo_write(const struct m_can_priv *priv, + u32 fpi, unsigned int offset, u32 val) +{ + writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off + + fpi * TXB_ELEMENT_SIZE + offset); +} + +static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv, + u32 fgi, + u32 offset) { + return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off + + fgi * TXE_ELEMENT_SIZE + offset); +} + +static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv) +{ + return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF); +} + +static inline void m_can_config_endisable(const struct m_can_priv *priv, + bool enable) +{ + u32 cccr = m_can_read(priv, M_CAN_CCCR); + u32 timeout = 10; + u32 val = 0; + + if (enable) { + /* enable m_can configuration */ + m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT); + udelay(5); + /* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */ + m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE); + } else { + m_can_write(priv, M_CAN_CCCR, cccr & ~(CCCR_INIT | CCCR_CCE)); + } + + /* there's a delay for module initialization */ + if (enable) + val = CCCR_INIT | CCCR_CCE; + + while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) { + if (timeout == 0) { + netdev_warn(priv->dev, "Failed to init module\n"); + return; + } + timeout--; + udelay(1); + } +} + +static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv) +{ + /* Only interrupt line 0 is used in this driver */ + m_can_write(priv, M_CAN_ILE, ILE_EINT0); +} + +static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv) +{ + m_can_write(priv, M_CAN_ILE, 0x0); +} + +static void m_can_read_fifo(struct net_device *dev, u32 rxfs) +{ + struct net_device_stats *stats = &dev->stats; + struct m_can_priv *priv = netdev_priv(dev); + struct canfd_frame *cf; + struct sk_buff *skb; + u32 id, fgi, dlc; + int i; + + /* calculate the fifo get index for where to read data */ + fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_SHIFT; + dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); + if (dlc & RX_BUF_FDF) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + if (dlc & RX_BUF_FDF) + cf->len = can_dlc2len((dlc >> 16) & 0x0F); + else + cf->len = get_can_dlc((dlc >> 16) & 0x0F); + + id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_ID); + if (id & RX_BUF_XTD) + cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (id >> 18) & CAN_SFF_MASK; + + if (id & RX_BUF_ESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(dev, "ESI Error\n"); + } + + if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if (dlc & RX_BUF_BRS) + cf->flags |= CANFD_BRS; + + for (i = 0; i < cf->len; i += 4) + *(u32 *)(cf->data + i) = + m_can_fifo_read(priv, fgi, + M_CAN_FIFO_DATA(i / 4)); + } + + /* acknowledge rx fifo 0 */ + m_can_write(priv, M_CAN_RXF0A, fgi); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_receive_skb(skb); +} + +static int m_can_do_rx_poll(struct net_device *dev, int quota) +{ + struct m_can_priv *priv = netdev_priv(dev); + u32 pkts = 0; + u32 rxfs; + + rxfs = m_can_read(priv, M_CAN_RXF0S); + if (!(rxfs & RXFS_FFL_MASK)) { + netdev_dbg(dev, "no messages in fifo0\n"); + return 0; + } + + while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) { + m_can_read_fifo(dev, rxfs); + + quota--; + pkts++; + rxfs = m_can_read(priv, M_CAN_RXF0S); + } + + if (pkts) + can_led_event(dev, CAN_LED_EVENT_RX); + + return pkts; +} + +static int m_can_handle_lost_msg(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(dev, "msg lost in rxf0\n"); + + stats->rx_errors++; + stats->rx_over_errors++; + + skb = alloc_can_err_skb(dev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + netif_receive_skb(skb); + + return 1; +} + +static int m_can_handle_lec_err(struct net_device *dev, + enum m_can_lec_type lec_type) +{ + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + /* 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 + */ + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + 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[3] = CAN_ERR_PROT_LOC_ACK; + 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[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int __m_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct m_can_priv *priv = netdev_priv(dev); + unsigned int ecr; + + ecr = m_can_read(priv, M_CAN_ECR); + bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; + bec->txerr = (ecr & ECR_TEC_MASK) >> ECR_TEC_SHIFT; + + return 0; +} + +static int m_can_clk_start(struct m_can_priv *priv) +{ + int err; + + err = pm_runtime_get_sync(priv->device); + if (err < 0) { + pm_runtime_put_noidle(priv->device); + return err; + } + + return 0; +} + +static void m_can_clk_stop(struct m_can_priv *priv) +{ + pm_runtime_put_sync(priv->device); +} + +static int m_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct m_can_priv *priv = netdev_priv(dev); + int err; + + err = m_can_clk_start(priv); + if (err) + return err; + + __m_can_get_berr_counter(dev, bec); + + m_can_clk_stop(priv); + + return 0; +} + +static int m_can_handle_state_change(struct net_device *dev, + enum can_state new_state) +{ + struct m_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; + unsigned int ecr; + + switch (new_state) { + case CAN_STATE_ERROR_WARNING: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + m_can_disable_all_interrupts(priv); + priv->can.can_stats.bus_off++; + can_bus_off(dev); + break; + default: + break; + } + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + __m_can_get_berr_counter(dev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_WARNING: + /* error warning state */ + 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 CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + ecr = m_can_read(priv, M_CAN_ECR); + if (ecr & ECR_RP) + 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 CAN_STATE_BUS_OFF: + /* bus-off state */ + cf->can_id |= CAN_ERR_BUSOFF; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int m_can_handle_state_errors(struct net_device *dev, u32 psr) +{ + struct m_can_priv *priv = netdev_priv(dev); + int work_done = 0; + + if ((psr & PSR_EW) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_ERROR_WARNING); + } + + if ((psr & PSR_EP) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(dev, "entered error passive state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((psr & PSR_BO) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(dev, "entered error bus off state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_BUS_OFF); + } + + return work_done; +} + +static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus) +{ + if (irqstatus & IR_WDI) + netdev_err(dev, "Message RAM Watchdog event due to missing READY\n"); + if (irqstatus & IR_BEU) + netdev_err(dev, "Bit Error Uncorrected\n"); + if (irqstatus & IR_BEC) + netdev_err(dev, "Bit Error Corrected\n"); + if (irqstatus & IR_TOO) + netdev_err(dev, "Timeout reached\n"); + if (irqstatus & IR_MRAF) + netdev_err(dev, "Message RAM access failure occurred\n"); +} + +static inline bool is_lec_err(u32 psr) +{ + psr &= LEC_UNUSED; + + return psr && (psr != LEC_UNUSED); +} + +static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, + u32 psr) +{ + struct m_can_priv *priv = netdev_priv(dev); + int work_done = 0; + + if (irqstatus & IR_RF0L) + work_done += m_can_handle_lost_msg(dev); + + /* handle lec errors on the bus */ + if ((priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && + is_lec_err(psr)) + work_done += m_can_handle_lec_err(dev, psr & LEC_UNUSED); + + /* other unproccessed error interrupts */ + m_can_handle_other_err(dev, irqstatus); + + return work_done; +} + +static int m_can_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + struct m_can_priv *priv = netdev_priv(dev); + int work_done = 0; + u32 irqstatus, psr; + + irqstatus = priv->irqstatus | m_can_read(priv, M_CAN_IR); + if (!irqstatus) + goto end; + + /* Errata workaround for issue "Needless activation of MRAF irq" + * During frame reception while the MCAN is in Error Passive state + * and the Receive Error Counter has the value MCAN_ECR.REC = 127, + * it may happen that MCAN_IR.MRAF is set although there was no + * Message RAM access failure. + * If MCAN_IR.MRAF is enabled, an interrupt to the Host CPU is generated + * The Message RAM Access Failure interrupt routine needs to check + * whether MCAN_ECR.RP = ’1’ and MCAN_ECR.REC = 127. + * In this case, reset MCAN_IR.MRAF. No further action is required. + */ + if ((priv->version <= 31) && (irqstatus & IR_MRAF) && + (m_can_read(priv, M_CAN_ECR) & ECR_RP)) { + struct can_berr_counter bec; + + __m_can_get_berr_counter(dev, &bec); + if (bec.rxerr == 127) { + m_can_write(priv, M_CAN_IR, IR_MRAF); + irqstatus &= ~IR_MRAF; + } + } + + psr = m_can_read(priv, M_CAN_PSR); + if (irqstatus & IR_ERR_STATE) + work_done += m_can_handle_state_errors(dev, psr); + + if (irqstatus & IR_ERR_BUS_30X) + work_done += m_can_handle_bus_errors(dev, irqstatus, psr); + + if (irqstatus & IR_RF0N) + work_done += m_can_do_rx_poll(dev, (quota - work_done)); + + if (work_done < quota) { + napi_complete_done(napi, work_done); + m_can_enable_all_interrupts(priv); + } + +end: + return work_done; +} + +static void m_can_echo_tx_event(struct net_device *dev) +{ + u32 txe_count = 0; + u32 m_can_txefs; + u32 fgi = 0; + int i = 0; + unsigned int msg_mark; + + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + + /* read tx event fifo status */ + m_can_txefs = m_can_read(priv, M_CAN_TXEFS); + + /* Get Tx Event fifo element count */ + txe_count = (m_can_txefs & TXEFS_EFFL_MASK) + >> TXEFS_EFFL_SHIFT; + + /* Get and process all sent elements */ + for (i = 0; i < txe_count; i++) { + /* retrieve get index */ + fgi = (m_can_read(priv, M_CAN_TXEFS) & TXEFS_EFGI_MASK) + >> TXEFS_EFGI_SHIFT; + + /* get message marker */ + msg_mark = (m_can_txe_fifo_read(priv, fgi, 4) & + TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; + + /* ack txe element */ + m_can_write(priv, M_CAN_TXEFA, (TXEFA_EFAI_MASK & + (fgi << TXEFA_EFAI_SHIFT))); + + /* update stats */ + stats->tx_bytes += can_get_echo_skb(dev, msg_mark); + stats->tx_packets++; + } +} + +static irqreturn_t m_can_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + u32 ir; + + ir = m_can_read(priv, M_CAN_IR); + if (!ir) + return IRQ_NONE; + + /* ACK all irqs */ + if (ir & IR_ALL_INT) + m_can_write(priv, M_CAN_IR, ir); + + /* schedule NAPI in case of + * - rx IRQ + * - state change IRQ + * - bus error IRQ and bus error reporting + */ + if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) { + priv->irqstatus = ir; + m_can_disable_all_interrupts(priv); + napi_schedule(&priv->napi); + } + + if (priv->version == 30) { + if (ir & IR_TC) { + /* Transmission Complete Interrupt*/ + stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_packets++; + can_led_event(dev, CAN_LED_EVENT_TX); + netif_wake_queue(dev); + } + } else { + if (ir & IR_TEFN) { + /* New TX FIFO Element arrived */ + m_can_echo_tx_event(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + if (netif_queue_stopped(dev) && + !m_can_tx_fifo_full(priv)) + netif_wake_queue(dev); + } + } + + return IRQ_HANDLED; +} + +static const struct can_bittiming_const m_can_bittiming_const_30X = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 64, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +static const struct can_bittiming_const m_can_data_bittiming_const_30X = { + .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 = 32, + .brp_inc = 1, +}; + +static const struct can_bittiming_const m_can_bittiming_const_31X = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 256, + .tseg2_min = 2, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +static const struct can_bittiming_const m_can_data_bittiming_const_31X = { + .name = KBUILD_MODNAME, + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 32, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + +static int m_can_set_bittiming(struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 reg_btp; + + brp = bt->brp - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) | + (tseg1 << NBTP_NTSEG1_SHIFT) | (tseg2 << NBTP_NTSEG2_SHIFT); + m_can_write(priv, M_CAN_NBTP, reg_btp); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + reg_btp = 0; + brp = dbt->brp - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + + /* TDC is only needed for bitrates beyond 2.5 MBit/s. + * This is mentioned in the "Bit Time Requirements for CAN FD" + * paper presented at the International CAN Conference 2013 + */ + if (dbt->bitrate > 2500000) { + u32 tdco, ssp; + + /* Use the same value of secondary sampling point + * as the data sampling point + */ + ssp = dbt->sample_point; + + /* Equation based on Bosch's M_CAN User Manual's + * Transmitter Delay Compensation Section + */ + tdco = (priv->can.clock.freq / 1000) * + ssp / dbt->bitrate; + + /* Max valid TDCO value is 127 */ + if (tdco > 127) { + netdev_warn(dev, "TDCO value of %u is beyond maximum. Using maximum possible value\n", + tdco); + tdco = 127; + } + + reg_btp |= DBTP_TDC; + m_can_write(priv, M_CAN_TDCR, + tdco << TDCR_TDCO_SHIFT); + } + + reg_btp |= (brp << DBTP_DBRP_SHIFT) | + (sjw << DBTP_DSJW_SHIFT) | + (tseg1 << DBTP_DTSEG1_SHIFT) | + (tseg2 << DBTP_DTSEG2_SHIFT); + + m_can_write(priv, M_CAN_DBTP, reg_btp); + } + + return 0; +} + +/* Configure M_CAN chip: + * - set rx buffer/fifo element size + * - configure rx fifo + * - accept non-matching frame into fifo 0 + * - configure tx buffer + * - >= v3.1.x: TX FIFO is used + * - configure mode + * - setup bittiming + */ +static void m_can_chip_config(struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + u32 cccr, test; + + m_can_config_endisable(priv, true); + + /* RX Buffer/FIFO Element Size 64 bytes data field */ + m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_64BYTES); + + /* Accept Non-matching Frames Into FIFO 0 */ + m_can_write(priv, M_CAN_GFC, 0x0); + + if (priv->version == 30) { + /* only support one Tx Buffer currently */ + m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) | + priv->mcfg[MRAM_TXB].off); + } else { + /* TX FIFO is used for newer IP Core versions */ + m_can_write(priv, M_CAN_TXBC, + (priv->mcfg[MRAM_TXB].num << TXBC_TFQS_SHIFT) | + (priv->mcfg[MRAM_TXB].off)); + } + + /* support 64 bytes payload */ + m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES); + + /* TX Event FIFO */ + if (priv->version == 30) { + m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) | + priv->mcfg[MRAM_TXE].off); + } else { + /* Full TX Event FIFO is used */ + m_can_write(priv, M_CAN_TXEFC, + ((priv->mcfg[MRAM_TXE].num << TXEFC_EFS_SHIFT) + & TXEFC_EFS_MASK) | + priv->mcfg[MRAM_TXE].off); + } + + /* rx fifo configuration, blocking mode, fifo size 1 */ + m_can_write(priv, M_CAN_RXF0C, + (priv->mcfg[MRAM_RXF0].num << RXFC_FS_SHIFT) | + priv->mcfg[MRAM_RXF0].off); + + m_can_write(priv, M_CAN_RXF1C, + (priv->mcfg[MRAM_RXF1].num << RXFC_FS_SHIFT) | + priv->mcfg[MRAM_RXF1].off); + + cccr = m_can_read(priv, M_CAN_CCCR); + test = m_can_read(priv, M_CAN_TEST); + test &= ~TEST_LBCK; + if (priv->version == 30) { + /* Version 3.0.x */ + + cccr &= ~(CCCR_TEST | CCCR_MON | + (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | + (CCCR_CME_MASK << CCCR_CME_SHIFT)); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; + + } else { + /* Version 3.1.x or 3.2.x */ + cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE | + CCCR_NISO); + + /* Only 3.2.x has NISO Bit implemented */ + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + cccr |= CCCR_NISO; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= (CCCR_BRSE | CCCR_FDOE); + } + + /* Loopback Mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + cccr |= CCCR_TEST | CCCR_MON; + test |= TEST_LBCK; + } + + /* Enable Monitoring (all versions) */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cccr |= CCCR_MON; + + /* Write config */ + m_can_write(priv, M_CAN_CCCR, cccr); + m_can_write(priv, M_CAN_TEST, test); + + /* Enable interrupts */ + m_can_write(priv, M_CAN_IR, IR_ALL_INT); + if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) + if (priv->version == 30) + m_can_write(priv, M_CAN_IE, IR_ALL_INT & + ~(IR_ERR_LEC_30X)); + else + m_can_write(priv, M_CAN_IE, IR_ALL_INT & + ~(IR_ERR_LEC_31X)); + else + m_can_write(priv, M_CAN_IE, IR_ALL_INT); + + /* route all interrupts to INT0 */ + m_can_write(priv, M_CAN_ILS, ILS_ALL_INT0); + + /* set bittiming params */ + m_can_set_bittiming(dev); + + m_can_config_endisable(priv, false); +} + +static void m_can_start(struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + + /* basic m_can configuration */ + m_can_chip_config(dev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + m_can_enable_all_interrupts(priv); +} + +static int m_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + m_can_start(dev); + netif_wake_queue(dev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* Checks core release number of M_CAN + * returns 0 if an unsupported device is detected + * else it returns the release and step coded as: + * return value = 10 * <release> + 1 * <step> + */ +static int m_can_check_core_release(void __iomem *m_can_base) +{ + u32 crel_reg; + u8 rel; + u8 step; + int res; + struct m_can_priv temp_priv = { + .base = m_can_base + }; + + /* Read Core Release Version and split into version number + * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1; + */ + crel_reg = m_can_read(&temp_priv, M_CAN_CREL); + rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT); + step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT); + + if (rel == 3) { + /* M_CAN v3.x.y: create return value */ + res = 30 + step; + } else { + /* Unsupported M_CAN version */ + res = 0; + } + + return res; +} + +/* Selectable Non ISO support only in version 3.2.x + * This function checks if the bit is writable. + */ +static bool m_can_niso_supported(const struct m_can_priv *priv) +{ + u32 cccr_reg, cccr_poll; + int niso_timeout; + + m_can_config_endisable(priv, true); + cccr_reg = m_can_read(priv, M_CAN_CCCR); + cccr_reg |= CCCR_NISO; + m_can_write(priv, M_CAN_CCCR, cccr_reg); + + niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll, + (cccr_poll == cccr_reg), 0, 10); + + /* Clear NISO */ + cccr_reg &= ~(CCCR_NISO); + m_can_write(priv, M_CAN_CCCR, cccr_reg); + + m_can_config_endisable(priv, false); + + /* return false if time out (-ETIMEDOUT), else return true */ + return !niso_timeout; +} + +static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev, + void __iomem *addr) +{ + struct m_can_priv *priv; + int m_can_version; + + m_can_version = m_can_check_core_release(addr); + /* return if unsupported version */ + if (!m_can_version) { + dev_err(&pdev->dev, "Unsupported version number: %2d", + m_can_version); + return -EINVAL; + } + + priv = netdev_priv(dev); + netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT); + + /* Shared properties of all M_CAN versions */ + priv->version = m_can_version; + priv->dev = dev; + priv->base = addr; + priv->can.do_set_mode = m_can_set_mode; + priv->can.do_get_berr_counter = m_can_get_berr_counter; + + /* Set M_CAN supported operations */ + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_FD; + + /* Set properties depending on M_CAN version */ + switch (priv->version) { + case 30: + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */ + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); + priv->can.bittiming_const = &m_can_bittiming_const_30X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_30X; + break; + case 31: + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */ + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); + priv->can.bittiming_const = &m_can_bittiming_const_31X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_31X; + break; + case 32: + priv->can.bittiming_const = &m_can_bittiming_const_31X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_31X; + priv->can.ctrlmode_supported |= (m_can_niso_supported(priv) + ? CAN_CTRLMODE_FD_NON_ISO + : 0); + break; + default: + dev_err(&pdev->dev, "Unsupported version number: %2d", + priv->version); + return -EINVAL; + } + + return 0; +} + +static int m_can_open(struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + int err; + + err = m_can_clk_start(priv); + if (err) + return err; + + /* open the can device */ + err = open_candev(dev); + if (err) { + netdev_err(dev, "failed to open can device\n"); + goto exit_disable_clks; + } + + /* register interrupt handler */ + err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name, + dev); + if (err < 0) { + netdev_err(dev, "failed to request interrupt\n"); + goto exit_irq_fail; + } + + /* start the m_can controller */ + m_can_start(dev); + + can_led_event(dev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(dev); + + return 0; + +exit_irq_fail: + close_candev(dev); +exit_disable_clks: + m_can_clk_stop(priv); + return err; +} + +static void m_can_stop(struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + + /* disable all interrupts */ + m_can_disable_all_interrupts(priv); + + /* set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int m_can_close(struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&priv->napi); + m_can_stop(dev); + m_can_clk_stop(priv); + free_irq(dev->irq, dev); + close_candev(dev); + can_led_event(dev, CAN_LED_EVENT_STOP); + + return 0; +} + +static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx) +{ + struct m_can_priv *priv = netdev_priv(dev); + /*get wrap around for loopback skb index */ + unsigned int wrap = priv->can.echo_skb_max; + int next_idx; + + /* calculate next index */ + next_idx = (++putidx >= wrap ? 0 : putidx); + + /* check if occupied */ + return !!priv->can.echo_skb[next_idx]; +} + +static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct m_can_priv *priv = netdev_priv(dev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 id, cccr, fdflags; + int i; + int putidx; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + /* Generate ID field for TX buffer Element */ + /* Common to all supported M_CAN versions */ + if (cf->can_id & CAN_EFF_FLAG) { + id = cf->can_id & CAN_EFF_MASK; + id |= TX_BUF_XTD; + } else { + id = ((cf->can_id & CAN_SFF_MASK) << 18); + } + + if (cf->can_id & CAN_RTR_FLAG) + id |= TX_BUF_RTR; + + if (priv->version == 30) { + netif_stop_queue(dev); + + /* message ram configuration */ + m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); + m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, + can_len2dlc(cf->len) << 16); + + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, 0, + M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + + can_put_echo_skb(skb, dev, 0); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + cccr = m_can_read(priv, M_CAN_CCCR); + cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); + if (can_is_canfd_skb(skb)) { + if (cf->flags & CANFD_BRS) + cccr |= CCCR_CMR_CANFD_BRS << + CCCR_CMR_SHIFT; + else + cccr |= CCCR_CMR_CANFD << + CCCR_CMR_SHIFT; + } else { + cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT; + } + m_can_write(priv, M_CAN_CCCR, cccr); + } + m_can_write(priv, M_CAN_TXBTIE, 0x1); + m_can_write(priv, M_CAN_TXBAR, 0x1); + /* End of xmit function for version 3.0.x */ + } else { + /* Transmit routine for version >= v3.1.x */ + + /* Check if FIFO full */ + if (m_can_tx_fifo_full(priv)) { + /* This shouldn't happen */ + netif_stop_queue(dev); + netdev_warn(dev, + "TX queue active although FIFO is full."); + return NETDEV_TX_BUSY; + } + + /* get put index for frame */ + putidx = ((m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQPI_MASK) + >> TXFQS_TFQPI_SHIFT); + /* Write ID Field to FIFO Element */ + m_can_fifo_write(priv, putidx, M_CAN_FIFO_ID, id); + + /* get CAN FD configuration of frame */ + fdflags = 0; + if (can_is_canfd_skb(skb)) { + fdflags |= TX_BUF_FDF; + if (cf->flags & CANFD_BRS) + fdflags |= TX_BUF_BRS; + } + + /* Construct DLC Field. Also contains CAN-FD configuration + * use put index of fifo as message marker + * it is used in TX interrupt for + * sending the correct echo frame + */ + m_can_fifo_write(priv, putidx, M_CAN_FIFO_DLC, + ((putidx << TX_BUF_MM_SHIFT) & + TX_BUF_MM_MASK) | + (can_len2dlc(cf->len) << 16) | + fdflags | TX_BUF_EFC); + + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, putidx, M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + + /* Push loopback echo. + * Will be looped back on TX interrupt based on message marker + */ + can_put_echo_skb(skb, dev, putidx); + + /* Enable TX FIFO element to start transfer */ + m_can_write(priv, M_CAN_TXBAR, (1 << putidx)); + + /* stop network queue if fifo full */ + if (m_can_tx_fifo_full(priv) || + m_can_next_echo_skb_occupied(dev, putidx)) + netif_stop_queue(dev); + } + + return NETDEV_TX_OK; +} + +static const struct net_device_ops m_can_netdev_ops = { + .ndo_open = m_can_open, + .ndo_stop = m_can_close, + .ndo_start_xmit = m_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int register_m_can_dev(struct net_device *dev) +{ + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &m_can_netdev_ops; + + return register_candev(dev); +} + +static void m_can_init_ram(struct m_can_priv *priv) +{ + int end, i, start; + + /* initialize the entire Message RAM in use to avoid possible + * ECC/parity checksum errors when reading an uninitialized buffer + */ + start = priv->mcfg[MRAM_SIDF].off; + end = priv->mcfg[MRAM_TXB].off + + priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE; + for (i = start; i < end; i += 4) + writel(0x0, priv->mram_base + i); +} + +static void m_can_of_parse_mram(struct m_can_priv *priv, + const u32 *mram_config_vals) +{ + priv->mcfg[MRAM_SIDF].off = mram_config_vals[0]; + priv->mcfg[MRAM_SIDF].num = mram_config_vals[1]; + priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off + + priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE; + priv->mcfg[MRAM_XIDF].num = mram_config_vals[2]; + priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off + + priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE; + priv->mcfg[MRAM_RXF0].num = mram_config_vals[3] & + (RXFC_FS_MASK >> RXFC_FS_SHIFT); + priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off + + priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE; + priv->mcfg[MRAM_RXF1].num = mram_config_vals[4] & + (RXFC_FS_MASK >> RXFC_FS_SHIFT); + priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off + + priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE; + priv->mcfg[MRAM_RXB].num = mram_config_vals[5]; + priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off + + priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE; + priv->mcfg[MRAM_TXE].num = mram_config_vals[6]; + priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off + + priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE; + priv->mcfg[MRAM_TXB].num = mram_config_vals[7] & + (TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT); + + dev_dbg(priv->device, + "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", + priv->mram_base, + priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num, + priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num, + priv->mcfg[MRAM_RXF0].off, priv->mcfg[MRAM_RXF0].num, + priv->mcfg[MRAM_RXF1].off, priv->mcfg[MRAM_RXF1].num, + priv->mcfg[MRAM_RXB].off, priv->mcfg[MRAM_RXB].num, + priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num, + priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num); + + m_can_init_ram(priv); +} + +static int m_can_plat_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct m_can_priv *priv; + struct resource *res; + void __iomem *addr; + void __iomem *mram_addr; + struct clk *hclk, *cclk; + int irq, ret; + struct device_node *np; + u32 mram_config_vals[MRAM_CFG_LEN]; + u32 tx_fifo_size; + + np = pdev->dev.of_node; + + hclk = devm_clk_get(&pdev->dev, "hclk"); + cclk = devm_clk_get(&pdev->dev, "cclk"); + + if (IS_ERR(hclk) || IS_ERR(cclk)) { + dev_err(&pdev->dev, "no clock found\n"); + ret = -ENODEV; + goto failed_ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); + addr = devm_ioremap_resource(&pdev->dev, res); + irq = platform_get_irq_byname(pdev, "int0"); + + if (IS_ERR(addr) || irq < 0) { + ret = -EINVAL; + goto failed_ret; + } + + /* message ram could be shared */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); + if (!res) { + ret = -ENODEV; + goto failed_ret; + } + + mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mram_addr) { + ret = -ENOMEM; + goto failed_ret; + } + + /* get message ram configuration */ + ret = of_property_read_u32_array(np, "bosch,mram-cfg", + mram_config_vals, + sizeof(mram_config_vals) / 4); + if (ret) { + dev_err(&pdev->dev, "Could not get Message RAM configuration."); + goto failed_ret; + } + + /* Get TX FIFO size + * Defines the total amount of echo buffers for loopback + */ + tx_fifo_size = mram_config_vals[7]; + + /* allocate the m_can device */ + dev = alloc_candev(sizeof(*priv), tx_fifo_size); + if (!dev) { + ret = -ENOMEM; + goto failed_ret; + } + + priv = netdev_priv(dev); + dev->irq = irq; + priv->device = &pdev->dev; + priv->hclk = hclk; + priv->cclk = cclk; + priv->can.clock.freq = clk_get_rate(cclk); + priv->mram_base = mram_addr; + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Enable clocks. Necessary to read Core Release in order to determine + * M_CAN version + */ + pm_runtime_enable(&pdev->dev); + ret = m_can_clk_start(priv); + if (ret) + goto pm_runtime_fail; + + ret = m_can_dev_setup(pdev, dev, addr); + if (ret) + goto clk_disable; + + ret = register_m_can_dev(dev); + if (ret) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + KBUILD_MODNAME, ret); + goto clk_disable; + } + + m_can_of_parse_mram(priv, mram_config_vals); + + devm_can_led_init(dev); + + of_can_transceiver(dev); + + dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n", + KBUILD_MODNAME, dev->irq, priv->version); + + /* Probe finished + * Stop clocks. They will be reactivated once the M_CAN device is opened + */ +clk_disable: + m_can_clk_stop(priv); +pm_runtime_fail: + if (ret) { + pm_runtime_disable(&pdev->dev); + free_candev(dev); + } +failed_ret: + return ret; +} + +static __maybe_unused int m_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct m_can_priv *priv = netdev_priv(ndev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + m_can_stop(ndev); + m_can_clk_stop(priv); + } + + pinctrl_pm_select_sleep_state(dev); + + priv->can.state = CAN_STATE_SLEEPING; + + return 0; +} + +static __maybe_unused int m_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct m_can_priv *priv = netdev_priv(ndev); + + pinctrl_pm_select_default_state(dev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + int ret; + + ret = m_can_clk_start(priv); + if (ret) + return ret; + + m_can_init_ram(priv); + m_can_start(ndev); + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +static void unregister_m_can_dev(struct net_device *dev) +{ + unregister_candev(dev); +} + +static int m_can_plat_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + unregister_m_can_dev(dev); + + pm_runtime_disable(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + + free_candev(dev); + + return 0; +} + +static int __maybe_unused m_can_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct m_can_priv *priv = netdev_priv(ndev); + + clk_disable_unprepare(priv->cclk); + clk_disable_unprepare(priv->hclk); + + return 0; +} + +static int __maybe_unused m_can_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct m_can_priv *priv = netdev_priv(ndev); + int err; + + err = clk_prepare_enable(priv->hclk); + if (err) + return err; + + err = clk_prepare_enable(priv->cclk); + if (err) + clk_disable_unprepare(priv->hclk); + + return err; +} + +static const struct dev_pm_ops m_can_pmops = { + SET_RUNTIME_PM_OPS(m_can_runtime_suspend, + m_can_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) +}; + +static const struct of_device_id m_can_of_table[] = { + { .compatible = "bosch,m_can", .data = NULL }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, m_can_of_table); + +static struct platform_driver m_can_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = m_can_of_table, + .pm = &m_can_pmops, + }, + .probe = m_can_plat_probe, + .remove = m_can_plat_remove, +}; + +module_platform_driver(m_can_plat_driver); + +MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller"); diff --git a/drivers/net/can/mscan/Kconfig b/drivers/net/can/mscan/Kconfig new file mode 100644 index 000000000..81c711719 --- /dev/null +++ b/drivers/net/can/mscan/Kconfig @@ -0,0 +1,24 @@ +config CAN_MSCAN + depends on PPC + 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 000000000..58903b45f --- /dev/null +++ b/drivers/net/can/mscan/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_CAN_MPC5XXX) += mscan-mpc5xxx.o +mscan-mpc5xxx-objs := mscan.o mpc5xxx_can.o diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c new file mode 100644 index 000000000..2949a381a --- /dev/null +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -0,0 +1,463 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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); + void (*put_clock)(struct platform_device *ofdev); +}; + +#ifdef CONFIG_PPC_MPC52xx +static const struct of_device_id mpc52xx_cdm_ids[] = { + { .compatible = "fsl,mpc5200-cdm", }, + {} +}; + +static u32 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 (!cdm) { + of_node_put(np_cdm); + dev_err(&ofdev->dev, "can't map clock node!\n"); + return 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 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 +static u32 mpc512x_can_get_clock(struct platform_device *ofdev, + const char *clock_source, int *mscan_clksrc) +{ + struct device_node *np; + u32 clockdiv; + enum { + CLK_FROM_AUTO, + CLK_FROM_IPS, + CLK_FROM_SYS, + CLK_FROM_REF, + } clk_from; + struct clk *clk_in, *clk_can; + unsigned long freq_calc; + struct mscan_priv *priv; + struct clk *clk_ipg; + + /* the caller passed in the clock source spec that was read from + * the device tree, get the optional clock divider as well + */ + np = ofdev->dev.of_node; + clockdiv = 1; + of_property_read_u32(np, "fsl,mscan-clock-divider", &clockdiv); + dev_dbg(&ofdev->dev, "device tree specs: clk src[%s] div[%d]\n", + clock_source ? clock_source : "<NULL>", clockdiv); + + /* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to + * get set, and the 'ips' clock is the input to the MSCAN + * component + * + * for clock-source values of 'ref' or 'sys' the CANCTL1[CLKSRC] + * bit needs to get cleared, an optional clock-divider may have + * been specified (the default value is 1), the appropriate + * MSCAN related MCLK is the input to the MSCAN component + * + * in the absence of a clock-source spec, first an optimal clock + * gets determined based on the 'sys' clock, if that fails the + * 'ref' clock is used + */ + clk_from = CLK_FROM_AUTO; + if (clock_source) { + /* interpret the device tree's spec for the clock source */ + if (!strcmp(clock_source, "ip")) + clk_from = CLK_FROM_IPS; + else if (!strcmp(clock_source, "sys")) + clk_from = CLK_FROM_SYS; + else if (!strcmp(clock_source, "ref")) + clk_from = CLK_FROM_REF; + else + goto err_invalid; + dev_dbg(&ofdev->dev, "got a clk source spec[%d]\n", clk_from); + } + if (clk_from == CLK_FROM_AUTO) { + /* no spec so far, try the 'sys' clock; round to the + * next MHz and see if we can get a multiple of 16MHz + */ + dev_dbg(&ofdev->dev, "no clk source spec, trying SYS\n"); + clk_in = devm_clk_get(&ofdev->dev, "sys"); + if (IS_ERR(clk_in)) + goto err_notavail; + freq_calc = clk_get_rate(clk_in); + freq_calc += 499999; + freq_calc /= 1000000; + freq_calc *= 1000000; + if ((freq_calc % 16000000) == 0) { + clk_from = CLK_FROM_SYS; + clockdiv = freq_calc / 16000000; + dev_dbg(&ofdev->dev, + "clk fit, sys[%lu] div[%d] freq[%lu]\n", + freq_calc, clockdiv, freq_calc / clockdiv); + } + } + if (clk_from == CLK_FROM_AUTO) { + /* no spec so far, use the 'ref' clock */ + dev_dbg(&ofdev->dev, "no clk source spec, trying REF\n"); + clk_in = devm_clk_get(&ofdev->dev, "ref"); + if (IS_ERR(clk_in)) + goto err_notavail; + clk_from = CLK_FROM_REF; + freq_calc = clk_get_rate(clk_in); + dev_dbg(&ofdev->dev, + "clk fit, ref[%lu] (no div) freq[%lu]\n", + freq_calc, freq_calc); + } + + /* select IPS or MCLK as the MSCAN input (returned to the caller), + * setup the MCLK mux source and rate if applicable, apply the + * optionally specified or derived above divider, and determine + * the actual resulting clock rate to return to the caller + */ + switch (clk_from) { + case CLK_FROM_IPS: + clk_can = devm_clk_get(&ofdev->dev, "ips"); + if (IS_ERR(clk_can)) + goto err_notavail; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_can = clk_can; + freq_calc = clk_get_rate(clk_can); + *mscan_clksrc = MSCAN_CLKSRC_IPS; + dev_dbg(&ofdev->dev, "clk from IPS, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + case CLK_FROM_SYS: + case CLK_FROM_REF: + clk_can = devm_clk_get(&ofdev->dev, "mclk"); + if (IS_ERR(clk_can)) + goto err_notavail; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_can = clk_can; + if (clk_from == CLK_FROM_SYS) + clk_in = devm_clk_get(&ofdev->dev, "sys"); + if (clk_from == CLK_FROM_REF) + clk_in = devm_clk_get(&ofdev->dev, "ref"); + if (IS_ERR(clk_in)) + goto err_notavail; + clk_set_parent(clk_can, clk_in); + freq_calc = clk_get_rate(clk_in); + freq_calc /= clockdiv; + clk_set_rate(clk_can, freq_calc); + freq_calc = clk_get_rate(clk_can); + *mscan_clksrc = MSCAN_CLKSRC_BUS; + dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + default: + goto err_invalid; + } + + /* the above clk_can item is used for the bitrate, access to + * the peripheral's register set needs the clk_ipg item + */ + clk_ipg = devm_clk_get(&ofdev->dev, "ipg"); + if (IS_ERR(clk_ipg)) + goto err_notavail_ipg; + if (clk_prepare_enable(clk_ipg)) + goto err_notavail_ipg; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_ipg = clk_ipg; + + /* return the determined clock source rate */ + return freq_calc; + +err_invalid: + dev_err(&ofdev->dev, "invalid clock source specification\n"); + /* clock source rate could not get determined */ + return 0; + +err_notavail: + dev_err(&ofdev->dev, "cannot acquire or setup bitrate clock source\n"); + /* clock source rate could not get determined */ + return 0; + +err_notavail_ipg: + dev_err(&ofdev->dev, "cannot acquire or setup register clock\n"); + /* clock source rate could not get determined */ + return 0; +} + +static void mpc512x_can_put_clock(struct platform_device *ofdev) +{ + struct mscan_priv *priv; + + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + if (priv->clk_ipg) + clk_disable_unprepare(priv->clk_ipg); +} +#else /* !CONFIG_PPC_MPC512x */ +static u32 mpc512x_can_get_clock(struct platform_device *ofdev, + const char *clock_name, int *mscan_clksrc) +{ + return 0; +} +#define mpc512x_can_put_clock NULL +#endif /* CONFIG_PPC_MPC512x */ + +static const struct of_device_id mpc5xxx_can_table[]; +static int mpc5xxx_can_probe(struct platform_device *ofdev) +{ + const struct of_device_id *match; + const 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; + platform_set_drvdata(ofdev, dev); + SET_NETDEV_DEV(dev, &ofdev->dev); + + 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; + } + + 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_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 mpc5xxx_can_remove(struct platform_device *ofdev) +{ + const struct of_device_id *match; + const struct mpc5xxx_can_data *data; + struct net_device *dev = platform_get_drvdata(ofdev); + struct mscan_priv *priv = netdev_priv(dev); + + match = of_match_device(mpc5xxx_can_table, &ofdev->dev); + data = match ? match->data : NULL; + + unregister_mscandev(dev); + if (data && data->put_clock) + data->put_clock(ofdev); + 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 = platform_get_drvdata(ofdev); + 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 = platform_get_drvdata(ofdev); + 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 const struct mpc5xxx_can_data mpc5200_can_data = { + .type = MSCAN_TYPE_MPC5200, + .get_clock = mpc52xx_can_get_clock, + /* .put_clock not applicable */ +}; + +static const struct mpc5xxx_can_data mpc5121_can_data = { + .type = MSCAN_TYPE_MPC5121, + .get_clock = mpc512x_can_get_clock, + .put_clock = mpc512x_can_put_clock, +}; + +static const struct of_device_id 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, }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc5xxx_can_table); + +static struct platform_driver mpc5xxx_can_driver = { + .driver = { + .name = "mpc5xxx_can", + .of_match_table = mpc5xxx_can_table, + }, + .probe = mpc5xxx_can_probe, + .remove = 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 000000000..0a7d818a0 --- /dev/null +++ b/drivers/net/can/mscan/mscan.c @@ -0,0 +1,716 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 const 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)) + netif_trans_update(dev); + + 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; +} + +static enum can_state get_new_state(struct net_device *dev, u8 canrflg) +{ + struct mscan_priv *priv = netdev_priv(dev); + + if (unlikely(canrflg & MSCAN_CSCIF)) + return state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + + return priv->can.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 new_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; + } + + new_state = get_new_state(dev, canrflg); + if (new_state != priv->can.state) { + can_change_state(dev, frame, + state_map[MSCAN_STATE_TX(canrflg)], + state_map[MSCAN_STATE_RX(canrflg)]); + + if (priv->can.state == CAN_STATE_BUS_OFF) { + /* + * 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); + } + } + 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 work_done = 0; + struct sk_buff *skb; + struct can_frame *frame; + u8 canrflg; + + while (work_done < 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; + work_done++; + netif_receive_skb(skb); + } + + if (work_done < quota) { + if (likely(napi_complete_done(&priv->napi, work_done))) { + clear_bit(F_RX_PROGRESS, &priv->flags); + if (priv->can.state < CAN_STATE_BUS_OFF) + out_8(®s->canrier, priv->shadow_canrier); + } + } + return work_done; +} + +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 { + netif_trans_update(dev); + } + + 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) +{ + int ret = 0; + + 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; + + if (priv->clk_ipg) { + ret = clk_prepare_enable(priv->clk_ipg); + if (ret) + goto exit_retcode; + } + if (priv->clk_can) { + ret = clk_prepare_enable(priv->clk_can); + if (ret) + goto exit_dis_ipg_clock; + } + + /* common open */ + ret = open_candev(dev); + if (ret) + goto exit_dis_can_clock; + + 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; + } + + 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: + free_irq(dev->irq, dev); +exit_napi_disable: + napi_disable(&priv->napi); + close_candev(dev); +exit_dis_can_clock: + if (priv->clk_can) + clk_disable_unprepare(priv->clk_can); +exit_dis_ipg_clock: + if (priv->clk_ipg) + clk_disable_unprepare(priv->clk_ipg); +exit_retcode: + 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); + + if (priv->clk_can) + clk_disable_unprepare(priv->clk_can); + if (priv->clk_ipg) + clk_disable_unprepare(priv->clk_ipg); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +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 000000000..ad8e08f9c --- /dev/null +++ b/drivers/net/can/mscan/mscan.h @@ -0,0 +1,303 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MSCAN_H__ +#define __MSCAN_H__ + +#include <linux/clk.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 */ + unsigned long flags; + void __iomem *reg_base; /* ioremap'ed address to registers */ + struct clk *clk_ipg; /* clock for registers */ + struct clk *clk_can; /* clock for bitrates */ + 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; +}; + +struct net_device *alloc_mscandev(void); +int register_mscandev(struct net_device *dev, int mscan_clksrc); +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 000000000..ced11ea89 --- /dev/null +++ b/drivers/net/can/pch_can.c @@ -0,0 +1,1279 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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/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 const 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 const struct pci_device_id 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; + priv->can.can_stats.bus_off++; + 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[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + 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; + } + + rcv_pkts++; + stats->rx_packets++; + quota--; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +static void 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); + 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 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 = pch_can_remove, + .suspend = pch_can_suspend, + .resume = pch_can_resume, +}; + +module_pci_driver(pch_can_pci_driver); + +MODULE_DESCRIPTION("Intel EG20T PCH CAN(Controller Area Network) Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.94"); diff --git a/drivers/net/can/peak_canfd/Kconfig b/drivers/net/can/peak_canfd/Kconfig new file mode 100644 index 000000000..84b30978a --- /dev/null +++ b/drivers/net/can/peak_canfd/Kconfig @@ -0,0 +1,13 @@ +config CAN_PEAK_PCIEFD + depends on PCI + tristate "PEAK-System PCAN-PCIe FD cards" + ---help--- + This driver adds support for the PEAK-System PCI Express FD + CAN-FD cards family. + These 1x or 2x CAN-FD channels cards offer CAN 2.0 a/b as well as + CAN-FD access to the CAN bus. Besides the nominal bitrate of up to + 1 Mbit/s, the data bytes of CAN-FD frames can be transmitted with + up to 12 Mbit/s. A galvanic isolation of the CAN ports protects the + electronics of the card and the respective computer against + disturbances of up to 500 Volts. The PCAN-PCI Express FD can be + operated with ambient temperatures in a range of -40 to +85 °C. diff --git a/drivers/net/can/peak_canfd/Makefile b/drivers/net/can/peak_canfd/Makefile new file mode 100644 index 000000000..3dc7a6a0b --- /dev/null +++ b/drivers/net/can/peak_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the PEAK-System CAN-FD IP module drivers +# +obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_pciefd.o +peak_pciefd-y := peak_pciefd_main.o peak_canfd.o diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c new file mode 100644 index 000000000..2d73667ea --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_canfd.c @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Copyright (C) 2016 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/can.h> +#include <linux/can/dev.h> + +#include "peak_canfd_user.h" + +/* internal IP core cache size (used as default echo skbs max number) */ +#define PCANFD_ECHO_SKB_MAX 24 + +/* bittiming ranges of the PEAK-System PC CAN-FD interfaces */ +static const struct can_bittiming_const peak_canfd_nominal_const = { + .name = "peak_canfd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const peak_canfd_data_const = { + .name = "peak_canfd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +static struct peak_canfd_priv *pucan_init_cmd(struct peak_canfd_priv *priv) +{ + priv->cmd_len = 0; + return priv; +} + +static void *pucan_add_cmd(struct peak_canfd_priv *priv, int cmd_op) +{ + struct pucan_command *cmd; + + if (priv->cmd_len + sizeof(*cmd) > priv->cmd_maxlen) + return NULL; + + cmd = priv->cmd_buffer + priv->cmd_len; + + /* reset all unused bit to default */ + memset(cmd, 0, sizeof(*cmd)); + + cmd->opcode_channel = pucan_cmd_opcode_channel(priv->index, cmd_op); + priv->cmd_len += sizeof(*cmd); + + return cmd; +} + +static int pucan_write_cmd(struct peak_canfd_priv *priv) +{ + int err; + + if (priv->pre_cmd) { + err = priv->pre_cmd(priv); + if (err) + return err; + } + + err = priv->write_cmd(priv); + if (err) + return err; + + if (priv->post_cmd) + err = priv->post_cmd(priv); + + return err; +} + +/* uCAN commands interface functions */ +static int pucan_set_reset_mode(struct peak_canfd_priv *priv) +{ + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RESET_MODE); + return pucan_write_cmd(priv); +} + +static int pucan_set_normal_mode(struct peak_canfd_priv *priv) +{ + int err; + + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_NORMAL_MODE); + err = pucan_write_cmd(priv); + if (!err) + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return err; +} + +static int pucan_set_listen_only_mode(struct peak_canfd_priv *priv) +{ + int err; + + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_LISTEN_ONLY_MODE); + err = pucan_write_cmd(priv); + if (!err) + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return err; +} + +static int pucan_set_timing_slow(struct peak_canfd_priv *priv, + const struct can_bittiming *pbt) +{ + struct pucan_timing_slow *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_SLOW); + + cmd->sjw_t = PUCAN_TSLOW_SJW_T(pbt->sjw - 1, + priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); + cmd->tseg1 = PUCAN_TSLOW_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); + cmd->tseg2 = PUCAN_TSLOW_TSEG2(pbt->phase_seg2 - 1); + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(pbt->brp - 1)); + + cmd->ewl = 96; /* default */ + + netdev_dbg(priv->ndev, + "nominal: brp=%u tseg1=%u tseg2=%u sjw=%u\n", + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw_t); + + return pucan_write_cmd(priv); +} + +static int pucan_set_timing_fast(struct peak_canfd_priv *priv, + const struct can_bittiming *pbt) +{ + struct pucan_timing_fast *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_FAST); + + cmd->sjw = PUCAN_TFAST_SJW(pbt->sjw - 1); + cmd->tseg1 = PUCAN_TFAST_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); + cmd->tseg2 = PUCAN_TFAST_TSEG2(pbt->phase_seg2 - 1); + cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(pbt->brp - 1)); + + netdev_dbg(priv->ndev, + "data: brp=%u tseg1=%u tseg2=%u sjw=%u\n", + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw); + + return pucan_write_cmd(priv); +} + +static int pucan_set_std_filter(struct peak_canfd_priv *priv, u8 row, u32 mask) +{ + struct pucan_std_filter *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_STD_FILTER); + + /* all the 11-bits CAN ID values are represented by one bit in a + * 64 rows array of 32 bits: the upper 6 bits of the CAN ID select the + * row while the lowest 5 bits select the bit in that row. + * + * bit filter + * 1 passed + * 0 discarded + */ + + /* select the row */ + cmd->idx = row; + + /* set/unset bits in the row */ + cmd->mask = cpu_to_le32(mask); + + return pucan_write_cmd(priv); +} + +static int pucan_tx_abort(struct peak_canfd_priv *priv, u16 flags) +{ + struct pucan_tx_abort *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TX_ABORT); + + cmd->flags = cpu_to_le16(flags); + + return pucan_write_cmd(priv); +} + +static int pucan_clr_err_counters(struct peak_canfd_priv *priv) +{ + struct pucan_wr_err_cnt *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_WR_ERR_CNT); + + cmd->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE | PUCAN_WRERRCNT_RE); + cmd->tx_counter = 0; + cmd->rx_counter = 0; + + return pucan_write_cmd(priv); +} + +static int pucan_set_options(struct peak_canfd_priv *priv, u16 opt_mask) +{ + struct pucan_options *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_EN_OPTION); + + cmd->options = cpu_to_le16(opt_mask); + + return pucan_write_cmd(priv); +} + +static int pucan_clr_options(struct peak_canfd_priv *priv, u16 opt_mask) +{ + struct pucan_options *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_CLR_DIS_OPTION); + + cmd->options = cpu_to_le16(opt_mask); + + return pucan_write_cmd(priv); +} + +static int pucan_setup_rx_barrier(struct peak_canfd_priv *priv) +{ + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RX_BARRIER); + + return pucan_write_cmd(priv); +} + +/* handle the reception of one CAN frame */ +static int pucan_handle_can_rx(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + const u16 rx_msg_flags = le16_to_cpu(msg->flags); + u8 cf_len; + + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) + cf_len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(msg))); + else + cf_len = get_can_dlc(pucan_msg_get_dlc(msg)); + + /* if this frame is an echo, */ + if (rx_msg_flags & PUCAN_MSG_LOOPED_BACK) { + unsigned long flags; + + spin_lock_irqsave(&priv->echo_lock, flags); + can_get_echo_skb(priv->ndev, msg->client); + + /* count bytes of the echo instead of skb */ + stats->tx_bytes += cf_len; + stats->tx_packets++; + + /* restart tx queue (a slot is free) */ + netif_wake_queue(priv->ndev); + + spin_unlock_irqrestore(&priv->echo_lock, flags); + + /* if this frame is only an echo, stop here. Otherwise, + * continue to push this application self-received frame into + * its own rx queue. + */ + if (!(rx_msg_flags & PUCAN_MSG_SELF_RECEIVE)) + return 0; + } + + /* otherwise, it should be pushed into rx fifo */ + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { + /* CANFD frame case */ + skb = alloc_canfd_skb(priv->ndev, &cf); + if (!skb) + return -ENOMEM; + + if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) + cf->flags |= CANFD_BRS; + + if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) + cf->flags |= CANFD_ESI; + } else { + /* CAN 2.0 frame case */ + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cf); + if (!skb) + return -ENOMEM; + } + + cf->can_id = le32_to_cpu(msg->can_id); + cf->len = cf_len; + + if (rx_msg_flags & PUCAN_MSG_EXT_ID) + cf->can_id |= CAN_EFF_FLAG; + + if (rx_msg_flags & PUCAN_MSG_RTR) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, msg->d, cf->len); + + stats->rx_bytes += cf->len; + stats->rx_packets++; + + netif_rx(skb); + + return 0; +} + +/* handle rx/tx error counters notification */ +static int pucan_handle_error(struct peak_canfd_priv *priv, + struct pucan_error_msg *msg) +{ + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + return 0; +} + +/* handle status notification */ +static int pucan_handle_status(struct peak_canfd_priv *priv, + struct pucan_status_msg *msg) +{ + struct net_device *ndev = priv->ndev; + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* this STATUS is the CNF of the RX_BARRIER: Tx path can be setup */ + if (pucan_status_is_rx_barrier(msg)) { + + if (priv->enable_tx_path) { + int err = priv->enable_tx_path(priv); + + if (err) + return err; + } + + /* wake network queue up (echo_skb array is empty) */ + netif_wake_queue(ndev); + + return 0; + } + + skb = alloc_can_err_skb(ndev, &cf); + + /* test state error bits according to their priority */ + if (pucan_status_is_busoff(msg)) { + netdev_dbg(ndev, "Bus-off entry status\n"); + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + + } else if (pucan_status_is_passive(msg)) { + netdev_dbg(ndev, "Error passive status\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + } + + } else if (pucan_status_is_warning(msg)) { + netdev_dbg(ndev, "Error warning status\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + } + + } else if (priv->can.state != CAN_STATE_ERROR_ACTIVE) { + /* back to ERROR_ACTIVE */ + netdev_dbg(ndev, "Error active status\n"); + can_change_state(ndev, cf, CAN_STATE_ERROR_ACTIVE, + CAN_STATE_ERROR_ACTIVE); + } else { + dev_kfree_skb(skb); + return 0; + } + + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +/* handle uCAN Rx overflow notification */ +static int pucan_handle_cache_critical(struct peak_canfd_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_rx(skb); + + return 0; +} + +/* handle a single uCAN message */ +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg) +{ + u16 msg_type = le16_to_cpu(msg->type); + int msg_size = le16_to_cpu(msg->size); + int err; + + if (!msg_size || !msg_type) { + /* null packet found: end of list */ + goto exit; + } + + switch (msg_type) { + case PUCAN_MSG_CAN_RX: + err = pucan_handle_can_rx(priv, (struct pucan_rx_msg *)msg); + break; + case PUCAN_MSG_ERROR: + err = pucan_handle_error(priv, (struct pucan_error_msg *)msg); + break; + case PUCAN_MSG_STATUS: + err = pucan_handle_status(priv, (struct pucan_status_msg *)msg); + break; + case PUCAN_MSG_CACHE_CRITICAL: + err = pucan_handle_cache_critical(priv); + break; + default: + err = 0; + } + + if (err < 0) + return err; + +exit: + return msg_size; +} + +/* handle a list of rx_count messages from rx_msg memory address */ +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg_list, int msg_count) +{ + void *msg_ptr = msg_list; + int i, msg_size = 0; + + for (i = 0; i < msg_count; i++) { + msg_size = peak_canfd_handle_msg(priv, msg_ptr); + + /* a null packet can be found at the end of a list */ + if (msg_size <= 0) + break; + + msg_ptr += ALIGN(msg_size, 4); + } + + if (msg_size < 0) + return msg_size; + + return i; +} + +static int peak_canfd_start(struct peak_canfd_priv *priv) +{ + int err; + + err = pucan_clr_err_counters(priv); + if (err) + goto err_exit; + + priv->echo_idx = 0; + + priv->bec.txerr = 0; + priv->bec.rxerr = 0; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + err = pucan_set_listen_only_mode(priv); + else + err = pucan_set_normal_mode(priv); + +err_exit: + return err; +} + +static void peak_canfd_stop(struct peak_canfd_priv *priv) +{ + int err; + + /* go back to RESET mode */ + err = pucan_set_reset_mode(priv); + if (err) { + netdev_err(priv->ndev, "channel %u reset failed\n", + priv->index); + } else { + /* abort last Tx (MUST be done in RESET mode only!) */ + pucan_tx_abort(priv, PUCAN_TX_ABORT_FLUSH); + } +} + +static int peak_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + switch (mode) { + case CAN_MODE_START: + peak_canfd_start(priv); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int peak_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + *bec = priv->bec; + return 0; +} + +static int peak_canfd_open(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + int i, err = 0; + + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto err_exit; + } + + err = pucan_set_reset_mode(priv); + if (err) + goto err_close; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + err = pucan_clr_options(priv, PUCAN_OPTION_CANDFDISO); + else + err = pucan_set_options(priv, PUCAN_OPTION_CANDFDISO); + + if (err) + goto err_close; + } + + /* set option: get rx/tx error counters */ + err = pucan_set_options(priv, PUCAN_OPTION_ERROR); + if (err) + goto err_close; + + /* accept all standard CAN ID */ + for (i = 0; i <= PUCAN_FLTSTD_ROW_IDX_MAX; i++) + pucan_set_std_filter(priv, i, 0xffffffff); + + err = peak_canfd_start(priv); + if (err) + goto err_close; + + /* receiving the RB status says when Tx path is ready */ + err = pucan_setup_rx_barrier(priv); + if (!err) + goto err_exit; + +err_close: + close_candev(ndev); +err_exit: + return err; +} + +static int peak_canfd_set_bittiming(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + return pucan_set_timing_slow(priv, &priv->can.bittiming); +} + +static int peak_canfd_set_data_bittiming(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + return pucan_set_timing_fast(priv, &priv->can.data_bittiming); +} + +static int peak_canfd_close(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + peak_canfd_stop(priv); + close_candev(ndev); + + return 0; +} + +static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + struct pucan_tx_msg *msg; + u16 msg_size, msg_flags; + unsigned long flags; + bool should_stop_tx_queue; + int room_left; + u8 can_dlc; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + msg_size = ALIGN(sizeof(*msg) + cf->len, 4); + msg = priv->alloc_tx_msg(priv, msg_size, &room_left); + + /* should never happen except under bus-off condition and (auto-)restart + * mechanism + */ + if (!msg) { + stats->tx_dropped++; + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + msg->size = cpu_to_le16(msg_size); + msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); + msg_flags = 0; + + if (cf->can_id & CAN_EFF_FLAG) { + msg_flags |= PUCAN_MSG_EXT_ID; + msg->can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK); + } else { + msg->can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) { + /* CAN FD frame format */ + can_dlc = can_len2dlc(cf->len); + + msg_flags |= PUCAN_MSG_EXT_DATA_LEN; + + if (cf->flags & CANFD_BRS) + msg_flags |= PUCAN_MSG_BITRATE_SWITCH; + + if (cf->flags & CANFD_ESI) + msg_flags |= PUCAN_MSG_ERROR_STATE_IND; + } else { + /* CAN 2.0 frame format */ + can_dlc = cf->len; + + if (cf->can_id & CAN_RTR_FLAG) + msg_flags |= PUCAN_MSG_RTR; + } + + /* always ask loopback for echo management */ + msg_flags |= PUCAN_MSG_LOOPED_BACK; + + /* set driver specific bit to differentiate with application loopback */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + msg_flags |= PUCAN_MSG_SELF_RECEIVE; + + msg->flags = cpu_to_le16(msg_flags); + msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, can_dlc); + memcpy(msg->d, cf->data, cf->len); + + /* struct msg client field is used as an index in the echo skbs ring */ + msg->client = priv->echo_idx; + + spin_lock_irqsave(&priv->echo_lock, flags); + + /* prepare and save echo skb in internal slot */ + can_put_echo_skb(skb, ndev, priv->echo_idx); + + /* move echo index to the next slot */ + priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max; + + /* if next slot is not free, stop network queue (no slot free in echo + * skb ring means that the controller did not write these frames on + * the bus: no need to continue). + */ + should_stop_tx_queue = !!(priv->can.echo_skb[priv->echo_idx]); + + /* stop network tx queue if not enough room to save one more msg too */ + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + should_stop_tx_queue |= (room_left < + (sizeof(*msg) + CANFD_MAX_DLEN)); + else + should_stop_tx_queue |= (room_left < + (sizeof(*msg) + CAN_MAX_DLEN)); + + if (should_stop_tx_queue) + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&priv->echo_lock, flags); + + /* write the skb on the interface */ + priv->write_tx_msg(priv, msg); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops peak_canfd_netdev_ops = { + .ndo_open = peak_canfd_open, + .ndo_stop = peak_canfd_close, + .ndo_start_xmit = peak_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, + int echo_skb_max) +{ + struct net_device *ndev; + struct peak_canfd_priv *priv; + + /* we DO support local echo */ + if (echo_skb_max < 0) + echo_skb_max = PCANFD_ECHO_SKB_MAX; + + /* allocate the candev object */ + ndev = alloc_candev(sizeof_priv, echo_skb_max); + if (!ndev) + return NULL; + + priv = netdev_priv(ndev); + + /* complete now socket-can initialization side */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.bittiming_const = &peak_canfd_nominal_const; + priv->can.data_bittiming_const = &peak_canfd_data_const; + + priv->can.do_set_mode = peak_canfd_set_mode; + priv->can.do_get_berr_counter = peak_canfd_get_berr_counter; + priv->can.do_set_bittiming = peak_canfd_set_bittiming; + priv->can.do_set_data_bittiming = peak_canfd_set_data_bittiming; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_BERR_REPORTING; + + priv->ndev = ndev; + priv->index = index; + priv->cmd_len = 0; + spin_lock_init(&priv->echo_lock); + + ndev->flags |= IFF_ECHO; + ndev->netdev_ops = &peak_canfd_netdev_ops; + ndev->dev_id = index; + + return ndev; +} diff --git a/drivers/net/can/peak_canfd/peak_canfd_user.h b/drivers/net/can/peak_canfd/peak_canfd_user.h new file mode 100644 index 000000000..bf6de47f6 --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_canfd_user.h @@ -0,0 +1,55 @@ +/* + * CAN driver for PEAK System micro-CAN based adapters + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2013 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 PEAK_CANFD_USER_H +#define PEAK_CANFD_USER_H + +#include <linux/can/dev/peak_canfd.h> + +#define PCANFD_ECHO_SKB_DEF -1 + +/* data structure private to each uCAN interface */ +struct peak_canfd_priv { + struct can_priv can; /* socket-can private data */ + struct net_device *ndev; /* network device */ + int index; /* channel index */ + + struct can_berr_counter bec; /* rx/tx err counters */ + + int echo_idx; /* echo skb free slot index */ + spinlock_t echo_lock; + + int cmd_len; + void *cmd_buffer; + int cmd_maxlen; + + int (*pre_cmd)(struct peak_canfd_priv *priv); + int (*write_cmd)(struct peak_canfd_priv *priv); + int (*post_cmd)(struct peak_canfd_priv *priv); + + int (*enable_tx_path)(struct peak_canfd_priv *priv); + void *(*alloc_tx_msg)(struct peak_canfd_priv *priv, u16 msg_size, + int *room_left); + int (*write_tx_msg)(struct peak_canfd_priv *priv, + struct pucan_tx_msg *msg); +}; + +struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, + int echo_skb_max); +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg); +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, + struct pucan_rx_msg *rx_msg, int rx_count); +#endif diff --git a/drivers/net/can/peak_canfd/peak_pciefd_main.c b/drivers/net/can/peak_canfd/peak_pciefd_main.c new file mode 100644 index 000000000..c458d5fdc --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_pciefd_main.c @@ -0,0 +1,877 @@ +/* + * 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/can.h> +#include <linux/can/dev.h> + +#include "peak_canfd_user.h" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe/M.2 FD family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCIe/M.2 FD CAN cards"); +MODULE_LICENSE("GPL v2"); + +#define PCIEFD_DRV_NAME "peak_pciefd" + +#define PEAK_PCI_VENDOR_ID 0x001c /* The PCI device and vendor IDs */ +#define PEAK_PCIEFD_ID 0x0013 /* for PCIe slot cards */ +#define PCAN_CPCIEFD_ID 0x0014 /* for Compact-PCI Serial slot cards */ +#define PCAN_PCIE104FD_ID 0x0017 /* for PCIe-104 Express slot cards */ +#define PCAN_MINIPCIEFD_ID 0x0018 /* for mini-PCIe slot cards */ +#define PCAN_PCIEFD_OEM_ID 0x0019 /* for PCIe slot OEM cards */ +#define PCAN_M2_ID 0x001a /* for M2 slot cards */ + +/* PEAK PCIe board access description */ +#define PCIEFD_BAR0_SIZE (64 * 1024) +#define PCIEFD_RX_DMA_SIZE (4 * 1024) +#define PCIEFD_TX_DMA_SIZE (4 * 1024) + +#define PCIEFD_TX_PAGE_SIZE (2 * 1024) + +/* System Control Registers */ +#define PCIEFD_REG_SYS_CTL_SET 0x0000 /* set bits */ +#define PCIEFD_REG_SYS_CTL_CLR 0x0004 /* clear bits */ + +/* Version info registers */ +#define PCIEFD_REG_SYS_VER1 0x0040 /* version reg #1 */ +#define PCIEFD_REG_SYS_VER2 0x0044 /* version reg #2 */ + +#define PCIEFD_FW_VERSION(x, y, z) (((u32)(x) << 24) | \ + ((u32)(y) << 16) | \ + ((u32)(z) << 8)) + +/* System Control Registers Bits */ +#define PCIEFD_SYS_CTL_TS_RST 0x00000001 /* timestamp clock */ +#define PCIEFD_SYS_CTL_CLK_EN 0x00000002 /* system clock */ + +/* CAN-FD channel addresses */ +#define PCIEFD_CANX_OFF(c) (((c) + 1) * 0x1000) + +#define PCIEFD_ECHO_SKB_MAX PCANFD_ECHO_SKB_DEF + +/* CAN-FD channel registers */ +#define PCIEFD_REG_CAN_MISC 0x0000 /* Misc. control */ +#define PCIEFD_REG_CAN_CLK_SEL 0x0008 /* Clock selector */ +#define PCIEFD_REG_CAN_CMD_PORT_L 0x0010 /* 64-bits command port */ +#define PCIEFD_REG_CAN_CMD_PORT_H 0x0014 +#define PCIEFD_REG_CAN_TX_REQ_ACC 0x0020 /* Tx request accumulator */ +#define PCIEFD_REG_CAN_TX_CTL_SET 0x0030 /* Tx control set register */ +#define PCIEFD_REG_CAN_TX_CTL_CLR 0x0038 /* Tx control clear register */ +#define PCIEFD_REG_CAN_TX_DMA_ADDR_L 0x0040 /* 64-bits addr for Tx DMA */ +#define PCIEFD_REG_CAN_TX_DMA_ADDR_H 0x0044 +#define PCIEFD_REG_CAN_RX_CTL_SET 0x0050 /* Rx control set register */ +#define PCIEFD_REG_CAN_RX_CTL_CLR 0x0058 /* Rx control clear register */ +#define PCIEFD_REG_CAN_RX_CTL_WRT 0x0060 /* Rx control write register */ +#define PCIEFD_REG_CAN_RX_CTL_ACK 0x0068 /* Rx control ACK register */ +#define PCIEFD_REG_CAN_RX_DMA_ADDR_L 0x0070 /* 64-bits addr for Rx DMA */ +#define PCIEFD_REG_CAN_RX_DMA_ADDR_H 0x0074 + +/* CAN-FD channel misc register bits */ +#define CANFD_MISC_TS_RST 0x00000001 /* timestamp cnt rst */ + +/* CAN-FD channel Clock SELector Source & DIVider */ +#define CANFD_CLK_SEL_DIV_MASK 0x00000007 +#define CANFD_CLK_SEL_DIV_60MHZ 0x00000000 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_40MHZ 0x00000001 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_30MHZ 0x00000002 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_24MHZ 0x00000003 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_20MHZ 0x00000004 /* SRC=240MHz only */ + +#define CANFD_CLK_SEL_SRC_MASK 0x00000008 /* 0=80MHz, 1=240MHz */ +#define CANFD_CLK_SEL_SRC_240MHZ 0x00000008 +#define CANFD_CLK_SEL_SRC_80MHZ (~CANFD_CLK_SEL_SRC_240MHZ & \ + CANFD_CLK_SEL_SRC_MASK) + +#define CANFD_CLK_SEL_20MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_20MHZ) +#define CANFD_CLK_SEL_24MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_24MHZ) +#define CANFD_CLK_SEL_30MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_30MHZ) +#define CANFD_CLK_SEL_40MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_40MHZ) +#define CANFD_CLK_SEL_60MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_60MHZ) +#define CANFD_CLK_SEL_80MHZ (CANFD_CLK_SEL_SRC_80MHZ) + +/* CAN-FD channel Rx/Tx control register bits */ +#define CANFD_CTL_UNC_BIT 0x00010000 /* Uncached DMA mem */ +#define CANFD_CTL_RST_BIT 0x00020000 /* reset DMA action */ +#define CANFD_CTL_IEN_BIT 0x00040000 /* IRQ enable */ + +/* Rx IRQ Count and Time Limits */ +#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */ +#define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */ + +#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD) + +/* Tx anticipation window (link logical address should be aligned on 2K + * boundary) + */ +#define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE) + +#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */ + +/* 32-bits IRQ status fields, heading Rx DMA area */ +static inline int pciefd_irq_tag(u32 irq_status) +{ + return irq_status & 0x0000000f; +} + +static inline int pciefd_irq_rx_cnt(u32 irq_status) +{ + return (irq_status & 0x000007f0) >> 4; +} + +static inline int pciefd_irq_is_lnk(u32 irq_status) +{ + return irq_status & 0x00010000; +} + +/* Rx record */ +struct pciefd_rx_dma { + __le32 irq_status; + __le32 sys_time_low; + __le32 sys_time_high; + struct pucan_rx_msg msg[0]; +} __packed __aligned(4); + +/* Tx Link record */ +struct pciefd_tx_link { + __le16 size; + __le16 type; + __le32 laddr_lo; + __le32 laddr_hi; +} __packed __aligned(4); + +/* Tx page descriptor */ +struct pciefd_page { + void *vbase; /* page virtual address */ + dma_addr_t lbase; /* page logical address */ + u32 offset; + u32 size; +}; + +/* CAN-FD channel object */ +struct pciefd_board; +struct pciefd_can { + struct peak_canfd_priv ucan; /* must be the first member */ + void __iomem *reg_base; /* channel config base addr */ + struct pciefd_board *board; /* reverse link */ + + struct pucan_command pucan_cmd; /* command buffer */ + + dma_addr_t rx_dma_laddr; /* DMA virtual and logical addr */ + void *rx_dma_vaddr; /* for Rx and Tx areas */ + dma_addr_t tx_dma_laddr; + void *tx_dma_vaddr; + + struct pciefd_page tx_pages[PCIEFD_TX_PAGE_COUNT]; + u16 tx_pages_free; /* free Tx pages counter */ + u16 tx_page_index; /* current page used for Tx */ + spinlock_t tx_lock; + + u32 irq_status; + u32 irq_tag; /* next irq tag */ +}; + +/* PEAK-PCIe FD board object */ +struct pciefd_board { + void __iomem *reg_base; + struct pci_dev *pci_dev; + int can_count; + spinlock_t cmd_lock; /* 64-bits cmds must be atomic */ + struct pciefd_can *can[0]; /* array of network devices */ +}; + +/* supported device ids. */ +static const struct pci_device_id peak_pciefd_tbl[] = { + {PEAK_PCI_VENDOR_ID, PEAK_PCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PCAN_CPCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PCAN_PCIE104FD_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PCAN_MINIPCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PCAN_PCIEFD_OEM_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PCAN_M2_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, peak_pciefd_tbl); + +/* read a 32 bits value from a SYS block register */ +static inline u32 pciefd_sys_readreg(const struct pciefd_board *priv, u16 reg) +{ + return readl(priv->reg_base + reg); +} + +/* write a 32 bits value into a SYS block register */ +static inline void pciefd_sys_writereg(const struct pciefd_board *priv, + u32 val, u16 reg) +{ + writel(val, priv->reg_base + reg); +} + +/* read a 32 bits value from CAN-FD block register */ +static inline u32 pciefd_can_readreg(const struct pciefd_can *priv, u16 reg) +{ + return readl(priv->reg_base + reg); +} + +/* write a 32 bits value into a CAN-FD block register */ +static inline void pciefd_can_writereg(const struct pciefd_can *priv, + u32 val, u16 reg) +{ + writel(val, priv->reg_base + reg); +} + +/* give a channel logical Rx DMA address to the board */ +static void pciefd_can_setup_rx_dma(struct pciefd_can *priv) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32); +#else + const u32 dma_addr_h = 0; +#endif + + /* (DMA must be reset for Rx) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET); + + /* write the logical address of the Rx DMA area for this channel */ + pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr, + PCIEFD_REG_CAN_RX_DMA_ADDR_L); + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H); + + /* also indicates that Rx DMA is cacheable */ + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_RX_CTL_CLR); +} + +/* clear channel logical Rx DMA address from the board */ +static void pciefd_can_clear_rx_dma(struct pciefd_can *priv) +{ + /* DMA must be reset for Rx */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET); + + /* clear the logical address of the Rx DMA area for this channel */ + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L); + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H); +} + +/* give a channel logical Tx DMA address to the board */ +static void pciefd_can_setup_tx_dma(struct pciefd_can *priv) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32); +#else + const u32 dma_addr_h = 0; +#endif + + /* (DMA must be reset for Tx) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET); + + /* write the logical address of the Tx DMA area for this channel */ + pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr, + PCIEFD_REG_CAN_TX_DMA_ADDR_L); + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H); + + /* also indicates that Tx DMA is cacheable */ + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_TX_CTL_CLR); +} + +/* clear channel logical Tx DMA address from the board */ +static void pciefd_can_clear_tx_dma(struct pciefd_can *priv) +{ + /* DMA must be reset for Tx */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET); + + /* clear the logical address of the Tx DMA area for this channel */ + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L); + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H); +} + +static void pciefd_can_ack_rx_dma(struct pciefd_can *priv) +{ + /* read value of current IRQ tag and inc it for next one */ + priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr); + priv->irq_tag++; + priv->irq_tag &= 0xf; + + /* write the next IRQ tag for this CAN */ + pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK); +} + +/* IRQ handler */ +static irqreturn_t pciefd_irq_handler(int irq, void *arg) +{ + struct pciefd_can *priv = arg; + struct pciefd_rx_dma *rx_dma = priv->rx_dma_vaddr; + + /* INTA mode only to sync with PCIe transaction */ + if (!pci_dev_msi_enabled(priv->board->pci_dev)) + (void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1); + + /* read IRQ status from the first 32-bits of the Rx DMA area */ + priv->irq_status = le32_to_cpu(rx_dma->irq_status); + + /* check if this (shared) IRQ is for this CAN */ + if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag) + return IRQ_NONE; + + /* handle rx messages (if any) */ + peak_canfd_handle_msgs_list(&priv->ucan, + rx_dma->msg, + pciefd_irq_rx_cnt(priv->irq_status)); + + /* handle tx link interrupt (if any) */ + if (pciefd_irq_is_lnk(priv->irq_status)) { + unsigned long flags; + + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_pages_free++; + spin_unlock_irqrestore(&priv->tx_lock, flags); + + /* wake producer up (only if enough room in echo_skb array) */ + spin_lock_irqsave(&priv->ucan.echo_lock, flags); + if (!priv->ucan.can.echo_skb[priv->ucan.echo_idx]) + netif_wake_queue(priv->ucan.ndev); + + spin_unlock_irqrestore(&priv->ucan.echo_lock, flags); + } + + /* re-enable Rx DMA transfer for this CAN */ + pciefd_can_ack_rx_dma(priv); + + return IRQ_HANDLED; +} + +static int pciefd_enable_tx_path(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + int i; + + /* initialize the Tx pages descriptors */ + priv->tx_pages_free = PCIEFD_TX_PAGE_COUNT - 1; + priv->tx_page_index = 0; + + priv->tx_pages[0].vbase = priv->tx_dma_vaddr; + priv->tx_pages[0].lbase = priv->tx_dma_laddr; + + for (i = 0; i < PCIEFD_TX_PAGE_COUNT; i++) { + priv->tx_pages[i].offset = 0; + priv->tx_pages[i].size = PCIEFD_TX_PAGE_SIZE - + sizeof(struct pciefd_tx_link); + if (i) { + priv->tx_pages[i].vbase = + priv->tx_pages[i - 1].vbase + + PCIEFD_TX_PAGE_SIZE; + priv->tx_pages[i].lbase = + priv->tx_pages[i - 1].lbase + + PCIEFD_TX_PAGE_SIZE; + } + } + + /* setup Tx DMA addresses into IP core */ + pciefd_can_setup_tx_dma(priv); + + /* start (TX_RST=0) Tx Path */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_CLR); + + return 0; +} + +/* board specific CANFD command pre-processing */ +static int pciefd_pre_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); + int err; + + /* pre-process command */ + switch (cmd) { + case PUCAN_CMD_NORMAL_MODE: + case PUCAN_CMD_LISTEN_ONLY_MODE: + + if (ucan->can.state == CAN_STATE_BUS_OFF) + break; + + /* going into operational mode: setup IRQ handler */ + err = request_irq(priv->ucan.ndev->irq, + pciefd_irq_handler, + IRQF_SHARED, + PCIEFD_DRV_NAME, + priv); + if (err) + return err; + + /* setup Rx DMA address */ + pciefd_can_setup_rx_dma(priv); + + /* setup max count of msgs per IRQ */ + pciefd_can_writereg(priv, (CANFD_CTL_IRQ_TL_DEF) << 8 | + CANFD_CTL_IRQ_CL_DEF, + PCIEFD_REG_CAN_RX_CTL_WRT); + + /* clear DMA RST for Rx (Rx start) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, + PCIEFD_REG_CAN_RX_CTL_CLR); + + /* reset timestamps */ + pciefd_can_writereg(priv, !CANFD_MISC_TS_RST, + PCIEFD_REG_CAN_MISC); + + /* do an initial ACK */ + pciefd_can_ack_rx_dma(priv); + + /* enable IRQ for this CAN after having set next irq_tag */ + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, + PCIEFD_REG_CAN_RX_CTL_SET); + + /* Tx path will be setup as soon as RX_BARRIER is received */ + break; + default: + break; + } + + return 0; +} + +/* write a command */ +static int pciefd_write_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + unsigned long flags; + + /* 64-bits command is atomic */ + spin_lock_irqsave(&priv->board->cmd_lock, flags); + + pciefd_can_writereg(priv, *(u32 *)ucan->cmd_buffer, + PCIEFD_REG_CAN_CMD_PORT_L); + pciefd_can_writereg(priv, *(u32 *)(ucan->cmd_buffer + 4), + PCIEFD_REG_CAN_CMD_PORT_H); + + spin_unlock_irqrestore(&priv->board->cmd_lock, flags); + + return 0; +} + +/* board specific CANFD command post-processing */ +static int pciefd_post_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); + + switch (cmd) { + case PUCAN_CMD_RESET_MODE: + + if (ucan->can.state == CAN_STATE_STOPPED) + break; + + /* controller now in reset mode: */ + + /* disable IRQ for this CAN */ + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, + PCIEFD_REG_CAN_RX_CTL_CLR); + + /* stop and reset DMA addresses in Tx/Rx engines */ + pciefd_can_clear_tx_dma(priv); + pciefd_can_clear_rx_dma(priv); + + /* wait for above commands to complete (read cycle) */ + (void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1); + + free_irq(priv->ucan.ndev->irq, priv); + + ucan->can.state = CAN_STATE_STOPPED; + + break; + } + + return 0; +} + +static void *pciefd_alloc_tx_msg(struct peak_canfd_priv *ucan, u16 msg_size, + int *room_left) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; + unsigned long flags; + void *msg; + + spin_lock_irqsave(&priv->tx_lock, flags); + + if (page->offset + msg_size > page->size) { + struct pciefd_tx_link *lk; + + /* not enough space in this page: try another one */ + if (!priv->tx_pages_free) { + spin_unlock_irqrestore(&priv->tx_lock, flags); + + /* Tx overflow */ + return NULL; + } + + priv->tx_pages_free--; + + /* keep address of the very last free slot of current page */ + lk = page->vbase + page->offset; + + /* next, move on a new free page */ + priv->tx_page_index = (priv->tx_page_index + 1) % + PCIEFD_TX_PAGE_COUNT; + page = priv->tx_pages + priv->tx_page_index; + + /* put link record to this new page at the end of prev one */ + lk->size = cpu_to_le16(sizeof(*lk)); + lk->type = cpu_to_le16(CANFD_MSG_LNK_TX); + lk->laddr_lo = cpu_to_le32(page->lbase); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + lk->laddr_hi = cpu_to_le32(page->lbase >> 32); +#else + lk->laddr_hi = 0; +#endif + /* next msgs will be put from the begininng of this new page */ + page->offset = 0; + } + + *room_left = priv->tx_pages_free * page->size; + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + msg = page->vbase + page->offset; + + /* give back room left in the tx ring */ + *room_left += page->size - (page->offset + msg_size); + + return msg; +} + +static int pciefd_write_tx_msg(struct peak_canfd_priv *ucan, + struct pucan_tx_msg *msg) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; + + /* this slot is now reserved for writing the frame */ + page->offset += le16_to_cpu(msg->size); + + /* tell the board a frame has been written in Tx DMA area */ + pciefd_can_writereg(priv, 1, PCIEFD_REG_CAN_TX_REQ_ACC); + + return 0; +} + +/* probe for CAN-FD channel #pciefd_board->can_count */ +static int pciefd_can_probe(struct pciefd_board *pciefd) +{ + struct net_device *ndev; + struct pciefd_can *priv; + u32 clk; + int err; + + /* allocate the candev object with default isize of echo skbs ring */ + ndev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count, + PCIEFD_ECHO_SKB_MAX); + if (!ndev) { + dev_err(&pciefd->pci_dev->dev, + "failed to alloc candev object\n"); + goto failure; + } + + priv = netdev_priv(ndev); + + /* fill-in candev private object: */ + + /* setup PCIe-FD own callbacks */ + priv->ucan.pre_cmd = pciefd_pre_cmd; + priv->ucan.write_cmd = pciefd_write_cmd; + priv->ucan.post_cmd = pciefd_post_cmd; + priv->ucan.enable_tx_path = pciefd_enable_tx_path; + priv->ucan.alloc_tx_msg = pciefd_alloc_tx_msg; + priv->ucan.write_tx_msg = pciefd_write_tx_msg; + + /* setup PCIe-FD own command buffer */ + priv->ucan.cmd_buffer = &priv->pucan_cmd; + priv->ucan.cmd_maxlen = sizeof(priv->pucan_cmd); + + priv->board = pciefd; + + /* CAN config regs block address */ + priv->reg_base = pciefd->reg_base + PCIEFD_CANX_OFF(priv->ucan.index); + + /* allocate non-cacheable DMA'able 4KB memory area for Rx */ + priv->rx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, + PCIEFD_RX_DMA_SIZE, + &priv->rx_dma_laddr, + GFP_KERNEL); + if (!priv->rx_dma_vaddr) { + dev_err(&pciefd->pci_dev->dev, + "Rx dmam_alloc_coherent(%u) failure\n", + PCIEFD_RX_DMA_SIZE); + goto err_free_candev; + } + + /* allocate non-cacheable DMA'able 4KB memory area for Tx */ + priv->tx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, + PCIEFD_TX_DMA_SIZE, + &priv->tx_dma_laddr, + GFP_KERNEL); + if (!priv->tx_dma_vaddr) { + dev_err(&pciefd->pci_dev->dev, + "Tx dmam_alloc_coherent(%u) failure\n", + PCIEFD_TX_DMA_SIZE); + goto err_free_candev; + } + + /* CAN clock in RST mode */ + pciefd_can_writereg(priv, CANFD_MISC_TS_RST, PCIEFD_REG_CAN_MISC); + + /* read current clock value */ + clk = pciefd_can_readreg(priv, PCIEFD_REG_CAN_CLK_SEL); + switch (clk) { + case CANFD_CLK_SEL_20MHZ: + priv->ucan.can.clock.freq = 20 * 1000 * 1000; + break; + case CANFD_CLK_SEL_24MHZ: + priv->ucan.can.clock.freq = 24 * 1000 * 1000; + break; + case CANFD_CLK_SEL_30MHZ: + priv->ucan.can.clock.freq = 30 * 1000 * 1000; + break; + case CANFD_CLK_SEL_40MHZ: + priv->ucan.can.clock.freq = 40 * 1000 * 1000; + break; + case CANFD_CLK_SEL_60MHZ: + priv->ucan.can.clock.freq = 60 * 1000 * 1000; + break; + default: + pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ, + PCIEFD_REG_CAN_CLK_SEL); + + /* fallthough */ + case CANFD_CLK_SEL_80MHZ: + priv->ucan.can.clock.freq = 80 * 1000 * 1000; + break; + } + + ndev->irq = pciefd->pci_dev->irq; + + SET_NETDEV_DEV(ndev, &pciefd->pci_dev->dev); + + err = register_candev(ndev); + if (err) { + dev_err(&pciefd->pci_dev->dev, + "couldn't register CAN device: %d\n", err); + goto err_free_candev; + } + + spin_lock_init(&priv->tx_lock); + + /* save the object address in the board structure */ + pciefd->can[pciefd->can_count] = priv; + + dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n", + ndev->name, priv->reg_base, ndev->irq); + + return 0; + +err_free_candev: + free_candev(ndev); + +failure: + return -ENOMEM; +} + +/* remove a CAN-FD channel by releasing all of its resources */ +static void pciefd_can_remove(struct pciefd_can *priv) +{ + /* unregister (close) the can device to go back to RST mode first */ + unregister_candev(priv->ucan.ndev); + + /* finally, free the candev object */ + free_candev(priv->ucan.ndev); +} + +/* remove all CAN-FD channels by releasing their own resources */ +static void pciefd_can_remove_all(struct pciefd_board *pciefd) +{ + while (pciefd->can_count > 0) + pciefd_can_remove(pciefd->can[--pciefd->can_count]); +} + +/* probe for the entire device */ +static int peak_pciefd_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pciefd_board *pciefd; + int err, can_count; + u16 sub_sys_id; + u8 hw_ver_major; + u8 hw_ver_minor; + u8 hw_ver_sub; + u32 v2; + + err = pci_enable_device(pdev); + if (err) + return err; + err = pci_request_regions(pdev, PCIEFD_DRV_NAME); + if (err) + goto err_disable_pci; + + /* the number of channels depends on sub-system id */ + err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (err) + goto err_release_regions; + + dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n", + pdev->vendor, pdev->device, sub_sys_id); + + if (sub_sys_id >= 0x0012) + can_count = 4; + else if (sub_sys_id >= 0x0010) + can_count = 3; + else if (sub_sys_id >= 0x0004) + can_count = 2; + else + can_count = 1; + + /* allocate board structure object */ + pciefd = devm_kzalloc(&pdev->dev, struct_size(pciefd, can, can_count), + GFP_KERNEL); + if (!pciefd) { + err = -ENOMEM; + goto err_release_regions; + } + + /* initialize the board structure */ + pciefd->pci_dev = pdev; + spin_lock_init(&pciefd->cmd_lock); + + /* save the PCI BAR0 virtual address for further system regs access */ + pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE); + if (!pciefd->reg_base) { + dev_err(&pdev->dev, "failed to map PCI resource #0\n"); + err = -ENOMEM; + goto err_release_regions; + } + + /* read the firmware version number */ + v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2); + + hw_ver_major = (v2 & 0x0000f000) >> 12; + hw_ver_minor = (v2 & 0x00000f00) >> 8; + hw_ver_sub = (v2 & 0x000000f0) >> 4; + + dev_info(&pdev->dev, + "%ux CAN-FD PCAN-PCIe FPGA v%u.%u.%u:\n", can_count, + hw_ver_major, hw_ver_minor, hw_ver_sub); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + /* FW < v3.3.0 DMA logic doesn't handle correctly the mix of 32-bit and + * 64-bit logical addresses: this workaround forces usage of 32-bit + * DMA addresses only when such a fw is detected. + */ + if (PCIEFD_FW_VERSION(hw_ver_major, hw_ver_minor, hw_ver_sub) < + PCIEFD_FW_VERSION(3, 3, 0)) { + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + dev_warn(&pdev->dev, + "warning: can't set DMA mask %llxh (err %d)\n", + DMA_BIT_MASK(32), err); + } +#endif + + /* stop system clock */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, + PCIEFD_REG_SYS_CTL_CLR); + + pci_set_master(pdev); + + /* create now the corresponding channels objects */ + while (pciefd->can_count < can_count) { + err = pciefd_can_probe(pciefd); + if (err) + goto err_free_canfd; + + pciefd->can_count++; + } + + /* set system timestamps counter in RST mode */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, + PCIEFD_REG_SYS_CTL_SET); + + /* wait a bit (read cycle) */ + (void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1); + + /* free all clocks */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, + PCIEFD_REG_SYS_CTL_CLR); + + /* start system clock */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, + PCIEFD_REG_SYS_CTL_SET); + + /* remember the board structure address in the device user data */ + pci_set_drvdata(pdev, pciefd); + + return 0; + +err_free_canfd: + pciefd_can_remove_all(pciefd); + + pci_iounmap(pdev, pciefd->reg_base); + +err_release_regions: + pci_release_regions(pdev); + +err_disable_pci: + pci_disable_device(pdev); + + /* pci_xxx_config_word() return positive PCIBIOS_xxx error codes while + * the probe() function must return a negative errno in case of failure + * (err is unchanged if negative) */ + return pcibios_err_to_errno(err); +} + +/* free the board structure object, as well as its resources: */ +static void peak_pciefd_remove(struct pci_dev *pdev) +{ + struct pciefd_board *pciefd = pci_get_drvdata(pdev); + + /* release CAN-FD channels resources */ + pciefd_can_remove_all(pciefd); + + pci_iounmap(pdev, pciefd->reg_base); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_driver peak_pciefd_driver = { + .name = PCIEFD_DRV_NAME, + .id_table = peak_pciefd_tbl, + .probe = peak_pciefd_probe, + .remove = peak_pciefd_remove, +}; + +module_pci_driver(peak_pciefd_driver); diff --git a/drivers/net/can/rcar/Kconfig b/drivers/net/can/rcar/Kconfig new file mode 100644 index 000000000..7b03a3a37 --- /dev/null +++ b/drivers/net/can/rcar/Kconfig @@ -0,0 +1,21 @@ +config CAN_RCAR + tristate "Renesas R-Car CAN controller" + depends on ARCH_RENESAS || ARM + ---help--- + Say Y here if you want to use CAN controller found on Renesas R-Car + SoCs. + + To compile this driver as a module, choose M here: the module will + be called rcar_can. + +config CAN_RCAR_CANFD + tristate "Renesas R-Car CAN FD controller" + depends on ARCH_RENESAS || ARM + ---help--- + Say Y here if you want to use CAN FD controller found on + Renesas R-Car SoCs. The driver puts the controller in CAN FD only + mode, which can interoperate with CAN2.0 nodes but does not support + dedicated CAN 2.0 mode. + + To compile this driver as a module, choose M here: the module will + be called rcar_canfd. diff --git a/drivers/net/can/rcar/Makefile b/drivers/net/can/rcar/Makefile new file mode 100644 index 000000000..08de36a4c --- /dev/null +++ b/drivers/net/can/rcar/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Renesas R-Car CAN & CAN FD controller drivers +# + +obj-$(CONFIG_CAN_RCAR) += rcar_can.o +obj-$(CONFIG_CAN_RCAR_CANFD) += rcar_canfd.o diff --git a/drivers/net/can/rcar/rcar_can.c b/drivers/net/can/rcar/rcar_can.c new file mode 100644 index 000000000..963da8eda --- /dev/null +++ b/drivers/net/can/rcar/rcar_can.c @@ -0,0 +1,935 @@ +/* Renesas R-Car CAN device driver + * + * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com> + * Copyright (C) 2013 Renesas Solutions Corp. + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/can/led.h> +#include <linux/can/dev.h> +#include <linux/clk.h> +#include <linux/can/platform/rcar_can.h> +#include <linux/of.h> + +#define RCAR_CAN_DRV_NAME "rcar_can" + +#define RCAR_SUPPORTED_CLOCKS (BIT(CLKR_CLKP1) | BIT(CLKR_CLKP2) | \ + BIT(CLKR_CLKEXT)) + +/* Mailbox configuration: + * mailbox 60 - 63 - Rx FIFO mailboxes + * mailbox 56 - 59 - Tx FIFO mailboxes + * non-FIFO mailboxes are not used + */ +#define RCAR_CAN_N_MBX 64 /* Number of mailboxes in non-FIFO mode */ +#define RCAR_CAN_RX_FIFO_MBX 60 /* Mailbox - window to Rx FIFO */ +#define RCAR_CAN_TX_FIFO_MBX 56 /* Mailbox - window to Tx FIFO */ +#define RCAR_CAN_FIFO_DEPTH 4 + +/* Mailbox registers structure */ +struct rcar_can_mbox_regs { + u32 id; /* IDE and RTR bits, SID and EID */ + u8 stub; /* Not used */ + u8 dlc; /* Data Length Code - bits [0..3] */ + u8 data[8]; /* Data Bytes */ + u8 tsh; /* Time Stamp Higher Byte */ + u8 tsl; /* Time Stamp Lower Byte */ +}; + +struct rcar_can_regs { + struct rcar_can_mbox_regs mb[RCAR_CAN_N_MBX]; /* Mailbox registers */ + u32 mkr_2_9[8]; /* Mask Registers 2-9 */ + u32 fidcr[2]; /* FIFO Received ID Compare Register */ + u32 mkivlr1; /* Mask Invalid Register 1 */ + u32 mier1; /* Mailbox Interrupt Enable Register 1 */ + u32 mkr_0_1[2]; /* Mask Registers 0-1 */ + u32 mkivlr0; /* Mask Invalid Register 0*/ + u32 mier0; /* Mailbox Interrupt Enable Register 0 */ + u8 pad_440[0x3c0]; + u8 mctl[64]; /* Message Control Registers */ + u16 ctlr; /* Control Register */ + u16 str; /* Status register */ + u8 bcr[3]; /* Bit Configuration Register */ + u8 clkr; /* Clock Select Register */ + u8 rfcr; /* Receive FIFO Control Register */ + u8 rfpcr; /* Receive FIFO Pointer Control Register */ + u8 tfcr; /* Transmit FIFO Control Register */ + u8 tfpcr; /* Transmit FIFO Pointer Control Register */ + u8 eier; /* Error Interrupt Enable Register */ + u8 eifr; /* Error Interrupt Factor Judge Register */ + u8 recr; /* Receive Error Count Register */ + u8 tecr; /* Transmit Error Count Register */ + u8 ecsr; /* Error Code Store Register */ + u8 cssr; /* Channel Search Support Register */ + u8 mssr; /* Mailbox Search Status Register */ + u8 msmr; /* Mailbox Search Mode Register */ + u16 tsr; /* Time Stamp Register */ + u8 afsr; /* Acceptance Filter Support Register */ + u8 pad_857; + u8 tcr; /* Test Control Register */ + u8 pad_859[7]; + u8 ier; /* Interrupt Enable Register */ + u8 isr; /* Interrupt Status Register */ + u8 pad_862; + u8 mbsmr; /* Mailbox Search Mask Register */ +}; + +struct rcar_can_priv { + struct can_priv can; /* Must be the first member! */ + struct net_device *ndev; + struct napi_struct napi; + struct rcar_can_regs __iomem *regs; + struct clk *clk; + struct clk *can_clk; + u8 tx_dlc[RCAR_CAN_FIFO_DEPTH]; + u32 tx_head; + u32 tx_tail; + u8 clock_select; + u8 ier; +}; + +static const struct can_bittiming_const rcar_can_bittiming_const = { + .name = RCAR_CAN_DRV_NAME, + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* Control Register bits */ +#define RCAR_CAN_CTLR_BOM (3 << 11) /* Bus-Off Recovery Mode Bits */ +#define RCAR_CAN_CTLR_BOM_ENT (1 << 11) /* Entry to halt mode */ + /* at bus-off entry */ +#define RCAR_CAN_CTLR_SLPM (1 << 10) +#define RCAR_CAN_CTLR_CANM (3 << 8) /* Operating Mode Select Bit */ +#define RCAR_CAN_CTLR_CANM_HALT (1 << 9) +#define RCAR_CAN_CTLR_CANM_RESET (1 << 8) +#define RCAR_CAN_CTLR_CANM_FORCE_RESET (3 << 8) +#define RCAR_CAN_CTLR_MLM (1 << 3) /* Message Lost Mode Select */ +#define RCAR_CAN_CTLR_IDFM (3 << 1) /* ID Format Mode Select Bits */ +#define RCAR_CAN_CTLR_IDFM_MIXED (1 << 2) /* Mixed ID mode */ +#define RCAR_CAN_CTLR_MBM (1 << 0) /* Mailbox Mode select */ + +/* Status Register bits */ +#define RCAR_CAN_STR_RSTST (1 << 8) /* Reset Status Bit */ + +/* FIFO Received ID Compare Registers 0 and 1 bits */ +#define RCAR_CAN_FIDCR_IDE (1 << 31) /* ID Extension Bit */ +#define RCAR_CAN_FIDCR_RTR (1 << 30) /* Remote Transmission Request Bit */ + +/* Receive FIFO Control Register bits */ +#define RCAR_CAN_RFCR_RFEST (1 << 7) /* Receive FIFO Empty Status Flag */ +#define RCAR_CAN_RFCR_RFE (1 << 0) /* Receive FIFO Enable */ + +/* Transmit FIFO Control Register bits */ +#define RCAR_CAN_TFCR_TFUST (7 << 1) /* Transmit FIFO Unsent Message */ + /* Number Status Bits */ +#define RCAR_CAN_TFCR_TFUST_SHIFT 1 /* Offset of Transmit FIFO Unsent */ + /* Message Number Status Bits */ +#define RCAR_CAN_TFCR_TFE (1 << 0) /* Transmit FIFO Enable */ + +#define RCAR_CAN_N_RX_MKREGS1 2 /* Number of mask registers */ + /* for Rx mailboxes 0-31 */ +#define RCAR_CAN_N_RX_MKREGS2 8 + +/* Bit Configuration Register settings */ +#define RCAR_CAN_BCR_TSEG1(x) (((x) & 0x0f) << 20) +#define RCAR_CAN_BCR_BPR(x) (((x) & 0x3ff) << 8) +#define RCAR_CAN_BCR_SJW(x) (((x) & 0x3) << 4) +#define RCAR_CAN_BCR_TSEG2(x) ((x) & 0x07) + +/* Mailbox and Mask Registers bits */ +#define RCAR_CAN_IDE (1 << 31) +#define RCAR_CAN_RTR (1 << 30) +#define RCAR_CAN_SID_SHIFT 18 + +/* Mailbox Interrupt Enable Register 1 bits */ +#define RCAR_CAN_MIER1_RXFIE (1 << 28) /* Receive FIFO Interrupt Enable */ +#define RCAR_CAN_MIER1_TXFIE (1 << 24) /* Transmit FIFO Interrupt Enable */ + +/* Interrupt Enable Register bits */ +#define RCAR_CAN_IER_ERSIE (1 << 5) /* Error (ERS) Interrupt Enable Bit */ +#define RCAR_CAN_IER_RXFIE (1 << 4) /* Reception FIFO Interrupt */ + /* Enable Bit */ +#define RCAR_CAN_IER_TXFIE (1 << 3) /* Transmission FIFO Interrupt */ + /* Enable Bit */ +/* Interrupt Status Register bits */ +#define RCAR_CAN_ISR_ERSF (1 << 5) /* Error (ERS) Interrupt Status Bit */ +#define RCAR_CAN_ISR_RXFF (1 << 4) /* Reception FIFO Interrupt */ + /* Status Bit */ +#define RCAR_CAN_ISR_TXFF (1 << 3) /* Transmission FIFO Interrupt */ + /* Status Bit */ + +/* Error Interrupt Enable Register bits */ +#define RCAR_CAN_EIER_BLIE (1 << 7) /* Bus Lock Interrupt Enable */ +#define RCAR_CAN_EIER_OLIE (1 << 6) /* Overload Frame Transmit */ + /* Interrupt Enable */ +#define RCAR_CAN_EIER_ORIE (1 << 5) /* Receive Overrun Interrupt Enable */ +#define RCAR_CAN_EIER_BORIE (1 << 4) /* Bus-Off Recovery Interrupt Enable */ +#define RCAR_CAN_EIER_BOEIE (1 << 3) /* Bus-Off Entry Interrupt Enable */ +#define RCAR_CAN_EIER_EPIE (1 << 2) /* Error Passive Interrupt Enable */ +#define RCAR_CAN_EIER_EWIE (1 << 1) /* Error Warning Interrupt Enable */ +#define RCAR_CAN_EIER_BEIE (1 << 0) /* Bus Error Interrupt Enable */ + +/* Error Interrupt Factor Judge Register bits */ +#define RCAR_CAN_EIFR_BLIF (1 << 7) /* Bus Lock Detect Flag */ +#define RCAR_CAN_EIFR_OLIF (1 << 6) /* Overload Frame Transmission */ + /* Detect Flag */ +#define RCAR_CAN_EIFR_ORIF (1 << 5) /* Receive Overrun Detect Flag */ +#define RCAR_CAN_EIFR_BORIF (1 << 4) /* Bus-Off Recovery Detect Flag */ +#define RCAR_CAN_EIFR_BOEIF (1 << 3) /* Bus-Off Entry Detect Flag */ +#define RCAR_CAN_EIFR_EPIF (1 << 2) /* Error Passive Detect Flag */ +#define RCAR_CAN_EIFR_EWIF (1 << 1) /* Error Warning Detect Flag */ +#define RCAR_CAN_EIFR_BEIF (1 << 0) /* Bus Error Detect Flag */ + +/* Error Code Store Register bits */ +#define RCAR_CAN_ECSR_EDPM (1 << 7) /* Error Display Mode Select Bit */ +#define RCAR_CAN_ECSR_ADEF (1 << 6) /* ACK Delimiter Error Flag */ +#define RCAR_CAN_ECSR_BE0F (1 << 5) /* Bit Error (dominant) Flag */ +#define RCAR_CAN_ECSR_BE1F (1 << 4) /* Bit Error (recessive) Flag */ +#define RCAR_CAN_ECSR_CEF (1 << 3) /* CRC Error Flag */ +#define RCAR_CAN_ECSR_AEF (1 << 2) /* ACK Error Flag */ +#define RCAR_CAN_ECSR_FEF (1 << 1) /* Form Error Flag */ +#define RCAR_CAN_ECSR_SEF (1 << 0) /* Stuff Error Flag */ + +#define RCAR_CAN_NAPI_WEIGHT 4 +#define MAX_STR_READS 0x100 + +static void tx_failure_cleanup(struct net_device *ndev) +{ + int i; + + for (i = 0; i < RCAR_CAN_FIFO_DEPTH; i++) + can_free_echo_skb(ndev, i); +} + +static void rcar_can_error(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 eifr, txerr = 0, rxerr = 0; + + /* Propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + + eifr = readb(&priv->regs->eifr); + if (eifr & (RCAR_CAN_EIFR_EWIF | RCAR_CAN_EIFR_EPIF)) { + txerr = readb(&priv->regs->tecr); + rxerr = readb(&priv->regs->recr); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } + if (eifr & RCAR_CAN_EIFR_BEIF) { + int rx_errors = 0, tx_errors = 0; + u8 ecsr; + + netdev_dbg(priv->ndev, "Bus error interrupt:\n"); + if (skb) + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + + ecsr = readb(&priv->regs->ecsr); + if (ecsr & RCAR_CAN_ECSR_ADEF) { + netdev_dbg(priv->ndev, "ACK Delimiter Error\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr); + if (skb) + cf->data[3] = CAN_ERR_PROT_LOC_ACK_DEL; + } + if (ecsr & RCAR_CAN_ECSR_BE0F) { + netdev_dbg(priv->ndev, "Bit Error (dominant)\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_BE0F, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_BIT0; + } + if (ecsr & RCAR_CAN_ECSR_BE1F) { + netdev_dbg(priv->ndev, "Bit Error (recessive)\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_BE1F, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_BIT1; + } + if (ecsr & RCAR_CAN_ECSR_CEF) { + netdev_dbg(priv->ndev, "CRC Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr); + if (skb) + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + } + if (ecsr & RCAR_CAN_ECSR_AEF) { + netdev_dbg(priv->ndev, "ACK Error\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr); + if (skb) { + cf->can_id |= CAN_ERR_ACK; + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + } + } + if (ecsr & RCAR_CAN_ECSR_FEF) { + netdev_dbg(priv->ndev, "Form Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_FEF, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (ecsr & RCAR_CAN_ECSR_SEF) { + netdev_dbg(priv->ndev, "Stuff Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_SEF, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + + priv->can.can_stats.bus_error++; + ndev->stats.rx_errors += rx_errors; + ndev->stats.tx_errors += tx_errors; + writeb(~RCAR_CAN_EIFR_BEIF, &priv->regs->eifr); + } + if (eifr & RCAR_CAN_EIFR_EWIF) { + netdev_dbg(priv->ndev, "Error warning interrupt\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_EWIF, &priv->regs->eifr); + if (skb) + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } + if (eifr & RCAR_CAN_EIFR_EPIF) { + netdev_dbg(priv->ndev, "Error passive interrupt\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_EPIF, &priv->regs->eifr); + if (skb) + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + if (eifr & RCAR_CAN_EIFR_BOEIF) { + netdev_dbg(priv->ndev, "Bus-off entry interrupt\n"); + tx_failure_cleanup(ndev); + priv->ier = RCAR_CAN_IER_ERSIE; + writeb(priv->ier, &priv->regs->ier); + priv->can.state = CAN_STATE_BUS_OFF; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr); + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } + if (eifr & RCAR_CAN_EIFR_ORIF) { + netdev_dbg(priv->ndev, "Receive overrun error interrupt\n"); + ndev->stats.rx_over_errors++; + ndev->stats.rx_errors++; + writeb(~RCAR_CAN_EIFR_ORIF, &priv->regs->eifr); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + } + if (eifr & RCAR_CAN_EIFR_OLIF) { + netdev_dbg(priv->ndev, + "Overload Frame Transmission error interrupt\n"); + ndev->stats.rx_over_errors++; + ndev->stats.rx_errors++; + writeb(~RCAR_CAN_EIFR_OLIF, &priv->regs->eifr); + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + } + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +static void rcar_can_tx_done(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u8 isr; + + while (1) { + u8 unsent = readb(&priv->regs->tfcr); + + unsent = (unsent & RCAR_CAN_TFCR_TFUST) >> + RCAR_CAN_TFCR_TFUST_SHIFT; + if (priv->tx_head - priv->tx_tail <= unsent) + break; + stats->tx_packets++; + stats->tx_bytes += priv->tx_dlc[priv->tx_tail % + RCAR_CAN_FIFO_DEPTH]; + priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0; + can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH); + priv->tx_tail++; + netif_wake_queue(ndev); + } + /* Clear interrupt */ + isr = readb(&priv->regs->isr); + writeb(isr & ~RCAR_CAN_ISR_TXFF, &priv->regs->isr); + can_led_event(ndev, CAN_LED_EVENT_TX); +} + +static irqreturn_t rcar_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct rcar_can_priv *priv = netdev_priv(ndev); + u8 isr; + + isr = readb(&priv->regs->isr); + if (!(isr & priv->ier)) + return IRQ_NONE; + + if (isr & RCAR_CAN_ISR_ERSF) + rcar_can_error(ndev); + + if (isr & RCAR_CAN_ISR_TXFF) + rcar_can_tx_done(ndev); + + if (isr & RCAR_CAN_ISR_RXFF) { + if (napi_schedule_prep(&priv->napi)) { + /* Disable Rx FIFO interrupts */ + priv->ier &= ~RCAR_CAN_IER_RXFIE; + writeb(priv->ier, &priv->regs->ier); + __napi_schedule(&priv->napi); + } + } + + return IRQ_HANDLED; +} + +static void rcar_can_set_bittiming(struct net_device *dev) +{ + struct rcar_can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 bcr; + + bcr = RCAR_CAN_BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) | + RCAR_CAN_BCR_BPR(bt->brp - 1) | RCAR_CAN_BCR_SJW(bt->sjw - 1) | + RCAR_CAN_BCR_TSEG2(bt->phase_seg2 - 1); + /* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access. + * All the registers are big-endian but they get byte-swapped on 32-bit + * read/write (but not on 8-bit, contrary to the manuals)... + */ + writel((bcr << 8) | priv->clock_select, &priv->regs->bcr); +} + +static void rcar_can_start(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int i; + + /* Set controller to known mode: + * - FIFO mailbox mode + * - accept all messages + * - overrun mode + * CAN is in sleep mode after MCU hardware or software reset. + */ + ctlr = readw(&priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + /* Go to reset mode */ + ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET; + writew(ctlr, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST) + break; + } + rcar_can_set_bittiming(ndev); + ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */ + ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically */ + /* at bus-off */ + ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */ + ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */ + writew(ctlr, &priv->regs->ctlr); + + /* Accept all SID and EID */ + writel(0, &priv->regs->mkr_2_9[6]); + writel(0, &priv->regs->mkr_2_9[7]); + /* In FIFO mailbox mode, write "0" to bits 24 to 31 */ + writel(0, &priv->regs->mkivlr1); + /* Accept all frames */ + writel(0, &priv->regs->fidcr[0]); + writel(RCAR_CAN_FIDCR_IDE | RCAR_CAN_FIDCR_RTR, &priv->regs->fidcr[1]); + /* Enable and configure FIFO mailbox interrupts */ + writel(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE, &priv->regs->mier1); + + priv->ier = RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE | + RCAR_CAN_IER_TXFIE; + writeb(priv->ier, &priv->regs->ier); + + /* Accumulate error codes */ + writeb(RCAR_CAN_ECSR_EDPM, &priv->regs->ecsr); + /* Enable error interrupts */ + writeb(RCAR_CAN_EIER_EWIE | RCAR_CAN_EIER_EPIE | RCAR_CAN_EIER_BOEIE | + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ? + RCAR_CAN_EIER_BEIE : 0) | RCAR_CAN_EIER_ORIE | + RCAR_CAN_EIER_OLIE, &priv->regs->eier); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Go to operation mode */ + writew(ctlr & ~RCAR_CAN_CTLR_CANM, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)) + break; + } + /* Enable Rx and Tx FIFO */ + writeb(RCAR_CAN_RFCR_RFE, &priv->regs->rfcr); + writeb(RCAR_CAN_TFCR_TFE, &priv->regs->tfcr); +} + +static int rcar_can_open(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(ndev, + "failed to enable peripheral clock, error %d\n", + err); + goto out; + } + err = clk_prepare_enable(priv->can_clk); + if (err) { + netdev_err(ndev, "failed to enable CAN clock, error %d\n", + err); + goto out_clock; + } + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto out_can_clock; + } + napi_enable(&priv->napi); + err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev); + if (err) { + netdev_err(ndev, "request_irq(%d) failed, error %d\n", + ndev->irq, err); + goto out_close; + } + can_led_event(ndev, CAN_LED_EVENT_OPEN); + rcar_can_start(ndev); + netif_start_queue(ndev); + return 0; +out_close: + napi_disable(&priv->napi); + close_candev(ndev); +out_can_clock: + clk_disable_unprepare(priv->can_clk); +out_clock: + clk_disable_unprepare(priv->clk); +out: + return err; +} + +static void rcar_can_stop(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int i; + + /* Go to (force) reset mode */ + ctlr = readw(&priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET; + writew(ctlr, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST) + break; + } + writel(0, &priv->regs->mier0); + writel(0, &priv->regs->mier1); + writeb(0, &priv->regs->ier); + writeb(0, &priv->regs->eier); + /* Go to sleep mode */ + ctlr |= RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_STOPPED; +} + +static int rcar_can_close(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + rcar_can_stop(ndev); + free_irq(ndev->irq, ndev); + napi_disable(&priv->napi); + clk_disable_unprepare(priv->can_clk); + clk_disable_unprepare(priv->clk); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + return 0; +} + +static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + u32 data, i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE; + else /* Standard frame format */ + data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT; + + if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */ + data |= RCAR_CAN_RTR; + } else { + for (i = 0; i < cf->can_dlc; i++) + writeb(cf->data[i], + &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]); + } + + writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id); + + writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc); + + priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc; + can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH); + priv->tx_head++; + /* Start Tx: write 0xff to the TFPCR register to increment + * the CPU-side pointer for the transmit FIFO to the next + * mailbox location + */ + writeb(0xff, &priv->regs->tfpcr); + /* Stop the queue if we've filled all FIFO entries */ + if (priv->tx_head - priv->tx_tail >= RCAR_CAN_FIFO_DEPTH) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops rcar_can_netdev_ops = { + .ndo_open = rcar_can_open, + .ndo_stop = rcar_can_close, + .ndo_start_xmit = rcar_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static void rcar_can_rx_pkt(struct rcar_can_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 data; + u8 dlc; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + data = readl(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].id); + if (data & RCAR_CAN_IDE) + cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK; + + dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc); + cf->can_dlc = get_can_dlc(dlc); + if (data & RCAR_CAN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (dlc = 0; dlc < cf->can_dlc; dlc++) + cf->data[dlc] = + readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]); + } + + can_led_event(priv->ndev, CAN_LED_EVENT_RX); + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_receive_skb(skb); +} + +static int rcar_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct rcar_can_priv *priv = container_of(napi, + struct rcar_can_priv, napi); + int num_pkts; + + for (num_pkts = 0; num_pkts < quota; num_pkts++) { + u8 rfcr, isr; + + isr = readb(&priv->regs->isr); + /* Clear interrupt bit */ + if (isr & RCAR_CAN_ISR_RXFF) + writeb(isr & ~RCAR_CAN_ISR_RXFF, &priv->regs->isr); + rfcr = readb(&priv->regs->rfcr); + if (rfcr & RCAR_CAN_RFCR_RFEST) + break; + rcar_can_rx_pkt(priv); + /* Write 0xff to the RFPCR register to increment + * the CPU-side pointer for the receive FIFO + * to the next mailbox location + */ + writeb(0xff, &priv->regs->rfpcr); + } + /* All packets processed */ + if (num_pkts < quota) { + napi_complete_done(napi, num_pkts); + priv->ier |= RCAR_CAN_IER_RXFIE; + writeb(priv->ier, &priv->regs->ier); + } + return num_pkts; +} + +static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + rcar_can_start(ndev); + netif_wake_queue(ndev); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int rcar_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct rcar_can_priv *priv = netdev_priv(dev); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + bec->txerr = readb(&priv->regs->tecr); + bec->rxerr = readb(&priv->regs->recr); + clk_disable_unprepare(priv->clk); + return 0; +} + +static const char * const clock_names[] = { + [CLKR_CLKP1] = "clkp1", + [CLKR_CLKP2] = "clkp2", + [CLKR_CLKEXT] = "can_clk", +}; + +static int rcar_can_probe(struct platform_device *pdev) +{ + struct rcar_can_platform_data *pdata; + struct rcar_can_priv *priv; + struct net_device *ndev; + struct resource *mem; + void __iomem *addr; + u32 clock_select = CLKR_CLKP1; + int err = -ENODEV; + int irq; + + if (pdev->dev.of_node) { + of_property_read_u32(pdev->dev.of_node, + "renesas,can-clock-select", &clock_select); + } else { + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + goto fail; + } + clock_select = pdata->clock_select; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + err = irq; + goto fail; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = PTR_ERR(addr); + goto fail; + } + + ndev = alloc_candev(sizeof(struct rcar_can_priv), RCAR_CAN_FIFO_DEPTH); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev() failed\n"); + err = -ENOMEM; + goto fail; + } + + priv = netdev_priv(ndev); + + priv->clk = devm_clk_get(&pdev->dev, "clkp1"); + if (IS_ERR(priv->clk)) { + err = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "cannot get peripheral clock, error %d\n", + err); + goto fail_clk; + } + + if (!(BIT(clock_select) & RCAR_SUPPORTED_CLOCKS)) { + err = -EINVAL; + dev_err(&pdev->dev, "invalid CAN clock selected\n"); + goto fail_clk; + } + priv->can_clk = devm_clk_get(&pdev->dev, clock_names[clock_select]); + if (IS_ERR(priv->can_clk)) { + err = PTR_ERR(priv->can_clk); + dev_err(&pdev->dev, "cannot get CAN clock, error %d\n", err); + goto fail_clk; + } + + ndev->netdev_ops = &rcar_can_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + priv->ndev = ndev; + priv->regs = addr; + priv->clock_select = clock_select; + priv->can.clock.freq = clk_get_rate(priv->can_clk); + priv->can.bittiming_const = &rcar_can_bittiming_const; + priv->can.do_set_mode = rcar_can_do_set_mode; + priv->can.do_get_berr_counter = rcar_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll, + RCAR_CAN_NAPI_WEIGHT); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed, error %d\n", + err); + goto fail_candev; + } + + devm_can_led_init(ndev); + + dev_info(&pdev->dev, "device registered (IRQ%d)\n", ndev->irq); + + return 0; +fail_candev: + netif_napi_del(&priv->napi); +fail_clk: + free_candev(ndev); +fail: + return err; +} + +static int rcar_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct rcar_can_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); + return 0; +} + +static int __maybe_unused rcar_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + + if (!netif_running(ndev)) + return 0; + + netif_stop_queue(ndev); + netif_device_detach(ndev); + + ctlr = readw(&priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_CANM_HALT; + writew(ctlr, &priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + return 0; +} + +static int __maybe_unused rcar_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int err; + + if (!netif_running(ndev)) + return 0; + + err = clk_enable(priv->clk); + if (err) { + netdev_err(ndev, "clk_enable() failed, error %d\n", err); + return err; + } + + ctlr = readw(&priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_CANM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + netif_device_attach(ndev); + netif_start_queue(ndev); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume); + +static const struct of_device_id rcar_can_of_table[] __maybe_unused = { + { .compatible = "renesas,can-r8a7778" }, + { .compatible = "renesas,can-r8a7779" }, + { .compatible = "renesas,can-r8a7790" }, + { .compatible = "renesas,can-r8a7791" }, + { .compatible = "renesas,rcar-gen1-can" }, + { .compatible = "renesas,rcar-gen2-can" }, + { .compatible = "renesas,rcar-gen3-can" }, + { } +}; +MODULE_DEVICE_TABLE(of, rcar_can_of_table); + +static struct platform_driver rcar_can_driver = { + .driver = { + .name = RCAR_CAN_DRV_NAME, + .of_match_table = of_match_ptr(rcar_can_of_table), + .pm = &rcar_can_pm_ops, + }, + .probe = rcar_can_probe, + .remove = rcar_can_remove, +}; + +module_platform_driver(rcar_can_driver); + +MODULE_AUTHOR("Cogent Embedded, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC"); +MODULE_ALIAS("platform:" RCAR_CAN_DRV_NAME); diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c new file mode 100644 index 000000000..a1634834b --- /dev/null +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -0,0 +1,1859 @@ +/* Renesas R-Car CAN FD device driver + * + * Copyright (C) 2015 Renesas Electronics Corp. + * + * 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. + */ + +/* The R-Car CAN FD controller can operate in either one of the below two modes + * - CAN FD only mode + * - Classical CAN (CAN 2.0) only mode + * + * This driver puts the controller in CAN FD only mode by default. In this + * mode, the controller acts as a CAN FD node that can also interoperate with + * CAN 2.0 nodes. + * + * To switch the controller to Classical CAN (CAN 2.0) only mode, add + * "renesas,no-can-fd" optional property to the device tree node. A h/w reset is + * also required to switch modes. + * + * Note: The h/w manual register naming convention is clumsy and not acceptable + * to use as it is in the driver. However, those names are added as comments + * wherever it is modified to a readable name. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/can/led.h> +#include <linux/can/dev.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/iopoll.h> + +#define RCANFD_DRV_NAME "rcar_canfd" + +/* Global register bits */ + +/* RSCFDnCFDGRMCFG */ +#define RCANFD_GRMCFG_RCMC BIT(0) + +/* RSCFDnCFDGCFG / RSCFDnGCFG */ +#define RCANFD_GCFG_EEFE BIT(6) +#define RCANFD_GCFG_CMPOC BIT(5) /* CAN FD only */ +#define RCANFD_GCFG_DCS BIT(4) +#define RCANFD_GCFG_DCE BIT(1) +#define RCANFD_GCFG_TPRI BIT(0) + +/* RSCFDnCFDGCTR / RSCFDnGCTR */ +#define RCANFD_GCTR_TSRST BIT(16) +#define RCANFD_GCTR_CFMPOFIE BIT(11) /* CAN FD only */ +#define RCANFD_GCTR_THLEIE BIT(10) +#define RCANFD_GCTR_MEIE BIT(9) +#define RCANFD_GCTR_DEIE BIT(8) +#define RCANFD_GCTR_GSLPR BIT(2) +#define RCANFD_GCTR_GMDC_MASK (0x3) +#define RCANFD_GCTR_GMDC_GOPM (0x0) +#define RCANFD_GCTR_GMDC_GRESET (0x1) +#define RCANFD_GCTR_GMDC_GTEST (0x2) + +/* RSCFDnCFDGSTS / RSCFDnGSTS */ +#define RCANFD_GSTS_GRAMINIT BIT(3) +#define RCANFD_GSTS_GSLPSTS BIT(2) +#define RCANFD_GSTS_GHLTSTS BIT(1) +#define RCANFD_GSTS_GRSTSTS BIT(0) +/* Non-operational status */ +#define RCANFD_GSTS_GNOPM (BIT(0) | BIT(1) | BIT(2) | BIT(3)) + +/* RSCFDnCFDGERFL / RSCFDnGERFL */ +#define RCANFD_GERFL_EEF1 BIT(17) +#define RCANFD_GERFL_EEF0 BIT(16) +#define RCANFD_GERFL_CMPOF BIT(3) /* CAN FD only */ +#define RCANFD_GERFL_THLES BIT(2) +#define RCANFD_GERFL_MES BIT(1) +#define RCANFD_GERFL_DEF BIT(0) + +#define RCANFD_GERFL_ERR(gpriv, x) ((x) & (RCANFD_GERFL_EEF1 |\ + RCANFD_GERFL_EEF0 | RCANFD_GERFL_MES |\ + (gpriv->fdmode ?\ + RCANFD_GERFL_CMPOF : 0))) + +/* AFL Rx rules registers */ + +/* RSCFDnCFDGAFLCFG0 / RSCFDnGAFLCFG0 */ +#define RCANFD_GAFLCFG_SETRNC(n, x) (((x) & 0xff) << (24 - n * 8)) +#define RCANFD_GAFLCFG_GETRNC(n, x) (((x) >> (24 - n * 8)) & 0xff) + +/* RSCFDnCFDGAFLECTR / RSCFDnGAFLECTR */ +#define RCANFD_GAFLECTR_AFLDAE BIT(8) +#define RCANFD_GAFLECTR_AFLPN(x) ((x) & 0x1f) + +/* RSCFDnCFDGAFLIDj / RSCFDnGAFLIDj */ +#define RCANFD_GAFLID_GAFLLB BIT(29) + +/* RSCFDnCFDGAFLP1_j / RSCFDnGAFLP1_j */ +#define RCANFD_GAFLP1_GAFLFDP(x) (1 << (x)) + +/* Channel register bits */ + +/* RSCFDnCmCFG - Classical CAN only */ +#define RCANFD_CFG_SJW(x) (((x) & 0x3) << 24) +#define RCANFD_CFG_TSEG2(x) (((x) & 0x7) << 20) +#define RCANFD_CFG_TSEG1(x) (((x) & 0xf) << 16) +#define RCANFD_CFG_BRP(x) (((x) & 0x3ff) << 0) + +/* RSCFDnCFDCmNCFG - CAN FD only */ +#define RCANFD_NCFG_NTSEG2(x) (((x) & 0x1f) << 24) +#define RCANFD_NCFG_NTSEG1(x) (((x) & 0x7f) << 16) +#define RCANFD_NCFG_NSJW(x) (((x) & 0x1f) << 11) +#define RCANFD_NCFG_NBRP(x) (((x) & 0x3ff) << 0) + +/* RSCFDnCFDCmCTR / RSCFDnCmCTR */ +#define RCANFD_CCTR_CTME BIT(24) +#define RCANFD_CCTR_ERRD BIT(23) +#define RCANFD_CCTR_BOM_MASK (0x3 << 21) +#define RCANFD_CCTR_BOM_ISO (0x0 << 21) +#define RCANFD_CCTR_BOM_BENTRY (0x1 << 21) +#define RCANFD_CCTR_BOM_BEND (0x2 << 21) +#define RCANFD_CCTR_TDCVFIE BIT(19) +#define RCANFD_CCTR_SOCOIE BIT(18) +#define RCANFD_CCTR_EOCOIE BIT(17) +#define RCANFD_CCTR_TAIE BIT(16) +#define RCANFD_CCTR_ALIE BIT(15) +#define RCANFD_CCTR_BLIE BIT(14) +#define RCANFD_CCTR_OLIE BIT(13) +#define RCANFD_CCTR_BORIE BIT(12) +#define RCANFD_CCTR_BOEIE BIT(11) +#define RCANFD_CCTR_EPIE BIT(10) +#define RCANFD_CCTR_EWIE BIT(9) +#define RCANFD_CCTR_BEIE BIT(8) +#define RCANFD_CCTR_CSLPR BIT(2) +#define RCANFD_CCTR_CHMDC_MASK (0x3) +#define RCANFD_CCTR_CHDMC_COPM (0x0) +#define RCANFD_CCTR_CHDMC_CRESET (0x1) +#define RCANFD_CCTR_CHDMC_CHLT (0x2) + +/* RSCFDnCFDCmSTS / RSCFDnCmSTS */ +#define RCANFD_CSTS_COMSTS BIT(7) +#define RCANFD_CSTS_RECSTS BIT(6) +#define RCANFD_CSTS_TRMSTS BIT(5) +#define RCANFD_CSTS_BOSTS BIT(4) +#define RCANFD_CSTS_EPSTS BIT(3) +#define RCANFD_CSTS_SLPSTS BIT(2) +#define RCANFD_CSTS_HLTSTS BIT(1) +#define RCANFD_CSTS_CRSTSTS BIT(0) + +#define RCANFD_CSTS_TECCNT(x) (((x) >> 24) & 0xff) +#define RCANFD_CSTS_RECCNT(x) (((x) >> 16) & 0xff) + +/* RSCFDnCFDCmERFL / RSCFDnCmERFL */ +#define RCANFD_CERFL_ADERR BIT(14) +#define RCANFD_CERFL_B0ERR BIT(13) +#define RCANFD_CERFL_B1ERR BIT(12) +#define RCANFD_CERFL_CERR BIT(11) +#define RCANFD_CERFL_AERR BIT(10) +#define RCANFD_CERFL_FERR BIT(9) +#define RCANFD_CERFL_SERR BIT(8) +#define RCANFD_CERFL_ALF BIT(7) +#define RCANFD_CERFL_BLF BIT(6) +#define RCANFD_CERFL_OVLF BIT(5) +#define RCANFD_CERFL_BORF BIT(4) +#define RCANFD_CERFL_BOEF BIT(3) +#define RCANFD_CERFL_EPF BIT(2) +#define RCANFD_CERFL_EWF BIT(1) +#define RCANFD_CERFL_BEF BIT(0) + +#define RCANFD_CERFL_ERR(x) ((x) & (0x7fff)) /* above bits 14:0 */ + +/* RSCFDnCFDCmDCFG */ +#define RCANFD_DCFG_DSJW(x) (((x) & 0x7) << 24) +#define RCANFD_DCFG_DTSEG2(x) (((x) & 0x7) << 20) +#define RCANFD_DCFG_DTSEG1(x) (((x) & 0xf) << 16) +#define RCANFD_DCFG_DBRP(x) (((x) & 0xff) << 0) + +/* RSCFDnCFDCmFDCFG */ +#define RCANFD_FDCFG_TDCE BIT(9) +#define RCANFD_FDCFG_TDCOC BIT(8) +#define RCANFD_FDCFG_TDCO(x) (((x) & 0x7f) >> 16) + +/* RSCFDnCFDRFCCx */ +#define RCANFD_RFCC_RFIM BIT(12) +#define RCANFD_RFCC_RFDC(x) (((x) & 0x7) << 8) +#define RCANFD_RFCC_RFPLS(x) (((x) & 0x7) << 4) +#define RCANFD_RFCC_RFIE BIT(1) +#define RCANFD_RFCC_RFE BIT(0) + +/* RSCFDnCFDRFSTSx */ +#define RCANFD_RFSTS_RFIF BIT(3) +#define RCANFD_RFSTS_RFMLT BIT(2) +#define RCANFD_RFSTS_RFFLL BIT(1) +#define RCANFD_RFSTS_RFEMP BIT(0) + +/* RSCFDnCFDRFIDx */ +#define RCANFD_RFID_RFIDE BIT(31) +#define RCANFD_RFID_RFRTR BIT(30) + +/* RSCFDnCFDRFPTRx */ +#define RCANFD_RFPTR_RFDLC(x) (((x) >> 28) & 0xf) +#define RCANFD_RFPTR_RFPTR(x) (((x) >> 16) & 0xfff) +#define RCANFD_RFPTR_RFTS(x) (((x) >> 0) & 0xffff) + +/* RSCFDnCFDRFFDSTSx */ +#define RCANFD_RFFDSTS_RFFDF BIT(2) +#define RCANFD_RFFDSTS_RFBRS BIT(1) +#define RCANFD_RFFDSTS_RFESI BIT(0) + +/* Common FIFO bits */ + +/* RSCFDnCFDCFCCk */ +#define RCANFD_CFCC_CFTML(x) (((x) & 0xf) << 20) +#define RCANFD_CFCC_CFM(x) (((x) & 0x3) << 16) +#define RCANFD_CFCC_CFIM BIT(12) +#define RCANFD_CFCC_CFDC(x) (((x) & 0x7) << 8) +#define RCANFD_CFCC_CFPLS(x) (((x) & 0x7) << 4) +#define RCANFD_CFCC_CFTXIE BIT(2) +#define RCANFD_CFCC_CFE BIT(0) + +/* RSCFDnCFDCFSTSk */ +#define RCANFD_CFSTS_CFMC(x) (((x) >> 8) & 0xff) +#define RCANFD_CFSTS_CFTXIF BIT(4) +#define RCANFD_CFSTS_CFMLT BIT(2) +#define RCANFD_CFSTS_CFFLL BIT(1) +#define RCANFD_CFSTS_CFEMP BIT(0) + +/* RSCFDnCFDCFIDk */ +#define RCANFD_CFID_CFIDE BIT(31) +#define RCANFD_CFID_CFRTR BIT(30) +#define RCANFD_CFID_CFID_MASK(x) ((x) & 0x1fffffff) + +/* RSCFDnCFDCFPTRk */ +#define RCANFD_CFPTR_CFDLC(x) (((x) & 0xf) << 28) +#define RCANFD_CFPTR_CFPTR(x) (((x) & 0xfff) << 16) +#define RCANFD_CFPTR_CFTS(x) (((x) & 0xff) << 0) + +/* RSCFDnCFDCFFDCSTSk */ +#define RCANFD_CFFDCSTS_CFFDF BIT(2) +#define RCANFD_CFFDCSTS_CFBRS BIT(1) +#define RCANFD_CFFDCSTS_CFESI BIT(0) + +/* This controller supports either Classical CAN only mode or CAN FD only mode. + * These modes are supported in two separate set of register maps & names. + * However, some of the register offsets are common for both modes. Those + * offsets are listed below as Common registers. + * + * The CAN FD only mode specific registers & Classical CAN only mode specific + * registers are listed separately. Their register names starts with + * RCANFD_F_xxx & RCANFD_C_xxx respectively. + */ + +/* Common registers */ + +/* RSCFDnCFDCmNCFG / RSCFDnCmCFG */ +#define RCANFD_CCFG(m) (0x0000 + (0x10 * (m))) +/* RSCFDnCFDCmCTR / RSCFDnCmCTR */ +#define RCANFD_CCTR(m) (0x0004 + (0x10 * (m))) +/* RSCFDnCFDCmSTS / RSCFDnCmSTS */ +#define RCANFD_CSTS(m) (0x0008 + (0x10 * (m))) +/* RSCFDnCFDCmERFL / RSCFDnCmERFL */ +#define RCANFD_CERFL(m) (0x000C + (0x10 * (m))) + +/* RSCFDnCFDGCFG / RSCFDnGCFG */ +#define RCANFD_GCFG (0x0084) +/* RSCFDnCFDGCTR / RSCFDnGCTR */ +#define RCANFD_GCTR (0x0088) +/* RSCFDnCFDGCTS / RSCFDnGCTS */ +#define RCANFD_GSTS (0x008c) +/* RSCFDnCFDGERFL / RSCFDnGERFL */ +#define RCANFD_GERFL (0x0090) +/* RSCFDnCFDGTSC / RSCFDnGTSC */ +#define RCANFD_GTSC (0x0094) +/* RSCFDnCFDGAFLECTR / RSCFDnGAFLECTR */ +#define RCANFD_GAFLECTR (0x0098) +/* RSCFDnCFDGAFLCFG0 / RSCFDnGAFLCFG0 */ +#define RCANFD_GAFLCFG0 (0x009c) +/* RSCFDnCFDGAFLCFG1 / RSCFDnGAFLCFG1 */ +#define RCANFD_GAFLCFG1 (0x00a0) +/* RSCFDnCFDRMNB / RSCFDnRMNB */ +#define RCANFD_RMNB (0x00a4) +/* RSCFDnCFDRMND / RSCFDnRMND */ +#define RCANFD_RMND(y) (0x00a8 + (0x04 * (y))) + +/* RSCFDnCFDRFCCx / RSCFDnRFCCx */ +#define RCANFD_RFCC(x) (0x00b8 + (0x04 * (x))) +/* RSCFDnCFDRFSTSx / RSCFDnRFSTSx */ +#define RCANFD_RFSTS(x) (0x00d8 + (0x04 * (x))) +/* RSCFDnCFDRFPCTRx / RSCFDnRFPCTRx */ +#define RCANFD_RFPCTR(x) (0x00f8 + (0x04 * (x))) + +/* Common FIFO Control registers */ + +/* RSCFDnCFDCFCCx / RSCFDnCFCCx */ +#define RCANFD_CFCC(ch, idx) (0x0118 + (0x0c * (ch)) + \ + (0x04 * (idx))) +/* RSCFDnCFDCFSTSx / RSCFDnCFSTSx */ +#define RCANFD_CFSTS(ch, idx) (0x0178 + (0x0c * (ch)) + \ + (0x04 * (idx))) +/* RSCFDnCFDCFPCTRx / RSCFDnCFPCTRx */ +#define RCANFD_CFPCTR(ch, idx) (0x01d8 + (0x0c * (ch)) + \ + (0x04 * (idx))) + +/* RSCFDnCFDFESTS / RSCFDnFESTS */ +#define RCANFD_FESTS (0x0238) +/* RSCFDnCFDFFSTS / RSCFDnFFSTS */ +#define RCANFD_FFSTS (0x023c) +/* RSCFDnCFDFMSTS / RSCFDnFMSTS */ +#define RCANFD_FMSTS (0x0240) +/* RSCFDnCFDRFISTS / RSCFDnRFISTS */ +#define RCANFD_RFISTS (0x0244) +/* RSCFDnCFDCFRISTS / RSCFDnCFRISTS */ +#define RCANFD_CFRISTS (0x0248) +/* RSCFDnCFDCFTISTS / RSCFDnCFTISTS */ +#define RCANFD_CFTISTS (0x024c) + +/* RSCFDnCFDTMCp / RSCFDnTMCp */ +#define RCANFD_TMC(p) (0x0250 + (0x01 * (p))) +/* RSCFDnCFDTMSTSp / RSCFDnTMSTSp */ +#define RCANFD_TMSTS(p) (0x02d0 + (0x01 * (p))) + +/* RSCFDnCFDTMTRSTSp / RSCFDnTMTRSTSp */ +#define RCANFD_TMTRSTS(y) (0x0350 + (0x04 * (y))) +/* RSCFDnCFDTMTARSTSp / RSCFDnTMTARSTSp */ +#define RCANFD_TMTARSTS(y) (0x0360 + (0x04 * (y))) +/* RSCFDnCFDTMTCSTSp / RSCFDnTMTCSTSp */ +#define RCANFD_TMTCSTS(y) (0x0370 + (0x04 * (y))) +/* RSCFDnCFDTMTASTSp / RSCFDnTMTASTSp */ +#define RCANFD_TMTASTS(y) (0x0380 + (0x04 * (y))) +/* RSCFDnCFDTMIECy / RSCFDnTMIECy */ +#define RCANFD_TMIEC(y) (0x0390 + (0x04 * (y))) + +/* RSCFDnCFDTXQCCm / RSCFDnTXQCCm */ +#define RCANFD_TXQCC(m) (0x03a0 + (0x04 * (m))) +/* RSCFDnCFDTXQSTSm / RSCFDnTXQSTSm */ +#define RCANFD_TXQSTS(m) (0x03c0 + (0x04 * (m))) +/* RSCFDnCFDTXQPCTRm / RSCFDnTXQPCTRm */ +#define RCANFD_TXQPCTR(m) (0x03e0 + (0x04 * (m))) + +/* RSCFDnCFDTHLCCm / RSCFDnTHLCCm */ +#define RCANFD_THLCC(m) (0x0400 + (0x04 * (m))) +/* RSCFDnCFDTHLSTSm / RSCFDnTHLSTSm */ +#define RCANFD_THLSTS(m) (0x0420 + (0x04 * (m))) +/* RSCFDnCFDTHLPCTRm / RSCFDnTHLPCTRm */ +#define RCANFD_THLPCTR(m) (0x0440 + (0x04 * (m))) + +/* RSCFDnCFDGTINTSTS0 / RSCFDnGTINTSTS0 */ +#define RCANFD_GTINTSTS0 (0x0460) +/* RSCFDnCFDGTINTSTS1 / RSCFDnGTINTSTS1 */ +#define RCANFD_GTINTSTS1 (0x0464) +/* RSCFDnCFDGTSTCFG / RSCFDnGTSTCFG */ +#define RCANFD_GTSTCFG (0x0468) +/* RSCFDnCFDGTSTCTR / RSCFDnGTSTCTR */ +#define RCANFD_GTSTCTR (0x046c) +/* RSCFDnCFDGLOCKK / RSCFDnGLOCKK */ +#define RCANFD_GLOCKK (0x047c) +/* RSCFDnCFDGRMCFG */ +#define RCANFD_GRMCFG (0x04fc) + +/* RSCFDnCFDGAFLIDj / RSCFDnGAFLIDj */ +#define RCANFD_GAFLID(offset, j) ((offset) + (0x10 * (j))) +/* RSCFDnCFDGAFLMj / RSCFDnGAFLMj */ +#define RCANFD_GAFLM(offset, j) ((offset) + 0x04 + (0x10 * (j))) +/* RSCFDnCFDGAFLP0j / RSCFDnGAFLP0j */ +#define RCANFD_GAFLP0(offset, j) ((offset) + 0x08 + (0x10 * (j))) +/* RSCFDnCFDGAFLP1j / RSCFDnGAFLP1j */ +#define RCANFD_GAFLP1(offset, j) ((offset) + 0x0c + (0x10 * (j))) + +/* Classical CAN only mode register map */ + +/* RSCFDnGAFLXXXj offset */ +#define RCANFD_C_GAFL_OFFSET (0x0500) + +/* RSCFDnRMXXXq -> RCANFD_C_RMXXX(q) */ +#define RCANFD_C_RMID(q) (0x0600 + (0x10 * (q))) +#define RCANFD_C_RMPTR(q) (0x0604 + (0x10 * (q))) +#define RCANFD_C_RMDF0(q) (0x0608 + (0x10 * (q))) +#define RCANFD_C_RMDF1(q) (0x060c + (0x10 * (q))) + +/* RSCFDnRFXXx -> RCANFD_C_RFXX(x) */ +#define RCANFD_C_RFOFFSET (0x0e00) +#define RCANFD_C_RFID(x) (RCANFD_C_RFOFFSET + (0x10 * (x))) +#define RCANFD_C_RFPTR(x) (RCANFD_C_RFOFFSET + 0x04 + \ + (0x10 * (x))) +#define RCANFD_C_RFDF(x, df) (RCANFD_C_RFOFFSET + 0x08 + \ + (0x10 * (x)) + (0x04 * (df))) + +/* RSCFDnCFXXk -> RCANFD_C_CFXX(ch, k) */ +#define RCANFD_C_CFOFFSET (0x0e80) +#define RCANFD_C_CFID(ch, idx) (RCANFD_C_CFOFFSET + (0x30 * (ch)) + \ + (0x10 * (idx))) +#define RCANFD_C_CFPTR(ch, idx) (RCANFD_C_CFOFFSET + 0x04 + \ + (0x30 * (ch)) + (0x10 * (idx))) +#define RCANFD_C_CFDF(ch, idx, df) (RCANFD_C_CFOFFSET + 0x08 + \ + (0x30 * (ch)) + (0x10 * (idx)) + \ + (0x04 * (df))) + +/* RSCFDnTMXXp -> RCANFD_C_TMXX(p) */ +#define RCANFD_C_TMID(p) (0x1000 + (0x10 * (p))) +#define RCANFD_C_TMPTR(p) (0x1004 + (0x10 * (p))) +#define RCANFD_C_TMDF0(p) (0x1008 + (0x10 * (p))) +#define RCANFD_C_TMDF1(p) (0x100c + (0x10 * (p))) + +/* RSCFDnTHLACCm */ +#define RCANFD_C_THLACC(m) (0x1800 + (0x04 * (m))) +/* RSCFDnRPGACCr */ +#define RCANFD_C_RPGACC(r) (0x1900 + (0x04 * (r))) + +/* CAN FD mode specific register map */ + +/* RSCFDnCFDCmXXX -> RCANFD_F_XXX(m) */ +#define RCANFD_F_DCFG(m) (0x0500 + (0x20 * (m))) +#define RCANFD_F_CFDCFG(m) (0x0504 + (0x20 * (m))) +#define RCANFD_F_CFDCTR(m) (0x0508 + (0x20 * (m))) +#define RCANFD_F_CFDSTS(m) (0x050c + (0x20 * (m))) +#define RCANFD_F_CFDCRC(m) (0x0510 + (0x20 * (m))) + +/* RSCFDnCFDGAFLXXXj offset */ +#define RCANFD_F_GAFL_OFFSET (0x1000) + +/* RSCFDnCFDRMXXXq -> RCANFD_F_RMXXX(q) */ +#define RCANFD_F_RMID(q) (0x2000 + (0x20 * (q))) +#define RCANFD_F_RMPTR(q) (0x2004 + (0x20 * (q))) +#define RCANFD_F_RMFDSTS(q) (0x2008 + (0x20 * (q))) +#define RCANFD_F_RMDF(q, b) (0x200c + (0x04 * (b)) + (0x20 * (q))) + +/* RSCFDnCFDRFXXx -> RCANFD_F_RFXX(x) */ +#define RCANFD_F_RFOFFSET (0x3000) +#define RCANFD_F_RFID(x) (RCANFD_F_RFOFFSET + (0x80 * (x))) +#define RCANFD_F_RFPTR(x) (RCANFD_F_RFOFFSET + 0x04 + \ + (0x80 * (x))) +#define RCANFD_F_RFFDSTS(x) (RCANFD_F_RFOFFSET + 0x08 + \ + (0x80 * (x))) +#define RCANFD_F_RFDF(x, df) (RCANFD_F_RFOFFSET + 0x0c + \ + (0x80 * (x)) + (0x04 * (df))) + +/* RSCFDnCFDCFXXk -> RCANFD_F_CFXX(ch, k) */ +#define RCANFD_F_CFOFFSET (0x3400) +#define RCANFD_F_CFID(ch, idx) (RCANFD_F_CFOFFSET + (0x180 * (ch)) + \ + (0x80 * (idx))) +#define RCANFD_F_CFPTR(ch, idx) (RCANFD_F_CFOFFSET + 0x04 + \ + (0x180 * (ch)) + (0x80 * (idx))) +#define RCANFD_F_CFFDCSTS(ch, idx) (RCANFD_F_CFOFFSET + 0x08 + \ + (0x180 * (ch)) + (0x80 * (idx))) +#define RCANFD_F_CFDF(ch, idx, df) (RCANFD_F_CFOFFSET + 0x0c + \ + (0x180 * (ch)) + (0x80 * (idx)) + \ + (0x04 * (df))) + +/* RSCFDnCFDTMXXp -> RCANFD_F_TMXX(p) */ +#define RCANFD_F_TMID(p) (0x4000 + (0x20 * (p))) +#define RCANFD_F_TMPTR(p) (0x4004 + (0x20 * (p))) +#define RCANFD_F_TMFDCTR(p) (0x4008 + (0x20 * (p))) +#define RCANFD_F_TMDF(p, b) (0x400c + (0x20 * (p)) + (0x04 * (b))) + +/* RSCFDnCFDTHLACCm */ +#define RCANFD_F_THLACC(m) (0x6000 + (0x04 * (m))) +/* RSCFDnCFDRPGACCr */ +#define RCANFD_F_RPGACC(r) (0x6400 + (0x04 * (r))) + +/* Constants */ +#define RCANFD_FIFO_DEPTH 8 /* Tx FIFO depth */ +#define RCANFD_NAPI_WEIGHT 8 /* Rx poll quota */ + +#define RCANFD_NUM_CHANNELS 2 /* Two channels max */ +#define RCANFD_CHANNELS_MASK BIT((RCANFD_NUM_CHANNELS) - 1) + +#define RCANFD_GAFL_PAGENUM(entry) ((entry) / 16) +#define RCANFD_CHANNEL_NUMRULES 1 /* only one rule per channel */ + +/* Rx FIFO is a global resource of the controller. There are 8 such FIFOs + * available. Each channel gets a dedicated Rx FIFO (i.e.) the channel + * number is added to RFFIFO index. + */ +#define RCANFD_RFFIFO_IDX 0 + +/* Tx/Rx or Common FIFO is a per channel resource. Each channel has 3 Common + * FIFOs dedicated to them. Use the first (index 0) FIFO out of the 3 for Tx. + */ +#define RCANFD_CFFIFO_IDX 0 + +/* fCAN clock select register settings */ +enum rcar_canfd_fcanclk { + RCANFD_CANFDCLK = 0, /* CANFD clock */ + RCANFD_EXTCLK, /* Externally input clock */ +}; + +struct rcar_canfd_global; + +/* Channel priv data */ +struct rcar_canfd_channel { + struct can_priv can; /* Must be the first member */ + struct net_device *ndev; + struct rcar_canfd_global *gpriv; /* Controller reference */ + void __iomem *base; /* Register base address */ + struct napi_struct napi; + u8 tx_len[RCANFD_FIFO_DEPTH]; /* For net stats */ + u32 tx_head; /* Incremented on xmit */ + u32 tx_tail; /* Incremented on xmit done */ + u32 channel; /* Channel number */ + spinlock_t tx_lock; /* To protect tx path */ +}; + +/* Global priv data */ +struct rcar_canfd_global { + struct rcar_canfd_channel *ch[RCANFD_NUM_CHANNELS]; + void __iomem *base; /* Register base address */ + struct platform_device *pdev; /* Respective platform device */ + struct clk *clkp; /* Peripheral clock */ + struct clk *can_clk; /* fCAN clock */ + enum rcar_canfd_fcanclk fcan; /* CANFD or Ext clock */ + unsigned long channels_mask; /* Enabled channels mask */ + bool fdmode; /* CAN FD or Classical CAN only mode */ +}; + +/* CAN FD mode nominal rate constants */ +static const struct can_bittiming_const rcar_canfd_nom_bittiming_const = { + .name = RCANFD_DRV_NAME, + .tseg1_min = 2, + .tseg1_max = 128, + .tseg2_min = 2, + .tseg2_max = 32, + .sjw_max = 32, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* CAN FD mode data rate constants */ +static const struct can_bittiming_const rcar_canfd_data_bittiming_const = { + .name = RCANFD_DRV_NAME, + .tseg1_min = 2, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 8, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +/* Classical CAN mode bitrate constants */ +static const struct can_bittiming_const rcar_canfd_bittiming_const = { + .name = RCANFD_DRV_NAME, + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* Helper functions */ +static inline void rcar_canfd_update(u32 mask, u32 val, u32 __iomem *reg) +{ + u32 data = readl(reg); + + data &= ~mask; + data |= (val & mask); + writel(data, reg); +} + +static inline u32 rcar_canfd_read(void __iomem *base, u32 offset) +{ + return readl(base + (offset)); +} + +static inline void rcar_canfd_write(void __iomem *base, u32 offset, u32 val) +{ + writel(val, base + (offset)); +} + +static void rcar_canfd_set_bit(void __iomem *base, u32 reg, u32 val) +{ + rcar_canfd_update(val, val, base + (reg)); +} + +static void rcar_canfd_clear_bit(void __iomem *base, u32 reg, u32 val) +{ + rcar_canfd_update(val, 0, base + (reg)); +} + +static void rcar_canfd_update_bit(void __iomem *base, u32 reg, + u32 mask, u32 val) +{ + rcar_canfd_update(mask, val, base + (reg)); +} + +static void rcar_canfd_get_data(struct rcar_canfd_channel *priv, + struct canfd_frame *cf, u32 off) +{ + u32 i, lwords; + + lwords = DIV_ROUND_UP(cf->len, sizeof(u32)); + for (i = 0; i < lwords; i++) + *((u32 *)cf->data + i) = + rcar_canfd_read(priv->base, off + (i * sizeof(u32))); +} + +static void rcar_canfd_put_data(struct rcar_canfd_channel *priv, + struct canfd_frame *cf, u32 off) +{ + u32 i, lwords; + + lwords = DIV_ROUND_UP(cf->len, sizeof(u32)); + for (i = 0; i < lwords; i++) + rcar_canfd_write(priv->base, off + (i * sizeof(u32)), + *((u32 *)cf->data + i)); +} + +static void rcar_canfd_tx_failure_cleanup(struct net_device *ndev) +{ + u32 i; + + for (i = 0; i < RCANFD_FIFO_DEPTH; i++) + can_free_echo_skb(ndev, i); +} + +static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv) +{ + u32 sts, ch; + int err; + + /* Check RAMINIT flag as CAN RAM initialization takes place + * after the MCU reset + */ + err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts, + !(sts & RCANFD_GSTS_GRAMINIT), 2, 500000); + if (err) { + dev_dbg(&gpriv->pdev->dev, "global raminit failed\n"); + return err; + } + + /* Transition to Global Reset mode */ + rcar_canfd_clear_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR); + rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, + RCANFD_GCTR_GMDC_MASK, RCANFD_GCTR_GMDC_GRESET); + + /* Ensure Global reset mode */ + err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts, + (sts & RCANFD_GSTS_GRSTSTS), 2, 500000); + if (err) { + dev_dbg(&gpriv->pdev->dev, "global reset failed\n"); + return err; + } + + /* Reset Global error flags */ + rcar_canfd_write(gpriv->base, RCANFD_GERFL, 0x0); + + /* Set the controller into appropriate mode */ + if (gpriv->fdmode) + rcar_canfd_set_bit(gpriv->base, RCANFD_GRMCFG, + RCANFD_GRMCFG_RCMC); + else + rcar_canfd_clear_bit(gpriv->base, RCANFD_GRMCFG, + RCANFD_GRMCFG_RCMC); + + /* Transition all Channels to reset mode */ + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + rcar_canfd_clear_bit(gpriv->base, + RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR); + + rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch), + RCANFD_CCTR_CHMDC_MASK, + RCANFD_CCTR_CHDMC_CRESET); + + /* Ensure Channel reset mode */ + err = readl_poll_timeout((gpriv->base + RCANFD_CSTS(ch)), sts, + (sts & RCANFD_CSTS_CRSTSTS), + 2, 500000); + if (err) { + dev_dbg(&gpriv->pdev->dev, + "channel %u reset failed\n", ch); + return err; + } + } + return 0; +} + +static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv) +{ + u32 cfg, ch; + + /* Global configuration settings */ + + /* ECC Error flag Enable */ + cfg = RCANFD_GCFG_EEFE; + + if (gpriv->fdmode) + /* Truncate payload to configured message size RFPLS */ + cfg |= RCANFD_GCFG_CMPOC; + + /* Set External Clock if selected */ + if (gpriv->fcan != RCANFD_CANFDCLK) + cfg |= RCANFD_GCFG_DCS; + + rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg); + + /* Channel configuration settings */ + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + rcar_canfd_set_bit(gpriv->base, RCANFD_CCTR(ch), + RCANFD_CCTR_ERRD); + rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch), + RCANFD_CCTR_BOM_MASK, + RCANFD_CCTR_BOM_BENTRY); + } +} + +static void rcar_canfd_configure_afl_rules(struct rcar_canfd_global *gpriv, + u32 ch) +{ + u32 cfg; + int offset, start, page, num_rules = RCANFD_CHANNEL_NUMRULES; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + if (ch == 0) { + start = 0; /* Channel 0 always starts from 0th rule */ + } else { + /* Get number of Channel 0 rules and adjust */ + cfg = rcar_canfd_read(gpriv->base, RCANFD_GAFLCFG0); + start = RCANFD_GAFLCFG_GETRNC(0, cfg); + } + + /* Enable write access to entry */ + page = RCANFD_GAFL_PAGENUM(start); + rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLECTR, + (RCANFD_GAFLECTR_AFLPN(page) | + RCANFD_GAFLECTR_AFLDAE)); + + /* Write number of rules for channel */ + rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLCFG0, + RCANFD_GAFLCFG_SETRNC(ch, num_rules)); + if (gpriv->fdmode) + offset = RCANFD_F_GAFL_OFFSET; + else + offset = RCANFD_C_GAFL_OFFSET; + + /* Accept all IDs */ + rcar_canfd_write(gpriv->base, RCANFD_GAFLID(offset, start), 0); + /* IDE or RTR is not considered for matching */ + rcar_canfd_write(gpriv->base, RCANFD_GAFLM(offset, start), 0); + /* Any data length accepted */ + rcar_canfd_write(gpriv->base, RCANFD_GAFLP0(offset, start), 0); + /* Place the msg in corresponding Rx FIFO entry */ + rcar_canfd_write(gpriv->base, RCANFD_GAFLP1(offset, start), + RCANFD_GAFLP1_GAFLFDP(ridx)); + + /* Disable write access to page */ + rcar_canfd_clear_bit(gpriv->base, + RCANFD_GAFLECTR, RCANFD_GAFLECTR_AFLDAE); +} + +static void rcar_canfd_configure_rx(struct rcar_canfd_global *gpriv, u32 ch) +{ + /* Rx FIFO is used for reception */ + u32 cfg; + u16 rfdc, rfpls; + + /* Select Rx FIFO based on channel */ + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + rfdc = 2; /* b010 - 8 messages Rx FIFO depth */ + if (gpriv->fdmode) + rfpls = 7; /* b111 - Max 64 bytes payload */ + else + rfpls = 0; /* b000 - Max 8 bytes payload */ + + cfg = (RCANFD_RFCC_RFIM | RCANFD_RFCC_RFDC(rfdc) | + RCANFD_RFCC_RFPLS(rfpls) | RCANFD_RFCC_RFIE); + rcar_canfd_write(gpriv->base, RCANFD_RFCC(ridx), cfg); +} + +static void rcar_canfd_configure_tx(struct rcar_canfd_global *gpriv, u32 ch) +{ + /* Tx/Rx(Common) FIFO configured in Tx mode is + * used for transmission + * + * Each channel has 3 Common FIFO dedicated to them. + * Use the 1st (index 0) out of 3 + */ + u32 cfg; + u16 cftml, cfm, cfdc, cfpls; + + cftml = 0; /* 0th buffer */ + cfm = 1; /* b01 - Transmit mode */ + cfdc = 2; /* b010 - 8 messages Tx FIFO depth */ + if (gpriv->fdmode) + cfpls = 7; /* b111 - Max 64 bytes payload */ + else + cfpls = 0; /* b000 - Max 8 bytes payload */ + + cfg = (RCANFD_CFCC_CFTML(cftml) | RCANFD_CFCC_CFM(cfm) | + RCANFD_CFCC_CFIM | RCANFD_CFCC_CFDC(cfdc) | + RCANFD_CFCC_CFPLS(cfpls) | RCANFD_CFCC_CFTXIE); + rcar_canfd_write(gpriv->base, RCANFD_CFCC(ch, RCANFD_CFFIFO_IDX), cfg); + + if (gpriv->fdmode) + /* Clear FD mode specific control/status register */ + rcar_canfd_write(gpriv->base, + RCANFD_F_CFFDCSTS(ch, RCANFD_CFFIFO_IDX), 0); +} + +static void rcar_canfd_enable_global_interrupts(struct rcar_canfd_global *gpriv) +{ + u32 ctr; + + /* Clear any stray error interrupt flags */ + rcar_canfd_write(gpriv->base, RCANFD_GERFL, 0); + + /* Global interrupts setup */ + ctr = RCANFD_GCTR_MEIE; + if (gpriv->fdmode) + ctr |= RCANFD_GCTR_CFMPOFIE; + + rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, ctr); +} + +static void rcar_canfd_disable_global_interrupts(struct rcar_canfd_global + *gpriv) +{ + /* Disable all interrupts */ + rcar_canfd_write(gpriv->base, RCANFD_GCTR, 0); + + /* Clear any stray error interrupt flags */ + rcar_canfd_write(gpriv->base, RCANFD_GERFL, 0); +} + +static void rcar_canfd_enable_channel_interrupts(struct rcar_canfd_channel + *priv) +{ + u32 ctr, ch = priv->channel; + + /* Clear any stray error flags */ + rcar_canfd_write(priv->base, RCANFD_CERFL(ch), 0); + + /* Channel interrupts setup */ + ctr = (RCANFD_CCTR_TAIE | + RCANFD_CCTR_ALIE | RCANFD_CCTR_BLIE | + RCANFD_CCTR_OLIE | RCANFD_CCTR_BORIE | + RCANFD_CCTR_BOEIE | RCANFD_CCTR_EPIE | + RCANFD_CCTR_EWIE | RCANFD_CCTR_BEIE); + rcar_canfd_set_bit(priv->base, RCANFD_CCTR(ch), ctr); +} + +static void rcar_canfd_disable_channel_interrupts(struct rcar_canfd_channel + *priv) +{ + u32 ctr, ch = priv->channel; + + ctr = (RCANFD_CCTR_TAIE | + RCANFD_CCTR_ALIE | RCANFD_CCTR_BLIE | + RCANFD_CCTR_OLIE | RCANFD_CCTR_BORIE | + RCANFD_CCTR_BOEIE | RCANFD_CCTR_EPIE | + RCANFD_CCTR_EWIE | RCANFD_CCTR_BEIE); + rcar_canfd_clear_bit(priv->base, RCANFD_CCTR(ch), ctr); + + /* Clear any stray error flags */ + rcar_canfd_write(priv->base, RCANFD_CERFL(ch), 0); +} + +static void rcar_canfd_global_error(struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; + struct net_device_stats *stats = &ndev->stats; + u32 ch = priv->channel; + u32 gerfl, sts; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); + if ((gerfl & RCANFD_GERFL_EEF0) && (ch == 0)) { + netdev_dbg(ndev, "Ch0: ECC Error flag\n"); + stats->tx_dropped++; + } + if ((gerfl & RCANFD_GERFL_EEF1) && (ch == 1)) { + netdev_dbg(ndev, "Ch1: ECC Error flag\n"); + stats->tx_dropped++; + } + if (gerfl & RCANFD_GERFL_MES) { + sts = rcar_canfd_read(priv->base, + RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + if (sts & RCANFD_CFSTS_CFMLT) { + netdev_dbg(ndev, "Tx Message Lost flag\n"); + stats->tx_dropped++; + rcar_canfd_write(priv->base, + RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX), + sts & ~RCANFD_CFSTS_CFMLT); + } + + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + if (sts & RCANFD_RFSTS_RFMLT) { + netdev_dbg(ndev, "Rx Message Lost flag\n"); + stats->rx_dropped++; + rcar_canfd_write(priv->base, RCANFD_RFSTS(ridx), + sts & ~RCANFD_RFSTS_RFMLT); + } + } + if (gpriv->fdmode && gerfl & RCANFD_GERFL_CMPOF) { + /* Message Lost flag will be set for respective channel + * when this condition happens with counters and flags + * already updated. + */ + netdev_dbg(ndev, "global payload overflow interrupt\n"); + } + + /* Clear all global error interrupts. Only affected channels bits + * get cleared + */ + rcar_canfd_write(priv->base, RCANFD_GERFL, 0); +} + +static void rcar_canfd_error(struct net_device *ndev, u32 cerfl, + u16 txerr, u16 rxerr) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 ch = priv->channel; + + netdev_dbg(ndev, "ch erfl %x txerr %u rxerr %u\n", cerfl, txerr, rxerr); + + /* Propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + /* Channel error interrupts */ + if (cerfl & RCANFD_CERFL_BEF) { + netdev_dbg(ndev, "Bus error\n"); + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_UNSPEC; + priv->can.can_stats.bus_error++; + } + if (cerfl & RCANFD_CERFL_ADERR) { + netdev_dbg(ndev, "ACK Delimiter Error\n"); + stats->tx_errors++; + cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL; + } + if (cerfl & RCANFD_CERFL_B0ERR) { + netdev_dbg(ndev, "Bit Error (dominant)\n"); + stats->tx_errors++; + cf->data[2] |= CAN_ERR_PROT_BIT0; + } + if (cerfl & RCANFD_CERFL_B1ERR) { + netdev_dbg(ndev, "Bit Error (recessive)\n"); + stats->tx_errors++; + cf->data[2] |= CAN_ERR_PROT_BIT1; + } + if (cerfl & RCANFD_CERFL_CERR) { + netdev_dbg(ndev, "CRC Error\n"); + stats->rx_errors++; + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + if (cerfl & RCANFD_CERFL_AERR) { + netdev_dbg(ndev, "ACK Error\n"); + stats->tx_errors++; + cf->can_id |= CAN_ERR_ACK; + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + } + if (cerfl & RCANFD_CERFL_FERR) { + netdev_dbg(ndev, "Form Error\n"); + stats->rx_errors++; + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (cerfl & RCANFD_CERFL_SERR) { + netdev_dbg(ndev, "Stuff Error\n"); + stats->rx_errors++; + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + if (cerfl & RCANFD_CERFL_ALF) { + netdev_dbg(ndev, "Arbitration lost Error\n"); + priv->can.can_stats.arbitration_lost++; + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] |= CAN_ERR_LOSTARB_UNSPEC; + } + if (cerfl & RCANFD_CERFL_BLF) { + netdev_dbg(ndev, "Bus Lock Error\n"); + stats->rx_errors++; + cf->can_id |= CAN_ERR_BUSERROR; + } + if (cerfl & RCANFD_CERFL_EWF) { + netdev_dbg(ndev, "Error warning interrupt\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + if (cerfl & RCANFD_CERFL_EPF) { + netdev_dbg(ndev, "Error passive interrupt\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + if (cerfl & RCANFD_CERFL_BOEF) { + netdev_dbg(ndev, "Bus-off entry interrupt\n"); + rcar_canfd_tx_failure_cleanup(ndev); + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + cf->can_id |= CAN_ERR_BUSOFF; + } + if (cerfl & RCANFD_CERFL_OVLF) { + netdev_dbg(ndev, + "Overload Frame Transmission error interrupt\n"); + stats->tx_errors++; + cf->can_id |= CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + } + + /* Clear channel error interrupts that are handled */ + rcar_canfd_write(priv->base, RCANFD_CERFL(ch), + RCANFD_CERFL_ERR(~cerfl)); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void rcar_canfd_tx_done(struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 sts; + unsigned long flags; + u32 ch = priv->channel; + + do { + u8 unsent, sent; + + sent = priv->tx_tail % RCANFD_FIFO_DEPTH; + stats->tx_packets++; + stats->tx_bytes += priv->tx_len[sent]; + priv->tx_len[sent] = 0; + can_get_echo_skb(ndev, sent); + + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_tail++; + sts = rcar_canfd_read(priv->base, + RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + unsent = RCANFD_CFSTS_CFMC(sts); + + /* Wake producer only when there is room */ + if (unsent != RCANFD_FIFO_DEPTH) + netif_wake_queue(ndev); + + if (priv->tx_head - priv->tx_tail <= unsent) { + spin_unlock_irqrestore(&priv->tx_lock, flags); + break; + } + spin_unlock_irqrestore(&priv->tx_lock, flags); + + } while (1); + + /* Clear interrupt */ + rcar_canfd_write(priv->base, RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX), + sts & ~RCANFD_CFSTS_CFTXIF); + can_led_event(ndev, CAN_LED_EVENT_TX); +} + +static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) +{ + struct rcar_canfd_global *gpriv = dev_id; + struct net_device *ndev; + struct rcar_canfd_channel *priv; + u32 sts, gerfl; + u32 ch, ridx; + + /* Global error interrupts still indicate a condition specific + * to a channel. RxFIFO interrupt is a global interrupt. + */ + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + priv = gpriv->ch[ch]; + ndev = priv->ndev; + ridx = ch + RCANFD_RFFIFO_IDX; + + /* Global error interrupts */ + gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); + if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl))) + rcar_canfd_global_error(ndev); + + /* Handle Rx interrupts */ + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + if (likely(sts & RCANFD_RFSTS_RFIF)) { + if (napi_schedule_prep(&priv->napi)) { + /* Disable Rx FIFO interrupts */ + rcar_canfd_clear_bit(priv->base, + RCANFD_RFCC(ridx), + RCANFD_RFCC_RFIE); + __napi_schedule(&priv->napi); + } + } + } + return IRQ_HANDLED; +} + +static void rcar_canfd_state_change(struct net_device *ndev, + u16 txerr, u16 rxerr) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + enum can_state rx_state, tx_state, state = priv->can.state; + struct can_frame *cf; + struct sk_buff *skb; + + /* Handle transition from error to normal states */ + if (txerr < 96 && rxerr < 96) + state = CAN_STATE_ERROR_ACTIVE; + else if (txerr < 128 && rxerr < 128) + state = CAN_STATE_ERROR_WARNING; + + if (state != priv->can.state) { + netdev_dbg(ndev, "state: new %d, old %d: txerr %u, rxerr %u\n", + state, priv->can.state, txerr, rxerr); + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + tx_state = txerr >= rxerr ? state : 0; + rx_state = txerr <= rxerr ? state : 0; + + can_change_state(ndev, cf, tx_state, rx_state); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) +{ + struct rcar_canfd_global *gpriv = dev_id; + struct net_device *ndev; + struct rcar_canfd_channel *priv; + u32 sts, ch, cerfl; + u16 txerr, rxerr; + + /* Common FIFO is a per channel resource */ + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + priv = gpriv->ch[ch]; + ndev = priv->ndev; + + /* Channel error interrupts */ + cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); + sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); + txerr = RCANFD_CSTS_TECCNT(sts); + rxerr = RCANFD_CSTS_RECCNT(sts); + if (unlikely(RCANFD_CERFL_ERR(cerfl))) + rcar_canfd_error(ndev, cerfl, txerr, rxerr); + + /* Handle state change to lower states */ + if (unlikely((priv->can.state != CAN_STATE_ERROR_ACTIVE) && + (priv->can.state != CAN_STATE_BUS_OFF))) + rcar_canfd_state_change(ndev, txerr, rxerr); + + /* Handle Tx interrupts */ + sts = rcar_canfd_read(priv->base, + RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + if (likely(sts & RCANFD_CFSTS_CFTXIF)) + rcar_canfd_tx_done(ndev); + } + return IRQ_HANDLED; +} + +static void rcar_canfd_set_bittiming(struct net_device *dev) +{ + struct rcar_canfd_channel *priv = netdev_priv(dev); + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 cfg; + u32 ch = priv->channel; + + /* Nominal bit timing settings */ + brp = bt->brp - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + /* CAN FD only mode */ + cfg = (RCANFD_NCFG_NTSEG1(tseg1) | RCANFD_NCFG_NBRP(brp) | + RCANFD_NCFG_NSJW(sjw) | RCANFD_NCFG_NTSEG2(tseg2)); + + rcar_canfd_write(priv->base, RCANFD_CCFG(ch), cfg); + netdev_dbg(priv->ndev, "nrate: brp %u, sjw %u, tseg1 %u, tseg2 %u\n", + brp, sjw, tseg1, tseg2); + + /* Data bit timing settings */ + brp = dbt->brp - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + + cfg = (RCANFD_DCFG_DTSEG1(tseg1) | RCANFD_DCFG_DBRP(brp) | + RCANFD_DCFG_DSJW(sjw) | RCANFD_DCFG_DTSEG2(tseg2)); + + rcar_canfd_write(priv->base, RCANFD_F_DCFG(ch), cfg); + netdev_dbg(priv->ndev, "drate: brp %u, sjw %u, tseg1 %u, tseg2 %u\n", + brp, sjw, tseg1, tseg2); + } else { + /* Classical CAN only mode */ + cfg = (RCANFD_CFG_TSEG1(tseg1) | RCANFD_CFG_BRP(brp) | + RCANFD_CFG_SJW(sjw) | RCANFD_CFG_TSEG2(tseg2)); + + rcar_canfd_write(priv->base, RCANFD_CCFG(ch), cfg); + netdev_dbg(priv->ndev, + "rate: brp %u, sjw %u, tseg1 %u, tseg2 %u\n", + brp, sjw, tseg1, tseg2); + } +} + +static int rcar_canfd_start(struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + int err = -EOPNOTSUPP; + u32 sts, ch = priv->channel; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + rcar_canfd_set_bittiming(ndev); + + rcar_canfd_enable_channel_interrupts(priv); + + /* Set channel to Operational mode */ + rcar_canfd_update_bit(priv->base, RCANFD_CCTR(ch), + RCANFD_CCTR_CHMDC_MASK, RCANFD_CCTR_CHDMC_COPM); + + /* Verify channel mode change */ + err = readl_poll_timeout((priv->base + RCANFD_CSTS(ch)), sts, + (sts & RCANFD_CSTS_COMSTS), 2, 500000); + if (err) { + netdev_err(ndev, "channel %u communication state failed\n", ch); + goto fail_mode_change; + } + + /* Enable Common & Rx FIFO */ + rcar_canfd_set_bit(priv->base, RCANFD_CFCC(ch, RCANFD_CFFIFO_IDX), + RCANFD_CFCC_CFE); + rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), RCANFD_RFCC_RFE); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + +fail_mode_change: + rcar_canfd_disable_channel_interrupts(priv); + return err; +} + +static int rcar_canfd_open(struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; + int err; + + /* Peripheral clock is already enabled in probe */ + err = clk_prepare_enable(gpriv->can_clk); + if (err) { + netdev_err(ndev, "failed to enable CAN clock, error %d\n", err); + goto out_clock; + } + + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto out_can_clock; + } + + napi_enable(&priv->napi); + err = rcar_canfd_start(ndev); + if (err) + goto out_close; + netif_start_queue(ndev); + can_led_event(ndev, CAN_LED_EVENT_OPEN); + return 0; +out_close: + napi_disable(&priv->napi); + close_candev(ndev); +out_can_clock: + clk_disable_unprepare(gpriv->can_clk); +out_clock: + return err; +} + +static void rcar_canfd_stop(struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + int err; + u32 sts, ch = priv->channel; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + /* Transition to channel reset mode */ + rcar_canfd_update_bit(priv->base, RCANFD_CCTR(ch), + RCANFD_CCTR_CHMDC_MASK, RCANFD_CCTR_CHDMC_CRESET); + + /* Check Channel reset mode */ + err = readl_poll_timeout((priv->base + RCANFD_CSTS(ch)), sts, + (sts & RCANFD_CSTS_CRSTSTS), 2, 500000); + if (err) + netdev_err(ndev, "channel %u reset failed\n", ch); + + rcar_canfd_disable_channel_interrupts(priv); + + /* Disable Common & Rx FIFO */ + rcar_canfd_clear_bit(priv->base, RCANFD_CFCC(ch, RCANFD_CFFIFO_IDX), + RCANFD_CFCC_CFE); + rcar_canfd_clear_bit(priv->base, RCANFD_RFCC(ridx), RCANFD_RFCC_RFE); + + /* Set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int rcar_canfd_close(struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; + + netif_stop_queue(ndev); + rcar_canfd_stop(ndev); + napi_disable(&priv->napi); + clk_disable_unprepare(gpriv->can_clk); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + return 0; +} + +static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 sts = 0, id, dlc; + unsigned long flags; + u32 ch = priv->channel; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (cf->can_id & CAN_EFF_FLAG) { + id = cf->can_id & CAN_EFF_MASK; + id |= RCANFD_CFID_CFIDE; + } else { + id = cf->can_id & CAN_SFF_MASK; + } + + if (cf->can_id & CAN_RTR_FLAG) + id |= RCANFD_CFID_CFRTR; + + dlc = RCANFD_CFPTR_CFDLC(can_len2dlc(cf->len)); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + rcar_canfd_write(priv->base, + RCANFD_F_CFID(ch, RCANFD_CFFIFO_IDX), id); + rcar_canfd_write(priv->base, + RCANFD_F_CFPTR(ch, RCANFD_CFFIFO_IDX), dlc); + + if (can_is_canfd_skb(skb)) { + /* CAN FD frame format */ + sts |= RCANFD_CFFDCSTS_CFFDF; + if (cf->flags & CANFD_BRS) + sts |= RCANFD_CFFDCSTS_CFBRS; + + if (priv->can.state == CAN_STATE_ERROR_PASSIVE) + sts |= RCANFD_CFFDCSTS_CFESI; + } + + rcar_canfd_write(priv->base, + RCANFD_F_CFFDCSTS(ch, RCANFD_CFFIFO_IDX), sts); + + rcar_canfd_put_data(priv, cf, + RCANFD_F_CFDF(ch, RCANFD_CFFIFO_IDX, 0)); + } else { + rcar_canfd_write(priv->base, + RCANFD_C_CFID(ch, RCANFD_CFFIFO_IDX), id); + rcar_canfd_write(priv->base, + RCANFD_C_CFPTR(ch, RCANFD_CFFIFO_IDX), dlc); + rcar_canfd_put_data(priv, cf, + RCANFD_C_CFDF(ch, RCANFD_CFFIFO_IDX, 0)); + } + + priv->tx_len[priv->tx_head % RCANFD_FIFO_DEPTH] = cf->len; + can_put_echo_skb(skb, ndev, priv->tx_head % RCANFD_FIFO_DEPTH); + + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_head++; + + /* Stop the queue if we've filled all FIFO entries */ + if (priv->tx_head - priv->tx_tail >= RCANFD_FIFO_DEPTH) + netif_stop_queue(ndev); + + /* Start Tx: Write 0xff to CFPC to increment the CPU-side + * pointer for the Common FIFO + */ + rcar_canfd_write(priv->base, + RCANFD_CFPCTR(ch, RCANFD_CFFIFO_IDX), 0xff); + + spin_unlock_irqrestore(&priv->tx_lock, flags); + return NETDEV_TX_OK; +} + +static void rcar_canfd_rx_pkt(struct rcar_canfd_channel *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + u32 sts = 0, id, dlc; + u32 ch = priv->channel; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + id = rcar_canfd_read(priv->base, RCANFD_F_RFID(ridx)); + dlc = rcar_canfd_read(priv->base, RCANFD_F_RFPTR(ridx)); + + sts = rcar_canfd_read(priv->base, RCANFD_F_RFFDSTS(ridx)); + if (sts & RCANFD_RFFDSTS_RFFDF) + skb = alloc_canfd_skb(priv->ndev, &cf); + else + skb = alloc_can_skb(priv->ndev, + (struct can_frame **)&cf); + } else { + id = rcar_canfd_read(priv->base, RCANFD_C_RFID(ridx)); + dlc = rcar_canfd_read(priv->base, RCANFD_C_RFPTR(ridx)); + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cf); + } + + if (!skb) { + stats->rx_dropped++; + return; + } + + if (id & RCANFD_RFID_RFIDE) + cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = id & CAN_SFF_MASK; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if (sts & RCANFD_RFFDSTS_RFFDF) + cf->len = can_dlc2len(RCANFD_RFPTR_RFDLC(dlc)); + else + cf->len = get_can_dlc(RCANFD_RFPTR_RFDLC(dlc)); + + if (sts & RCANFD_RFFDSTS_RFESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(priv->ndev, "ESI Error\n"); + } + + if (!(sts & RCANFD_RFFDSTS_RFFDF) && (id & RCANFD_RFID_RFRTR)) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if (sts & RCANFD_RFFDSTS_RFBRS) + cf->flags |= CANFD_BRS; + + rcar_canfd_get_data(priv, cf, RCANFD_F_RFDF(ridx, 0)); + } + } else { + cf->len = get_can_dlc(RCANFD_RFPTR_RFDLC(dlc)); + if (id & RCANFD_RFID_RFRTR) + cf->can_id |= CAN_RTR_FLAG; + else + rcar_canfd_get_data(priv, cf, RCANFD_C_RFDF(ridx, 0)); + } + + /* Write 0xff to RFPC to increment the CPU-side + * pointer of the Rx FIFO + */ + rcar_canfd_write(priv->base, RCANFD_RFPCTR(ridx), 0xff); + + can_led_event(priv->ndev, CAN_LED_EVENT_RX); + + stats->rx_bytes += cf->len; + stats->rx_packets++; + netif_receive_skb(skb); +} + +static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota) +{ + struct rcar_canfd_channel *priv = + container_of(napi, struct rcar_canfd_channel, napi); + int num_pkts; + u32 sts; + u32 ch = priv->channel; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + + for (num_pkts = 0; num_pkts < quota; num_pkts++) { + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + /* Check FIFO empty condition */ + if (sts & RCANFD_RFSTS_RFEMP) + break; + + rcar_canfd_rx_pkt(priv); + + /* Clear interrupt bit */ + if (sts & RCANFD_RFSTS_RFIF) + rcar_canfd_write(priv->base, RCANFD_RFSTS(ridx), + sts & ~RCANFD_RFSTS_RFIF); + } + + /* All packets processed */ + if (num_pkts < quota) { + if (napi_complete_done(napi, num_pkts)) { + /* Enable Rx FIFO interrupts */ + rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), + RCANFD_RFCC_RFIE); + } + } + return num_pkts; +} + +static int rcar_canfd_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = rcar_canfd_start(ndev); + if (err) + return err; + netif_wake_queue(ndev); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int rcar_canfd_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct rcar_canfd_channel *priv = netdev_priv(dev); + u32 val, ch = priv->channel; + + /* Peripheral clock is already enabled in probe */ + val = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); + bec->txerr = RCANFD_CSTS_TECCNT(val); + bec->rxerr = RCANFD_CSTS_RECCNT(val); + return 0; +} + +static const struct net_device_ops rcar_canfd_netdev_ops = { + .ndo_open = rcar_canfd_open, + .ndo_stop = rcar_canfd_close, + .ndo_start_xmit = rcar_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, + u32 fcan_freq) +{ + struct platform_device *pdev = gpriv->pdev; + struct rcar_canfd_channel *priv; + struct net_device *ndev; + int err = -ENODEV; + + ndev = alloc_candev(sizeof(*priv), RCANFD_FIFO_DEPTH); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev() failed\n"); + err = -ENOMEM; + goto fail; + } + priv = netdev_priv(ndev); + + ndev->netdev_ops = &rcar_canfd_netdev_ops; + ndev->flags |= IFF_ECHO; + priv->ndev = ndev; + priv->base = gpriv->base; + priv->channel = ch; + priv->can.clock.freq = fcan_freq; + dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq); + + if (gpriv->fdmode) { + priv->can.bittiming_const = &rcar_canfd_nom_bittiming_const; + priv->can.data_bittiming_const = + &rcar_canfd_data_bittiming_const; + + /* Controller starts in CAN FD only mode */ + can_set_static_ctrlmode(ndev, CAN_CTRLMODE_FD); + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING; + } else { + /* Controller starts in Classical CAN only mode */ + priv->can.bittiming_const = &rcar_canfd_bittiming_const; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING; + } + + priv->can.do_set_mode = rcar_canfd_do_set_mode; + priv->can.do_get_berr_counter = rcar_canfd_get_berr_counter; + priv->gpriv = gpriv; + SET_NETDEV_DEV(ndev, &pdev->dev); + + netif_napi_add(ndev, &priv->napi, rcar_canfd_rx_poll, + RCANFD_NAPI_WEIGHT); + spin_lock_init(&priv->tx_lock); + devm_can_led_init(ndev); + gpriv->ch[priv->channel] = priv; + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, + "register_candev() failed, error %d\n", err); + goto fail_candev; + } + dev_info(&pdev->dev, "device registered (channel %u)\n", priv->channel); + return 0; + +fail_candev: + netif_napi_del(&priv->napi); + free_candev(ndev); +fail: + return err; +} + +static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch) +{ + struct rcar_canfd_channel *priv = gpriv->ch[ch]; + + if (priv) { + unregister_candev(priv->ndev); + netif_napi_del(&priv->napi); + free_candev(priv->ndev); + } +} + +static int rcar_canfd_probe(struct platform_device *pdev) +{ + struct resource *mem; + void __iomem *addr; + u32 sts, ch, fcan_freq; + struct rcar_canfd_global *gpriv; + struct device_node *of_child; + unsigned long channels_mask = 0; + int err, ch_irq, g_irq; + bool fdmode = true; /* CAN FD only mode - default */ + + if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) + fdmode = false; /* Classical CAN only mode */ + + of_child = of_get_child_by_name(pdev->dev.of_node, "channel0"); + if (of_child && of_device_is_available(of_child)) + channels_mask |= BIT(0); /* Channel 0 */ + + of_child = of_get_child_by_name(pdev->dev.of_node, "channel1"); + if (of_child && of_device_is_available(of_child)) + channels_mask |= BIT(1); /* Channel 1 */ + + ch_irq = platform_get_irq(pdev, 0); + if (ch_irq < 0) { + dev_err(&pdev->dev, "no Channel IRQ resource\n"); + err = ch_irq; + goto fail_dev; + } + + g_irq = platform_get_irq(pdev, 1); + if (g_irq < 0) { + dev_err(&pdev->dev, "no Global IRQ resource\n"); + err = g_irq; + goto fail_dev; + } + + /* Global controller context */ + gpriv = devm_kzalloc(&pdev->dev, sizeof(*gpriv), GFP_KERNEL); + if (!gpriv) { + err = -ENOMEM; + goto fail_dev; + } + gpriv->pdev = pdev; + gpriv->channels_mask = channels_mask; + gpriv->fdmode = fdmode; + + /* Peripheral clock */ + gpriv->clkp = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(gpriv->clkp)) { + err = PTR_ERR(gpriv->clkp); + dev_err(&pdev->dev, "cannot get peripheral clock, error %d\n", + err); + goto fail_dev; + } + + /* fCAN clock: Pick External clock. If not available fallback to + * CANFD clock + */ + gpriv->can_clk = devm_clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(gpriv->can_clk) || (clk_get_rate(gpriv->can_clk) == 0)) { + gpriv->can_clk = devm_clk_get(&pdev->dev, "canfd"); + if (IS_ERR(gpriv->can_clk)) { + err = PTR_ERR(gpriv->can_clk); + dev_err(&pdev->dev, + "cannot get canfd clock, error %d\n", err); + goto fail_dev; + } + gpriv->fcan = RCANFD_CANFDCLK; + + } else { + gpriv->fcan = RCANFD_EXTCLK; + } + fcan_freq = clk_get_rate(gpriv->can_clk); + + if (gpriv->fcan == RCANFD_CANFDCLK) + /* CANFD clock is further divided by (1/2) within the IP */ + fcan_freq /= 2; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = PTR_ERR(addr); + goto fail_dev; + } + gpriv->base = addr; + + /* Request IRQ that's common for both channels */ + err = devm_request_irq(&pdev->dev, ch_irq, + rcar_canfd_channel_interrupt, 0, + "canfd.chn", gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", + ch_irq, err); + goto fail_dev; + } + err = devm_request_irq(&pdev->dev, g_irq, + rcar_canfd_global_interrupt, 0, + "canfd.gbl", gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", + g_irq, err); + goto fail_dev; + } + + /* Enable peripheral clock for register access */ + err = clk_prepare_enable(gpriv->clkp); + if (err) { + dev_err(&pdev->dev, + "failed to enable peripheral clock, error %d\n", err); + goto fail_dev; + } + + err = rcar_canfd_reset_controller(gpriv); + if (err) { + dev_err(&pdev->dev, "reset controller failed\n"); + goto fail_clk; + } + + /* Controller in Global reset & Channel reset mode */ + rcar_canfd_configure_controller(gpriv); + + /* Configure per channel attributes */ + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + /* Configure Channel's Rx fifo */ + rcar_canfd_configure_rx(gpriv, ch); + + /* Configure Channel's Tx (Common) fifo */ + rcar_canfd_configure_tx(gpriv, ch); + + /* Configure receive rules */ + rcar_canfd_configure_afl_rules(gpriv, ch); + } + + /* Configure common interrupts */ + rcar_canfd_enable_global_interrupts(gpriv); + + /* Start Global operation mode */ + rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK, + RCANFD_GCTR_GMDC_GOPM); + + /* Verify mode change */ + err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts, + !(sts & RCANFD_GSTS_GNOPM), 2, 500000); + if (err) { + dev_err(&pdev->dev, "global operational mode failed\n"); + goto fail_mode; + } + + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq); + if (err) + goto fail_channel; + } + + platform_set_drvdata(pdev, gpriv); + dev_info(&pdev->dev, "global operational state (clk %d, fdmode %d)\n", + gpriv->fcan, gpriv->fdmode); + return 0; + +fail_channel: + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + rcar_canfd_channel_remove(gpriv, ch); +fail_mode: + rcar_canfd_disable_global_interrupts(gpriv); +fail_clk: + clk_disable_unprepare(gpriv->clkp); +fail_dev: + return err; +} + +static int rcar_canfd_remove(struct platform_device *pdev) +{ + struct rcar_canfd_global *gpriv = platform_get_drvdata(pdev); + u32 ch; + + rcar_canfd_reset_controller(gpriv); + rcar_canfd_disable_global_interrupts(gpriv); + + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]); + rcar_canfd_channel_remove(gpriv, ch); + } + + /* Enter global sleep mode */ + rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR); + clk_disable_unprepare(gpriv->clkp); + return 0; +} + +static int __maybe_unused rcar_canfd_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused rcar_canfd_resume(struct device *dev) +{ + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend, + rcar_canfd_resume); + +static const struct of_device_id rcar_canfd_of_table[] = { + { .compatible = "renesas,rcar-gen3-canfd" }, + { } +}; + +MODULE_DEVICE_TABLE(of, rcar_canfd_of_table); + +static struct platform_driver rcar_canfd_driver = { + .driver = { + .name = RCANFD_DRV_NAME, + .of_match_table = of_match_ptr(rcar_canfd_of_table), + .pm = &rcar_canfd_pm_ops, + }, + .probe = rcar_canfd_probe, + .remove = rcar_canfd_remove, +}; + +module_platform_driver(rcar_canfd_driver); + +MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CAN FD driver for Renesas R-Car SoC"); +MODULE_ALIAS("platform:" RCANFD_DRV_NAME); diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c new file mode 100644 index 000000000..5cf4171df --- /dev/null +++ b/drivers/net/can/rx-offload.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2014 David Jander, Protonic Holland + * Copyright (C) 2014-2017 Pengutronix, Marc Kleine-Budde <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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/can/dev.h> +#include <linux/can/rx-offload.h> + +struct can_rx_offload_cb { + u32 timestamp; +}; + +static inline struct can_rx_offload_cb *can_rx_offload_get_cb(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct can_rx_offload_cb) > sizeof(skb->cb)); + + return (struct can_rx_offload_cb *)skb->cb; +} + +static inline bool can_rx_offload_le(struct can_rx_offload *offload, unsigned int a, unsigned int b) +{ + if (offload->inc) + return a <= b; + else + return a >= b; +} + +static inline unsigned int can_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val) +{ + if (offload->inc) + return (*val)++; + else + return (*val)--; +} + +static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota) +{ + struct can_rx_offload *offload = container_of(napi, struct can_rx_offload, napi); + struct net_device *dev = offload->dev; + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + int work_done = 0; + + while ((work_done < quota) && + (skb = skb_dequeue(&offload->skb_queue))) { + struct can_frame *cf = (struct can_frame *)skb->data; + + work_done++; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + } + + if (work_done < quota) { + napi_complete_done(napi, work_done); + + /* Check if there was another interrupt */ + if (!skb_queue_empty(&offload->skb_queue)) + napi_reschedule(&offload->napi); + } + + can_led_event(offload->dev, CAN_LED_EVENT_RX); + + return work_done; +} + +static inline void __skb_queue_add_sort(struct sk_buff_head *head, struct sk_buff *new, + int (*compare)(struct sk_buff *a, struct sk_buff *b)) +{ + struct sk_buff *pos, *insert = (struct sk_buff *)head; + + skb_queue_reverse_walk(head, pos) { + const struct can_rx_offload_cb *cb_pos, *cb_new; + + cb_pos = can_rx_offload_get_cb(pos); + cb_new = can_rx_offload_get_cb(new); + + netdev_dbg(new->dev, + "%s: pos=0x%08x, new=0x%08x, diff=%10d, queue_len=%d\n", + __func__, + cb_pos->timestamp, cb_new->timestamp, + cb_new->timestamp - cb_pos->timestamp, + skb_queue_len(head)); + + if (compare(pos, new) < 0) + continue; + insert = pos; + break; + } + + __skb_queue_after(head, insert, new); +} + +static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b) +{ + const struct can_rx_offload_cb *cb_a, *cb_b; + + cb_a = can_rx_offload_get_cb(a); + cb_b = can_rx_offload_get_cb(b); + + /* Substract two u32 and return result as int, to keep + * difference steady around the u32 overflow. + */ + return cb_b->timestamp - cb_a->timestamp; +} + +/** + * can_rx_offload_offload_one() - Read one CAN frame from HW + * @offload: pointer to rx_offload context + * @n: number of mailbox to read + * + * The task of this function is to read a CAN frame from mailbox @n + * from the device and return the mailbox's content as a struct + * sk_buff. + * + * If the struct can_rx_offload::skb_queue exceeds the maximal queue + * length (struct can_rx_offload::skb_queue_len_max) or no skb can be + * allocated, the mailbox contents is discarded by reading it into an + * overflow buffer. This way the mailbox is marked as free by the + * driver. + * + * Return: A pointer to skb containing the CAN frame on success. + * + * NULL if the mailbox @n is empty. + * + * ERR_PTR() in case of an error + */ +static struct sk_buff * +can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n) +{ + struct sk_buff *skb = NULL, *skb_error = NULL; + struct can_rx_offload_cb *cb; + struct can_frame *cf; + int ret; + + if (likely(skb_queue_len(&offload->skb_queue) < + offload->skb_queue_len_max)) { + skb = alloc_can_skb(offload->dev, &cf); + if (unlikely(!skb)) + skb_error = ERR_PTR(-ENOMEM); /* skb alloc failed */ + } else { + skb_error = ERR_PTR(-ENOBUFS); /* skb_queue is full */ + } + + /* If queue is full or skb not available, drop by reading into + * overflow buffer. + */ + if (unlikely(skb_error)) { + struct can_frame cf_overflow; + u32 timestamp; + + ret = offload->mailbox_read(offload, &cf_overflow, + ×tamp, n); + + /* Mailbox was empty. */ + if (unlikely(!ret)) + return NULL; + + /* Mailbox has been read and we're dropping it or + * there was a problem reading the mailbox. + * + * Increment error counters in any case. + */ + offload->dev->stats.rx_dropped++; + offload->dev->stats.rx_fifo_errors++; + + /* There was a problem reading the mailbox, propagate + * error value. + */ + if (unlikely(ret < 0)) + return ERR_PTR(ret); + + return skb_error; + } + + cb = can_rx_offload_get_cb(skb); + ret = offload->mailbox_read(offload, cf, &cb->timestamp, n); + + /* Mailbox was empty. */ + if (unlikely(!ret)) { + kfree_skb(skb); + return NULL; + } + + /* There was a problem reading the mailbox, propagate error value. */ + if (unlikely(ret < 0)) { + kfree_skb(skb); + + offload->dev->stats.rx_dropped++; + offload->dev->stats.rx_fifo_errors++; + + return ERR_PTR(ret); + } + + /* Mailbox was read. */ + return skb; +} + +int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 pending) +{ + struct sk_buff_head skb_queue; + unsigned int i; + + __skb_queue_head_init(&skb_queue); + + for (i = offload->mb_first; + can_rx_offload_le(offload, i, offload->mb_last); + can_rx_offload_inc(offload, &i)) { + struct sk_buff *skb; + + if (!(pending & BIT_ULL(i))) + continue; + + skb = can_rx_offload_offload_one(offload, i); + if (IS_ERR_OR_NULL(skb)) + continue; + + __skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare); + } + + if (!skb_queue_empty(&skb_queue)) { + unsigned long flags; + u32 queue_len; + + spin_lock_irqsave(&offload->skb_queue.lock, flags); + skb_queue_splice_tail(&skb_queue, &offload->skb_queue); + spin_unlock_irqrestore(&offload->skb_queue.lock, flags); + + if ((queue_len = skb_queue_len(&offload->skb_queue)) > + (offload->skb_queue_len_max / 8)) + netdev_dbg(offload->dev, "%s: queue_len=%d\n", + __func__, queue_len); + + can_rx_offload_schedule(offload); + } + + return skb_queue_len(&skb_queue); +} +EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_timestamp); + +int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload) +{ + struct sk_buff *skb; + int received = 0; + + while (1) { + skb = can_rx_offload_offload_one(offload, 0); + if (IS_ERR(skb)) + continue; + if (!skb) + break; + + skb_queue_tail(&offload->skb_queue, skb); + received++; + } + + if (received) + can_rx_offload_schedule(offload); + + return received; +} +EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo); + +int can_rx_offload_queue_sorted(struct can_rx_offload *offload, + struct sk_buff *skb, u32 timestamp) +{ + struct can_rx_offload_cb *cb; + unsigned long flags; + + if (skb_queue_len(&offload->skb_queue) > + offload->skb_queue_len_max) { + dev_kfree_skb_any(skb); + return -ENOBUFS; + } + + cb = can_rx_offload_get_cb(skb); + cb->timestamp = timestamp; + + spin_lock_irqsave(&offload->skb_queue.lock, flags); + __skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare); + spin_unlock_irqrestore(&offload->skb_queue.lock, flags); + + can_rx_offload_schedule(offload); + + return 0; +} +EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted); + +unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, + unsigned int idx, u32 timestamp) +{ + struct net_device *dev = offload->dev; + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + u8 len; + int err; + + skb = __can_get_echo_skb(dev, idx, &len); + if (!skb) + return 0; + + err = can_rx_offload_queue_sorted(offload, skb, timestamp); + if (err) { + stats->rx_errors++; + stats->tx_fifo_errors++; + } + + return len; +} +EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb); + +int can_rx_offload_queue_tail(struct can_rx_offload *offload, + struct sk_buff *skb) +{ + if (skb_queue_len(&offload->skb_queue) > + offload->skb_queue_len_max) { + dev_kfree_skb_any(skb); + return -ENOBUFS; + } + + skb_queue_tail(&offload->skb_queue, skb); + can_rx_offload_schedule(offload); + + return 0; +} +EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail); + +static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight) +{ + offload->dev = dev; + + /* Limit queue len to 4x the weight (rounted to next power of two) */ + offload->skb_queue_len_max = 2 << fls(weight); + offload->skb_queue_len_max *= 4; + skb_queue_head_init(&offload->skb_queue); + + can_rx_offload_reset(offload); + netif_napi_add(dev, &offload->napi, can_rx_offload_napi_poll, weight); + + dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n", + __func__, offload->skb_queue_len_max); + + return 0; +} + +int can_rx_offload_add_timestamp(struct net_device *dev, struct can_rx_offload *offload) +{ + unsigned int weight; + + if (offload->mb_first > BITS_PER_LONG_LONG || + offload->mb_last > BITS_PER_LONG_LONG || !offload->mailbox_read) + return -EINVAL; + + if (offload->mb_first < offload->mb_last) { + offload->inc = true; + weight = offload->mb_last - offload->mb_first; + } else { + offload->inc = false; + weight = offload->mb_first - offload->mb_last; + } + + return can_rx_offload_init_queue(dev, offload, weight); +} +EXPORT_SYMBOL_GPL(can_rx_offload_add_timestamp); + +int can_rx_offload_add_fifo(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight) +{ + if (!offload->mailbox_read) + return -EINVAL; + + return can_rx_offload_init_queue(dev, offload, weight); +} +EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo); + +void can_rx_offload_enable(struct can_rx_offload *offload) +{ + can_rx_offload_reset(offload); + napi_enable(&offload->napi); +} +EXPORT_SYMBOL_GPL(can_rx_offload_enable); + +void can_rx_offload_del(struct can_rx_offload *offload) +{ + netif_napi_del(&offload->napi); + skb_queue_purge(&offload->skb_queue); +} +EXPORT_SYMBOL_GPL(can_rx_offload_del); + +void can_rx_offload_reset(struct can_rx_offload *offload) +{ +} +EXPORT_SYMBOL_GPL(can_rx_offload_reset); diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig new file mode 100644 index 000000000..1e65cb6c2 --- /dev/null +++ b/drivers/net/can/sja1000/Kconfig @@ -0,0 +1,103 @@ +menuconfig CAN_SJA1000 + tristate "Philips/NXP SJA1000 devices" + depends on 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. It also provides 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 + depends on HAS_IOPORT_MAP + ---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 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/) + - Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card (http://www.connecttech.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 000000000..9253aaf9e --- /dev/null +++ b/drivers/net/can/sja1000/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# 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_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 diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c new file mode 100644 index 000000000..7481c324a --- /dev/null +++ b/drivers/net/can/sja1000/ems_pci.c @@ -0,0 +1,372 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 const struct pci_device_id 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, SJA1000_MOD, 1); + + priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN); + + /* read reset-values */ + res = priv->read_reg(priv, SJA1000_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); +} + +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 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) { + 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); + dev->dev_id = i; + + 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, +}; + +module_pci_driver(ems_pci_driver); diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c new file mode 100644 index 000000000..fef5c59c0 --- /dev/null +++ b/drivers/net/can/sja1000/ems_pcmcia.c @@ -0,0 +1,325 @@ +/* + * 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, SJA1000_MOD, 1); + ems_pcmcia_write_reg(priv, SJA1000_CDR, CDR_PELICAN); + + /* read reset-values */ + if (ems_pcmcia_read_reg(priv, SJA1000_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 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); + dev->dev_id = i; + + 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); + } + + if (!card->channels) { + err = -ENODEV; + goto failure_cleanup; + } + + err = request_irq(pdev->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 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, +}; +module_pcmcia_driver(ems_pcmcia_driver); diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c new file mode 100644 index 000000000..15c00faee --- /dev/null +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -0,0 +1,396 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 const struct pci_device_id 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) + SJA1000_MOD); + status = ioread8(base_addr + + (i * KVASER_PCI_PORT_BYTES) + SJA1000_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; + + 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; + + /* 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; + + 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); + dev->dev_id = channel; + + /* 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 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 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); +} + +static struct pci_driver kvaser_pci_driver = { + .name = DRV_NAME, + .id_table = kvaser_pci_tbl, + .probe = kvaser_pci_init_one, + .remove = kvaser_pci_remove_one, +}; + +module_pci_driver(kvaser_pci_driver); diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c new file mode 100644 index 000000000..e989841b4 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pci.c @@ -0,0 +1,768 @@ +/* + * 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("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe/PCIeC miniPCI CAN cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN miniPCIe/cPCI PC/104+ PCI/104e 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_CPCI_DEVICE_ID 0x0004 /* for nextgen cPCI slot cards */ +#define PEAK_MPCI_DEVICE_ID 0x0005 /* for nextgen miniPCI slot cards */ +#define PEAK_PC_104P_DEVICE_ID 0x0006 /* PCAN-PC/104+ cards */ +#define PEAK_PCI_104E_DEVICE_ID 0x0007 /* PCAN-PCI/104 Express cards */ +#define PEAK_MPCIE_DEVICE_ID 0x0008 /* The miniPCIe slot cards */ +#define PEAK_PCIE_OEM_ID 0x0009 /* PCAN-PCI Express OEM */ +#define PEAK_PCIEC34_DEVICE_ID 0x000A /* PCAN-PCI Express 34 (one channel) */ + +#define PEAK_PCI_CHAN_MAX 4 + +static const u16 peak_pci_icr_masks[PEAK_PCI_CHAN_MAX] = { + 0x02, 0x01, 0x40, 0x80 +}; + +static const struct pci_device_id 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,}, + {PEAK_PCI_VENDOR_ID, PEAK_MPCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PC_104P_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCI_104E_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_CPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_OEM_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,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIEC34_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) +{ + 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 == SJA1000_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) + 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 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, *prev_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"); + err = -ENOMEM; + 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"); + err = -ENOMEM; + 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); + usleep_range(5000, 6000); + /* 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); + dev->dev_id = i; + + /* 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 || + pdev->device == PEAK_PCIEC34_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 = prev_dev) { + priv = netdev_priv(dev); + chan = priv->priv; + prev_dev = chan->prev_dev; + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + /* 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); + + /* pci_xxx_config_word() return positive PCIBIOS_xxx error codes while + * the probe() function must return a negative errno in case of failure + * (err is unchanged if negative) */ + return pcibios_err_to_errno(err); +} + +static void 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) { + struct net_device *prev_dev = chan->prev_dev; + + dev_info(&pdev->dev, "removing device %s\n", dev->name); + /* do that only for first channel */ + if (!prev_dev && chan->pciec_card) + peak_pciec_remove(chan->pciec_card); + unregister_sja1000dev(dev); + free_sja1000dev(dev); + dev = prev_dev; + + if (!dev) + 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); +} + +static struct pci_driver peak_pci_driver = { + .name = DRV_NAME, + .id_table = peak_pci_tbl, + .probe = peak_pci_probe, + .remove = peak_pci_remove, +}; + +module_pci_driver(peak_pci_driver); diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c new file mode 100644 index 000000000..179bfcd54 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -0,0 +1,742 @@ +/* + * 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 == SJA1000_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(struct timer_list *t) +{ + struct pcan_pccard *card = from_timer(card, t, led_timer); + 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; + + strlcpy(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, SJA1000_MOD, 1); + pcan_write_canreg(priv, SJA1000_CDR, CDR_PELICAN); + + /* read reset-values */ + if (pcan_read_canreg(priv, SJA1000_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 */ + usleep_range(2000, 3000); + + 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); + netdev->dev_id = i; + + 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 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) { + 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) { + err = -ENOMEM; + goto probe_err_4; + } + + /* init the timer which controls the leds */ + timer_setup(&card->led_timer, pcan_led_timer, 0); + + /* 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, +}; +module_pcmcia_driver(pcan_driver); diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c new file mode 100644 index 000000000..f8ff25c8e --- /dev/null +++ b/drivers/net/can/sja1000/plx_pci.c @@ -0,0 +1,716 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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, " + "Marathon CAN-bus-PCIe, " + "TEWS TECHNOLOGIES TPMC810, " + "esd CAN-PCI/CPCI/PCI104/200, " + "esd CAN-PCI/PMC/266, " + "esd CAN-PCIe/2000, " + "Connect Tech Inc. CANpro/104-Plus Opto (CRG001), " + "IXXAT PC-I 04/PCI, " + "ELCUS CAN-200-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 CAN200PCI_DEVICE_ID 0x9030 +#define CAN200PCI_VENDOR_ID 0x10b5 +#define CAN200PCI_SUB_DEVICE_ID 0x0301 +#define CAN200PCI_SUB_VENDOR_ID 0xe1c5 + +#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 MARATHON_PCIE_DEVICE_ID 0x3432 + +#define TEWS_PCI_VENDOR_ID 0x1498 +#define TEWS_PCI_DEVICE_ID_TMPC810 0x032A + +#define CTI_PCI_VENDOR_ID 0x12c4 +#define CTI_PCI_DEVICE_ID_CRG001 0x0900 + +#define MOXA_PCI_VENDOR_ID 0x1393 +#define MOXA_PCI_DEVICE_ID 0x0100 + +static void plx_pci_reset_common(struct pci_dev *pdev); +static void plx9056_pci_reset_common(struct pci_dev *pdev); +static void plx_pci_reset_marathon_pci(struct pci_dev *pdev); +static void plx_pci_reset_marathon_pcie(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 = { + "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 = { + "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 = { + "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 = { + "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 = { + "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 = { + "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_pci = { + "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_pci + /* based on PLX9052 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_marathon_pcie = { + "Marathon CAN-bus-PCIe", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {3, 0x80, 0x00} }, + &plx_pci_reset_marathon_pcie + /* based on PEX8311 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_tews = { + "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 struct plx_pci_card_info plx_pci_card_info_cti = { + "Connect Tech Inc. CANpro/104-Plus Opto (CRG001)", 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 struct plx_pci_card_info plx_pci_card_info_elcus = { + "Eclus CAN-200-PCI", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {1, 0x00, 0x00}, { {2, 0x00, 0x80}, {3, 0x00, 0x80} }, + &plx_pci_reset_common + /* based on PLX9030 */ +}; + +static struct plx_pci_card_info plx_pci_card_info_moxa = { + "MOXA", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {0, 0x00, 0x80}, {1, 0x00, 0x80} }, + &plx_pci_reset_common + /* based on PLX9052 */ +}; + +static const struct pci_device_id 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_pci + }, + { + /* Marathon CAN-bus-PCIe card */ + PCI_VENDOR_ID_PLX, MARATHON_PCIE_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_marathon_pcie + }, + { + /* 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 + }, + { + /* Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card */ + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + CTI_PCI_VENDOR_ID, CTI_PCI_DEVICE_ID_CRG001, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_cti + }, + { + /* Elcus CAN-200-PCI */ + CAN200PCI_VENDOR_ID, CAN200PCI_DEVICE_ID, + CAN200PCI_SUB_VENDOR_ID, CAN200PCI_SUB_DEVICE_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_elcus + }, + { + /* moxa */ + MOXA_PCI_VENDOR_ID, MOXA_PCI_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_moxa + }, + { 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, SJA1000_SR) == REG_SR_BASICCAN_INITIAL) && + (priv->read_reg(priv, SJA1000_IR) == REG_IR_BASICCAN_INITIAL)) + flag = 1; + + /* Bring the SJA1000 into the PeliCAN mode*/ + priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN); + + /* + * Check registers after reset in the PeliCAN mode. + * See states on p. 23 of the Datasheet. + */ + if (priv->read_reg(priv, SJA1000_MOD) == REG_MOD_PELICAN_INITIAL && + priv->read_reg(priv, SJA1000_SR) == REG_SR_PELICAN_INITIAL && + priv->read_reg(priv, SJA1000_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 CAN-bus-PCI card */ +static void plx_pci_reset_marathon_pci(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); + } + } +} + +/* Special reset function for Marathon CAN-bus-PCIe card */ +static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev) +{ + void __iomem *addr; + void __iomem *reset_addr; + int i; + + plx9056_pci_reset_common(pdev); + + for (i = 0; i < 2; i++) { + struct plx_pci_channel_map *chan_map = + &plx_pci_card_info_marathon_pcie.chan_map_tbl[i]; + addr = pci_iomap(pdev, chan_map->bar, chan_map->size); + if (!addr) { + dev_err(&pdev->dev, "Failed to remap reset " + "space %d (BAR%d)\n", i, chan_map->bar); + } else { + /* reset the SJA1000 chip */ + #define MARATHON_PCIE_RESET_OFFSET 32 + reset_addr = addr + chan_map->offset + + MARATHON_PCIE_RESET_OFFSET; + iowrite8(0x1, reset_addr); + udelay(100); + pci_iounmap(pdev, 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 && + pdev->device != MARATHON_PCIE_DEVICE_ID) + 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); +} + +/* + * Probe PLX90xx based device for the SJA1000 chips and register each + * available CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int 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) { + 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); + dev->dev_id = i; + + /* 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 && + pdev->device != MARATHON_PCIE_DEVICE_ID) { + 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, +}; + +module_pci_driver(plx_pci_driver); diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c new file mode 100644 index 000000000..9f107798f --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.c @@ -0,0 +1,708 @@ +/* + * 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 <linux/can/led.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 const 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, SJA1000_CMR, val); + priv->read_reg(priv, SJA1000_SR); + spin_unlock_irqrestore(&priv->cmdreg_lock, flags); +} + +static int sja1000_is_absent(struct sja1000_priv *priv) +{ + return (priv->read_reg(priv, SJA1000_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)) { + netdev_err(dev, "probing failed\n"); + 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, SJA1000_MOD); + int i; + + /* disable interrupts */ + priv->write_reg(priv, SJA1000_IER, IRQ_OFF); + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if (status & MOD_RM) { + priv->can.state = CAN_STATE_STOPPED; + return; + } + + /* reset chip */ + priv->write_reg(priv, SJA1000_MOD, MOD_RM); + udelay(10); + status = priv->read_reg(priv, SJA1000_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, SJA1000_MOD); + u8 mod_reg_val = 0x00; + 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, SJA1000_IER, IRQ_ALL); + else + priv->write_reg(priv, SJA1000_IER, + IRQ_ALL & ~IRQ_BEI); + return; + } + + /* set chip to normal mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mod_reg_val |= MOD_LOM; + if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK) + mod_reg_val |= MOD_STM; + priv->write_reg(priv, SJA1000_MOD, mod_reg_val); + + udelay(10); + + status = priv->read_reg(priv, SJA1000_MOD); + } + + netdev_err(dev, "setting SJA1000 into normal mode failed!\n"); +} + +/* + * 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, SJA1000_CDR, priv->cdr | CDR_PELICAN); + + /* set acceptance filter (accept all) */ + priv->write_reg(priv, SJA1000_ACCC0, 0x00); + priv->write_reg(priv, SJA1000_ACCC1, 0x00); + priv->write_reg(priv, SJA1000_ACCC2, 0x00); + priv->write_reg(priv, SJA1000_ACCC3, 0x00); + + priv->write_reg(priv, SJA1000_ACCM0, 0xFF); + priv->write_reg(priv, SJA1000_ACCM1, 0xFF); + priv->write_reg(priv, SJA1000_ACCM2, 0xFF); + priv->write_reg(priv, SJA1000_ACCM3, 0xFF); + + priv->write_reg(priv, SJA1000_OCR, priv->ocr | OCR_MODE_NORMAL); +} + +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); + + /* Initialize chip if uninitialized at this stage */ + if (!(priv->read_reg(priv, SJA1000_CDR) & CDR_PELICAN)) + chipset_init(dev); + + /* Clear error counters and error code capture */ + priv->write_reg(priv, SJA1000_TXERR, 0x0); + priv->write_reg(priv, SJA1000_RXERR, 0x0); + priv->read_reg(priv, SJA1000_ECC); + + /* clear interrupt flags */ + priv->read_reg(priv, SJA1000_IR); + + /* leave reset mode */ + set_normal_mode(dev); +} + +static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) +{ + 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, SJA1000_BTR0, btr0); + priv->write_reg(priv, SJA1000_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, SJA1000_TXERR); + bec->rxerr = priv->read_reg(priv, SJA1000_RXERR); + + return 0; +} + +/* + * 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; + u8 cmd_reg_val = 0x00; + 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 |= SJA1000_FI_RTR; + + if (id & CAN_EFF_FLAG) { + fi |= SJA1000_FI_FF; + dreg = SJA1000_EFF_BUF; + priv->write_reg(priv, SJA1000_FI, fi); + priv->write_reg(priv, SJA1000_ID1, (id & 0x1fe00000) >> 21); + priv->write_reg(priv, SJA1000_ID2, (id & 0x001fe000) >> 13); + priv->write_reg(priv, SJA1000_ID3, (id & 0x00001fe0) >> 5); + priv->write_reg(priv, SJA1000_ID4, (id & 0x0000001f) << 3); + } else { + dreg = SJA1000_SFF_BUF; + priv->write_reg(priv, SJA1000_FI, fi); + priv->write_reg(priv, SJA1000_ID1, (id & 0x000007f8) >> 3); + priv->write_reg(priv, SJA1000_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); + + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + cmd_reg_val |= CMD_AT; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + cmd_reg_val |= CMD_SRR; + else + cmd_reg_val |= CMD_TR; + + sja1000_write_cmdreg(priv, cmd_reg_val); + + 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, SJA1000_FI); + + if (fi & SJA1000_FI_FF) { + /* extended frame format (EFF) */ + dreg = SJA1000_EFF_BUF; + id = (priv->read_reg(priv, SJA1000_ID1) << 21) + | (priv->read_reg(priv, SJA1000_ID2) << 13) + | (priv->read_reg(priv, SJA1000_ID3) << 5) + | (priv->read_reg(priv, SJA1000_ID4) >> 3); + id |= CAN_EFF_FLAG; + } else { + /* standard frame format (SFF) */ + dreg = SJA1000_SFF_BUF; + id = (priv->read_reg(priv, SJA1000_ID1) << 3) + | (priv->read_reg(priv, SJA1000_ID2) >> 5); + } + + cf->can_dlc = get_can_dlc(fi & 0x0F); + if (fi & SJA1000_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); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(dev, CAN_LED_EVENT_RX); +} + +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; + enum can_state rx_state, tx_state; + unsigned int rxerr, txerr; + uint8_t ecc, alc; + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + txerr = priv->read_reg(priv, SJA1000_TXERR); + rxerr = priv->read_reg(priv, SJA1000_RXERR); + + cf->data[6] = txerr; + cf->data[7] = rxerr; + + 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; + 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, SJA1000_ECC); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + /* set error type */ + 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: + break; + } + + /* set error location */ + cf->data[3] = ecc & ECC_SEG; + + /* 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 (state == CAN_STATE_ERROR_PASSIVE) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_PASSIVE; + } + if (isrc & IRQ_ALI) { + /* arbitration lost interrupt */ + netdev_dbg(dev, "arbitration lost interrupt\n"); + alc = priv->read_reg(priv, SJA1000_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) { + tx_state = txerr >= rxerr ? state : 0; + rx_state = txerr <= rxerr ? state : 0; + + can_change_state(dev, cf, tx_state, rx_state); + + if(state == CAN_STATE_BUS_OFF) + can_bus_off(dev); + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + 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; + + if (priv->pre_irq) + priv->pre_irq(priv); + + /* Shared interrupts and IRQ off? */ + if (priv->read_reg(priv, SJA1000_IER) == IRQ_OFF) + goto out; + + while ((isrc = priv->read_reg(priv, SJA1000_IR)) && + (n < SJA1000_MAX_IRQ)) { + + status = priv->read_reg(priv, SJA1000_SR); + /* check for absent controller due to hw unplug */ + if (status == 0xFF && sja1000_is_absent(priv)) + goto out; + + if (isrc & IRQ_WUI) + netdev_warn(dev, "wakeup interrupt\n"); + + if (isrc & IRQ_TI) { + /* transmission buffer released */ + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT && + !(status & SR_TCS)) { + stats->tx_errors++; + can_free_echo_skb(dev, 0); + } else { + /* transmission complete */ + stats->tx_bytes += + priv->read_reg(priv, SJA1000_FI) & 0xf; + stats->tx_packets++; + can_get_echo_skb(dev, 0); + } + netif_wake_queue(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + } + if (isrc & IRQ_RI) { + /* receive interrupt */ + while (status & SR_RBS) { + sja1000_rx(dev); + status = priv->read_reg(priv, SJA1000_SR); + /* check for absent controller */ + if (status == 0xFF && sja1000_is_absent(priv)) + goto out; + } + } + if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { + /* error interrupt */ + if (sja1000_err(dev, isrc, status)) + break; + } + n++; + } +out: + 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); + + can_led_event(dev, CAN_LED_EVENT_OPEN); + + 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); + + can_led_event(dev, CAN_LED_EVENT_STOP); + + 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_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_ONE_SHOT | + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_PRESUME_ACK; + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +int register_sja1000dev(struct net_device *dev) +{ + int ret; + + 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); + + ret = register_candev(dev); + + if (!ret) + devm_can_led_init(dev); + + return ret; +} +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 000000000..9d46398f8 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.h @@ -0,0 +1,182 @@ +/* + * 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 SJA1000_MOD 0x00 +#define SJA1000_CMR 0x01 +#define SJA1000_SR 0x02 +#define SJA1000_IR 0x03 +#define SJA1000_IER 0x04 +#define SJA1000_ALC 0x0B +#define SJA1000_ECC 0x0C +#define SJA1000_EWL 0x0D +#define SJA1000_RXERR 0x0E +#define SJA1000_TXERR 0x0F +#define SJA1000_ACCC0 0x10 +#define SJA1000_ACCC1 0x11 +#define SJA1000_ACCC2 0x12 +#define SJA1000_ACCC3 0x13 +#define SJA1000_ACCM0 0x14 +#define SJA1000_ACCM1 0x15 +#define SJA1000_ACCM2 0x16 +#define SJA1000_ACCM3 0x17 +#define SJA1000_RMC 0x1D +#define SJA1000_RBSA 0x1E + +/* Common registers - manual section 6.5 */ +#define SJA1000_BTR0 0x06 +#define SJA1000_BTR1 0x07 +#define SJA1000_OCR 0x08 +#define SJA1000_CDR 0x1F + +#define SJA1000_FI 0x10 +#define SJA1000_SFF_BUF 0x13 +#define SJA1000_EFF_BUF 0x15 + +#define SJA1000_FI_FF 0x80 +#define SJA1000_FI_RTR 0x40 + +#define SJA1000_ID1 0x11 +#define SJA1000_ID2 0x12 +#define SJA1000_ID3 0x13 +#define SJA1000_ID4 0x14 + +#define SJA1000_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 */ + 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 000000000..1a2ae6ce8 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_isa.c @@ -0,0 +1,323 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 irq[MAXDEV]; +static int clk[MAXDEV]; +static unsigned char cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; +static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; +static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */ + +module_param_hw_array(port, ulong, ioport, NULL, 0444); +MODULE_PARM_DESC(port, "I/O port number"); + +module_param_hw_array(mem, ulong, iomem, NULL, 0444); +MODULE_PARM_DESC(mem, "I/O memory address"); + +module_param_hw_array(indirect, int, ioport, NULL, 0444); +MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); + +module_param_hw_array(irq, int, irq, NULL, 0444); +MODULE_PARM_DESC(irq, "IRQ number"); + +module_param_array(clk, int, NULL, 0444); +MODULE_PARM_DESC(clk, "External oscillator clock frequency " + "(default=16000000 [16 MHz])"); + +module_param_array(cdr, byte, NULL, 0444); +MODULE_PARM_DESC(cdr, "Clock divider register " + "(default=0x48 [CDR_CBP | CDR_CLK_OFF])"); + +module_param_array(ocr, byte, NULL, 0444); +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 flags, base = (unsigned long)priv->reg_base; + u8 readval; + + spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); + outb(reg, base); + readval = inb(base + 1); + spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags); + + return readval; +} + +static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv, + int reg, u8 val) +{ + unsigned long flags, base = (unsigned long)priv->reg_base; + + spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); + outb(reg, base); + outb(val, base + 1); + spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags); +} + +static int 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; + spin_lock_init(&indirect_lock[idx]); + } 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; + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + dev->dev_id = idx; + + 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 sja1000_isa_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sja1000_priv *priv = netdev_priv(dev); + int idx = pdev->id; + + unregister_sja1000dev(dev); + + 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 = sja1000_isa_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +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_platform.c b/drivers/net/can/sja1000/sja1000_platform.c new file mode 100644 index 000000000..dc9c6db96 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -0,0 +1,329 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_platform" +#define SP_CAN_CLOCK (16000000 / 2) + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_LICENSE("GPL v2"); + +struct sja1000_of_data { + size_t priv_sz; + int (*init)(struct sja1000_priv *priv, struct device_node *of); +}; + +struct technologic_priv { + spinlock_t io_lock; +}; + +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 u8 sp_technologic_read_reg16(const struct sja1000_priv *priv, int reg) +{ + struct technologic_priv *tp = priv->priv; + unsigned long flags; + u8 val; + + spin_lock_irqsave(&tp->io_lock, flags); + iowrite16(reg, priv->reg_base + 0); + val = ioread16(priv->reg_base + 2); + spin_unlock_irqrestore(&tp->io_lock, flags); + + return val; +} + +static void sp_technologic_write_reg16(const struct sja1000_priv *priv, + int reg, u8 val) +{ + struct technologic_priv *tp = priv->priv; + unsigned long flags; + + spin_lock_irqsave(&tp->io_lock, flags); + iowrite16(reg, priv->reg_base + 0); + iowrite16(val, priv->reg_base + 2); + spin_unlock_irqrestore(&tp->io_lock, flags); +} + +static int sp_technologic_init(struct sja1000_priv *priv, struct device_node *of) +{ + struct technologic_priv *tp = priv->priv; + + priv->read_reg = sp_technologic_read_reg16; + priv->write_reg = sp_technologic_write_reg16; + spin_lock_init(&tp->io_lock); + + return 0; +} + +static void sp_populate(struct sja1000_priv *priv, + struct sja1000_platform_data *pdata, + unsigned long resource_mem_flags) +{ + /* 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 (resource_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; + } +} + +static void sp_populate_of(struct sja1000_priv *priv, struct device_node *of) +{ + int err; + u32 prop; + + err = of_property_read_u32(of, "reg-io-width", &prop); + if (err) + prop = 1; /* 8 bit is default */ + + switch (prop) { + case 4: + priv->read_reg = sp_read_reg32; + priv->write_reg = sp_write_reg32; + break; + case 2: + priv->read_reg = sp_read_reg16; + priv->write_reg = sp_write_reg16; + break; + case 1: /* fallthrough */ + default: + priv->read_reg = sp_read_reg8; + priv->write_reg = sp_write_reg8; + } + + err = of_property_read_u32(of, "nxp,external-clock-frequency", &prop); + if (!err) + priv->can.clock.freq = prop / 2; + else + priv->can.clock.freq = SP_CAN_CLOCK; /* default */ + + err = of_property_read_u32(of, "nxp,tx-output-mode", &prop); + if (!err) + priv->ocr |= prop & OCR_MODE_MASK; + else + priv->ocr |= OCR_MODE_NORMAL; /* default */ + + err = of_property_read_u32(of, "nxp,tx-output-config", &prop); + if (!err) + priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK; + else + priv->ocr |= OCR_TX0_PULLDOWN; /* default */ + + err = of_property_read_u32(of, "nxp,clock-out-frequency", &prop); + if (!err && 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 */ + } + + if (!of_property_read_bool(of, "nxp,no-comparator-bypass")) + priv->cdr |= CDR_CBP; /* default */ +} + +static struct sja1000_of_data technologic_data = { + .priv_sz = sizeof(struct technologic_priv), + .init = sp_technologic_init, +}; + +static const struct of_device_id sp_of_table[] = { + { .compatible = "nxp,sja1000", .data = NULL, }, + { .compatible = "technologic,sja1000", .data = &technologic_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sp_of_table); + +static int sp_probe(struct platform_device *pdev) +{ + int err, irq = 0; + void __iomem *addr; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource *res_mem, *res_irq = NULL; + struct sja1000_platform_data *pdata; + struct device_node *of = pdev->dev.of_node; + const struct of_device_id *of_id; + const struct sja1000_of_data *of_data = NULL; + size_t priv_sz = 0; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata && !of) { + dev_err(&pdev->dev, "No platform data provided!\n"); + return -ENODEV; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + if (!devm_request_mem_region(&pdev->dev, res_mem->start, + resource_size(res_mem), DRV_NAME)) + return -EBUSY; + + addr = devm_ioremap_nocache(&pdev->dev, res_mem->start, + resource_size(res_mem)); + if (!addr) + return -ENOMEM; + + if (of) + irq = irq_of_parse_and_map(of, 0); + else + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + if (!irq && !res_irq) + return -ENODEV; + + of_id = of_match_device(sp_of_table, &pdev->dev); + if (of_id && of_id->data) { + of_data = of_id->data; + priv_sz = of_data->priv_sz; + } + + dev = alloc_sja1000dev(priv_sz); + if (!dev) + return -ENOMEM; + priv = netdev_priv(dev); + + if (res_irq) { + irq = res_irq->start; + priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK; + if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE) + priv->irq_flags |= IRQF_SHARED; + } else { + priv->irq_flags = IRQF_SHARED; + } + + dev->irq = irq; + priv->reg_base = addr; + + if (of) { + sp_populate_of(priv, of); + + if (of_data && of_data->init) { + err = of_data->init(priv, of); + if (err) + goto exit_free; + } + } else { + sp_populate(priv, pdata, res_mem->flags); + } + + platform_set_drvdata(pdev, 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); + return err; +} + +static int sp_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + + return 0; +} + +static struct platform_driver sp_driver = { + .probe = sp_probe, + .remove = sp_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = sp_of_table, + }, +}; + +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 000000000..79572457a --- /dev/null +++ b/drivers/net/can/sja1000/tscan1.c @@ -0,0 +1,206 @@ +/* + * 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[] = { + 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 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 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 = tscan1_remove, + .driver = { + .name = "tscan1", + }, +}; + +module_isa_driver(tscan1_isa_driver, TSCAN1_MAXDEV); diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c new file mode 100644 index 000000000..f99cd9450 --- /dev/null +++ b/drivers/net/can/slcan.c @@ -0,0 +1,789 @@ +/* + * 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, see 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/workqueue.h> +#include <linux/can.h> +#include <linux/can/skb.h> + +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) + +#define SLC_CMD_LEN 1 +#define SLC_SFF_ID_LEN 3 +#define SLC_EFF_ID_LEN 8 + +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; + struct work_struct tx_work; /* Flushes transmit buffer */ + + /* 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, tmp; + u32 tmpid; + char *cmd = sl->rbuff; + + memset(&cf, 0, sizeof(cf)); + + switch (*cmd) { + case 'r': + cf.can_id = CAN_RTR_FLAG; + /* fallthrough */ + case 't': + /* store dlc ASCII value and terminate SFF CAN ID string */ + cf.can_dlc = sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN]; + sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN] = 0; + /* point to payload data behind the dlc */ + cmd += SLC_CMD_LEN + SLC_SFF_ID_LEN + 1; + break; + case 'R': + cf.can_id = CAN_RTR_FLAG; + /* fallthrough */ + case 'T': + cf.can_id |= CAN_EFF_FLAG; + /* store dlc ASCII value and terminate EFF CAN ID string */ + cf.can_dlc = sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN]; + sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN] = 0; + /* point to payload data behind the dlc */ + cmd += SLC_CMD_LEN + SLC_EFF_ID_LEN + 1; + break; + default: + return; + } + + if (kstrtou32(sl->rbuff + SLC_CMD_LEN, 16, &tmpid)) + return; + + cf.can_id |= tmpid; + + /* get can_dlc from sanitized ASCII value */ + if (cf.can_dlc >= '0' && cf.can_dlc < '9') + cf.can_dlc -= '0'; + else + return; + + /* RTR frames may have a dlc > 0 but they never have any data bytes */ + if (!(cf.can_id & CAN_RTR_FLAG)) { + for (i = 0; i < cf.can_dlc; i++) { + tmp = hex_to_bin(*cmd++); + if (tmp < 0) + return; + cf.data[i] = (tmp << 4); + tmp = hex_to_bin(*cmd++); + if (tmp < 0) + return; + cf.data[i] |= tmp; + } + } + + skb = dev_alloc_skb(sizeof(struct can_frame) + + sizeof(struct can_skb_priv)); + if (!skb) + return; + + skb->dev = sl->dev; + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = sl->dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + skb_put_data(skb, &cf, sizeof(struct can_frame)); + + sl->dev->stats.rx_packets++; + sl->dev->stats.rx_bytes += cf.can_dlc; + netif_rx_ni(skb); +} + +/* 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, i; + unsigned char *pos; + unsigned char *endpos; + canid_t id = cf->can_id; + + pos = sl->xbuff; + + if (cf->can_id & CAN_RTR_FLAG) + *pos = 'R'; /* becomes 'r' in standard frame format (SFF) */ + else + *pos = 'T'; /* becomes 't' in standard frame format (SSF) */ + + /* determine number of chars for the CAN-identifier */ + if (cf->can_id & CAN_EFF_FLAG) { + id &= CAN_EFF_MASK; + endpos = pos + SLC_EFF_ID_LEN; + } else { + *pos |= 0x20; /* convert R/T to lower case for SFF */ + id &= CAN_SFF_MASK; + endpos = pos + SLC_SFF_ID_LEN; + } + + /* build 3 (SFF) or 8 (EFF) digit CAN identifier */ + pos++; + while (endpos >= pos) { + *endpos-- = hex_asc_upper[id & 0xf]; + id >>= 4; + } + + pos += (cf->can_id & CAN_EFF_FLAG) ? SLC_EFF_ID_LEN : SLC_SFF_ID_LEN; + + *pos++ = cf->can_dlc + '0'; + + /* RTR frames may have a dlc > 0 but they never have any data bytes */ + if (!(cf->can_id & CAN_RTR_FLAG)) { + for (i = 0; i < cf->can_dlc; i++) + pos = hex_byte_pack_upper(pos, cf->data[i]); + } + + *pos++ = '\r'; + + /* 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, pos - sl->xbuff); + sl->xleft = (pos - sl->xbuff) - actual; + sl->xhead = sl->xbuff + actual; + sl->dev->stats.tx_bytes += cf->can_dlc; +} + +/* Write out any remaining transmit buffer. Scheduled when tty is writable */ +static void slcan_transmit(struct work_struct *work) +{ + struct slcan *sl = container_of(work, struct slcan, tx_work); + int actual; + + spin_lock_bh(&sl->lock); + /* First make sure we're connected. */ + if (!sl->tty || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) { + spin_unlock_bh(&sl->lock); + 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, &sl->tty->flags); + spin_unlock_bh(&sl->lock); + netif_wake_queue(sl->dev); + return; + } + + actual = sl->tty->ops->write(sl->tty, sl->xhead, sl->xleft); + sl->xleft -= actual; + sl->xhead += actual; + spin_unlock_bh(&sl->lock); +} + +/* + * Called by the driver when there's room for more data. + * Schedule the transmit. + */ +static void slcan_write_wakeup(struct tty_struct *tty) +{ + struct slcan *sl; + + rcu_read_lock(); + sl = rcu_dereference(tty->disc_data); + if (!sl) + goto out; + + schedule_work(&sl->tx_work); +out: + rcu_read_unlock(); +} + +/* 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 != CAN_MTU) + 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; + + slcan_devs[i] = NULL; +} + +static int slcan_change_mtu(struct net_device *dev, int new_mtu) +{ + return -EINVAL; +} + +static const struct net_device_ops slc_netdev_ops = { + .ndo_open = slc_open, + .ndo_stop = slc_close, + .ndo_start_xmit = slc_xmit, + .ndo_change_mtu = slcan_change_mtu, +}; + +static void slc_setup(struct net_device *dev) +{ + dev->netdev_ops = &slc_netdev_ops; + dev->needs_free_netdev = true; + dev->priv_destructor = slc_free_netdev; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; + + dev->mtu = CAN_MTU; + 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(void) +{ + 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, NET_NAME_UNKNOWN, 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); + INIT_WORK(&sl->tx_work, slcan_transmit); + 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(); + 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); + slc_free_netdev(sl->dev); + /* do not call free_netdev before rtnl_unlock */ + rtnl_unlock(); + free_netdev(sl->dev); + return err; + +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; + + spin_lock_bh(&sl->lock); + rcu_assign_pointer(tty->disc_data, NULL); + sl->tty = NULL; + spin_unlock_bh(&sl->lock); + + synchronize_rcu(); + flush_work(&sl->tx_work); + + /* 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 */ + + pr_info("slcan: serial line CAN interface driver\n"); + pr_info("slcan: %d dynamic interface channels.\n", maxdev); + + slcan_devs = kcalloc(maxdev, sizeof(struct net_device *), 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); + } + + 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 000000000..96b6fe158 --- /dev/null +++ b/drivers/net/can/softing/Kconfig @@ -0,0 +1,30 @@ +config CAN_SOFTING + tristate "Softing Gmbh CAN generic support" + depends on 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 000000000..a23da492d --- /dev/null +++ b/drivers/net/can/softing/Makefile @@ -0,0 +1,4 @@ + +softing-y := softing_main.o softing_fw.o +obj-$(CONFIG_CAN_SOFTING) += softing.o +obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h new file mode 100644 index 000000000..2893007ea --- /dev/null +++ b/drivers/net/can/softing/softing.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * 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; +}; + +int softing_default_output(struct net_device *netdev); + +ktime_t softing_raw2ktime(struct softing *card, u32 raw); + +int softing_chip_poweron(struct softing *card); + +int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg); + +/* Load firmware after reset */ +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 */ +int softing_load_app_fw(const char *file, struct softing *card); + +/* + * enable or disable irq + * only called with fw.lock locked + */ +int softing_enable_irq(struct softing *card, int enable); + +/* start/stop 1 bus on card */ +int softing_startstop(struct net_device *netdev, int up); + +/* netif_rx() */ +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 000000000..efe7d576a --- /dev/null +++ b/drivers/net/can/softing/softing_cs.c @@ -0,0 +1,346 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 DEFINE_SPINLOCK(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 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 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 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 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: + platform_device_put(pdev); +mem_failed: +pcmcia_bad: +pcmcia_failed: + pcmcia_disable_device(pcmcia); + pcmcia->priv = NULL; + return ret; +} + +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 = softingcs_remove, +}; + +module_pcmcia_driver(softingcs_driver); + +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 000000000..209eddeb8 --- /dev/null +++ b/drivers/net/can/softing/softing_fw.c @@ -0,0 +1,693 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/firmware.h> +#include <linux/sched/signal.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, *new_buf; + 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); + new_buf = krealloc(buf, buflen, GFP_KERNEL); + if (!new_buf) { + ret = -ENOMEM; + goto failed; + } + buf = new_buf; + } + /* 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(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 (0 && 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, 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 000000000..bed5ffa75 --- /dev/null +++ b/drivers/net/can/softing/softing_main.c @@ -0,0 +1,875 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.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, 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() */ + ++priv->can.can_stats.bus_off; + 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) + return ret; + + ret = softing_startstop(ndev, 1); + if (ret < 0) + close_candev(ndev); + + 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 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_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 = kstrtoul(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(chip, 0444, show_chip, NULL); +static const DEVICE_ATTR(output, 0644, show_output, store_output); + +static const struct attribute *const netdev_sysfs_attrs[] = { + &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, + .ndo_change_mtu = can_change_mtu, +}; + +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 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 int softing_netdev_register(struct net_device *netdev) +{ + int ret; + + ret = register_candev(netdev); + if (ret) { + dev_alert(&netdev->dev, "register failed\n"); + return ret; + } + if (sysfs_create_group(&netdev->dev.kobj, &netdev_sysfs_group) < 0) + netdev_alert(netdev, "sysfs group failed\n"); + + return 0; +} + +static void softing_netdev_cleanup(struct net_device *netdev) +{ + sysfs_remove_group(&netdev->dev.kobj, &netdev_sysfs_group); + 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); + +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, + NULL, +}; + +static const struct attribute_group softing_pdev_group = { + .name = NULL, + .attrs = softing_pdev_attrs, +}; + +/* + * platform driver + */ +static 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 int softing_pdev_probe(struct platform_device *pdev) +{ + const struct softing_platform_data *pdat = dev_get_platdata(&pdev->dev); + 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; + } + + 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); + ret = -ENOMEM; + goto netdev_failed; + } + netdev->dev_id = j; + 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", + }, + .probe = softing_pdev_probe, + .remove = 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 000000000..68a161547 --- /dev/null +++ b/drivers/net/can/softing/softing_platform.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#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/spi/Kconfig b/drivers/net/can/spi/Kconfig new file mode 100644 index 000000000..792e9c6c4 --- /dev/null +++ b/drivers/net/can/spi/Kconfig @@ -0,0 +1,17 @@ +menu "CAN SPI interfaces" + depends on SPI + +config CAN_HI311X + tristate "Holt HI311x SPI CAN controllers" + depends on CAN_DEV && SPI && HAS_DMA + ---help--- + Driver for the Holt HI311x SPI CAN controllers. + +config CAN_MCP251X + tristate "Microchip MCP251x and MCP25625 SPI CAN controllers" + depends on HAS_DMA + ---help--- + Driver for the Microchip MCP251x and MCP25625 SPI CAN + controllers. + +endmenu diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile new file mode 100644 index 000000000..f59fa3731 --- /dev/null +++ b/drivers/net/can/spi/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux Controller Area Network SPI drivers. +# + + +obj-$(CONFIG_CAN_HI311X) += hi311x.o +obj-$(CONFIG_CAN_MCP251X) += mcp251x.o diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c new file mode 100644 index 000000000..472175e37 --- /dev/null +++ b/drivers/net/can/spi/hi311x.c @@ -0,0 +1,1079 @@ +/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface + * + * Copyright(C) Timesys Corporation 2016 + * + * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver + * Copyright 2009 Christian Pellegrin EVOL S.r.l. + * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved. + * 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 GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/can/led.h> +#include <linux/clk.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/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/uaccess.h> + +#define HI3110_MASTER_RESET 0x56 +#define HI3110_READ_CTRL0 0xD2 +#define HI3110_READ_CTRL1 0xD4 +#define HI3110_READ_STATF 0xE2 +#define HI3110_WRITE_CTRL0 0x14 +#define HI3110_WRITE_CTRL1 0x16 +#define HI3110_WRITE_INTE 0x1C +#define HI3110_WRITE_BTR0 0x18 +#define HI3110_WRITE_BTR1 0x1A +#define HI3110_READ_BTR0 0xD6 +#define HI3110_READ_BTR1 0xD8 +#define HI3110_READ_INTF 0xDE +#define HI3110_READ_ERR 0xDC +#define HI3110_READ_FIFO_WOTIME 0x48 +#define HI3110_WRITE_FIFO 0x12 +#define HI3110_READ_MESSTAT 0xDA +#define HI3110_READ_REC 0xEA +#define HI3110_READ_TEC 0xEC + +#define HI3110_CTRL0_MODE_MASK (7 << 5) +#define HI3110_CTRL0_NORMAL_MODE (0 << 5) +#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5) +#define HI3110_CTRL0_MONITOR_MODE (2 << 5) +#define HI3110_CTRL0_SLEEP_MODE (3 << 5) +#define HI3110_CTRL0_INIT_MODE (4 << 5) + +#define HI3110_CTRL1_TXEN BIT(7) + +#define HI3110_INT_RXTMP BIT(7) +#define HI3110_INT_RXFIFO BIT(6) +#define HI3110_INT_TXCPLT BIT(5) +#define HI3110_INT_BUSERR BIT(4) +#define HI3110_INT_MCHG BIT(3) +#define HI3110_INT_WAKEUP BIT(2) +#define HI3110_INT_F1MESS BIT(1) +#define HI3110_INT_F0MESS BIT(0) + +#define HI3110_ERR_BUSOFF BIT(7) +#define HI3110_ERR_TXERRP BIT(6) +#define HI3110_ERR_RXERRP BIT(5) +#define HI3110_ERR_BITERR BIT(4) +#define HI3110_ERR_FRMERR BIT(3) +#define HI3110_ERR_CRCERR BIT(2) +#define HI3110_ERR_ACKERR BIT(1) +#define HI3110_ERR_STUFERR BIT(0) +#define HI3110_ERR_PROTOCOL_MASK (0x1F) +#define HI3110_ERR_PASSIVE_MASK (0x60) + +#define HI3110_STAT_RXFMTY BIT(1) +#define HI3110_STAT_BUSOFF BIT(2) +#define HI3110_STAT_ERRP BIT(3) +#define HI3110_STAT_ERRW BIT(4) +#define HI3110_STAT_TXMTY BIT(7) + +#define HI3110_BTR0_SJW_SHIFT 6 +#define HI3110_BTR0_BRP_SHIFT 0 + +#define HI3110_BTR1_SAMP_3PERBIT (1 << 7) +#define HI3110_BTR1_SAMP_1PERBIT (0 << 7) +#define HI3110_BTR1_TSEG2_SHIFT 4 +#define HI3110_BTR1_TSEG1_SHIFT 0 + +#define HI3110_FIFO_WOTIME_TAG_OFF 0 +#define HI3110_FIFO_WOTIME_ID_OFF 1 +#define HI3110_FIFO_WOTIME_DLC_OFF 5 +#define HI3110_FIFO_WOTIME_DAT_OFF 6 + +#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7) +#define HI3110_FIFO_WOTIME_ID_RTR BIT(0) + +#define HI3110_FIFO_TAG_OFF 0 +#define HI3110_FIFO_ID_OFF 1 +#define HI3110_FIFO_STD_DLC_OFF 3 +#define HI3110_FIFO_STD_DATA_OFF 4 +#define HI3110_FIFO_EXT_DLC_OFF 5 +#define HI3110_FIFO_EXT_DATA_OFF 6 + +#define HI3110_CAN_MAX_DATA_LEN 8 +#define HI3110_RX_BUF_LEN 15 +#define HI3110_TX_STD_BUF_LEN 12 +#define HI3110_TX_EXT_BUF_LEN 14 +#define HI3110_CAN_FRAME_MAX_BITS 128 +#define HI3110_EFF_FLAGS 0x18 /* IDE + SRR */ + +#define HI3110_TX_ECHO_SKB_MAX 1 + +#define HI3110_OST_DELAY_MS (10) + +#define DEVICE_NAME "hi3110" + +static int hi3110_enable_dma = 1; /* Enable SPI DMA. Default: 1 (On) */ +module_param(hi3110_enable_dma, int, 0444); +MODULE_PARM_DESC(hi3110_enable_dma, "Enable SPI DMA. Default: 1 (On)"); + +static const struct can_bittiming_const hi3110_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 2, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +enum hi3110_model { + CAN_HI3110_HI3110 = 0x3110, +}; + +struct hi3110_priv { + struct can_priv can; + struct net_device *net; + struct spi_device *spi; + enum hi3110_model model; + + struct mutex hi3110_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 HI3110_AFTER_SUSPEND_UP 1 +#define HI3110_AFTER_SUSPEND_DOWN 2 +#define HI3110_AFTER_SUSPEND_POWER 4 +#define HI3110_AFTER_SUSPEND_RESTART 8 + int restart_tx; + struct regulator *power; + struct regulator *transceiver; + struct clk *clk; +}; + +static void hi3110_clean(struct net_device *net) +{ + struct hi3110_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 hi3110_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 hi3110_spi_trans(struct spi_device *spi, int len) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + 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 (hi3110_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 int hi3110_cmd(struct spi_device *spi, u8 command) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = command; + dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command); + + return hi3110_spi_trans(spi, 1); +} + +static u8 hi3110_read(struct spi_device *spi, u8 command) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + u8 val = 0; + + priv->spi_tx_buf[0] = command; + hi3110_spi_trans(spi, 2); + val = priv->spi_rx_buf[1]; + + return val; +} + +static void hi3110_write(struct spi_device *spi, u8 reg, u8 val) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = reg; + priv->spi_tx_buf[1] = val; + hi3110_spi_trans(spi, 2); +} + +static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = HI3110_WRITE_FIFO; + memcpy(priv->spi_tx_buf + 1, buf, len); + hi3110_spi_trans(spi, len + 1); +} + +static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame) +{ + u8 buf[HI3110_TX_EXT_BUF_LEN]; + + buf[HI3110_FIFO_TAG_OFF] = 0; + + if (frame->can_id & CAN_EFF_FLAG) { + /* Extended frame */ + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21; + buf[HI3110_FIFO_ID_OFF + 1] = + (((frame->can_id & CAN_EFF_MASK) >> 13) & 0xe0) | + HI3110_EFF_FLAGS | + (((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07); + buf[HI3110_FIFO_ID_OFF + 2] = + (frame->can_id & CAN_EFF_MASK) >> 7; + buf[HI3110_FIFO_ID_OFF + 3] = + ((frame->can_id & CAN_EFF_MASK) << 1) | + ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0); + + buf[HI3110_FIFO_EXT_DLC_OFF] = frame->can_dlc; + + memcpy(buf + HI3110_FIFO_EXT_DATA_OFF, + frame->data, frame->can_dlc); + + hi3110_hw_tx_frame(spi, buf, HI3110_TX_EXT_BUF_LEN - + (HI3110_CAN_MAX_DATA_LEN - frame->can_dlc)); + } else { + /* Standard frame */ + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_SFF_MASK) >> 3; + buf[HI3110_FIFO_ID_OFF + 1] = + ((frame->can_id & CAN_SFF_MASK) << 5) | + ((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0); + + buf[HI3110_FIFO_STD_DLC_OFF] = frame->can_dlc; + + memcpy(buf + HI3110_FIFO_STD_DATA_OFF, + frame->data, frame->can_dlc); + + hi3110_hw_tx_frame(spi, buf, HI3110_TX_STD_BUF_LEN - + (HI3110_CAN_MAX_DATA_LEN - frame->can_dlc)); + } +} + +static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME; + hi3110_spi_trans(spi, HI3110_RX_BUF_LEN); + memcpy(buf, priv->spi_rx_buf + 1, HI3110_RX_BUF_LEN - 1); +} + +static void hi3110_hw_rx(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct sk_buff *skb; + struct can_frame *frame; + u8 buf[HI3110_RX_BUF_LEN - 1]; + + skb = alloc_can_skb(priv->net, &frame); + if (!skb) { + priv->net->stats.rx_dropped++; + return; + } + + hi3110_hw_rx_frame(spi, buf); + if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) { + /* IDE is recessive (1), indicating extended 29-bit frame */ + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) | + (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) | + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) | + (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) | + (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1); + } else { + /* IDE is dominant (0), frame indicating standard 11-bit */ + frame->can_id = + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) | + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5); + } + + /* Data length */ + frame->can_dlc = get_can_dlc(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F); + + if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR) + frame->can_id |= CAN_RTR_FLAG; + else + memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF, + frame->can_dlc); + + priv->net->stats.rx_packets++; + priv->net->stats.rx_bytes += frame->can_dlc; + + can_led_event(priv->net, CAN_LED_EVENT_RX); + + netif_rx_ni(skb); +} + +static void hi3110_hw_sleep(struct spi_device *spi) +{ + hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE); +} + +static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + if (priv->tx_skb || priv->tx_len) { + dev_err(&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 hi3110_do_set_mode(struct net_device *net, enum can_mode mode) +{ + struct hi3110_priv *priv = netdev_priv(net); + + switch (mode) { + case CAN_MODE_START: + hi3110_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 = HI3110_AFTER_SUSPEND_RESTART; + queue_work(priv->wq, &priv->restart_work); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int hi3110_get_berr_counter(const struct net_device *net, + struct can_berr_counter *bec) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + mutex_lock(&priv->hi3110_lock); + bec->txerr = hi3110_read(spi, HI3110_READ_TEC); + bec->rxerr = hi3110_read(spi, HI3110_READ_REC); + mutex_unlock(&priv->hi3110_lock); + + return 0; +} + +static int hi3110_set_normal_mode(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + u8 reg = 0; + + hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR | + HI3110_INT_RXFIFO | HI3110_INT_TXCPLT); + + /* Enable TX */ + hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg = HI3110_CTRL0_LOOPBACK_MODE; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg = HI3110_CTRL0_MONITOR_MODE; + else + reg = HI3110_CTRL0_NORMAL_MODE; + + hi3110_write(spi, HI3110_WRITE_CTRL0, reg); + + /* Wait for the device to enter the mode */ + mdelay(HI3110_OST_DELAY_MS); + reg = hi3110_read(spi, HI3110_READ_CTRL0); + if ((reg & HI3110_CTRL0_MODE_MASK) != reg) + return -EBUSY; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +static int hi3110_do_set_bittiming(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct can_bittiming *bt = &priv->can.bittiming; + struct spi_device *spi = priv->spi; + + hi3110_write(spi, HI3110_WRITE_BTR0, + ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) | + ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT)); + + hi3110_write(spi, HI3110_WRITE_BTR1, + (priv->can.ctrlmode & + CAN_CTRLMODE_3_SAMPLES ? + HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) | + ((bt->phase_seg1 + bt->prop_seg - 1) + << HI3110_BTR1_TSEG1_SHIFT) | + ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT)); + + dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n", + hi3110_read(spi, HI3110_READ_BTR0), + hi3110_read(spi, HI3110_READ_BTR1)); + + return 0; +} + +static int hi3110_setup(struct net_device *net) +{ + hi3110_do_set_bittiming(net); + return 0; +} + +static int hi3110_hw_reset(struct spi_device *spi) +{ + u8 reg; + int ret; + + /* Wait for oscillator startup timer after power up */ + mdelay(HI3110_OST_DELAY_MS); + + ret = hi3110_cmd(spi, HI3110_MASTER_RESET); + if (ret) + return ret; + + /* Wait for oscillator startup timer after reset */ + mdelay(HI3110_OST_DELAY_MS); + + reg = hi3110_read(spi, HI3110_READ_CTRL0); + if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE) + return -ENODEV; + + /* As per the datasheet it appears the error flags are + * not cleared on reset. Explicitly clear them by performing a read + */ + hi3110_read(spi, HI3110_READ_ERR); + + return 0; +} + +static int hi3110_hw_probe(struct spi_device *spi) +{ + u8 statf; + + hi3110_hw_reset(spi); + + /* Confirm correct operation by checking against reset values + * in datasheet + */ + statf = hi3110_read(spi, HI3110_READ_STATF); + + dev_dbg(&spi->dev, "statf: %02X\n", statf); + + if (statf != 0x82) + return -ENODEV; + + return 0; +} + +static int hi3110_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR_OR_NULL(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static int hi3110_stop(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + close_candev(net); + + priv->force_quit = 1; + free_irq(spi->irq, priv); + destroy_workqueue(priv->wq); + priv->wq = NULL; + + mutex_lock(&priv->hi3110_lock); + + /* Disable transmit, interrupts and clear flags */ + hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0); + hi3110_write(spi, HI3110_WRITE_INTE, 0x0); + hi3110_read(spi, HI3110_READ_INTF); + + hi3110_clean(net); + + hi3110_hw_sleep(spi); + + hi3110_power_enable(priv->transceiver, 0); + + priv->can.state = CAN_STATE_STOPPED; + + mutex_unlock(&priv->hi3110_lock); + + can_led_event(net, CAN_LED_EVENT_STOP); + + return 0; +} + +static void hi3110_tx_work_handler(struct work_struct *ws) +{ + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv, + tx_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + struct can_frame *frame; + + mutex_lock(&priv->hi3110_lock); + if (priv->tx_skb) { + if (priv->can.state == CAN_STATE_BUS_OFF) { + hi3110_clean(net); + } else { + frame = (struct can_frame *)priv->tx_skb->data; + hi3110_hw_tx(spi, frame); + priv->tx_len = 1 + frame->can_dlc; + can_put_echo_skb(priv->tx_skb, net, 0); + priv->tx_skb = NULL; + } + } + mutex_unlock(&priv->hi3110_lock); +} + +static void hi3110_restart_work_handler(struct work_struct *ws) +{ + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv, + restart_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->hi3110_lock); + if (priv->after_suspend) { + hi3110_hw_reset(spi); + hi3110_setup(net); + if (priv->after_suspend & HI3110_AFTER_SUSPEND_RESTART) { + hi3110_set_normal_mode(spi); + } else if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) { + netif_device_attach(net); + hi3110_clean(net); + hi3110_set_normal_mode(spi); + netif_wake_queue(net); + } else { + hi3110_hw_sleep(spi); + } + priv->after_suspend = 0; + priv->force_quit = 0; + } + + if (priv->restart_tx) { + priv->restart_tx = 0; + hi3110_hw_reset(spi); + hi3110_setup(net); + hi3110_clean(net); + hi3110_set_normal_mode(spi); + netif_wake_queue(net); + } + mutex_unlock(&priv->hi3110_lock); +} + +static irqreturn_t hi3110_can_ist(int irq, void *dev_id) +{ + struct hi3110_priv *priv = dev_id; + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->hi3110_lock); + + while (!priv->force_quit) { + enum can_state new_state; + u8 intf, eflag, statf; + + while (!(HI3110_STAT_RXFMTY & + (statf = hi3110_read(spi, HI3110_READ_STATF)))) { + hi3110_hw_rx(spi); + } + + intf = hi3110_read(spi, HI3110_READ_INTF); + eflag = hi3110_read(spi, HI3110_READ_ERR); + /* Update can state */ + if (eflag & HI3110_ERR_BUSOFF) + new_state = CAN_STATE_BUS_OFF; + else if (eflag & HI3110_ERR_PASSIVE_MASK) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (statf & HI3110_STAT_ERRW) + new_state = CAN_STATE_ERROR_WARNING; + else + new_state = CAN_STATE_ERROR_ACTIVE; + + if (new_state != priv->can.state) { + struct can_frame *cf; + struct sk_buff *skb; + enum can_state rx_state, tx_state; + u8 rxerr, txerr; + + skb = alloc_can_err_skb(net, &cf); + if (!skb) + break; + + txerr = hi3110_read(spi, HI3110_READ_TEC); + rxerr = hi3110_read(spi, HI3110_READ_REC); + cf->data[6] = txerr; + cf->data[7] = rxerr; + tx_state = txerr >= rxerr ? new_state : 0; + rx_state = txerr <= rxerr ? new_state : 0; + can_change_state(net, cf, tx_state, rx_state); + netif_rx_ni(skb); + + if (new_state == CAN_STATE_BUS_OFF) { + can_bus_off(net); + if (priv->can.restart_ms == 0) { + priv->force_quit = 1; + hi3110_hw_sleep(spi); + break; + } + } + } + + /* Update bus errors */ + if ((intf & HI3110_INT_BUSERR) && + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) { + struct can_frame *cf; + struct sk_buff *skb; + + /* Check for protocol errors */ + if (eflag & HI3110_ERR_PROTOCOL_MASK) { + skb = alloc_can_err_skb(net, &cf); + if (!skb) + break; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + priv->can.can_stats.bus_error++; + priv->net->stats.rx_errors++; + if (eflag & HI3110_ERR_BITERR) + cf->data[2] |= CAN_ERR_PROT_BIT; + else if (eflag & HI3110_ERR_FRMERR) + cf->data[2] |= CAN_ERR_PROT_FORM; + else if (eflag & HI3110_ERR_STUFERR) + cf->data[2] |= CAN_ERR_PROT_STUFF; + else if (eflag & HI3110_ERR_CRCERR) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + else if (eflag & HI3110_ERR_ACKERR) + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + + cf->data[6] = hi3110_read(spi, HI3110_READ_TEC); + cf->data[7] = hi3110_read(spi, HI3110_READ_REC); + netdev_dbg(priv->net, "Bus Error\n"); + netif_rx_ni(skb); + } + } + + if (priv->tx_len && statf & HI3110_STAT_TXMTY) { + net->stats.tx_packets++; + net->stats.tx_bytes += priv->tx_len - 1; + can_led_event(net, CAN_LED_EVENT_TX); + if (priv->tx_len) { + can_get_echo_skb(net, 0); + priv->tx_len = 0; + } + netif_wake_queue(net); + } + + if (intf == 0) + break; + } + mutex_unlock(&priv->hi3110_lock); + return IRQ_HANDLED; +} + +static int hi3110_open(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_HIGH; + int ret; + + ret = open_candev(net); + if (ret) + return ret; + + mutex_lock(&priv->hi3110_lock); + hi3110_power_enable(priv->transceiver, 1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_len = 0; + + ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist, + flags, DEVICE_NAME, priv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + goto out_close; + } + + priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, + 0); + if (!priv->wq) { + ret = -ENOMEM; + goto out_free_irq; + } + INIT_WORK(&priv->tx_work, hi3110_tx_work_handler); + INIT_WORK(&priv->restart_work, hi3110_restart_work_handler); + + ret = hi3110_hw_reset(spi); + if (ret) + goto out_free_wq; + + ret = hi3110_setup(net); + if (ret) + goto out_free_wq; + + ret = hi3110_set_normal_mode(spi); + if (ret) + goto out_free_wq; + + can_led_event(net, CAN_LED_EVENT_OPEN); + netif_wake_queue(net); + mutex_unlock(&priv->hi3110_lock); + + return 0; + + out_free_wq: + destroy_workqueue(priv->wq); + out_free_irq: + free_irq(spi->irq, priv); + hi3110_hw_sleep(spi); + out_close: + hi3110_power_enable(priv->transceiver, 0); + close_candev(net); + mutex_unlock(&priv->hi3110_lock); + return ret; +} + +static const struct net_device_ops hi3110_netdev_ops = { + .ndo_open = hi3110_open, + .ndo_stop = hi3110_stop, + .ndo_start_xmit = hi3110_hard_start_xmit, +}; + +static const struct of_device_id hi3110_of_match[] = { + { + .compatible = "holt,hi3110", + .data = (void *)CAN_HI3110_HI3110, + }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3110_of_match); + +static const struct spi_device_id hi3110_id_table[] = { + { + .name = "hi3110", + .driver_data = (kernel_ulong_t)CAN_HI3110_HI3110, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, hi3110_id_table); + +static int hi3110_can_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id = of_match_device(hi3110_of_match, + &spi->dev); + struct net_device *net; + struct hi3110_priv *priv; + struct clk *clk; + int freq, ret; + + clk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&spi->dev, "no CAN clock source defined\n"); + return PTR_ERR(clk); + } + freq = clk_get_rate(clk); + + /* Sanity check */ + if (freq > 40000000) + return -ERANGE; + + /* Allocate can/net device */ + net = alloc_candev(sizeof(struct hi3110_priv), HI3110_TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + } + + net->netdev_ops = &hi3110_netdev_ops; + net->flags |= IFF_ECHO; + + priv = netdev_priv(net); + priv->can.bittiming_const = &hi3110_bittiming_const; + priv->can.do_set_mode = hi3110_do_set_mode; + priv->can.do_get_berr_counter = hi3110_get_berr_counter; + priv->can.clock.freq = freq / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING; + + if (of_id) + priv->model = (enum hi3110_model)of_id->data; + else + priv->model = spi_get_device_id(spi)->driver_data; + priv->net = net; + priv->clk = clk; + + spi_set_drvdata(spi, priv); + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get_optional(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto out_clk; + } + + ret = hi3110_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + priv->spi = spi; + mutex_init(&priv->hi3110_lock); + + /* If requested, allocate DMA buffers */ + if (hi3110_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 = dmam_alloc_coherent(&spi->dev, + PAGE_SIZE, + &priv->spi_tx_dma, + GFP_DMA); + + if (priv->spi_tx_buf) { + priv->spi_rx_buf = (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 */ + hi3110_enable_dma = 0; + } + } + + /* Allocate non-DMA buffers */ + if (!hi3110_enable_dma) { + priv->spi_tx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_probe; + } + priv->spi_rx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN, + GFP_KERNEL); + + if (!priv->spi_rx_buf) { + ret = -ENOMEM; + goto error_probe; + } + } + + SET_NETDEV_DEV(net, &spi->dev); + + ret = hi3110_hw_probe(spi); + if (ret) { + if (ret == -ENODEV) + dev_err(&spi->dev, "Cannot initialize %x. Wrong wiring?\n", + priv->model); + goto error_probe; + } + hi3110_hw_sleep(spi); + + ret = register_candev(net); + if (ret) + goto error_probe; + + devm_can_led_init(net); + netdev_info(net, "%x successfully initialized.\n", priv->model); + + return 0; + + error_probe: + hi3110_power_enable(priv->power, 0); + + out_clk: + if (!IS_ERR(clk)) + clk_disable_unprepare(clk); + + out_free: + free_candev(net); + + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret); + return ret; +} + +static int hi3110_can_remove(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + unregister_candev(net); + + hi3110_power_enable(priv->power, 0); + + if (!IS_ERR(priv->clk)) + clk_disable_unprepare(priv->clk); + + free_candev(net); + + return 0; +} + +static int __maybe_unused hi3110_can_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct hi3110_priv *priv = spi_get_drvdata(spi); + 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); + + hi3110_hw_sleep(spi); + hi3110_power_enable(priv->transceiver, 0); + priv->after_suspend = HI3110_AFTER_SUSPEND_UP; + } else { + priv->after_suspend = HI3110_AFTER_SUSPEND_DOWN; + } + + if (!IS_ERR_OR_NULL(priv->power)) { + regulator_disable(priv->power); + priv->after_suspend |= HI3110_AFTER_SUSPEND_POWER; + } + + return 0; +} + +static int __maybe_unused hi3110_can_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct hi3110_priv *priv = spi_get_drvdata(spi); + + if (priv->after_suspend & HI3110_AFTER_SUSPEND_POWER) + hi3110_power_enable(priv->power, 1); + + if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) { + hi3110_power_enable(priv->transceiver, 1); + queue_work(priv->wq, &priv->restart_work); + } else { + priv->after_suspend = 0; + } + + priv->force_quit = 0; + enable_irq(spi->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend, hi3110_can_resume); + +static struct spi_driver hi3110_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = hi3110_of_match, + .pm = &hi3110_can_pm_ops, + }, + .id_table = hi3110_id_table, + .probe = hi3110_can_probe, + .remove = hi3110_can_remove, +}; + +module_spi_driver(hi3110_can_driver); + +MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>"); +MODULE_AUTHOR("Casey Fitzpatrick <casey.fitzpatrick@timesys.com>"); +MODULE_DESCRIPTION("Holt HI-3110 CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c new file mode 100644 index 000000000..0b0dd3f09 --- /dev/null +++ b/drivers/net/can/spi/mcp251x.c @@ -0,0 +1,1274 @@ +/* + * CAN bus driver for Microchip 251x/25625 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, see <http://www.gnu.org/licenses/>. + * + * + * + * Your platform definition file should specify something like: + * + * static struct mcp251x_platform_data mcp251x_info = { + * .oscillator_frequency = 8000000, + * }; + * + * static struct spi_board_info spi_board_info[] = { + * { + * .modalias = "mcp2510", + * // "mcp2515" or "mcp25625" 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/led.h> +#include <linux/can/platform/mcp251x.h> +#include <linux/clk.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/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/uaccess.h> +#include <linux/regulator/consumer.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 +#define RTS_TXB0 0x01 +#define RTS_TXB1 0x02 +#define RTS_TXB2 0x04 +#define INSTRUCTION_RTS(n) (0x80 | ((n) & 0x07)) + + +/* 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 RXFSID(n) ((n < 3) ? 0 : 4) +#define RXFSIDH(n) ((n) * 4 + RXFSID(n)) +#define RXFSIDL(n) ((n) * 4 + 1 + RXFSID(n)) +#define RXFEID8(n) ((n) * 4 + 2 + RXFSID(n)) +#define RXFEID0(n) ((n) * 4 + 3 + RXFSID(n)) +#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 MCP251X_OST_DELAY_MS (5) + +#define DEVICE_NAME "mcp251x" + +static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */ +module_param(mcp251x_enable_dma, int, 0444); +MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)"); + +static const 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, + CAN_MCP251X_MCP25625 = 0x25625, +}; + +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; + struct regulator *power; + struct regulator *transceiver; + struct clk *clk; +}; + +#define MCP251X_IS(_model) \ +static inline int mcp251x_is_##_model(struct spi_device *spi) \ +{ \ + struct mcp251x_priv *priv = spi_get_drvdata(spi); \ + return priv->model == CAN_MCP251X_MCP##_model; \ +} + +MCP251X_IS(2510); + +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 = spi_get_drvdata(spi); + 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 = spi_get_drvdata(spi); + 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 = spi_get_drvdata(spi); + + 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 = spi_get_drvdata(spi); + + 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 = spi_get_drvdata(spi); + + 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 = spi_get_drvdata(spi); + + 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) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + 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); + + /* use INSTRUCTION_RTS, to avoid "repeated frame problem" */ + priv->spi_tx_buf[0] = INSTRUCTION_RTS(1 << tx_buf_idx); + mcp251x_spi_trans(priv->spi, 1); +} + +static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, + int buf_idx) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + 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 = spi_get_drvdata(spi); + 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; + + can_led_event(priv->net, CAN_LED_EVENT_RX); + + 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 = spi_get_drvdata(spi); + 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_dbg(&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 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 = spi_get_drvdata(spi); + unsigned long timeout; + int ret; + + /* Wait for oscillator startup timer after power up */ + mdelay(MCP251X_OST_DELAY_MS); + + priv->spi_tx_buf[0] = INSTRUCTION_RESET; + ret = mcp251x_spi_trans(spi, 1); + if (ret) + return ret; + + /* Wait for oscillator startup timer after reset */ + mdelay(MCP251X_OST_DELAY_MS); + + /* Wait for reset to finish */ + timeout = jiffies + HZ; + while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) != + CANCTRL_REQOP_CONF) { + usleep_range(MCP251X_OST_DELAY_MS * 1000, + MCP251X_OST_DELAY_MS * 1000 * 2); + + 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) +{ + u8 ctrl; + int ret; + + ret = mcp251x_hw_reset(spi); + if (ret) + return ret; + + ctrl = mcp251x_read_reg(spi, CANCTRL); + + dev_dbg(&spi->dev, "CANCTRL 0x%02x\n", ctrl); + + /* Check for power up default value */ + if ((ctrl & 0x17) != 0x07) + return -ENODEV; + + return 0; +} + +static int mcp251x_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR_OR_NULL(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static int mcp251x_stop(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + 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); + + mcp251x_power_enable(priv->transceiver, 0); + + priv->can.state = CAN_STATE_STOPPED; + + mutex_unlock(&priv->mcp_lock); + + can_led_event(net, CAN_LED_EVENT_STOP); + + 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) { + mcp251x_hw_reset(spi); + mcp251x_setup(net, spi); + priv->force_quit = 0; + 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; + } + + 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/25625 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/25625 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 & (EFLG_RX0OVR | EFLG_RX1OVR)) + 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; + priv->can.can_stats.bus_off++; + 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; + can_led_event(net, CAN_LED_EVENT_TX); + 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; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_FALLING; + 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); + mcp251x_power_enable(priv->transceiver, 1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_len = 0; + + ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, + flags | IRQF_ONESHOT, DEVICE_NAME, priv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + goto out_close; + } + + priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, + 0); + if (!priv->wq) { + ret = -ENOMEM; + goto out_clean; + } + 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) + goto out_free_wq; + ret = mcp251x_setup(net, spi); + if (ret) + goto out_free_wq; + ret = mcp251x_set_normal_mode(spi); + if (ret) + goto out_free_wq; + + can_led_event(net, CAN_LED_EVENT_OPEN); + + netif_wake_queue(net); + mutex_unlock(&priv->mcp_lock); + + return 0; + +out_free_wq: + destroy_workqueue(priv->wq); +out_clean: + free_irq(spi->irq, priv); + mcp251x_hw_sleep(spi); +out_close: + mcp251x_power_enable(priv->transceiver, 0); + close_candev(net); + 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, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct of_device_id mcp251x_of_match[] = { + { + .compatible = "microchip,mcp2510", + .data = (void *)CAN_MCP251X_MCP2510, + }, + { + .compatible = "microchip,mcp2515", + .data = (void *)CAN_MCP251X_MCP2515, + }, + { + .compatible = "microchip,mcp25625", + .data = (void *)CAN_MCP251X_MCP25625, + }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp251x_of_match); + +static const struct spi_device_id mcp251x_id_table[] = { + { + .name = "mcp2510", + .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP2510, + }, + { + .name = "mcp2515", + .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP2515, + }, + { + .name = "mcp25625", + .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP25625, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp251x_id_table); + +static int mcp251x_can_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id = of_match_device(mcp251x_of_match, + &spi->dev); + struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev); + struct net_device *net; + struct mcp251x_priv *priv; + struct clk *clk; + int freq, ret; + + clk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(clk)) { + if (pdata) + freq = pdata->oscillator_frequency; + else + return PTR_ERR(clk); + } else { + freq = clk_get_rate(clk); + } + + /* Sanity check */ + if (freq < 1000000 || freq > 25000000) + return -ERANGE; + + /* Allocate can/net device */ + net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + } + + 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 = freq / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; + if (of_id) + priv->model = (enum mcp251x_model)of_id->data; + else + priv->model = spi_get_device_id(spi)->driver_data; + priv->net = net; + priv->clk = clk; + + spi_set_drvdata(spi, priv); + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + if (mcp251x_is_2510(spi)) + spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000; + else + spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get_optional(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto out_clk; + } + + ret = mcp251x_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + 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 = dmam_alloc_coherent(&spi->dev, + PAGE_SIZE, + &priv->spi_tx_dma, + GFP_DMA); + + if (priv->spi_tx_buf) { + priv->spi_rx_buf = (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 = devm_kzalloc(&spi->dev, SPI_TRANSFER_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_probe; + } + priv->spi_rx_buf = devm_kzalloc(&spi->dev, SPI_TRANSFER_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_rx_buf) { + ret = -ENOMEM; + goto error_probe; + } + } + + SET_NETDEV_DEV(net, &spi->dev); + + /* Here is OK to not lock the MCP, no one knows about it yet */ + ret = mcp251x_hw_probe(spi); + if (ret) { + if (ret == -ENODEV) + dev_err(&spi->dev, "Cannot initialize MCP%x. Wrong wiring?\n", priv->model); + goto error_probe; + } + + mcp251x_hw_sleep(spi); + + ret = register_candev(net); + if (ret) + goto error_probe; + + devm_can_led_init(net); + + netdev_info(net, "MCP%x successfully initialized.\n", priv->model); + return 0; + +error_probe: + mcp251x_power_enable(priv->power, 0); + +out_clk: + if (!IS_ERR(clk)) + clk_disable_unprepare(clk); + +out_free: + free_candev(net); + + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret); + return ret; +} + +static int mcp251x_can_remove(struct spi_device *spi) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + unregister_candev(net); + + mcp251x_power_enable(priv->power, 0); + + if (!IS_ERR(priv->clk)) + clk_disable_unprepare(priv->clk); + + free_candev(net); + + return 0; +} + +static int __maybe_unused mcp251x_can_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); + 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); + mcp251x_power_enable(priv->transceiver, 0); + priv->after_suspend = AFTER_SUSPEND_UP; + } else { + priv->after_suspend = AFTER_SUSPEND_DOWN; + } + + if (!IS_ERR_OR_NULL(priv->power)) { + regulator_disable(priv->power); + priv->after_suspend |= AFTER_SUSPEND_POWER; + } + + return 0; +} + +static int __maybe_unused mcp251x_can_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + if (priv->after_suspend & AFTER_SUSPEND_POWER) + mcp251x_power_enable(priv->power, 1); + + if (priv->after_suspend & AFTER_SUSPEND_UP) { + mcp251x_power_enable(priv->transceiver, 1); + queue_work(priv->wq, &priv->restart_work); + } else { + priv->after_suspend = 0; + } + + priv->force_quit = 0; + enable_irq(spi->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(mcp251x_can_pm_ops, mcp251x_can_suspend, + mcp251x_can_resume); + +static struct spi_driver mcp251x_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = mcp251x_of_match, + .pm = &mcp251x_can_pm_ops, + }, + .id_table = mcp251x_id_table, + .probe = mcp251x_can_probe, + .remove = mcp251x_can_remove, +}; +module_spi_driver(mcp251x_can_driver); + +MODULE_AUTHOR("Chris Elston <celston@katalix.com>, " + "Christian Pellegrin <chripell@evolware.org>"); +MODULE_DESCRIPTION("Microchip 251x/25625 CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c new file mode 100644 index 000000000..093fc9a52 --- /dev/null +++ b/drivers/net/can/sun4i_can.c @@ -0,0 +1,863 @@ +/* + * sun4i_can.c - CAN bus controller driver for Allwinner SUN4I&SUN7I based SoCs + * + * Copyright (C) 2013 Peter Chen + * Copyright (C) 2015 Gerhard Bertelsmann + * All rights reserved. + * + * Parts of this software are based on (derived from) the SJA1000 code by: + * Copyright (C) 2014 Oliver Hartkopp <oliver.hartkopp@volkswagen.de> + * 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 + * + * 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/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#define DRV_NAME "sun4i_can" + +/* Registers address (physical base address 0x01C2BC00) */ +#define SUN4I_REG_MSEL_ADDR 0x0000 /* CAN Mode Select */ +#define SUN4I_REG_CMD_ADDR 0x0004 /* CAN Command */ +#define SUN4I_REG_STA_ADDR 0x0008 /* CAN Status */ +#define SUN4I_REG_INT_ADDR 0x000c /* CAN Interrupt Flag */ +#define SUN4I_REG_INTEN_ADDR 0x0010 /* CAN Interrupt Enable */ +#define SUN4I_REG_BTIME_ADDR 0x0014 /* CAN Bus Timing 0 */ +#define SUN4I_REG_TEWL_ADDR 0x0018 /* CAN Tx Error Warning Limit */ +#define SUN4I_REG_ERRC_ADDR 0x001c /* CAN Error Counter */ +#define SUN4I_REG_RMCNT_ADDR 0x0020 /* CAN Receive Message Counter */ +#define SUN4I_REG_RBUFSA_ADDR 0x0024 /* CAN Receive Buffer Start Address */ +#define SUN4I_REG_BUF0_ADDR 0x0040 /* CAN Tx/Rx Buffer 0 */ +#define SUN4I_REG_BUF1_ADDR 0x0044 /* CAN Tx/Rx Buffer 1 */ +#define SUN4I_REG_BUF2_ADDR 0x0048 /* CAN Tx/Rx Buffer 2 */ +#define SUN4I_REG_BUF3_ADDR 0x004c /* CAN Tx/Rx Buffer 3 */ +#define SUN4I_REG_BUF4_ADDR 0x0050 /* CAN Tx/Rx Buffer 4 */ +#define SUN4I_REG_BUF5_ADDR 0x0054 /* CAN Tx/Rx Buffer 5 */ +#define SUN4I_REG_BUF6_ADDR 0x0058 /* CAN Tx/Rx Buffer 6 */ +#define SUN4I_REG_BUF7_ADDR 0x005c /* CAN Tx/Rx Buffer 7 */ +#define SUN4I_REG_BUF8_ADDR 0x0060 /* CAN Tx/Rx Buffer 8 */ +#define SUN4I_REG_BUF9_ADDR 0x0064 /* CAN Tx/Rx Buffer 9 */ +#define SUN4I_REG_BUF10_ADDR 0x0068 /* CAN Tx/Rx Buffer 10 */ +#define SUN4I_REG_BUF11_ADDR 0x006c /* CAN Tx/Rx Buffer 11 */ +#define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */ +#define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */ +#define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */ +#define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */ +#define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */ + +/* Controller Register Description */ + +/* mode select register (r/w) + * offset:0x0000 default:0x0000_0001 + */ +#define SUN4I_MSEL_SLEEP_MODE (0x01 << 4) /* write in reset mode */ +#define SUN4I_MSEL_WAKE_UP (0x00 << 4) +#define SUN4I_MSEL_SINGLE_FILTER (0x01 << 3) /* write in reset mode */ +#define SUN4I_MSEL_DUAL_FILTERS (0x00 << 3) +#define SUN4I_MSEL_LOOPBACK_MODE BIT(2) +#define SUN4I_MSEL_LISTEN_ONLY_MODE BIT(1) +#define SUN4I_MSEL_RESET_MODE BIT(0) + +/* command register (w) + * offset:0x0004 default:0x0000_0000 + */ +#define SUN4I_CMD_BUS_OFF_REQ BIT(5) +#define SUN4I_CMD_SELF_RCV_REQ BIT(4) +#define SUN4I_CMD_CLEAR_OR_FLAG BIT(3) +#define SUN4I_CMD_RELEASE_RBUF BIT(2) +#define SUN4I_CMD_ABORT_REQ BIT(1) +#define SUN4I_CMD_TRANS_REQ BIT(0) + +/* status register (r) + * offset:0x0008 default:0x0000_003c + */ +#define SUN4I_STA_BIT_ERR (0x00 << 22) +#define SUN4I_STA_FORM_ERR (0x01 << 22) +#define SUN4I_STA_STUFF_ERR (0x02 << 22) +#define SUN4I_STA_OTHER_ERR (0x03 << 22) +#define SUN4I_STA_MASK_ERR (0x03 << 22) +#define SUN4I_STA_ERR_DIR BIT(21) +#define SUN4I_STA_ERR_SEG_CODE (0x1f << 16) +#define SUN4I_STA_START (0x03 << 16) +#define SUN4I_STA_ID28_21 (0x02 << 16) +#define SUN4I_STA_ID20_18 (0x06 << 16) +#define SUN4I_STA_SRTR (0x04 << 16) +#define SUN4I_STA_IDE (0x05 << 16) +#define SUN4I_STA_ID17_13 (0x07 << 16) +#define SUN4I_STA_ID12_5 (0x0f << 16) +#define SUN4I_STA_ID4_0 (0x0e << 16) +#define SUN4I_STA_RTR (0x0c << 16) +#define SUN4I_STA_RB1 (0x0d << 16) +#define SUN4I_STA_RB0 (0x09 << 16) +#define SUN4I_STA_DLEN (0x0b << 16) +#define SUN4I_STA_DATA_FIELD (0x0a << 16) +#define SUN4I_STA_CRC_SEQUENCE (0x08 << 16) +#define SUN4I_STA_CRC_DELIMITER (0x18 << 16) +#define SUN4I_STA_ACK (0x19 << 16) +#define SUN4I_STA_ACK_DELIMITER (0x1b << 16) +#define SUN4I_STA_END (0x1a << 16) +#define SUN4I_STA_INTERMISSION (0x12 << 16) +#define SUN4I_STA_ACTIVE_ERROR (0x11 << 16) +#define SUN4I_STA_PASSIVE_ERROR (0x16 << 16) +#define SUN4I_STA_TOLERATE_DOMINANT_BITS (0x13 << 16) +#define SUN4I_STA_ERROR_DELIMITER (0x17 << 16) +#define SUN4I_STA_OVERLOAD (0x1c << 16) +#define SUN4I_STA_BUS_OFF BIT(7) +#define SUN4I_STA_ERR_STA BIT(6) +#define SUN4I_STA_TRANS_BUSY BIT(5) +#define SUN4I_STA_RCV_BUSY BIT(4) +#define SUN4I_STA_TRANS_OVER BIT(3) +#define SUN4I_STA_TBUF_RDY BIT(2) +#define SUN4I_STA_DATA_ORUN BIT(1) +#define SUN4I_STA_RBUF_RDY BIT(0) + +/* interrupt register (r) + * offset:0x000c default:0x0000_0000 + */ +#define SUN4I_INT_BUS_ERR BIT(7) +#define SUN4I_INT_ARB_LOST BIT(6) +#define SUN4I_INT_ERR_PASSIVE BIT(5) +#define SUN4I_INT_WAKEUP BIT(4) +#define SUN4I_INT_DATA_OR BIT(3) +#define SUN4I_INT_ERR_WRN BIT(2) +#define SUN4I_INT_TBUF_VLD BIT(1) +#define SUN4I_INT_RBUF_VLD BIT(0) + +/* interrupt enable register (r/w) + * offset:0x0010 default:0x0000_0000 + */ +#define SUN4I_INTEN_BERR BIT(7) +#define SUN4I_INTEN_ARB_LOST BIT(6) +#define SUN4I_INTEN_ERR_PASSIVE BIT(5) +#define SUN4I_INTEN_WAKEUP BIT(4) +#define SUN4I_INTEN_OR BIT(3) +#define SUN4I_INTEN_ERR_WRN BIT(2) +#define SUN4I_INTEN_TX BIT(1) +#define SUN4I_INTEN_RX BIT(0) + +/* error code */ +#define SUN4I_ERR_INRCV (0x1 << 5) +#define SUN4I_ERR_INTRANS (0x0 << 5) + +/* filter mode */ +#define SUN4I_FILTER_CLOSE 0 +#define SUN4I_SINGLE_FLTER_MODE 1 +#define SUN4I_DUAL_FILTER_MODE 2 + +/* message buffer flags */ +#define SUN4I_MSG_EFF_FLAG BIT(7) +#define SUN4I_MSG_RTR_FLAG BIT(6) + +/* max. number of interrupts handled in ISR */ +#define SUN4I_CAN_MAX_IRQ 20 +#define SUN4I_MODE_MAX_RETRIES 100 + +struct sun4ican_priv { + struct can_priv can; + void __iomem *base; + struct clk *clk; + spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */ +}; + +static const struct can_bittiming_const sun4ican_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 sun4i_can_write_cmdreg(struct sun4ican_priv *priv, u8 val) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->cmdreg_lock, flags); + writel(val, priv->base + SUN4I_REG_CMD_ADDR); + spin_unlock_irqrestore(&priv->cmdreg_lock, flags); +} + +static int set_normal_mode(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int retry = SUN4I_MODE_MAX_RETRIES; + u32 mod_reg_val = 0; + + do { + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + mod_reg_val &= ~SUN4I_MSEL_RESET_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + } while (retry-- && (mod_reg_val & SUN4I_MSEL_RESET_MODE)); + + if (readl(priv->base + SUN4I_REG_MSEL_ADDR) & SUN4I_MSEL_RESET_MODE) { + netdev_err(dev, + "setting controller into normal mode failed!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int set_reset_mode(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int retry = SUN4I_MODE_MAX_RETRIES; + u32 mod_reg_val = 0; + + do { + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + mod_reg_val |= SUN4I_MSEL_RESET_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + } while (retry-- && !(mod_reg_val & SUN4I_MSEL_RESET_MODE)); + + if (!(readl(priv->base + SUN4I_REG_MSEL_ADDR) & + SUN4I_MSEL_RESET_MODE)) { + netdev_err(dev, "setting controller into reset mode failed!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* bittiming is called in reset_mode only */ +static int sun4ican_set_bittiming(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 cfg; + + cfg = ((bt->brp - 1) & 0x3FF) | + (((bt->sjw - 1) & 0x3) << 14) | + (((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16) | + (((bt->phase_seg2 - 1) & 0x7) << 20); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + cfg |= 0x800000; + + netdev_dbg(dev, "setting BITTIMING=0x%08x\n", cfg); + writel(cfg, priv->base + SUN4I_REG_BTIME_ADDR); + + return 0; +} + +static int sun4ican_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + u32 errors; + int err; + + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(dev, "could not enable clock\n"); + return err; + } + + errors = readl(priv->base + SUN4I_REG_ERRC_ADDR); + + bec->txerr = errors & 0xFF; + bec->rxerr = (errors >> 16) & 0xFF; + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int sun4i_can_start(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + u32 mod_reg_val; + + /* we need to enter the reset mode */ + err = set_reset_mode(dev); + if (err) { + netdev_err(dev, "could not enter reset mode\n"); + return err; + } + + /* set filters - we accept all */ + writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR); + writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR); + + /* clear error counters and error code capture */ + writel(0, priv->base + SUN4I_REG_ERRC_ADDR); + + /* enable interrupts */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + writel(0xFF, priv->base + SUN4I_REG_INTEN_ADDR); + else + writel(0xFF & ~SUN4I_INTEN_BERR, + priv->base + SUN4I_REG_INTEN_ADDR); + + /* enter the selected mode */ + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + + err = sun4ican_set_bittiming(dev); + if (err) + return err; + + /* we are ready to enter the normal mode */ + err = set_normal_mode(dev); + if (err) { + netdev_err(dev, "could not enter normal mode\n"); + return err; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static int sun4i_can_stop(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + + priv->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + err = set_reset_mode(dev); + if (err) { + netdev_err(dev, "could not enter reset mode\n"); + return err; + } + + /* disable all interrupts */ + writel(0, priv->base + SUN4I_REG_INTEN_ADDR); + + return 0; +} + +static int sun4ican_set_mode(struct net_device *dev, enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = sun4i_can_start(dev); + if (err) { + netdev_err(dev, "starting CAN controller failed!\n"); + return err; + } + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* 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 sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc; + u32 dreg, msg_flag_n; + canid_t id; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + id = cf->can_id; + dlc = cf->can_dlc; + msg_flag_n = dlc; + + if (id & CAN_RTR_FLAG) + msg_flag_n |= SUN4I_MSG_RTR_FLAG; + + if (id & CAN_EFF_FLAG) { + msg_flag_n |= SUN4I_MSG_EFF_FLAG; + dreg = SUN4I_REG_BUF5_ADDR; + writel((id >> 21) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); + writel((id >> 13) & 0xFF, priv->base + SUN4I_REG_BUF2_ADDR); + writel((id >> 5) & 0xFF, priv->base + SUN4I_REG_BUF3_ADDR); + writel((id << 3) & 0xF8, priv->base + SUN4I_REG_BUF4_ADDR); + } else { + dreg = SUN4I_REG_BUF3_ADDR; + writel((id >> 3) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); + writel((id << 5) & 0xE0, priv->base + SUN4I_REG_BUF2_ADDR); + } + + for (i = 0; i < dlc; i++) + writel(cf->data[i], priv->base + (dreg + i * 4)); + + writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR); + + can_put_echo_skb(skb, dev, 0); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ); + else + sun4i_can_write_cmdreg(priv, SUN4I_CMD_TRANS_REQ); + + return NETDEV_TX_OK; +} + +static void sun4i_can_rx(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 fi; + u32 dreg; + canid_t id; + int i; + + /* create zero'ed CAN frame buffer */ + skb = alloc_can_skb(dev, &cf); + if (!skb) + return; + + fi = readl(priv->base + SUN4I_REG_BUF0_ADDR); + cf->can_dlc = get_can_dlc(fi & 0x0F); + if (fi & SUN4I_MSG_EFF_FLAG) { + dreg = SUN4I_REG_BUF5_ADDR; + id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 21) | + (readl(priv->base + SUN4I_REG_BUF2_ADDR) << 13) | + (readl(priv->base + SUN4I_REG_BUF3_ADDR) << 5) | + ((readl(priv->base + SUN4I_REG_BUF4_ADDR) >> 3) & 0x1f); + id |= CAN_EFF_FLAG; + } else { + dreg = SUN4I_REG_BUF3_ADDR; + id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 3) | + ((readl(priv->base + SUN4I_REG_BUF2_ADDR) >> 5) & 0x7); + } + + /* remote frame ? */ + if (fi & SUN4I_MSG_RTR_FLAG) + id |= CAN_RTR_FLAG; + else + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = readl(priv->base + dreg + i * 4); + + cf->can_id = id; + + sun4i_can_write_cmdreg(priv, SUN4I_CMD_RELEASE_RBUF); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(dev, CAN_LED_EVENT_RX); +} + +static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status) +{ + struct sun4ican_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; + enum can_state rx_state, tx_state; + unsigned int rxerr, txerr, errc; + u32 ecc, alc; + + /* we don't skip if alloc fails because we want the stats anyhow */ + skb = alloc_can_err_skb(dev, &cf); + + errc = readl(priv->base + SUN4I_REG_ERRC_ADDR); + rxerr = (errc >> 16) & 0xFF; + txerr = errc & 0xFF; + + if (skb) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (isrc & SUN4I_INT_DATA_OR) { + /* data overrun interrupt */ + netdev_dbg(dev, "data overrun interrupt\n"); + if (likely(skb)) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + stats->rx_over_errors++; + stats->rx_errors++; + + /* reset the CAN IP by entering reset mode + * ignoring timeout error + */ + set_reset_mode(dev); + set_normal_mode(dev); + + /* clear bit */ + sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG); + } + if (isrc & SUN4I_INT_ERR_WRN) { + /* error warning interrupt */ + netdev_dbg(dev, "error warning interrupt\n"); + + if (status & SUN4I_STA_BUS_OFF) + state = CAN_STATE_BUS_OFF; + else if (status & SUN4I_STA_ERR_STA) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & SUN4I_INT_BUS_ERR) { + /* bus error interrupt */ + netdev_dbg(dev, "bus error interrupt\n"); + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + if (likely(skb)) { + ecc = readl(priv->base + SUN4I_REG_STA_ADDR); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SUN4I_STA_MASK_ERR) { + case SUN4I_STA_BIT_ERR: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SUN4I_STA_FORM_ERR: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SUN4I_STA_STUFF_ERR: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE) + >> 16; + break; + } + /* error occurred during transmission? */ + if ((ecc & SUN4I_STA_ERR_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + } + if (isrc & SUN4I_INT_ERR_PASSIVE) { + /* error passive interrupt */ + netdev_dbg(dev, "error passive interrupt\n"); + if (state == CAN_STATE_ERROR_PASSIVE) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_PASSIVE; + } + if (isrc & SUN4I_INT_ARB_LOST) { + /* arbitration lost interrupt */ + netdev_dbg(dev, "arbitration lost interrupt\n"); + alc = readl(priv->base + SUN4I_REG_STA_ADDR); + priv->can.can_stats.arbitration_lost++; + stats->tx_errors++; + if (likely(skb)) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = (alc >> 8) & 0x1f; + } + } + + if (state != priv->can.state) { + tx_state = txerr >= rxerr ? state : 0; + rx_state = txerr <= rxerr ? state : 0; + + if (likely(skb)) + can_change_state(dev, cf, tx_state, rx_state); + else + priv->can.state = state; + if (state == CAN_STATE_BUS_OFF) + can_bus_off(dev); + } + + if (likely(skb)) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } else { + return -ENOMEM; + } + + return 0; +} + +static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + u8 isrc, status; + int n = 0; + + while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) && + (n < SUN4I_CAN_MAX_IRQ)) { + n++; + status = readl(priv->base + SUN4I_REG_STA_ADDR); + + if (isrc & SUN4I_INT_WAKEUP) + netdev_warn(dev, "wakeup interrupt\n"); + + if (isrc & SUN4I_INT_TBUF_VLD) { + /* transmission complete interrupt */ + stats->tx_bytes += + readl(priv->base + + SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf; + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + } + if ((isrc & SUN4I_INT_RBUF_VLD) && + !(isrc & SUN4I_INT_DATA_OR)) { + /* receive interrupt - don't read if overrun occurred */ + while (status & SUN4I_STA_RBUF_RDY) { + /* RX buffer is not empty */ + sun4i_can_rx(dev); + status = readl(priv->base + SUN4I_REG_STA_ADDR); + } + } + if (isrc & + (SUN4I_INT_DATA_OR | SUN4I_INT_ERR_WRN | SUN4I_INT_BUS_ERR | + SUN4I_INT_ERR_PASSIVE | SUN4I_INT_ARB_LOST)) { + /* error interrupt */ + if (sun4i_can_err(dev, isrc, status)) + netdev_err(dev, "can't allocate buffer - clearing pending interrupts\n"); + } + /* clear interrupts */ + writel(isrc, priv->base + SUN4I_REG_INT_ADDR); + readl(priv->base + SUN4I_REG_INT_ADDR); + } + if (n >= SUN4I_CAN_MAX_IRQ) + netdev_dbg(dev, "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} + +static int sun4ican_open(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + + /* common open */ + err = open_candev(dev); + if (err) + return err; + + /* register interrupt handler */ + err = request_irq(dev->irq, sun4i_can_interrupt, 0, dev->name, dev); + if (err) { + netdev_err(dev, "request_irq err: %d\n", err); + goto exit_irq; + } + + /* turn on clocking for CAN peripheral block */ + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(dev, "could not enable CAN peripheral clock\n"); + goto exit_clock; + } + + err = sun4i_can_start(dev); + if (err) { + netdev_err(dev, "could not start CAN peripheral\n"); + goto exit_can_start; + } + + can_led_event(dev, CAN_LED_EVENT_OPEN); + netif_start_queue(dev); + + return 0; + +exit_can_start: + clk_disable_unprepare(priv->clk); +exit_clock: + free_irq(dev->irq, dev); +exit_irq: + close_candev(dev); + return err; +} + +static int sun4ican_close(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + sun4i_can_stop(dev); + clk_disable_unprepare(priv->clk); + + free_irq(dev->irq, dev); + close_candev(dev); + can_led_event(dev, CAN_LED_EVENT_STOP); + + return 0; +} + +static const struct net_device_ops sun4ican_netdev_ops = { + .ndo_open = sun4ican_open, + .ndo_stop = sun4ican_close, + .ndo_start_xmit = sun4ican_start_xmit, +}; + +static const struct of_device_id sun4ican_of_match[] = { + {.compatible = "allwinner,sun4i-a10-can"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, sun4ican_of_match); + +static int sun4ican_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + unregister_netdev(dev); + free_candev(dev); + + return 0; +} + +static int sun4ican_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *mem; + struct clk *clk; + void __iomem *addr; + int err, irq; + struct net_device *dev; + struct sun4ican_priv *priv; + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to request clock\n"); + err = -ENODEV; + goto exit; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get a valid irq\n"); + err = -ENODEV; + goto exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = -EBUSY; + goto exit; + } + + dev = alloc_candev(sizeof(struct sun4ican_priv), 1); + if (!dev) { + dev_err(&pdev->dev, + "could not allocate memory for CAN device\n"); + err = -ENOMEM; + goto exit; + } + + dev->netdev_ops = &sun4ican_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 = &sun4ican_bittiming_const; + priv->can.do_set_mode = sun4ican_set_mode; + priv->can.do_get_berr_counter = sun4ican_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_3_SAMPLES; + priv->base = addr; + priv->clk = clk; + spin_lock_init(&priv->cmdreg_lock); + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + devm_can_led_init(dev); + + dev_info(&pdev->dev, "device registered (base=%p, irq=%d)\n", + priv->base, dev->irq); + + return 0; + +exit_free: + free_candev(dev); +exit: + return err; +} + +static struct platform_driver sun4i_can_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = sun4ican_of_match, + }, + .probe = sun4ican_probe, + .remove = sun4ican_remove, +}; + +module_platform_driver(sun4i_can_driver); + +MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>"); +MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20)"); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c new file mode 100644 index 000000000..81a3fdd5e --- /dev/null +++ b/drivers/net/can/ti_hecc.c @@ -0,0 +1,1072 @@ +/* + * 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. + * + */ + +#include <linux/module.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/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.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 const 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; + void __iomem *hecc_ram; + void __iomem *mbx; + bool use_hecc1int; + spinlock_t mbx_lock; /* CANME register needs protection */ + u32 tx_head; + u32 tx_tail; + u32 rx_next; + struct regulator *reg_xceiver; +}; + +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->hecc_ram + mbxno * 4); +} + +static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno, + u32 reg, u32 val) +{ + __raw_writel(val, priv->mbx + mbxno * 0x10 + reg); +} + +static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg) +{ + return __raw_readl(priv->mbx + 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_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 int ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, + int on) +{ + if (!priv->reg_xceiver) + return 0; + + if (on) + return regulator_enable(priv->reg_xceiver); + else + return regulator_disable(priv->reg_xceiver); +} + +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->use_hecc1int) { + 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(*(__be32 *)(cf->data))); + if (cf->can_dlc > 4) + hecc_write_mbx(priv, mbxno, HECC_CANMDH, + be32_to_cpu(*(__be32 *)(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); + *(__be32 *)(cf->data) = cpu_to_be32(data); + if (cf->can_dlc > 4) { + data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); + *(__be32 *)(cf->data + 4) = cpu_to_be32(data); + } + 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; + can_led_event(priv->ndev, CAN_LED_EVENT_RX); + 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); + } else { + /* repoll is done only if whole budget is used */ + num_pkts = quota; + } + + 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); + ++priv->can.can_stats.bus_off; + 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; + 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[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + } + if (err_status & HECC_CANES_ACKE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + } + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + 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->use_hecc1int) ? 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_led_event(ndev, CAN_LED_EVENT_TX); + 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->use_hecc1int) { + 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; + } + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + + 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); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct of_device_id ti_hecc_dt_ids[] = { + { + .compatible = "ti,am3517-hecc", + }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_hecc_dt_ids); + +static int ti_hecc_probe(struct platform_device *pdev) +{ + struct net_device *ndev = (struct net_device *)0; + struct ti_hecc_priv *priv; + struct device_node *np = pdev->dev.of_node; + struct resource *res, *irq; + struct regulator *reg_xceiver; + int err = -ENODEV; + + if (!IS_ENABLED(CONFIG_OF) || !np) + return -EINVAL; + + reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (IS_ERR(reg_xceiver)) + reg_xceiver = NULL; + + ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + return -ENOMEM; + } + priv = netdev_priv(ndev); + + /* handle hecc memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc"); + if (!res) { + dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc\n"); + return -EINVAL; + } + + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "hecc ioremap failed\n"); + err = PTR_ERR(priv->base); + goto probe_exit_candev; + } + + /* handle hecc-ram memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc-ram"); + if (!res) { + dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc-ram\n"); + return -EINVAL; + } + + priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->hecc_ram)) { + dev_err(&pdev->dev, "hecc-ram ioremap failed\n"); + err = PTR_ERR(priv->hecc_ram); + goto probe_exit_candev; + } + + /* handle mbx memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbx"); + if (!res) { + dev_err(&pdev->dev, "can't get IORESOURCE_MEM mbx\n"); + return -EINVAL; + } + + priv->mbx = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->mbx)) { + dev_err(&pdev->dev, "mbx ioremap failed\n"); + err = PTR_ERR(priv->mbx); + goto probe_exit_candev; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No irq resource\n"); + goto probe_exit_candev; + } + + priv->ndev = ndev; + priv->reg_xceiver = reg_xceiver; + priv->use_hecc1int = of_property_read_bool(np, "ti,use-hecc1int"); + + priv->can.bittiming_const = &ti_hecc_bittiming_const; + priv->can.do_set_mode = ti_hecc_do_set_mode; + 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); + + err = clk_prepare_enable(priv->clk); + if (err) { + dev_err(&pdev->dev, "clk_prepare_enable() failed\n"); + goto probe_exit_clk; + } + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + goto probe_exit_clk; + } + + devm_can_led_init(ndev); + + 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); + + return err; +} + +static int ti_hecc_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + free_candev(ndev); + + 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_unprepare(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); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + 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, + .of_match_table = ti_hecc_dt_ids, + }, + .probe = ti_hecc_probe, + .remove = 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); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig new file mode 100644 index 000000000..750d04d9e --- /dev/null +++ b/drivers/net/can/usb/Kconfig @@ -0,0 +1,126 @@ +menu "CAN USB interfaces" + depends on USB + +config CAN_8DEV_USB + tristate "8 devices USB2CAN interface" + ---help--- + This driver supports the USB2CAN interface + from 8 devices (http://www.8devices.com). + +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_GS_USB + tristate "Geschwister Schneider UG interfaces" + ---help--- + This driver supports the Geschwister Schneider and bytewerk.org + candleLight USB CAN interfaces USB/CAN devices + If unsure choose N, + choose Y for built in support, + M to compile as module (module will be named: gs_usb). + +config CAN_KVASER_USB + tristate "Kvaser CAN/USB interface" + ---help--- + This driver adds support for Kvaser CAN/USB devices like Kvaser + Leaf Light, Kvaser USBcan II and Kvaser Memorator Pro 5xHS. + + The driver provides support for the following devices: + - Kvaser Leaf Light + - Kvaser Leaf Professional HS + - Kvaser Leaf SemiPro HS + - Kvaser Leaf Professional LS + - Kvaser Leaf Professional SWC + - Kvaser Leaf Professional LIN + - Kvaser Leaf SemiPro LS + - Kvaser Leaf SemiPro SWC + - Kvaser Memorator II HS/HS + - Kvaser USBcan Professional HS/HS + - Kvaser Leaf Light GI + - Kvaser Leaf Professional HS (OBD-II connector) + - Kvaser Memorator Professional HS/LS + - Kvaser Leaf Light "China" + - Kvaser BlackBird SemiPro + - Kvaser USBcan R + - Kvaser Leaf Light v2 + - Kvaser Mini PCI Express HS + - Kvaser Mini PCI Express 2xHS + - Kvaser USBcan Light 2xHS + - Kvaser USBcan II HS/HS + - Kvaser USBcan II HS/LS + - Kvaser USBcan Rugged ("USBcan Rev B") + - Kvaser Memorator HS/HS + - Kvaser Memorator HS/LS + - Scania VCI2 (if you have the Kvaser logo on top) + - Kvaser BlackBird v2 + - Kvaser Leaf Pro HS v2 + - Kvaser Hybrid 2xCAN/LIN + - Kvaser Hybrid Pro 2xCAN/LIN + - Kvaser Memorator 2xHS v2 + - Kvaser Memorator Pro 2xHS v2 + - Kvaser Memorator Pro 5xHS + - Kvaser USBcan Light 4xHS + - Kvaser USBcan Pro 2xHS v2 + - Kvaser USBcan Pro 5xHS + - ATI Memorator Pro 2xHS v2 + - ATI USBcan Pro 2xHS v2 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called kvaser_usb. + +config CAN_MCBA_USB + tristate "Microchip CAN BUS Analyzer interface" + ---help--- + This driver supports the CAN BUS Analyzer interface + from Microchip (http://www.microchip.com/development-tools/). + +config CAN_PEAK_USB + tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD" + ---help--- + This driver supports the PEAK-System Technik USB adapters that enable + access to the CAN bus, with repect to the CAN 2.0b and/or CAN-FD + standards, that is: + + PCAN-USB single CAN 2.0b channel USB adapter + PCAN-USB Pro dual CAN 2.0b channels USB adapter + PCAN-USB FD single CAN-FD channel USB adapter + PCAN-USB Pro FD dual CAN-FD channels USB adapter + PCAN-Chip USB CAN-FD to USB stamp module + PCAN-USB X6 6 CAN-FD channels USB adapter + + (see also http://www.peak-system.com). + +config CAN_MCBA_USB + tristate "Microchip CAN BUS Analyzer interface" + ---help--- + This driver supports the CAN BUS Analyzer interface + from Microchip (http://www.microchip.com/development-tools/). + +config CAN_UCAN + tristate "Theobroma Systems UCAN interface" + ---help--- + This driver supports the Theobroma Systems + UCAN USB-CAN interface. + + The UCAN driver supports the microcontroller-based USB/CAN + adapters from Theobroma Systems. There are two form-factors + that run essentially the same firmware: + + * Seal: standalone USB stick + https://www.theobroma-systems.com/seal) + * Mule: integrated on the PCB of various System-on-Modules + from Theobroma Systems like the A31-µQ7 and the RK3399-Q7 + (https://www.theobroma-systems.com/rk3399-q7) + +endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile new file mode 100644 index 000000000..aa0f17c0b --- /dev/null +++ b/drivers/net/can/usb/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux Controller Area Network USB drivers. +# + +obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o +obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o +obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_GS_USB) += gs_usb.o +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/ +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ +obj-$(CONFIG_CAN_UCAN) += ucan.o diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c new file mode 100644 index 000000000..31a070226 --- /dev/null +++ b/drivers/net/can/usb/ems_usb.c @@ -0,0 +1,1099 @@ +/* + * 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/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 + +#define CPC_TX_QUEUE_TRIGGER_LOW 25 +#define CPC_TX_QUEUE_TRIGGER_HIGH 35 + +/* + * 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 { + __le32 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 */ + __le32 ts_sec; /* timestamp in seconds */ + __le32 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 */ + + 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 */ + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +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]; + if (dev->free_slots > CPC_TX_QUEUE_TRIGGER_HIGH && + netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -EPIPE: + case -EPROTO: + 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]; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +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; + + dev->can.can_stats.bus_off++; + 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[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->can_id |= CAN_ERR_CRTL; + 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++; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +/* + * 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, start; + + msg_count = 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); + + netif_trans_update(netdev); + + /* 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; + +} + +/* + * 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 = 50; /* initial size */ + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &buf_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + urb->transfer_dma = buf_dma; + + 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); + usb_free_urb(urb); + break; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + + /* 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); + + for (i = 0; i < MAX_RX_URBS; ++i) + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, + dev->rxbuf[i], dev->rxbuf_dma[i]); + + 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; + } + + + 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) + 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 = cpu_to_le32(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; + } + + 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_free_coherent(dev->udev, size, buf, urb->transfer_dma); + usb_free_urb(urb); + + 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); + + 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 { + netif_trans_update(netdev); + + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS || + dev->free_slots < CPC_TX_QUEUE_TRIGGER_LOW) { + 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); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +static const 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); + + 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) + goto cleanup_candev; + + dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL); + if (!dev->intr_in_buffer) + goto cleanup_intr_urb; + + dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE + + sizeof(struct ems_cpc_msg), GFP_KERNEL); + if (!dev->tx_msg_buffer) + 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); + + unlink_all_urbs(dev); + + usb_free_urb(dev->intr_urb); + + kfree(dev->intr_in_buffer); + kfree(dev->tx_msg_buffer); + + free_candev(dev->netdev); + } +} + +/* 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 000000000..d4e6b40f0 --- /dev/null +++ b/drivers/net/can/usb/esd_usb2.c @@ -0,0 +1,1167 @@ +/* + * CAN driver for esd CAN-USB/2 and CAN-USB/Micro + * + * Copyright (C) 2010-2012 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/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 and CAN-USB/Micro 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 USB_CANUSBM_PRODUCT_ID 0x0011 + +#define ESD_USB2_CAN_CLOCK 60000000 +#define ESD_USBM_CAN_CLOCK 36000000 +#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_USBM_SJW_SHIFT 24 +#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; + u32 hnd; /* opaque handle, not used by device */ + __le32 id; /* upper 3 bits contain flags */ + u8 data[8]; +}; + +struct tx_done_msg { + u8 len; + u8 cmd; + u8 net; + u8 status; + u32 hnd; /* opaque handle, not used by device */ + __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)}, + {USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSBM_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; + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +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]; + + 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 rxerr = msg->msg.rx.data[2]; + u8 txerr = 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; + priv->can.can_stats.bus_off++; + 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[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; + } + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +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 & ~ESD_RTR); + + 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]; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } + + 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 -EPIPE: + case -EPROTO: + 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: + if (msg->msg.rx.net >= dev->net_count) { + dev_err(dev->udev->dev.parent, "format error\n"); + break; + } + + esd_usb2_rx_can_msg(dev->nets[msg->msg.rx.net], msg); + break; + + case CMD_CAN_TX: + if (msg->msg.txdone.net >= dev->net_count) { + dev_err(dev->udev->dev.parent, "format error\n"); + break; + } + + 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 net_device *netdev; + size_t size = sizeof(struct esd_usb2_msg); + + WARN_ON(!context); + + priv = context->priv; + netdev = priv->netdev; + + /* 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); + + netif_trans_update(netdev); +} + +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, 0444, 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, 0444, 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, 0444, 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; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &buf_dma); + if (!buf) { + dev_warn(dev->udev->dev.parent, + "No memory left for USB buffer\n"); + err = -ENOMEM; + goto freeurb; + } + + urb->transfer_dma = buf_dma; + + 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); + goto freeurb; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_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; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + err = -ENOMEM; + goto out; + } + + /* + * 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 out; + + err = esd_usb2_setup_rx_urbs(dev); + if (err) + goto out; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + +out: + if (err == -ENODEV) + netif_device_detach(netdev); + if (err) + netdev_err(netdev, "couldn't start device: %d\n", err); + + kfree(msg); + 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 < MAX_RX_URBS; ++i) + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, + dev->rxbuf[i], dev->rxbuf_dma[i]); + + 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; + } + + 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) { + 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; + } + + netif_trans_update(netdev); + + /* + * 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; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + /* 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); + + kfree(msg); + + 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, + .ndo_change_mtu = can_change_mtu, +}; + +static const 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; + int err; + u32 canbtr; + int sjw_shift; + + canbtr = ESD_USB2_UBR; + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + canbtr |= ESD_USB2_LOM; + + canbtr |= (bt->brp - 1) & (ESD_USB2_BRP_MAX - 1); + + if (le16_to_cpu(priv->usb2->udev->descriptor.idProduct) == + USB_CANUSBM_PRODUCT_ID) + sjw_shift = ESD_USBM_SJW_SHIFT; + else + sjw_shift = ESD_USB2_SJW_SHIFT; + + canbtr |= ((bt->sjw - 1) & (ESD_USB2_SJW_MAX - 1)) + << 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 = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + 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); + + err = esd_usb2_send_msg(priv->usb2, msg); + + kfree(msg); + return err; +} + +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) +{ + 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.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY; + + if (le16_to_cpu(dev->udev->descriptor.idProduct) == + USB_CANUSBM_PRODUCT_ID) + priv->can.clock.freq = ESD_USBM_CAN_CLOCK; + else { + priv->can.clock.freq = ESD_USB2_CAN_CLOCK; + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + } + + 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; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + netdev->netdev_ops = &esd_usb2_netdev_ops; + + SET_NETDEV_DEV(netdev, &intf->dev); + netdev->dev_id = index; + + 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); + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + err = -ENOMEM; + goto free_msg; + } + + /* 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_msg; + } + + err = esd_usb2_wait_msg(dev, msg); + if (err < 0) { + dev_err(&intf->dev, "no version message answer\n"); + goto free_msg; + } + + 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); + +free_msg: + kfree(msg); + if (err) + 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); + kfree(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/gs_usb.c b/drivers/net/can/usb/gs_usb.c new file mode 100644 index 000000000..b2a187bf6 --- /dev/null +++ b/drivers/net/can/usb/gs_usb.c @@ -0,0 +1,1066 @@ +/* CAN driver for Geschwister Schneider USB/CAN devices + * and bytewerk.org candleLight USB CAN interfaces. + * + * Copyright (C) 2013-2016 Geschwister Schneider Technologie-, + * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). + * Copyright (C) 2016 Hubert Denkmair + * + * Many thanks to all socketcan devs! + * + * 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/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +/* Device specific constants */ +#define USB_GSUSB_1_VENDOR_ID 0x1d50 +#define USB_GSUSB_1_PRODUCT_ID 0x606f + +#define USB_CANDLELIGHT_VENDOR_ID 0x1209 +#define USB_CANDLELIGHT_PRODUCT_ID 0x2323 + +#define GSUSB_ENDPOINT_IN 1 +#define GSUSB_ENDPOINT_OUT 2 + +/* Device specific constants */ +enum gs_usb_breq { + GS_USB_BREQ_HOST_FORMAT = 0, + GS_USB_BREQ_BITTIMING, + GS_USB_BREQ_MODE, + GS_USB_BREQ_BERR, + GS_USB_BREQ_BT_CONST, + GS_USB_BREQ_DEVICE_CONFIG, + GS_USB_BREQ_TIMESTAMP, + GS_USB_BREQ_IDENTIFY, +}; + +enum gs_can_mode { + /* reset a channel. turns it off */ + GS_CAN_MODE_RESET = 0, + /* starts a channel */ + GS_CAN_MODE_START +}; + +enum gs_can_state { + GS_CAN_STATE_ERROR_ACTIVE = 0, + GS_CAN_STATE_ERROR_WARNING, + GS_CAN_STATE_ERROR_PASSIVE, + GS_CAN_STATE_BUS_OFF, + GS_CAN_STATE_STOPPED, + GS_CAN_STATE_SLEEPING +}; + +enum gs_can_identify_mode { + GS_CAN_IDENTIFY_OFF = 0, + GS_CAN_IDENTIFY_ON +}; + +/* data types passed between host and device */ + +/* The firmware on the original USB2CAN by Geschwister Schneider + * Technologie Entwicklungs- und Vertriebs UG exchanges all data + * between the host and the device in host byte order. This is done + * with the struct gs_host_config::byte_order member, which is sent + * first to indicate the desired byte order. + * + * The widely used open source firmware candleLight doesn't support + * this feature and exchanges the data in little endian byte order. + */ +struct gs_host_config { + __le32 byte_order; +} __packed; + +struct gs_device_config { + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 icount; + __le32 sw_version; + __le32 hw_version; +} __packed; + +#define GS_CAN_MODE_NORMAL 0 +#define GS_CAN_MODE_LISTEN_ONLY BIT(0) +#define GS_CAN_MODE_LOOP_BACK BIT(1) +#define GS_CAN_MODE_TRIPLE_SAMPLE BIT(2) +#define GS_CAN_MODE_ONE_SHOT BIT(3) + +struct gs_device_mode { + __le32 mode; + __le32 flags; +} __packed; + +struct gs_device_state { + __le32 state; + __le32 rxerr; + __le32 txerr; +} __packed; + +struct gs_device_bittiming { + __le32 prop_seg; + __le32 phase_seg1; + __le32 phase_seg2; + __le32 sjw; + __le32 brp; +} __packed; + +struct gs_identify_mode { + __le32 mode; +} __packed; + +#define GS_CAN_FEATURE_LISTEN_ONLY BIT(0) +#define GS_CAN_FEATURE_LOOP_BACK BIT(1) +#define GS_CAN_FEATURE_TRIPLE_SAMPLE BIT(2) +#define GS_CAN_FEATURE_ONE_SHOT BIT(3) +#define GS_CAN_FEATURE_HW_TIMESTAMP BIT(4) +#define GS_CAN_FEATURE_IDENTIFY BIT(5) + +struct gs_device_bt_const { + __le32 feature; + __le32 fclk_can; + __le32 tseg1_min; + __le32 tseg1_max; + __le32 tseg2_min; + __le32 tseg2_max; + __le32 sjw_max; + __le32 brp_min; + __le32 brp_max; + __le32 brp_inc; +} __packed; + +#define GS_CAN_FLAG_OVERFLOW 1 + +struct gs_host_frame { + u32 echo_id; + __le32 can_id; + + u8 can_dlc; + u8 channel; + u8 flags; + u8 reserved; + + u8 data[8]; +} __packed; +/* The GS USB devices make use of the same flags and masks as in + * linux/can.h and linux/can/error.h, and no additional mapping is necessary. + */ + +/* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */ +#define GS_MAX_TX_URBS 10 +/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ +#define GS_MAX_RX_URBS 30 +/* Maximum number of interfaces the driver supports per device. + * Current hardware only supports 2 interfaces. The future may vary. + */ +#define GS_MAX_INTF 2 + +struct gs_tx_context { + struct gs_can *dev; + unsigned int echo_id; +}; + +struct gs_can { + struct can_priv can; /* must be the first member */ + + struct gs_usb *parent; + + struct net_device *netdev; + struct usb_device *udev; + struct usb_interface *iface; + + struct can_bittiming_const bt_const; + unsigned int channel; /* channel number */ + + /* This lock prevents a race condition between xmit and receive. */ + spinlock_t tx_ctx_lock; + struct gs_tx_context tx_context[GS_MAX_TX_URBS]; + + struct usb_anchor tx_submitted; + atomic_t active_tx_urbs; +}; + +/* usb interface struct */ +struct gs_usb { + struct gs_can *canch[GS_MAX_INTF]; + struct usb_anchor rx_submitted; + struct usb_device *udev; + u8 active_channels; +}; + +/* 'allocate' a tx context. + * returns a valid tx context or NULL if there is no space. + */ +static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev) +{ + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&dev->tx_ctx_lock, flags); + + for (; i < GS_MAX_TX_URBS; i++) { + if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) { + dev->tx_context[i].echo_id = i; + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return &dev->tx_context[i]; + } + } + + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return NULL; +} + +/* releases a tx context + */ +static void gs_free_tx_context(struct gs_tx_context *txc) +{ + txc->echo_id = GS_MAX_TX_URBS; +} + +/* Get a tx context by id. + */ +static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, + unsigned int id) +{ + unsigned long flags; + + if (id < GS_MAX_TX_URBS) { + spin_lock_irqsave(&dev->tx_ctx_lock, flags); + if (dev->tx_context[id].echo_id == id) { + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return &dev->tx_context[id]; + } + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + } + return NULL; +} + +static int gs_cmd_reset(struct gs_can *gsdev) +{ + struct gs_device_mode *dm; + struct usb_interface *intf = gsdev->iface; + int rc; + + dm = kzalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return -ENOMEM; + + dm->mode = GS_CAN_MODE_RESET; + + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + gsdev->channel, + 0, + dm, + sizeof(*dm), + 1000); + + kfree(dm); + + return rc; +} + +static void gs_update_state(struct gs_can *dev, struct can_frame *cf) +{ + struct can_device_stats *can_stats = &dev->can.can_stats; + + if (cf->can_id & CAN_ERR_RESTARTED) { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + can_stats->restarts++; + } else if (cf->can_id & CAN_ERR_BUSOFF) { + dev->can.state = CAN_STATE_BUS_OFF; + can_stats->bus_off++; + } else if (cf->can_id & CAN_ERR_CRTL) { + if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) || + (cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) { + dev->can.state = CAN_STATE_ERROR_WARNING; + can_stats->error_warning++; + } else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) || + (cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) { + dev->can.state = CAN_STATE_ERROR_PASSIVE; + can_stats->error_passive++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + } + } +} + +static void gs_usb_receive_bulk_callback(struct urb *urb) +{ + struct gs_usb *usbcan = urb->context; + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct net_device_stats *stats; + struct gs_host_frame *hf = urb->transfer_buffer; + struct gs_tx_context *txc; + struct can_frame *cf; + struct sk_buff *skb; + + BUG_ON(!usbcan); + + switch (urb->status) { + case 0: /* success */ + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + /* do not resubmit aborted urbs. eg: when device goes down */ + return; + } + + /* device reports out of range channel id */ + if (hf->channel >= GS_MAX_INTF) + goto device_detach; + + dev = usbcan->canch[hf->channel]; + + netdev = dev->netdev; + stats = &netdev->stats; + + if (!netif_device_present(netdev)) + return; + + if (hf->echo_id == -1) { /* normal rx */ + skb = alloc_can_skb(dev->netdev, &cf); + if (!skb) + return; + + cf->can_id = le32_to_cpu(hf->can_id); + + cf->can_dlc = get_can_dlc(hf->can_dlc); + memcpy(cf->data, hf->data, 8); + + /* ERROR frames tell us information about the controller */ + if (le32_to_cpu(hf->can_id) & CAN_ERR_FLAG) + gs_update_state(dev, cf); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += hf->can_dlc; + + netif_rx(skb); + } else { /* echo_id == hf->echo_id */ + if (hf->echo_id >= GS_MAX_TX_URBS) { + netdev_err(netdev, + "Unexpected out of range echo id %d\n", + hf->echo_id); + goto resubmit_urb; + } + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += hf->can_dlc; + + txc = gs_get_tx_context(dev, hf->echo_id); + + /* bad devices send bad echo_ids. */ + if (!txc) { + netdev_err(netdev, + "Unexpected unused echo id %d\n", + hf->echo_id); + goto resubmit_urb; + } + + can_get_echo_skb(netdev, hf->echo_id); + + gs_free_tx_context(txc); + + atomic_dec(&dev->active_tx_urbs); + + netif_wake_queue(netdev); + } + + if (hf->flags & GS_CAN_FLAG_OVERFLOW) { + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) + goto resubmit_urb; + + cf->can_id |= CAN_ERR_CRTL; + cf->can_dlc = CAN_ERR_DLC; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + netif_rx(skb); + } + + resubmit_urb: + usb_fill_bulk_urb(urb, + usbcan->udev, + usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN), + hf, + sizeof(struct gs_host_frame), + gs_usb_receive_bulk_callback, + usbcan + ); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + + /* USB failure take down all interfaces */ + if (rc == -ENODEV) { + device_detach: + for (rc = 0; rc < GS_MAX_INTF; rc++) { + if (usbcan->canch[rc]) + netif_device_detach(usbcan->canch[rc]->netdev); + } + } +} + +static int gs_usb_set_bittiming(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + struct usb_interface *intf = dev->iface; + int rc; + struct gs_device_bittiming *dbt; + + dbt = kmalloc(sizeof(*dbt), GFP_KERNEL); + if (!dbt) + return -ENOMEM; + + dbt->prop_seg = cpu_to_le32(bt->prop_seg); + dbt->phase_seg1 = cpu_to_le32(bt->phase_seg1); + dbt->phase_seg2 = cpu_to_le32(bt->phase_seg2); + dbt->sjw = cpu_to_le32(bt->sjw); + dbt->brp = cpu_to_le32(bt->brp); + + /* request bit timings */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BITTIMING, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, + 0, + dbt, + sizeof(*dbt), + 1000); + + kfree(dbt); + + if (rc < 0) + dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)", + rc); + + return (rc > 0) ? 0 : rc; +} + +static void gs_usb_xmit_callback(struct urb *urb) +{ + struct gs_tx_context *txc = urb->context; + struct gs_can *dev = txc->dev; + struct net_device *netdev = dev->netdev; + + if (urb->status) + netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id); + + usb_free_coherent(urb->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); +} + +static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct net_device_stats *stats = &dev->netdev->stats; + struct urb *urb; + struct gs_host_frame *hf; + struct can_frame *cf; + int rc; + unsigned int idx; + struct gs_tx_context *txc; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* find an empty context to keep track of transmission */ + txc = gs_alloc_tx_context(dev); + if (!txc) + return NETDEV_TX_BUSY; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + goto nomem_urb; + + hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC, + &urb->transfer_dma); + if (!hf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + goto nomem_hf; + } + + idx = txc->echo_id; + + if (idx >= GS_MAX_TX_URBS) { + netdev_err(netdev, "Invalid tx context %d\n", idx); + goto badidx; + } + + hf->echo_id = idx; + hf->channel = dev->channel; + hf->flags = 0; + hf->reserved = 0; + + cf = (struct can_frame *)skb->data; + + hf->can_id = cpu_to_le32(cf->can_id); + hf->can_dlc = cf->can_dlc; + memcpy(hf->data, cf->data, cf->can_dlc); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT), + hf, + sizeof(*hf), + gs_usb_xmit_callback, + txc); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, idx); + + atomic_inc(&dev->active_tx_urbs); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(rc)) { /* usb send failed */ + atomic_dec(&dev->active_tx_urbs); + + can_free_echo_skb(netdev, idx); + gs_free_tx_context(txc); + + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, + sizeof(*hf), + hf, + urb->transfer_dma); + + if (rc == -ENODEV) { + netif_device_detach(netdev); + } else { + netdev_err(netdev, "usb_submit failed (err=%d)\n", rc); + stats->tx_dropped++; + } + } else { + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + /* let usb core take care of this urb */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + + badidx: + usb_free_coherent(dev->udev, + sizeof(*hf), + hf, + urb->transfer_dma); + nomem_hf: + usb_free_urb(urb); + + nomem_urb: + gs_free_tx_context(txc); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; +} + +static int gs_can_open(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_usb *parent = dev->parent; + int rc, i; + struct gs_device_mode *dm; + u32 ctrlmode; + u32 flags = 0; + + rc = open_candev(netdev); + if (rc) + return rc; + + if (!parent->active_channels) { + for (i = 0; i < GS_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + /* alloc rx urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + /* alloc rx buffer */ + buf = usb_alloc_coherent(dev->udev, + sizeof(struct gs_host_frame), + GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + /* fill, anchor, and submit rx urb */ + usb_fill_bulk_urb(urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, + GSUSB_ENDPOINT_IN), + buf, + sizeof(struct gs_host_frame), + gs_usb_receive_bulk_callback, + parent); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &parent->rx_submitted); + + rc = usb_submit_urb(urb, GFP_KERNEL); + if (rc) { + if (rc == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_err(netdev, + "usb_submit failed (err=%d)\n", + rc); + + usb_unanchor_urb(urb); + usb_free_urb(urb); + break; + } + + /* Drop reference, + * USB core will take care of freeing it + */ + usb_free_urb(urb); + } + } + + dm = kmalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return -ENOMEM; + + /* flags */ + ctrlmode = dev->can.ctrlmode; + + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + flags |= GS_CAN_MODE_LOOP_BACK; + else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + flags |= GS_CAN_MODE_LISTEN_ONLY; + + /* Controller is not allowed to retry TX + * this mode is unavailable on atmels uc3c hardware + */ + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) + flags |= GS_CAN_MODE_ONE_SHOT; + + if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) + flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + + /* finally start device */ + dm->mode = cpu_to_le32(GS_CAN_MODE_START); + dm->flags = cpu_to_le32(flags); + rc = usb_control_msg(interface_to_usbdev(dev->iface), + usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0), + GS_USB_BREQ_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + dev->channel, + 0, + dm, + sizeof(*dm), + 1000); + + if (rc < 0) { + netdev_err(netdev, "Couldn't start device (err=%d)\n", rc); + kfree(dm); + return rc; + } + + kfree(dm); + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + parent->active_channels++; + if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_start_queue(netdev); + + return 0; +} + +static int gs_can_close(struct net_device *netdev) +{ + int rc; + struct gs_can *dev = netdev_priv(netdev); + struct gs_usb *parent = dev->parent; + + netif_stop_queue(netdev); + + /* Stop polling */ + parent->active_channels--; + if (!parent->active_channels) + usb_kill_anchored_urbs(&parent->rx_submitted); + + /* Stop sending URBs */ + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + /* reset the device */ + rc = gs_cmd_reset(dev); + if (rc < 0) + netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc); + + /* reset tx contexts */ + for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { + dev->tx_context[rc].dev = dev; + dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; + } + + /* close the netdev */ + close_candev(netdev); + + return 0; +} + +static const struct net_device_ops gs_usb_netdev_ops = { + .ndo_open = gs_can_open, + .ndo_stop = gs_can_close, + .ndo_start_xmit = gs_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int gs_usb_set_identify(struct net_device *netdev, bool do_identify) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_identify_mode *imode; + int rc; + + imode = kmalloc(sizeof(*imode), GFP_KERNEL); + + if (!imode) + return -ENOMEM; + + if (do_identify) + imode->mode = cpu_to_le32(GS_CAN_IDENTIFY_ON); + else + imode->mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF); + + rc = usb_control_msg(interface_to_usbdev(dev->iface), + usb_sndctrlpipe(interface_to_usbdev(dev->iface), + 0), + GS_USB_BREQ_IDENTIFY, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + dev->channel, + 0, + imode, + sizeof(*imode), + 100); + + kfree(imode); + + return (rc > 0) ? 0 : rc; +} + +/* blink LED's for finding the this interface */ +static int gs_usb_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + int rc = 0; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + rc = gs_usb_set_identify(dev, GS_CAN_IDENTIFY_ON); + break; + case ETHTOOL_ID_INACTIVE: + rc = gs_usb_set_identify(dev, GS_CAN_IDENTIFY_OFF); + break; + default: + break; + } + + return rc; +} + +static const struct ethtool_ops gs_usb_ethtool_ops = { + .set_phys_id = gs_usb_set_phys_id, +}; + +static struct gs_can *gs_make_candev(unsigned int channel, + struct usb_interface *intf, + struct gs_device_config *dconf) +{ + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct gs_device_bt_const *bt_const; + u32 feature; + + bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL); + if (!bt_const) + return ERR_PTR(-ENOMEM); + + /* fetch bit timing constants */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BT_CONST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + channel, + 0, + bt_const, + sizeof(*bt_const), + 1000); + + if (rc < 0) { + dev_err(&intf->dev, + "Couldn't get bit timing const for channel (err=%d)\n", + rc); + kfree(bt_const); + return ERR_PTR(rc); + } + + /* create netdev */ + netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't allocate candev\n"); + kfree(bt_const); + return ERR_PTR(-ENOMEM); + } + + dev = netdev_priv(netdev); + + netdev->netdev_ops = &gs_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ + + /* dev settup */ + strcpy(dev->bt_const.name, "gs_usb"); + dev->bt_const.tseg1_min = le32_to_cpu(bt_const->tseg1_min); + dev->bt_const.tseg1_max = le32_to_cpu(bt_const->tseg1_max); + dev->bt_const.tseg2_min = le32_to_cpu(bt_const->tseg2_min); + dev->bt_const.tseg2_max = le32_to_cpu(bt_const->tseg2_max); + dev->bt_const.sjw_max = le32_to_cpu(bt_const->sjw_max); + dev->bt_const.brp_min = le32_to_cpu(bt_const->brp_min); + dev->bt_const.brp_max = le32_to_cpu(bt_const->brp_max); + dev->bt_const.brp_inc = le32_to_cpu(bt_const->brp_inc); + + dev->udev = interface_to_usbdev(intf); + dev->iface = intf; + dev->netdev = netdev; + dev->channel = channel; + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + spin_lock_init(&dev->tx_ctx_lock); + for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { + dev->tx_context[rc].dev = dev; + dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; + } + + /* can settup */ + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = le32_to_cpu(bt_const->fclk_can); + dev->can.bittiming_const = &dev->bt_const; + dev->can.do_set_bittiming = gs_usb_set_bittiming; + + dev->can.ctrlmode_supported = 0; + + feature = le32_to_cpu(bt_const->feature); + if (feature & GS_CAN_FEATURE_LISTEN_ONLY) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + if (feature & GS_CAN_FEATURE_LOOP_BACK) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; + + if (feature & GS_CAN_FEATURE_TRIPLE_SAMPLE) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + + if (feature & GS_CAN_FEATURE_ONE_SHOT) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; + + SET_NETDEV_DEV(netdev, &intf->dev); + + if (le32_to_cpu(dconf->sw_version) > 1) + if (feature & GS_CAN_FEATURE_IDENTIFY) + netdev->ethtool_ops = &gs_usb_ethtool_ops; + + kfree(bt_const); + + rc = register_candev(dev->netdev); + if (rc) { + free_candev(dev->netdev); + dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc); + return ERR_PTR(rc); + } + + return dev; +} + +static void gs_destroy_candev(struct gs_can *dev) +{ + unregister_candev(dev->netdev); + usb_kill_anchored_urbs(&dev->tx_submitted); + free_candev(dev->netdev); +} + +static int gs_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct gs_usb *dev; + int rc = -ENOMEM; + unsigned int icount, i; + struct gs_host_config *hconf; + struct gs_device_config *dconf; + + hconf = kmalloc(sizeof(*hconf), GFP_KERNEL); + if (!hconf) + return -ENOMEM; + + hconf->byte_order = cpu_to_le32(0x0000beef); + + /* send host config */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_HOST_FORMAT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 1, + intf->cur_altsetting->desc.bInterfaceNumber, + hconf, + sizeof(*hconf), + 1000); + + kfree(hconf); + + if (rc < 0) { + dev_err(&intf->dev, "Couldn't send data format (err=%d)\n", + rc); + return rc; + } + + dconf = kmalloc(sizeof(*dconf), GFP_KERNEL); + if (!dconf) + return -ENOMEM; + + /* read device config */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_DEVICE_CONFIG, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 1, + intf->cur_altsetting->desc.bInterfaceNumber, + dconf, + sizeof(*dconf), + 1000); + if (rc < 0) { + dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n", + rc); + kfree(dconf); + return rc; + } + + icount = dconf->icount + 1; + dev_info(&intf->dev, "Configuring for %d interfaces\n", icount); + + if (icount > GS_MAX_INTF) { + dev_err(&intf->dev, + "Driver cannot handle more that %d CAN interfaces\n", + GS_MAX_INTF); + kfree(dconf); + return -EINVAL; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + kfree(dconf); + return -ENOMEM; + } + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + dev->udev = interface_to_usbdev(intf); + + for (i = 0; i < icount; i++) { + dev->canch[i] = gs_make_candev(i, intf, dconf); + if (IS_ERR_OR_NULL(dev->canch[i])) { + /* save error code to return later */ + rc = PTR_ERR(dev->canch[i]); + + /* on failure destroy previously created candevs */ + icount = i; + for (i = 0; i < icount; i++) + gs_destroy_candev(dev->canch[i]); + + usb_kill_anchored_urbs(&dev->rx_submitted); + kfree(dconf); + kfree(dev); + return rc; + } + dev->canch[i]->parent = dev; + } + + kfree(dconf); + + return 0; +} + +static void gs_usb_disconnect(struct usb_interface *intf) +{ + unsigned i; + struct gs_usb *dev = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + if (!dev) { + dev_err(&intf->dev, "Disconnect (nodata)\n"); + return; + } + + for (i = 0; i < GS_MAX_INTF; i++) + if (dev->canch[i]) + gs_destroy_candev(dev->canch[i]); + + usb_kill_anchored_urbs(&dev->rx_submitted); + kfree(dev); +} + +static const struct usb_device_id gs_usb_table[] = { + { USB_DEVICE_INTERFACE_NUMBER(USB_GSUSB_1_VENDOR_ID, + USB_GSUSB_1_PRODUCT_ID, 0) }, + { USB_DEVICE_INTERFACE_NUMBER(USB_CANDLELIGHT_VENDOR_ID, + USB_CANDLELIGHT_PRODUCT_ID, 0) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, gs_usb_table); + +static struct usb_driver gs_usb_driver = { + .name = "gs_usb", + .probe = gs_usb_probe, + .disconnect = gs_usb_disconnect, + .id_table = gs_usb_table, +}; + +module_usb_driver(gs_usb_driver); + +MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>"); +MODULE_DESCRIPTION( +"Socket CAN device driver for Geschwister Schneider Technologie-, " +"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces\n" +"and bytewerk.org candleLight USB CAN interfaces."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/kvaser_usb/Makefile b/drivers/net/can/usb/kvaser_usb/Makefile new file mode 100644 index 000000000..9f41ddab6 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o +kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h new file mode 100644 index 000000000..390b6bde8 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) + * - Kvaser linux mhydra driver (version 5.24) + * + * Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. + */ + +#ifndef KVASER_USB_H +#define KVASER_USB_H + +/* Kvaser USB CAN dongles are divided into three major platforms: + * - Hydra: Running firmware labeled as 'mhydra' + * - Leaf: Based on Renesas M32C or Freescale i.MX28, running firmware labeled + * as 'filo' + * - UsbcanII: Based on Renesas M16C, running firmware labeled as 'helios' + */ + +#include <linux/completion.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> + +#define KVASER_USB_MAX_RX_URBS 4 +#define KVASER_USB_MAX_TX_URBS 128 +#define KVASER_USB_TIMEOUT 1000 /* msecs */ +#define KVASER_USB_RX_BUFFER_SIZE 3072 +#define KVASER_USB_MAX_NET_DEVICES 5 + +/* USB devices features */ +#define KVASER_USB_HAS_SILENT_MODE BIT(0) +#define KVASER_USB_HAS_TXRX_ERRORS BIT(1) + +/* Device capabilities */ +#define KVASER_USB_CAP_BERR_CAP 0x01 +#define KVASER_USB_CAP_EXT_CAP 0x02 +#define KVASER_USB_HYDRA_CAP_EXT_CMD 0x04 + +struct kvaser_usb_dev_cfg; + +enum kvaser_usb_leaf_family { + KVASER_LEAF, + KVASER_USBCAN, +}; + +#define KVASER_USB_HYDRA_MAX_CMD_LEN 128 +struct kvaser_usb_dev_card_data_hydra { + u8 channel_to_he[KVASER_USB_MAX_NET_DEVICES]; + u8 sysdbg_he; + spinlock_t transid_lock; /* lock for transid */ + u16 transid; + /* lock for usb_rx_leftover and usb_rx_leftover_len */ + spinlock_t usb_rx_leftover_lock; + u8 usb_rx_leftover[KVASER_USB_HYDRA_MAX_CMD_LEN]; + u8 usb_rx_leftover_len; +}; +struct kvaser_usb_dev_card_data { + u32 ctrlmode_supported; + u32 capabilities; + union { + struct { + enum kvaser_usb_leaf_family family; + } leaf; + struct kvaser_usb_dev_card_data_hydra hydra; + }; +}; + +/* Context for an outstanding, not yet ACKed, transmission */ +struct kvaser_usb_tx_urb_context { + struct kvaser_usb_net_priv *priv; + u32 echo_index; + int dlc; +}; + +struct kvaser_usb { + struct usb_device *udev; + struct usb_interface *intf; + struct kvaser_usb_net_priv *nets[KVASER_USB_MAX_NET_DEVICES]; + const struct kvaser_usb_dev_ops *ops; + const struct kvaser_usb_dev_cfg *cfg; + + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + struct usb_anchor rx_submitted; + + /* @max_tx_urbs: Firmware-reported maximum number of outstanding, + * not yet ACKed, transmissions on this device. This value is + * also used as a sentinel for marking free tx contexts. + */ + u32 fw_version; + unsigned int nchannels; + unsigned int max_tx_urbs; + struct kvaser_usb_dev_card_data card_data; + + bool rxinitdone; + void *rxbuf[KVASER_USB_MAX_RX_URBS]; + dma_addr_t rxbuf_dma[KVASER_USB_MAX_RX_URBS]; +}; + +struct kvaser_usb_net_priv { + struct can_priv can; + struct can_berr_counter bec; + + struct kvaser_usb *dev; + struct net_device *netdev; + int channel; + + struct completion start_comp, stop_comp, flush_comp; + struct usb_anchor tx_submitted; + + spinlock_t tx_contexts_lock; /* lock for active_tx_contexts */ + int active_tx_contexts; + struct kvaser_usb_tx_urb_context tx_contexts[]; +}; + +/** + * struct kvaser_usb_dev_ops - Device specific functions + * @dev_set_mode: used for can.do_set_mode + * @dev_set_bittiming: used for can.do_set_bittiming + * @dev_set_data_bittiming: used for can.do_set_data_bittiming + * @dev_get_berr_counter: used for can.do_get_berr_counter + * + * @dev_setup_endpoints: setup USB in and out endpoints + * @dev_init_card: initialize card + * @dev_get_software_info: get software info + * @dev_get_software_details: get software details + * @dev_get_card_info: get card info + * @dev_get_capabilities: discover device capabilities + * + * @dev_set_opt_mode: set ctrlmod + * @dev_start_chip: start the CAN controller + * @dev_stop_chip: stop the CAN controller + * @dev_reset_chip: reset the CAN controller + * @dev_flush_queue: flush outstanding CAN messages + * @dev_read_bulk_callback: handle incoming commands + * @dev_frame_to_cmd: translate struct can_frame into device command + */ +struct kvaser_usb_dev_ops { + int (*dev_set_mode)(struct net_device *netdev, enum can_mode mode); + int (*dev_set_bittiming)(struct net_device *netdev); + int (*dev_set_data_bittiming)(struct net_device *netdev); + int (*dev_get_berr_counter)(const struct net_device *netdev, + struct can_berr_counter *bec); + int (*dev_setup_endpoints)(struct kvaser_usb *dev); + int (*dev_init_card)(struct kvaser_usb *dev); + int (*dev_get_software_info)(struct kvaser_usb *dev); + int (*dev_get_software_details)(struct kvaser_usb *dev); + int (*dev_get_card_info)(struct kvaser_usb *dev); + int (*dev_get_capabilities)(struct kvaser_usb *dev); + int (*dev_set_opt_mode)(const struct kvaser_usb_net_priv *priv); + int (*dev_start_chip)(struct kvaser_usb_net_priv *priv); + int (*dev_stop_chip)(struct kvaser_usb_net_priv *priv); + int (*dev_reset_chip)(struct kvaser_usb *dev, int channel); + int (*dev_flush_queue)(struct kvaser_usb_net_priv *priv); + void (*dev_read_bulk_callback)(struct kvaser_usb *dev, void *buf, + int len); + void *(*dev_frame_to_cmd)(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid); +}; + +struct kvaser_usb_dev_cfg { + const struct can_clock clock; + const unsigned int timestamp_freq; + const struct can_bittiming_const * const bittiming_const; + const struct can_bittiming_const * const data_bittiming_const; +}; + +extern const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops; +extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops; + +int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, + int *actual_len); + +int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len); + +int kvaser_usb_send_cmd_async(struct kvaser_usb_net_priv *priv, void *cmd, + int len); + +int kvaser_usb_can_rx_over_error(struct net_device *netdev); +#endif /* KVASER_USB_H */ diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c new file mode 100644 index 000000000..c89c7d490 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -0,0 +1,835 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) + * - Kvaser linux mhydra driver (version 5.24) + * + * Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/if.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +/* Kvaser USB vendor id. */ +#define KVASER_VENDOR_ID 0x0bfd + +/* Kvaser Leaf USB devices product ids */ +#define USB_LEAF_DEVEL_PRODUCT_ID 10 +#define USB_LEAF_LITE_PRODUCT_ID 11 +#define USB_LEAF_PRO_PRODUCT_ID 12 +#define USB_LEAF_SPRO_PRODUCT_ID 14 +#define USB_LEAF_PRO_LS_PRODUCT_ID 15 +#define USB_LEAF_PRO_SWC_PRODUCT_ID 16 +#define USB_LEAF_PRO_LIN_PRODUCT_ID 17 +#define USB_LEAF_SPRO_LS_PRODUCT_ID 18 +#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19 +#define USB_MEMO2_DEVEL_PRODUCT_ID 22 +#define USB_MEMO2_HSHS_PRODUCT_ID 23 +#define USB_UPRO_HSHS_PRODUCT_ID 24 +#define USB_LEAF_LITE_GI_PRODUCT_ID 25 +#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26 +#define USB_MEMO2_HSLS_PRODUCT_ID 27 +#define USB_LEAF_LITE_CH_PRODUCT_ID 28 +#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29 +#define USB_OEM_MERCURY_PRODUCT_ID 34 +#define USB_OEM_LEAF_PRODUCT_ID 35 +#define USB_CAN_R_PRODUCT_ID 39 +#define USB_LEAF_LITE_V2_PRODUCT_ID 288 +#define USB_MINI_PCIE_HS_PRODUCT_ID 289 +#define USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID 290 +#define USB_USBCAN_LIGHT_2HS_PRODUCT_ID 291 +#define USB_MINI_PCIE_2HS_PRODUCT_ID 292 + +/* Kvaser USBCan-II devices product ids */ +#define USB_USBCAN_REVB_PRODUCT_ID 2 +#define USB_VCI2_PRODUCT_ID 3 +#define USB_USBCAN2_PRODUCT_ID 4 +#define USB_MEMORATOR_PRODUCT_ID 5 + +/* Kvaser Minihydra USB devices product ids */ +#define USB_BLACKBIRD_V2_PRODUCT_ID 258 +#define USB_MEMO_PRO_5HS_PRODUCT_ID 260 +#define USB_USBCAN_PRO_5HS_PRODUCT_ID 261 +#define USB_USBCAN_LIGHT_4HS_PRODUCT_ID 262 +#define USB_LEAF_PRO_HS_V2_PRODUCT_ID 263 +#define USB_USBCAN_PRO_2HS_V2_PRODUCT_ID 264 +#define USB_MEMO_2HS_PRODUCT_ID 265 +#define USB_MEMO_PRO_2HS_V2_PRODUCT_ID 266 +#define USB_HYBRID_CANLIN_PRODUCT_ID 267 +#define USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID 268 +#define USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID 269 +#define USB_HYBRID_PRO_CANLIN_PRODUCT_ID 270 + +static inline bool kvaser_is_leaf(const struct usb_device_id *id) +{ + return (id->idProduct >= USB_LEAF_DEVEL_PRODUCT_ID && + id->idProduct <= USB_CAN_R_PRODUCT_ID) || + (id->idProduct >= USB_LEAF_LITE_V2_PRODUCT_ID && + id->idProduct <= USB_MINI_PCIE_2HS_PRODUCT_ID); +} + +static inline bool kvaser_is_usbcan(const struct usb_device_id *id) +{ + return id->idProduct >= USB_USBCAN_REVB_PRODUCT_ID && + id->idProduct <= USB_MEMORATOR_PRODUCT_ID; +} + +static inline bool kvaser_is_hydra(const struct usb_device_id *id) +{ + return id->idProduct >= USB_BLACKBIRD_V2_PRODUCT_ID && + id->idProduct <= USB_HYBRID_PRO_CANLIN_PRODUCT_ID; +} + +static const struct usb_device_id kvaser_usb_table[] = { + /* Leaf USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS | + KVASER_USB_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_2HS_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_2HS_PRODUCT_ID) }, + + /* USBCANII USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_REVB_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMORATOR_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_VCI2_PRODUCT_ID), + .driver_info = KVASER_USB_HAS_TXRX_ERRORS }, + + /* Minihydra USB product IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_PRO_5HS_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_PRO_5HS_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_4HS_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_HS_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_PRO_2HS_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_2HS_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_PRO_2HS_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_HYBRID_CANLIN_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_HYBRID_PRO_CANLIN_PRODUCT_ID) }, + { } +}; +MODULE_DEVICE_TABLE(usb, kvaser_usb_table); + +int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len) +{ + int actual_len; /* Not used */ + + return usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + cmd, len, &actual_len, KVASER_USB_TIMEOUT); +} + +int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, + int *actual_len) +{ + return usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + cmd, len, actual_len, KVASER_USB_TIMEOUT); +} + +static void kvaser_usb_send_cmd_callback(struct urb *urb) +{ + struct net_device *netdev = urb->context; + + kfree(urb->transfer_buffer); + + if (urb->status) + netdev_warn(netdev, "urb status received: %d\n", urb->status); +} + +int kvaser_usb_send_cmd_async(struct kvaser_usb_net_priv *priv, void *cmd, + int len) +{ + struct kvaser_usb *dev = priv->dev; + struct net_device *netdev = priv->netdev; + struct urb *urb; + int err; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + cmd, len, kvaser_usb_send_cmd_callback, netdev); + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + netdev_err(netdev, "Error transmitting URB\n"); + usb_unanchor_urb(urb); + } + usb_free_urb(urb); + + return 0; +} + +int kvaser_usb_can_rx_over_error(struct net_device *netdev) +{ + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return -ENOMEM; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +static void kvaser_usb_read_bulk_callback(struct urb *urb) +{ + struct kvaser_usb *dev = urb->context; + int err; + unsigned int i; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + default: + dev_info(&dev->intf->dev, "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + dev->ops->dev_read_bulk_callback(dev, urb->transfer_buffer, + urb->actual_length); + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + urb->transfer_buffer, KVASER_USB_RX_BUFFER_SIZE, + kvaser_usb_read_bulk_callback, dev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err == -ENODEV) { + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + netif_device_detach(dev->nets[i]->netdev); + } + } else if (err) { + dev_err(&dev->intf->dev, + "Failed resubmitting read bulk urb: %d\n", err); + } +} + +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev) +{ + int i, err = 0; + + if (dev->rxinitdone) + return 0; + + for (i = 0; i < KVASER_USB_MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + dma_addr_t buf_dma; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, KVASER_USB_RX_BUFFER_SIZE, + GFP_KERNEL, &buf_dma); + if (!buf) { + dev_warn(&dev->intf->dev, + "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->bulk_in->bEndpointAddress), + buf, KVASER_USB_RX_BUFFER_SIZE, + kvaser_usb_read_bulk_callback, dev); + urb->transfer_dma = buf_dma; + 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, + KVASER_USB_RX_BUFFER_SIZE, buf, + buf_dma); + usb_free_urb(urb); + break; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + + usb_free_urb(urb); + } + + if (i == 0) { + dev_warn(&dev->intf->dev, "Cannot setup read URBs, error %d\n", + err); + return err; + } else if (i < KVASER_USB_MAX_RX_URBS) { + dev_warn(&dev->intf->dev, "RX performances may be slow\n"); + } + + dev->rxinitdone = true; + + return 0; +} + +static int kvaser_usb_open(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + int err; + + err = open_candev(netdev); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(dev); + if (err) + goto error; + + err = dev->ops->dev_set_opt_mode(priv); + if (err) + goto error; + + err = dev->ops->dev_start_chip(priv); + if (err) { + netdev_warn(netdev, "Cannot start device, error %d\n", err); + goto error; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +error: + close_candev(netdev); + return err; +} + +static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv) +{ + int i, max_tx_urbs; + + max_tx_urbs = priv->dev->max_tx_urbs; + + priv->active_tx_contexts = 0; + for (i = 0; i < max_tx_urbs; i++) + priv->tx_contexts[i].echo_index = max_tx_urbs; +} + +/* This method might sleep. Do not call it in the atomic context + * of URB completions. + */ +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) +{ + usb_kill_anchored_urbs(&priv->tx_submitted); + kvaser_usb_reset_tx_urb_contexts(priv); +} + +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev) +{ + int i; + + usb_kill_anchored_urbs(&dev->rx_submitted); + + for (i = 0; i < KVASER_USB_MAX_RX_URBS; i++) + usb_free_coherent(dev->udev, KVASER_USB_RX_BUFFER_SIZE, + dev->rxbuf[i], dev->rxbuf_dma[i]); + + for (i = 0; i < dev->nchannels; i++) { + struct kvaser_usb_net_priv *priv = dev->nets[i]; + + if (priv) + kvaser_usb_unlink_tx_urbs(priv); + } +} + +static int kvaser_usb_close(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + int err; + + netif_stop_queue(netdev); + + err = dev->ops->dev_flush_queue(priv); + if (err) + netdev_warn(netdev, "Cannot flush queue, error %d\n", err); + + if (dev->ops->dev_reset_chip) { + err = dev->ops->dev_reset_chip(dev, priv->channel); + if (err) + netdev_warn(netdev, "Cannot reset card, error %d\n", + err); + } + + err = dev->ops->dev_stop_chip(priv); + if (err) + netdev_warn(netdev, "Cannot stop device, error %d\n", err); + + /* reset tx contexts */ + kvaser_usb_unlink_tx_urbs(priv); + + priv->can.state = CAN_STATE_STOPPED; + close_candev(priv->netdev); + + return 0; +} + +static void kvaser_usb_write_bulk_callback(struct urb *urb) +{ + struct kvaser_usb_tx_urb_context *context = urb->context; + struct kvaser_usb_net_priv *priv; + struct net_device *netdev; + + if (WARN_ON(!context)) + return; + + priv = context->priv; + netdev = priv->netdev; + + kfree(urb->transfer_buffer); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); +} + +static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + struct net_device_stats *stats = &netdev->stats; + struct kvaser_usb_tx_urb_context *context = NULL; + struct urb *urb; + void *buf; + int cmd_len = 0; + int err, ret = NETDEV_TX_OK; + unsigned int i; + unsigned long flags; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + stats->tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + for (i = 0; i < dev->max_tx_urbs; i++) { + if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) { + context = &priv->tx_contexts[i]; + + context->echo_index = i; + ++priv->active_tx_contexts; + if (priv->active_tx_contexts >= (int)dev->max_tx_urbs) + netif_stop_queue(netdev); + + break; + } + } + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); + + /* This should never happen; it implies a flow control bug */ + if (!context) { + netdev_warn(netdev, "cannot find free context\n"); + + ret = NETDEV_TX_BUSY; + goto freeurb; + } + + buf = dev->ops->dev_frame_to_cmd(priv, skb, &context->dlc, &cmd_len, + context->echo_index); + if (!buf) { + stats->tx_dropped++; + dev_kfree_skb(skb); + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); + goto freeurb; + } + + context->priv = priv; + + can_put_echo_skb(skb, netdev, context->echo_index); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + buf, cmd_len, kvaser_usb_write_bulk_callback, + context); + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + + can_free_echo_skb(netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); + + usb_unanchor_urb(urb); + kfree(buf); + + stats->tx_dropped++; + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "Failed tx_urb %d\n", err); + + goto freeurb; + } + + ret = NETDEV_TX_OK; + +freeurb: + usb_free_urb(urb); + return ret; +} + +static const struct net_device_ops kvaser_usb_netdev_ops = { + .ndo_open = kvaser_usb_open, + .ndo_stop = kvaser_usb_close, + .ndo_start_xmit = kvaser_usb_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) +{ + int i; + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + unregister_candev(dev->nets[i]->netdev); + } + + kvaser_usb_unlink_all_urbs(dev); + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + free_candev(dev->nets[i]->netdev); + } +} + +static int kvaser_usb_init_one(struct kvaser_usb *dev, + const struct usb_device_id *id, int channel) +{ + struct net_device *netdev; + struct kvaser_usb_net_priv *priv; + int err; + + if (dev->ops->dev_reset_chip) { + err = dev->ops->dev_reset_chip(dev, channel); + if (err) + return err; + } + + netdev = alloc_candev(sizeof(*priv) + + dev->max_tx_urbs * sizeof(*priv->tx_contexts), + dev->max_tx_urbs); + if (!netdev) { + dev_err(&dev->intf->dev, "Cannot alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + init_usb_anchor(&priv->tx_submitted); + init_completion(&priv->start_comp); + init_completion(&priv->stop_comp); + priv->can.ctrlmode_supported = 0; + + priv->dev = dev; + priv->netdev = netdev; + priv->channel = channel; + + spin_lock_init(&priv->tx_contexts_lock); + kvaser_usb_reset_tx_urb_contexts(priv); + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = dev->cfg->clock.freq; + priv->can.bittiming_const = dev->cfg->bittiming_const; + priv->can.do_set_bittiming = dev->ops->dev_set_bittiming; + priv->can.do_set_mode = dev->ops->dev_set_mode; + if ((id->driver_info & KVASER_USB_HAS_TXRX_ERRORS) || + (priv->dev->card_data.capabilities & KVASER_USB_CAP_BERR_CAP)) + priv->can.do_get_berr_counter = dev->ops->dev_get_berr_counter; + if (id->driver_info & KVASER_USB_HAS_SILENT_MODE) + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + priv->can.ctrlmode_supported |= dev->card_data.ctrlmode_supported; + + if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) { + priv->can.data_bittiming_const = dev->cfg->data_bittiming_const; + priv->can.do_set_data_bittiming = + dev->ops->dev_set_data_bittiming; + } + + netdev->flags |= IFF_ECHO; + + netdev->netdev_ops = &kvaser_usb_netdev_ops; + + SET_NETDEV_DEV(netdev, &dev->intf->dev); + netdev->dev_id = channel; + + dev->nets[channel] = priv; + + err = register_candev(netdev); + if (err) { + dev_err(&dev->intf->dev, "Failed to register CAN device\n"); + free_candev(netdev); + dev->nets[channel] = NULL; + return err; + } + + netdev_dbg(netdev, "device registered\n"); + + return 0; +} + +static int kvaser_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct kvaser_usb *dev; + int err; + int i; + + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + if (kvaser_is_leaf(id)) { + dev->card_data.leaf.family = KVASER_LEAF; + dev->ops = &kvaser_usb_leaf_dev_ops; + } else if (kvaser_is_usbcan(id)) { + dev->card_data.leaf.family = KVASER_USBCAN; + dev->ops = &kvaser_usb_leaf_dev_ops; + } else if (kvaser_is_hydra(id)) { + dev->ops = &kvaser_usb_hydra_dev_ops; + } else { + dev_err(&intf->dev, + "Product ID (%d) is not a supported Kvaser USB device\n", + id->idProduct); + return -ENODEV; + } + + dev->intf = intf; + + err = dev->ops->dev_setup_endpoints(dev); + if (err) { + dev_err(&intf->dev, "Cannot get usb endpoint(s)"); + return err; + } + + dev->udev = interface_to_usbdev(intf); + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + + dev->card_data.ctrlmode_supported = 0; + dev->card_data.capabilities = 0; + err = dev->ops->dev_init_card(dev); + if (err) { + dev_err(&intf->dev, + "Failed to initialize card, error %d\n", err); + return err; + } + + err = dev->ops->dev_get_software_info(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get software info, error %d\n", err); + return err; + } + + if (dev->ops->dev_get_software_details) { + err = dev->ops->dev_get_software_details(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get software details, error %d\n", err); + return err; + } + } + + if (WARN_ON(!dev->cfg)) + return -ENODEV; + + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", + ((dev->fw_version >> 24) & 0xff), + ((dev->fw_version >> 16) & 0xff), + (dev->fw_version & 0xffff)); + + dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs); + + err = dev->ops->dev_get_card_info(dev); + if (err) { + dev_err(&intf->dev, "Cannot get card info, error %d\n", err); + return err; + } + + if (dev->ops->dev_get_capabilities) { + err = dev->ops->dev_get_capabilities(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get capabilities, error %d\n", err); + kvaser_usb_remove_interfaces(dev); + return err; + } + } + + for (i = 0; i < dev->nchannels; i++) { + err = kvaser_usb_init_one(dev, id, i); + if (err) { + kvaser_usb_remove_interfaces(dev); + return err; + } + } + + return 0; +} + +static void kvaser_usb_disconnect(struct usb_interface *intf) +{ + struct kvaser_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (!dev) + return; + + kvaser_usb_remove_interfaces(dev); +} + +static struct usb_driver kvaser_usb_driver = { + .name = "kvaser_usb", + .probe = kvaser_usb_probe, + .disconnect = kvaser_usb_disconnect, + .id_table = kvaser_usb_table, +}; + +module_usb_driver(kvaser_usb_driver); + +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); +MODULE_AUTHOR("Kvaser AB <support@kvaser.com>"); +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c new file mode 100644 index 000000000..218fadc91 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -0,0 +1,2028 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux mhydra driver (version 5.24) + * - CAN driver for esd CAN-USB/2 + * + * Copyright (C) 2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * + * Known issues: + * - Transition from CAN_STATE_ERROR_WARNING to CAN_STATE_ERROR_ACTIVE is only + * reported after a call to do_get_berr_counter(), since firmware does not + * distinguish between ERROR_WARNING and ERROR_ACTIVE. + * - Hardware timestamps are not set for CAN Tx frames. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +/* Forward declarations */ +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_kcan; +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_flexc; + +#define KVASER_USB_HYDRA_BULK_EP_IN_ADDR 0x82 +#define KVASER_USB_HYDRA_BULK_EP_OUT_ADDR 0x02 + +#define KVASER_USB_HYDRA_MAX_TRANSID 0xff +#define KVASER_USB_HYDRA_MIN_TRANSID 0x01 + +/* Minihydra command IDs */ +#define CMD_SET_BUSPARAMS_REQ 16 +#define CMD_GET_CHIP_STATE_REQ 19 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_DRIVERMODE_REQ 21 +#define CMD_START_CHIP_REQ 26 +#define CMD_START_CHIP_RESP 27 +#define CMD_STOP_CHIP_REQ 28 +#define CMD_STOP_CHIP_RESP 29 +#define CMD_TX_CAN_MESSAGE 33 +#define CMD_GET_CARD_INFO_REQ 34 +#define CMD_GET_CARD_INFO_RESP 35 +#define CMD_GET_SOFTWARE_INFO_REQ 38 +#define CMD_GET_SOFTWARE_INFO_RESP 39 +#define CMD_ERROR_EVENT 45 +#define CMD_FLUSH_QUEUE 48 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_FLUSH_QUEUE_RESP 66 +#define CMD_SET_BUSPARAMS_FD_REQ 69 +#define CMD_SET_BUSPARAMS_FD_RESP 70 +#define CMD_SET_BUSPARAMS_RESP 85 +#define CMD_GET_CAPABILITIES_REQ 95 +#define CMD_GET_CAPABILITIES_RESP 96 +#define CMD_RX_MESSAGE 106 +#define CMD_MAP_CHANNEL_REQ 200 +#define CMD_MAP_CHANNEL_RESP 201 +#define CMD_GET_SOFTWARE_DETAILS_REQ 202 +#define CMD_GET_SOFTWARE_DETAILS_RESP 203 +#define CMD_EXTENDED 255 + +/* Minihydra extended command IDs */ +#define CMD_TX_CAN_MESSAGE_FD 224 +#define CMD_TX_ACKNOWLEDGE_FD 225 +#define CMD_RX_MESSAGE_FD 226 + +/* Hydra commands are handled by different threads in firmware. + * The threads are denoted hydra entity (HE). Each HE got a unique 6-bit + * address. The address is used in hydra commands to get/set source and + * destination HE. There are two predefined HE addresses, the remaining + * addresses are different between devices and firmware versions. Hence, we need + * to enumerate the addresses (see kvaser_usb_hydra_map_channel()). + */ + +/* Well-known HE addresses */ +#define KVASER_USB_HYDRA_HE_ADDRESS_ROUTER 0x00 +#define KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL 0x3e + +#define KVASER_USB_HYDRA_TRANSID_CANHE 0x40 +#define KVASER_USB_HYDRA_TRANSID_SYSDBG 0x61 + +struct kvaser_cmd_map_ch_req { + char name[16]; + u8 channel; + u8 reserved[11]; +} __packed; + +struct kvaser_cmd_map_ch_res { + u8 he_addr; + u8 channel; + u8 reserved[26]; +} __packed; + +struct kvaser_cmd_card_info { + __le32 serial_number; + __le32 clock_res; + __le32 mfg_date; + __le32 ean[2]; + u8 hw_version; + u8 usb_mode; + u8 hw_type; + u8 reserved0; + u8 nchannels; + u8 reserved1[3]; +} __packed; + +struct kvaser_cmd_sw_info { + u8 reserved0[8]; + __le16 max_outstanding_tx; + u8 reserved1[18]; +} __packed; + +struct kvaser_cmd_sw_detail_req { + u8 use_ext_cmd; + u8 reserved[27]; +} __packed; + +/* Software detail flags */ +#define KVASER_USB_HYDRA_SW_FLAG_FW_BETA BIT(2) +#define KVASER_USB_HYDRA_SW_FLAG_FW_BAD BIT(4) +#define KVASER_USB_HYDRA_SW_FLAG_FREQ_80M BIT(5) +#define KVASER_USB_HYDRA_SW_FLAG_EXT_CMD BIT(9) +#define KVASER_USB_HYDRA_SW_FLAG_CANFD BIT(10) +#define KVASER_USB_HYDRA_SW_FLAG_NONISO BIT(11) +#define KVASER_USB_HYDRA_SW_FLAG_EXT_CAP BIT(12) +struct kvaser_cmd_sw_detail_res { + __le32 sw_flags; + __le32 sw_version; + __le32 sw_name; + __le32 ean[2]; + __le32 max_bitrate; + u8 reserved[4]; +} __packed; + +/* Sub commands for cap_req and cap_res */ +#define KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE 0x02 +#define KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT 0x05 +#define KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT 0x06 +struct kvaser_cmd_cap_req { + __le16 cap_cmd; + u8 reserved[26]; +} __packed; + +/* Status codes for cap_res */ +#define KVASER_USB_HYDRA_CAP_STAT_OK 0x00 +#define KVASER_USB_HYDRA_CAP_STAT_NOT_IMPL 0x01 +#define KVASER_USB_HYDRA_CAP_STAT_UNAVAIL 0x02 +struct kvaser_cmd_cap_res { + __le16 cap_cmd; + __le16 status; + __le32 mask; + __le32 value; + u8 reserved[16]; +} __packed; + +/* CMD_ERROR_EVENT error codes */ +#define KVASER_USB_HYDRA_ERROR_EVENT_CAN 0x01 +#define KVASER_USB_HYDRA_ERROR_EVENT_PARAM 0x09 +struct kvaser_cmd_error_event { + __le16 timestamp[3]; + u8 reserved; + u8 error_code; + __le16 info1; + __le16 info2; +} __packed; + +/* Chip state status flags. Used for chip_state_event and err_frame_data. */ +#define KVASER_USB_HYDRA_BUS_ERR_ACT 0x00 +#define KVASER_USB_HYDRA_BUS_ERR_PASS BIT(5) +#define KVASER_USB_HYDRA_BUS_BUS_OFF BIT(6) +struct kvaser_cmd_chip_state_event { + __le16 timestamp[3]; + u8 tx_err_counter; + u8 rx_err_counter; + u8 bus_status; + u8 reserved[19]; +} __packed; + +/* Busparam modes */ +#define KVASER_USB_HYDRA_BUS_MODE_CAN 0x00 +#define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01 +#define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02 +struct kvaser_cmd_set_busparams { + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 nsamples; + u8 reserved0[4]; + __le32 bitrate_d; + u8 tseg1_d; + u8 tseg2_d; + u8 sjw_d; + u8 nsamples_d; + u8 canfd_mode; + u8 reserved1[7]; +} __packed; + +/* Ctrl modes */ +#define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01 +#define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02 +struct kvaser_cmd_set_ctrlmode { + u8 mode; + u8 reserved[27]; +} __packed; + +struct kvaser_err_frame_data { + u8 bus_status; + u8 reserved0; + u8 tx_err_counter; + u8 rx_err_counter; + u8 reserved1[4]; +} __packed; + +struct kvaser_cmd_rx_can { + u8 cmd_len; + u8 cmd_no; + u8 channel; + u8 flags; + __le16 timestamp[3]; + u8 dlc; + u8 padding; + __le32 id; + union { + u8 data[8]; + struct kvaser_err_frame_data err_frame_data; + }; +} __packed; + +/* Extended CAN ID flag. Used in rx_can and tx_can */ +#define KVASER_USB_HYDRA_EXTENDED_FRAME_ID BIT(31) +struct kvaser_cmd_tx_can { + __le32 id; + u8 data[8]; + u8 dlc; + u8 flags; + __le16 transid; + u8 channel; + u8 reserved[11]; +} __packed; + +struct kvaser_cmd_header { + u8 cmd_no; + /* The destination HE address is stored in 0..5 of he_addr. + * The upper part of source HE address is stored in 6..7 of he_addr, and + * the lower part is stored in 12..15 of transid. + */ + u8 he_addr; + __le16 transid; +} __packed; + +struct kvaser_cmd { + struct kvaser_cmd_header header; + union { + struct kvaser_cmd_map_ch_req map_ch_req; + struct kvaser_cmd_map_ch_res map_ch_res; + + struct kvaser_cmd_card_info card_info; + struct kvaser_cmd_sw_info sw_info; + struct kvaser_cmd_sw_detail_req sw_detail_req; + struct kvaser_cmd_sw_detail_res sw_detail_res; + + struct kvaser_cmd_cap_req cap_req; + struct kvaser_cmd_cap_res cap_res; + + struct kvaser_cmd_error_event error_event; + + struct kvaser_cmd_set_busparams set_busparams_req; + + struct kvaser_cmd_chip_state_event chip_state_event; + + struct kvaser_cmd_set_ctrlmode set_ctrlmode; + + struct kvaser_cmd_rx_can rx_can; + struct kvaser_cmd_tx_can tx_can; + } __packed; +} __packed; + +/* CAN frame flags. Used in rx_can, ext_rx_can, tx_can and ext_tx_can */ +#define KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME BIT(0) +#define KVASER_USB_HYDRA_CF_FLAG_OVERRUN BIT(1) +#define KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME BIT(4) +#define KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID BIT(5) +/* CAN frame flags. Used in ext_rx_can and ext_tx_can */ +#define KVASER_USB_HYDRA_CF_FLAG_OSM_NACK BIT(12) +#define KVASER_USB_HYDRA_CF_FLAG_ABL BIT(13) +#define KVASER_USB_HYDRA_CF_FLAG_FDF BIT(16) +#define KVASER_USB_HYDRA_CF_FLAG_BRS BIT(17) +#define KVASER_USB_HYDRA_CF_FLAG_ESI BIT(18) + +/* KCAN packet header macros. Used in ext_rx_can and ext_tx_can */ +#define KVASER_USB_KCAN_DATA_DLC_BITS 4 +#define KVASER_USB_KCAN_DATA_DLC_SHIFT 8 +#define KVASER_USB_KCAN_DATA_DLC_MASK \ + GENMASK(KVASER_USB_KCAN_DATA_DLC_BITS - 1 + \ + KVASER_USB_KCAN_DATA_DLC_SHIFT, \ + KVASER_USB_KCAN_DATA_DLC_SHIFT) + +#define KVASER_USB_KCAN_DATA_BRS BIT(14) +#define KVASER_USB_KCAN_DATA_FDF BIT(15) +#define KVASER_USB_KCAN_DATA_OSM BIT(16) +#define KVASER_USB_KCAN_DATA_AREQ BIT(31) +#define KVASER_USB_KCAN_DATA_SRR BIT(31) +#define KVASER_USB_KCAN_DATA_RTR BIT(29) +#define KVASER_USB_KCAN_DATA_IDE BIT(30) +struct kvaser_cmd_ext_rx_can { + __le32 flags; + __le32 id; + __le32 kcan_id; + __le32 kcan_header; + __le64 timestamp; + union { + u8 kcan_payload[64]; + struct kvaser_err_frame_data err_frame_data; + }; +} __packed; + +struct kvaser_cmd_ext_tx_can { + __le32 flags; + __le32 id; + __le32 kcan_id; + __le32 kcan_header; + u8 databytes; + u8 dlc; + u8 reserved[6]; + u8 kcan_payload[64]; +} __packed; + +struct kvaser_cmd_ext_tx_ack { + __le32 flags; + u8 reserved0[4]; + __le64 timestamp; + u8 reserved1[8]; +} __packed; + +/* struct for extended commands (CMD_EXTENDED) */ +struct kvaser_cmd_ext { + struct kvaser_cmd_header header; + __le16 len; + u8 cmd_no_ext; + u8 reserved; + + union { + struct kvaser_cmd_ext_rx_can rx_can; + struct kvaser_cmd_ext_tx_can tx_can; + struct kvaser_cmd_ext_tx_ack tx_ack; + } __packed; +} __packed; + +static const struct can_bittiming_const kvaser_usb_hydra_kcan_bittiming_c = { + .name = "kvaser_usb_kcan", + .tseg1_min = 1, + .tseg1_max = 255, + .tseg2_min = 1, + .tseg2_max = 32, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 8192, + .brp_inc = 1, +}; + +static const struct can_bittiming_const kvaser_usb_hydra_flexc_bittiming_c = { + .name = "kvaser_usb_flex", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +#define KVASER_USB_HYDRA_TRANSID_BITS 12 +#define KVASER_USB_HYDRA_TRANSID_MASK \ + GENMASK(KVASER_USB_HYDRA_TRANSID_BITS - 1, 0) +#define KVASER_USB_HYDRA_HE_ADDR_SRC_MASK GENMASK(7, 6) +#define KVASER_USB_HYDRA_HE_ADDR_DEST_MASK GENMASK(5, 0) +#define KVASER_USB_HYDRA_HE_ADDR_SRC_BITS 2 +static inline u16 kvaser_usb_hydra_get_cmd_transid(const struct kvaser_cmd *cmd) +{ + return le16_to_cpu(cmd->header.transid) & KVASER_USB_HYDRA_TRANSID_MASK; +} + +static inline void kvaser_usb_hydra_set_cmd_transid(struct kvaser_cmd *cmd, + u16 transid) +{ + cmd->header.transid = + cpu_to_le16(transid & KVASER_USB_HYDRA_TRANSID_MASK); +} + +static inline u8 kvaser_usb_hydra_get_cmd_src_he(const struct kvaser_cmd *cmd) +{ + return (cmd->header.he_addr & KVASER_USB_HYDRA_HE_ADDR_SRC_MASK) >> + KVASER_USB_HYDRA_HE_ADDR_SRC_BITS | + le16_to_cpu(cmd->header.transid) >> + KVASER_USB_HYDRA_TRANSID_BITS; +} + +static inline void kvaser_usb_hydra_set_cmd_dest_he(struct kvaser_cmd *cmd, + u8 dest_he) +{ + cmd->header.he_addr = + (cmd->header.he_addr & KVASER_USB_HYDRA_HE_ADDR_SRC_MASK) | + (dest_he & KVASER_USB_HYDRA_HE_ADDR_DEST_MASK); +} + +static u8 kvaser_usb_hydra_channel_from_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + int i; + u8 channel = 0xff; + u8 src_he = kvaser_usb_hydra_get_cmd_src_he(cmd); + + for (i = 0; i < KVASER_USB_MAX_NET_DEVICES; i++) { + if (dev->card_data.hydra.channel_to_he[i] == src_he) { + channel = i; + break; + } + } + + return channel; +} + +static u16 kvaser_usb_hydra_get_next_transid(struct kvaser_usb *dev) +{ + unsigned long flags; + u16 transid; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + spin_lock_irqsave(&card_data->transid_lock, flags); + transid = card_data->transid; + if (transid >= KVASER_USB_HYDRA_MAX_TRANSID) + transid = KVASER_USB_HYDRA_MIN_TRANSID; + else + transid++; + card_data->transid = transid; + spin_unlock_irqrestore(&card_data->transid_lock, flags); + + return transid; +} + +static size_t kvaser_usb_hydra_cmd_size(struct kvaser_cmd *cmd) +{ + size_t ret; + + if (cmd->header.cmd_no == CMD_EXTENDED) + ret = le16_to_cpu(((struct kvaser_cmd_ext *)cmd)->len); + else + ret = sizeof(struct kvaser_cmd); + + return ret; +} + +static struct kvaser_usb_net_priv * +kvaser_usb_hydra_net_priv_from_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv = NULL; + u8 channel = kvaser_usb_hydra_channel_from_cmd(dev, cmd); + + if (channel >= dev->nchannels) + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + else + priv = dev->nets[channel]; + + return priv; +} + +static ktime_t +kvaser_usb_hydra_ktime_from_rx_cmd(const struct kvaser_usb_dev_cfg *cfg, + const struct kvaser_cmd *cmd) +{ + u64 ticks; + + if (cmd->header.cmd_no == CMD_EXTENDED) { + struct kvaser_cmd_ext *cmd_ext = (struct kvaser_cmd_ext *)cmd; + + ticks = le64_to_cpu(cmd_ext->rx_can.timestamp); + } else { + ticks = le16_to_cpu(cmd->rx_can.timestamp[0]); + ticks += (u64)(le16_to_cpu(cmd->rx_can.timestamp[1])) << 16; + ticks += (u64)(le16_to_cpu(cmd->rx_can.timestamp[2])) << 32; + } + + return ns_to_ktime(div_u64(ticks * 1000, cfg->timestamp_freq)); +} + +static int kvaser_usb_hydra_send_simple_cmd(struct kvaser_usb *dev, + u8 cmd_no, int channel) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = cmd_no; + if (channel < 0) { + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); + } else { + if (channel >= KVASER_USB_MAX_NET_DEVICES) { + dev_err(&dev->intf->dev, "channel (%d) out of range.\n", + channel); + err = -EINVAL; + goto end; + } + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[channel]); + } + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + +end: + kfree(cmd); + + return err; +} + +static int +kvaser_usb_hydra_send_simple_cmd_async(struct kvaser_usb_net_priv *priv, + u8 cmd_no) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb *dev = priv->dev; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = cmd_no; + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd_async(priv, cmd, + kvaser_usb_hydra_cmd_size(cmd)); + if (err) + kfree(cmd); + + return err; +} + +/* This function is used for synchronously waiting on hydra control commands. + * Note: Compared to kvaser_usb_hydra_read_bulk_callback(), we never need to + * handle partial hydra commands. Since hydra control commands are always + * non-extended commands. + */ +static int kvaser_usb_hydra_wait_cmd(const struct kvaser_usb *dev, u8 cmd_no, + struct kvaser_cmd *cmd) +{ + void *buf; + int err; + unsigned long timeout = jiffies + msecs_to_jiffies(KVASER_USB_TIMEOUT); + + if (cmd->header.cmd_no == CMD_EXTENDED) { + dev_err(&dev->intf->dev, "Wait for CMD_EXTENDED not allowed\n"); + return -EINVAL; + } + + buf = kzalloc(KVASER_USB_RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + int actual_len = 0; + int pos = 0; + + err = kvaser_usb_recv_cmd(dev, buf, KVASER_USB_RX_BUFFER_SIZE, + &actual_len); + if (err < 0) + goto end; + + while (pos < actual_len) { + struct kvaser_cmd *tmp_cmd; + size_t cmd_len; + + tmp_cmd = buf + pos; + cmd_len = kvaser_usb_hydra_cmd_size(tmp_cmd); + if (pos + cmd_len > actual_len) { + dev_err_ratelimited(&dev->intf->dev, + "Format error\n"); + break; + } + + if (tmp_cmd->header.cmd_no == cmd_no) { + memcpy(cmd, tmp_cmd, cmd_len); + goto end; + } + pos += cmd_len; + } + } while (time_before(jiffies, timeout)); + + err = -EINVAL; + +end: + kfree(buf); + + return err; +} + +static int kvaser_usb_hydra_map_channel_resp(struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u8 he, channel; + u16 transid = kvaser_usb_hydra_get_cmd_transid(cmd); + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + if (transid > 0x007f || transid < 0x0040) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_RESP, invalid transid: 0x%x\n", + transid); + return -EINVAL; + } + + switch (transid) { + case KVASER_USB_HYDRA_TRANSID_CANHE: + case KVASER_USB_HYDRA_TRANSID_CANHE + 1: + case KVASER_USB_HYDRA_TRANSID_CANHE + 2: + case KVASER_USB_HYDRA_TRANSID_CANHE + 3: + case KVASER_USB_HYDRA_TRANSID_CANHE + 4: + channel = transid & 0x000f; + he = cmd->map_ch_res.he_addr; + card_data->channel_to_he[channel] = he; + break; + case KVASER_USB_HYDRA_TRANSID_SYSDBG: + card_data->sysdbg_he = cmd->map_ch_res.he_addr; + break; + default: + dev_warn(&dev->intf->dev, + "Unknown CMD_MAP_CHANNEL_RESP transid=0x%x\n", + transid); + break; + } + + return 0; +} + +static int kvaser_usb_hydra_map_channel(struct kvaser_usb *dev, u16 transid, + u8 channel, const char *name) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + strcpy(cmd->map_ch_req.name, name); + cmd->header.cmd_no = CMD_MAP_CHANNEL_REQ; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ROUTER); + cmd->map_ch_req.channel = channel; + + kvaser_usb_hydra_set_cmd_transid(cmd, transid); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_MAP_CHANNEL_RESP, cmd); + if (err) + goto end; + + err = kvaser_usb_hydra_map_channel_resp(dev, cmd); + if (err) + goto end; + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_single_capability(struct kvaser_usb *dev, + u16 cap_cmd_req, u16 *status) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + struct kvaser_cmd *cmd; + u32 value = 0; + u32 mask = 0; + u16 cap_cmd_res; + int err; + int i; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_CAPABILITIES_REQ; + cmd->cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); + + kvaser_usb_hydra_set_cmd_dest_he(cmd, card_data->hydra.sysdbg_he); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd); + if (err) + goto end; + + *status = le16_to_cpu(cmd->cap_res.status); + + if (*status != KVASER_USB_HYDRA_CAP_STAT_OK) + goto end; + + cap_cmd_res = le16_to_cpu(cmd->cap_res.cap_cmd); + switch (cap_cmd_res) { + case KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE: + case KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT: + case KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT: + value = le32_to_cpu(cmd->cap_res.value); + mask = le32_to_cpu(cmd->cap_res.mask); + break; + default: + dev_warn(&dev->intf->dev, "Unknown capability command %u\n", + cap_cmd_res); + break; + } + + for (i = 0; i < dev->nchannels; i++) { + if (BIT(i) & (value & mask)) { + switch (cap_cmd_res) { + case KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_LISTENONLY; + break; + case KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT: + card_data->capabilities |= + KVASER_USB_CAP_BERR_CAP; + break; + case KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_ONE_SHOT; + break; + } + } + } + +end: + kfree(cmd); + + return err; +} + +static void kvaser_usb_hydra_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + if (completion_done(&priv->start_comp) && + netif_queue_stopped(priv->netdev)) { + netif_wake_queue(priv->netdev); + } else { + netif_start_queue(priv->netdev); + complete(&priv->start_comp); + } +} + +static void kvaser_usb_hydra_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_hydra_flush_queue_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + complete(&priv->flush_comp); +} + +static void +kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv, + u8 bus_status, + const struct can_berr_counter *bec, + enum can_state *new_state) +{ + if (bus_status & KVASER_USB_HYDRA_BUS_BUS_OFF) { + *new_state = CAN_STATE_BUS_OFF; + } else if (bus_status & KVASER_USB_HYDRA_BUS_ERR_PASS) { + *new_state = CAN_STATE_ERROR_PASSIVE; + } else if (bus_status == KVASER_USB_HYDRA_BUS_ERR_ACT) { + if (bec->txerr >= 128 || bec->rxerr >= 128) { + netdev_warn(priv->netdev, + "ERR_ACTIVE but err tx=%u or rx=%u >=128\n", + bec->txerr, bec->rxerr); + *new_state = CAN_STATE_ERROR_PASSIVE; + } else if (bec->txerr >= 96 || bec->rxerr >= 96) { + *new_state = CAN_STATE_ERROR_WARNING; + } else { + *new_state = CAN_STATE_ERROR_ACTIVE; + } + } +} + +static void kvaser_usb_hydra_update_state(struct kvaser_usb_net_priv *priv, + u8 bus_status, + const struct can_berr_counter *bec) +{ + struct net_device *netdev = priv->netdev; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + enum can_state new_state, old_state; + + old_state = priv->can.state; + + kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, bec, + &new_state); + + if (new_state == old_state) + return; + + /* Ignore state change if previous state was STOPPED and the new state + * is BUS_OFF. Firmware always report this as BUS_OFF, since firmware + * does not distinguish between BUS_OFF and STOPPED. + */ + if (old_state == CAN_STATE_STOPPED && new_state == CAN_STATE_BUS_OFF) + return; + + skb = alloc_can_err_skb(netdev, &cf); + if (skb) { + enum can_state tx_state, rx_state; + + tx_state = (bec->txerr >= bec->rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (bec->txerr <= bec->rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + can_change_state(netdev, cf, tx_state, rx_state); + } + + if (new_state == CAN_STATE_BUS_OFF && old_state < CAN_STATE_BUS_OFF) { + if (!priv->can.restart_ms) + kvaser_usb_hydra_send_simple_cmd_async + (priv, CMD_STOP_CHIP_REQ); + + can_bus_off(netdev); + } + + if (!skb) { + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + priv->can.can_stats.restarts++; + + cf->data[6] = bec->txerr; + cf->data[7] = bec->rxerr; + + stats = &netdev->stats; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_state_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct can_berr_counter bec; + u8 bus_status; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + bus_status = cmd->chip_state_event.bus_status; + bec.txerr = cmd->chip_state_event.tx_err_counter; + bec.rxerr = cmd->chip_state_event.rx_err_counter; + + kvaser_usb_hydra_update_state(priv, bus_status, &bec); + priv->bec.txerr = bec.txerr; + priv->bec.rxerr = bec.rxerr; +} + +static void kvaser_usb_hydra_error_event_parameter(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + /* info1 will contain the offending cmd_no */ + switch (le16_to_cpu(cmd->error_event.info1)) { + case CMD_START_CHIP_REQ: + dev_warn(&dev->intf->dev, + "CMD_START_CHIP_REQ error in parameter\n"); + break; + + case CMD_STOP_CHIP_REQ: + dev_warn(&dev->intf->dev, + "CMD_STOP_CHIP_REQ error in parameter\n"); + break; + + case CMD_FLUSH_QUEUE: + dev_warn(&dev->intf->dev, + "CMD_FLUSH_QUEUE error in parameter\n"); + break; + + case CMD_SET_BUSPARAMS_REQ: + dev_warn(&dev->intf->dev, + "Set bittiming failed. Error in parameter\n"); + break; + + case CMD_SET_BUSPARAMS_FD_REQ: + dev_warn(&dev->intf->dev, + "Set data bittiming failed. Error in parameter\n"); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled parameter error event cmd_no (%u)\n", + le16_to_cpu(cmd->error_event.info1)); + break; + } +} + +static void kvaser_usb_hydra_error_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + switch (cmd->error_event.error_code) { + case KVASER_USB_HYDRA_ERROR_EVENT_PARAM: + kvaser_usb_hydra_error_event_parameter(dev, cmd); + break; + + case KVASER_USB_HYDRA_ERROR_EVENT_CAN: + /* Wrong channel mapping?! This should never happen! + * info1 will contain the offending cmd_no + */ + dev_err(&dev->intf->dev, + "Received CAN error event for cmd_no (%u)\n", + le16_to_cpu(cmd->error_event.info1)); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled error event (%d)\n", + cmd->error_event.error_code); + break; + } +} + +static void +kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv, + const struct kvaser_err_frame_data *err_frame_data, + ktime_t hwtstamp) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct can_berr_counter bec; + enum can_state new_state, old_state; + u8 bus_status; + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + bus_status = err_frame_data->bus_status; + bec.txerr = err_frame_data->tx_err_counter; + bec.rxerr = err_frame_data->rx_err_counter; + + old_state = priv->can.state; + kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, &bec, + &new_state); + + skb = alloc_can_err_skb(netdev, &cf); + + if (new_state != old_state) { + if (skb) { + enum can_state tx_state, rx_state; + + tx_state = (bec.txerr >= bec.rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (bec.txerr <= bec.rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + + can_change_state(netdev, cf, tx_state, rx_state); + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + cf->can_id |= CAN_ERR_RESTARTED; + } + + if (new_state == CAN_STATE_BUS_OFF) { + if (!priv->can.restart_ms) + kvaser_usb_hydra_send_simple_cmd_async + (priv, CMD_STOP_CHIP_REQ); + + can_bus_off(netdev); + } + } + + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id |= CAN_ERR_BUSERROR; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + priv->bec.txerr = bec.txerr; + priv->bec.rxerr = bec.rxerr; +} + +static void kvaser_usb_hydra_one_shot_fail(struct kvaser_usb_net_priv *priv, + const struct kvaser_cmd_ext *cmd) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 flags; + + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) { + stats->rx_dropped++; + netdev_warn(netdev, "No memory left for err_skb\n"); + return; + } + + cf->can_id |= CAN_ERR_BUSERROR; + flags = le32_to_cpu(cmd->tx_ack.flags); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OSM_NACK) + cf->can_id |= CAN_ERR_ACK; + if (flags & KVASER_USB_HYDRA_CF_FLAG_ABL) { + cf->can_id |= CAN_ERR_LOSTARB; + priv->can.can_stats.arbitration_lost++; + } + + stats->tx_errors++; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + unsigned long irq_flags; + bool one_shot_fail = false; + u16 transid = kvaser_usb_hydra_get_cmd_transid(cmd); + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + if (!netif_device_present(priv->netdev)) + return; + + if (cmd->header.cmd_no == CMD_EXTENDED) { + struct kvaser_cmd_ext *cmd_ext = (struct kvaser_cmd_ext *)cmd; + u32 flags = le32_to_cpu(cmd_ext->tx_ack.flags); + + if (flags & (KVASER_USB_HYDRA_CF_FLAG_OSM_NACK | + KVASER_USB_HYDRA_CF_FLAG_ABL)) { + kvaser_usb_hydra_one_shot_fail(priv, cmd_ext); + one_shot_fail = true; + } + } + + context = &priv->tx_contexts[transid % dev->max_tx_urbs]; + if (!one_shot_fail) { + struct net_device_stats *stats = &priv->netdev->stats; + + stats->tx_packets++; + stats->tx_bytes += can_dlc2len(context->dlc); + } + + spin_lock_irqsave(&priv->tx_contexts_lock, irq_flags); + + can_get_echo_skb(priv->netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(priv->netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, irq_flags); +} + +static void kvaser_usb_hydra_rx_msg_std(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv = NULL; + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct net_device_stats *stats; + u8 flags; + ktime_t hwtstamp; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + stats = &priv->netdev->stats; + + flags = cmd->rx_can.flags; + hwtstamp = kvaser_usb_hydra_ktime_from_rx_cmd(dev->cfg, cmd); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME) { + kvaser_usb_hydra_error_frame(priv, &cmd->rx_can.err_frame_data, + hwtstamp); + return; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id = le32_to_cpu(cmd->rx_can.id); + + if (cf->can_id & KVASER_USB_HYDRA_EXTENDED_FRAME_ID) { + cf->can_id &= CAN_EFF_MASK; + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id &= CAN_SFF_MASK; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); + + cf->can_dlc = get_can_dlc(cmd->rx_can.dlc); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, cmd->rx_can.data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_hydra_rx_msg_ext(const struct kvaser_usb *dev, + const struct kvaser_cmd_ext *cmd) +{ + struct kvaser_cmd *std_cmd = (struct kvaser_cmd *)cmd; + struct kvaser_usb_net_priv *priv; + struct canfd_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *shhwtstamps; + struct net_device_stats *stats; + u32 flags; + u8 dlc; + u32 kcan_header; + ktime_t hwtstamp; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, std_cmd); + if (!priv) + return; + + stats = &priv->netdev->stats; + + kcan_header = le32_to_cpu(cmd->rx_can.kcan_header); + dlc = (kcan_header & KVASER_USB_KCAN_DATA_DLC_MASK) >> + KVASER_USB_KCAN_DATA_DLC_SHIFT; + + flags = le32_to_cpu(cmd->rx_can.flags); + hwtstamp = kvaser_usb_hydra_ktime_from_rx_cmd(dev->cfg, std_cmd); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME) { + kvaser_usb_hydra_error_frame(priv, &cmd->rx_can.err_frame_data, + hwtstamp); + return; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_FDF) + skb = alloc_canfd_skb(priv->netdev, &cf); + else + skb = alloc_can_skb(priv->netdev, (struct can_frame **)&cf); + + if (!skb) { + stats->rx_dropped++; + return; + } + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = hwtstamp; + + cf->can_id = le32_to_cpu(cmd->rx_can.id); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID) { + cf->can_id &= CAN_EFF_MASK; + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id &= CAN_SFF_MASK; + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); + + if (flags & KVASER_USB_HYDRA_CF_FLAG_FDF) { + cf->len = can_dlc2len(get_canfd_dlc(dlc)); + if (flags & KVASER_USB_HYDRA_CF_FLAG_BRS) + cf->flags |= CANFD_BRS; + if (flags & KVASER_USB_HYDRA_CF_FLAG_ESI) + cf->flags |= CANFD_ESI; + } else { + cf->len = get_can_dlc(dlc); + } + + if (flags & KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, cmd->rx_can.kcan_payload, cf->len); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_rx(skb); +} + +static void kvaser_usb_hydra_handle_cmd_std(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + switch (cmd->header.cmd_no) { + case CMD_START_CHIP_RESP: + kvaser_usb_hydra_start_chip_reply(dev, cmd); + break; + + case CMD_STOP_CHIP_RESP: + kvaser_usb_hydra_stop_chip_reply(dev, cmd); + break; + + case CMD_FLUSH_QUEUE_RESP: + kvaser_usb_hydra_flush_queue_reply(dev, cmd); + break; + + case CMD_CHIP_STATE_EVENT: + kvaser_usb_hydra_state_event(dev, cmd); + break; + + case CMD_ERROR_EVENT: + kvaser_usb_hydra_error_event(dev, cmd); + break; + + case CMD_TX_ACKNOWLEDGE: + kvaser_usb_hydra_tx_acknowledge(dev, cmd); + break; + + case CMD_RX_MESSAGE: + kvaser_usb_hydra_rx_msg_std(dev, cmd); + break; + + /* Ignored commands */ + case CMD_SET_BUSPARAMS_RESP: + case CMD_SET_BUSPARAMS_FD_RESP: + break; + + default: + dev_warn(&dev->intf->dev, "Unhandled command (%d)\n", + cmd->header.cmd_no); + break; + } +} + +static void kvaser_usb_hydra_handle_cmd_ext(const struct kvaser_usb *dev, + const struct kvaser_cmd_ext *cmd) +{ + switch (cmd->cmd_no_ext) { + case CMD_TX_ACKNOWLEDGE_FD: + kvaser_usb_hydra_tx_acknowledge(dev, (struct kvaser_cmd *)cmd); + break; + + case CMD_RX_MESSAGE_FD: + kvaser_usb_hydra_rx_msg_ext(dev, cmd); + break; + + default: + dev_warn(&dev->intf->dev, "Unhandled extended command (%d)\n", + cmd->header.cmd_no); + break; + } +} + +static void kvaser_usb_hydra_handle_cmd(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + if (cmd->header.cmd_no == CMD_EXTENDED) + kvaser_usb_hydra_handle_cmd_ext + (dev, (struct kvaser_cmd_ext *)cmd); + else + kvaser_usb_hydra_handle_cmd_std(dev, cmd); +} + +static void * +kvaser_usb_hydra_frame_to_cmd_ext(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd_ext *cmd; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u8 dlc = can_len2dlc(cf->len); + u8 nbr_of_bytes = cf->len; + u32 flags; + u32 id; + u32 kcan_id; + u32 kcan_header; + + *frame_len = nbr_of_bytes; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd_ext), GFP_ATOMIC); + if (!cmd) + return NULL; + + kvaser_usb_hydra_set_cmd_dest_he + ((struct kvaser_cmd *)cmd, + dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid((struct kvaser_cmd *)cmd, transid); + + cmd->header.cmd_no = CMD_EXTENDED; + cmd->cmd_no_ext = CMD_TX_CAN_MESSAGE_FD; + + *cmd_len = ALIGN(sizeof(struct kvaser_cmd_ext) - + sizeof(cmd->tx_can.kcan_payload) + nbr_of_bytes, + 8); + + cmd->len = cpu_to_le16(*cmd_len); + + cmd->tx_can.databytes = nbr_of_bytes; + cmd->tx_can.dlc = dlc; + + if (cf->can_id & CAN_EFF_FLAG) { + id = cf->can_id & CAN_EFF_MASK; + flags = KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID; + kcan_id = (cf->can_id & CAN_EFF_MASK) | + KVASER_USB_KCAN_DATA_IDE | KVASER_USB_KCAN_DATA_SRR; + } else { + id = cf->can_id & CAN_SFF_MASK; + flags = 0; + kcan_id = cf->can_id & CAN_SFF_MASK; + } + + if (cf->can_id & CAN_ERR_FLAG) + flags |= KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME; + + kcan_header = ((dlc << KVASER_USB_KCAN_DATA_DLC_SHIFT) & + KVASER_USB_KCAN_DATA_DLC_MASK) | + KVASER_USB_KCAN_DATA_AREQ | + (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT ? + KVASER_USB_KCAN_DATA_OSM : 0); + + if (can_is_canfd_skb(skb)) { + kcan_header |= KVASER_USB_KCAN_DATA_FDF | + (cf->flags & CANFD_BRS ? + KVASER_USB_KCAN_DATA_BRS : 0); + } else { + if (cf->can_id & CAN_RTR_FLAG) { + kcan_id |= KVASER_USB_KCAN_DATA_RTR; + cmd->tx_can.databytes = 0; + flags |= KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME; + } + } + + cmd->tx_can.kcan_id = cpu_to_le32(kcan_id); + cmd->tx_can.id = cpu_to_le32(id); + cmd->tx_can.flags = cpu_to_le32(flags); + cmd->tx_can.kcan_header = cpu_to_le32(kcan_header); + + memcpy(cmd->tx_can.kcan_payload, cf->data, nbr_of_bytes); + + return cmd; +} + +static void * +kvaser_usb_hydra_frame_to_cmd_std(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 flags; + u32 id; + + *frame_len = cf->can_dlc; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_ATOMIC); + if (!cmd) + return NULL; + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid(cmd, transid); + + cmd->header.cmd_no = CMD_TX_CAN_MESSAGE; + + *cmd_len = ALIGN(sizeof(struct kvaser_cmd), 8); + + if (cf->can_id & CAN_EFF_FLAG) { + id = (cf->can_id & CAN_EFF_MASK); + id |= KVASER_USB_HYDRA_EXTENDED_FRAME_ID; + } else { + id = cf->can_id & CAN_SFF_MASK; + } + + cmd->tx_can.dlc = cf->can_dlc; + + flags = (cf->can_id & CAN_EFF_FLAG ? + KVASER_USB_HYDRA_CF_FLAG_EXTENDED_ID : 0); + + if (cf->can_id & CAN_RTR_FLAG) + flags |= KVASER_USB_HYDRA_CF_FLAG_REMOTE_FRAME; + + flags |= (cf->can_id & CAN_ERR_FLAG ? + KVASER_USB_HYDRA_CF_FLAG_ERROR_FRAME : 0); + + cmd->tx_can.id = cpu_to_le32(id); + cmd->tx_can.flags = flags; + + memcpy(cmd->tx_can.data, cf->data, *frame_len); + + return cmd; +} + +static int kvaser_usb_hydra_set_mode(struct net_device *netdev, + enum can_mode mode) +{ + int err = 0; + + switch (mode) { + case CAN_MODE_START: + /* CAN controller automatically recovers from BUS_OFF */ + break; + default: + err = -EOPNOTSUPP; + } + + return err; +} + +static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct can_bittiming *bt = &priv->can.bittiming; + struct kvaser_usb *dev = priv->dev; + int tseg1 = bt->prop_seg + bt->phase_seg1; + int tseg2 = bt->phase_seg2; + int sjw = bt->sjw; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_SET_BUSPARAMS_REQ; + cmd->set_busparams_req.bitrate = cpu_to_le32(bt->bitrate); + cmd->set_busparams_req.sjw = (u8)sjw; + cmd->set_busparams_req.tseg1 = (u8)tseg1; + cmd->set_busparams_req.tseg2 = (u8)tseg2; + cmd->set_busparams_req.nsamples = 1; + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) +{ + struct kvaser_cmd *cmd; + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct can_bittiming *dbt = &priv->can.data_bittiming; + struct kvaser_usb *dev = priv->dev; + int tseg1 = dbt->prop_seg + dbt->phase_seg1; + int tseg2 = dbt->phase_seg2; + int sjw = dbt->sjw; + int err; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_SET_BUSPARAMS_FD_REQ; + cmd->set_busparams_req.bitrate_d = cpu_to_le32(dbt->bitrate); + cmd->set_busparams_req.sjw_d = (u8)sjw; + cmd->set_busparams_req.tseg1_d = (u8)tseg1; + cmd->set_busparams_req.tseg2_d = (u8)tseg2; + cmd->set_busparams_req.nsamples_d = 1; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + cmd->set_busparams_req.canfd_mode = + KVASER_USB_HYDRA_BUS_MODE_NONISO; + else + cmd->set_busparams_req.canfd_mode = + KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO; + } + + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + int err; + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, + CMD_GET_CHIP_STATE_REQ, + priv->channel); + if (err) + return err; + + *bec = priv->bec; + + return 0; +} + +static int kvaser_usb_hydra_setup_endpoints(struct kvaser_usb *dev) +{ + const struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *ep; + int i; + + iface_desc = dev->intf->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + ep = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in && usb_endpoint_is_bulk_in(ep) && + ep->bEndpointAddress == KVASER_USB_HYDRA_BULK_EP_IN_ADDR) + dev->bulk_in = ep; + + if (!dev->bulk_out && usb_endpoint_is_bulk_out(ep) && + ep->bEndpointAddress == KVASER_USB_HYDRA_BULK_EP_OUT_ADDR) + dev->bulk_out = ep; + + if (dev->bulk_in && dev->bulk_out) + return 0; + } + + return -ENODEV; +} + +static int kvaser_usb_hydra_init_card(struct kvaser_usb *dev) +{ + int err; + unsigned int i; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + + card_data->transid = KVASER_USB_HYDRA_MIN_TRANSID; + spin_lock_init(&card_data->transid_lock); + + memset(card_data->usb_rx_leftover, 0, KVASER_USB_HYDRA_MAX_CMD_LEN); + card_data->usb_rx_leftover_len = 0; + spin_lock_init(&card_data->usb_rx_leftover_lock); + + memset(card_data->channel_to_he, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL, + sizeof(card_data->channel_to_he)); + card_data->sysdbg_he = 0; + + for (i = 0; i < KVASER_USB_MAX_NET_DEVICES; i++) { + err = kvaser_usb_hydra_map_channel + (dev, + (KVASER_USB_HYDRA_TRANSID_CANHE | i), + i, "CAN"); + if (err) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_REQ failed for CAN%u\n", i); + return err; + } + } + + err = kvaser_usb_hydra_map_channel(dev, KVASER_USB_HYDRA_TRANSID_SYSDBG, + 0, "SYSDBG"); + if (err) { + dev_err(&dev->intf->dev, + "CMD_MAP_CHANNEL_REQ failed for SYSDBG\n"); + return err; + } + + return 0; +} + +static int kvaser_usb_hydra_get_software_info(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_hydra_send_simple_cmd(dev, CMD_GET_SOFTWARE_INFO_REQ, + -1); + if (err) + return err; + + memset(&cmd, 0, sizeof(struct kvaser_cmd)); + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_SOFTWARE_INFO_RESP, &cmd); + if (err) + return err; + + dev->max_tx_urbs = min_t(unsigned int, KVASER_USB_MAX_TX_URBS, + le16_to_cpu(cmd.sw_info.max_outstanding_tx)); + + return 0; +} + +static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) +{ + struct kvaser_cmd *cmd; + int err; + u32 flags; + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_SOFTWARE_DETAILS_REQ; + cmd->sw_detail_req.use_ext_cmd = 1; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); + + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + goto end; + + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_SOFTWARE_DETAILS_RESP, + cmd); + if (err) + goto end; + + dev->fw_version = le32_to_cpu(cmd->sw_detail_res.sw_version); + flags = le32_to_cpu(cmd->sw_detail_res.sw_flags); + + if (flags & KVASER_USB_HYDRA_SW_FLAG_FW_BAD) { + dev_err(&dev->intf->dev, + "Bad firmware, device refuse to run!\n"); + err = -EINVAL; + goto end; + } + + if (flags & KVASER_USB_HYDRA_SW_FLAG_FW_BETA) + dev_info(&dev->intf->dev, "Beta firmware in use\n"); + + if (flags & KVASER_USB_HYDRA_SW_FLAG_EXT_CAP) + card_data->capabilities |= KVASER_USB_CAP_EXT_CAP; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_EXT_CMD) + card_data->capabilities |= KVASER_USB_HYDRA_CAP_EXT_CMD; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_CANFD) + card_data->ctrlmode_supported |= CAN_CTRLMODE_FD; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_NONISO) + card_data->ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO; + + if (flags & KVASER_USB_HYDRA_SW_FLAG_FREQ_80M) + dev->cfg = &kvaser_usb_hydra_dev_cfg_kcan; + else + dev->cfg = &kvaser_usb_hydra_dev_cfg_flexc; + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_get_card_info(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_hydra_send_simple_cmd(dev, CMD_GET_CARD_INFO_REQ, -1); + if (err) + return err; + + memset(&cmd, 0, sizeof(struct kvaser_cmd)); + err = kvaser_usb_hydra_wait_cmd(dev, CMD_GET_CARD_INFO_RESP, &cmd); + if (err) + return err; + + dev->nchannels = cmd.card_info.nchannels; + if (dev->nchannels > KVASER_USB_MAX_NET_DEVICES) + return -EINVAL; + + return 0; +} + +static int kvaser_usb_hydra_get_capabilities(struct kvaser_usb *dev) +{ + int err; + u16 status; + + if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) { + dev_info(&dev->intf->dev, + "No extended capability support. Upgrade your device.\n"); + return 0; + } + + err = kvaser_usb_hydra_get_single_capability + (dev, + KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_HYDRA_CAP_CMD_LISTEN_MODE failed %u\n", + status); + + err = kvaser_usb_hydra_get_single_capability + (dev, + KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_HYDRA_CAP_CMD_ERR_REPORT failed %u\n", + status); + + err = kvaser_usb_hydra_get_single_capability + (dev, KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_HYDRA_CAP_CMD_ONE_SHOT failed %u\n", + status); + + return 0; +} + +static int kvaser_usb_hydra_set_opt_mode(const struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + int err; + + if ((priv->can.ctrlmode & + (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO)) == + CAN_CTRLMODE_FD_NON_ISO) { + netdev_warn(priv->netdev, + "CTRLMODE_FD shall be on if CTRLMODE_FD_NON_ISO is on\n"); + return -EINVAL; + } + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_SET_DRIVERMODE_REQ; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cmd->set_ctrlmode.mode = KVASER_USB_HYDRA_CTRLMODE_LISTEN; + else + cmd->set_ctrlmode.mode = KVASER_USB_HYDRA_CTRLMODE_NORMAL; + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + kfree(cmd); + + return err; +} + +static int kvaser_usb_hydra_start_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->start_comp); + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, CMD_START_CHIP_REQ, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->start_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_hydra_stop_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->stop_comp); + + /* Make sure we do not report invalid BUS_OFF from CMD_CHIP_STATE_EVENT + * see comment in kvaser_usb_hydra_update_state() + */ + priv->can.state = CAN_STATE_STOPPED; + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, CMD_STOP_CHIP_REQ, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->stop_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_hydra_flush_queue(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->flush_comp); + + err = kvaser_usb_hydra_send_simple_cmd(priv->dev, CMD_FLUSH_QUEUE, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->flush_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +/* A single extended hydra command can be transmitted in multiple transfers + * We have to buffer partial hydra commands, and handle them on next callback. + */ +static void kvaser_usb_hydra_read_bulk_callback(struct kvaser_usb *dev, + void *buf, int len) +{ + unsigned long irq_flags; + struct kvaser_cmd *cmd; + int pos = 0; + size_t cmd_len; + struct kvaser_usb_dev_card_data_hydra *card_data = + &dev->card_data.hydra; + int usb_rx_leftover_len; + spinlock_t *usb_rx_leftover_lock = &card_data->usb_rx_leftover_lock; + + spin_lock_irqsave(usb_rx_leftover_lock, irq_flags); + usb_rx_leftover_len = card_data->usb_rx_leftover_len; + if (usb_rx_leftover_len) { + int remaining_bytes; + + cmd = (struct kvaser_cmd *)card_data->usb_rx_leftover; + + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + + remaining_bytes = min_t(unsigned int, len, + cmd_len - usb_rx_leftover_len); + /* Make sure we do not overflow usb_rx_leftover */ + if (remaining_bytes + usb_rx_leftover_len > + KVASER_USB_HYDRA_MAX_CMD_LEN) { + dev_err(&dev->intf->dev, "Format error\n"); + spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags); + return; + } + + memcpy(card_data->usb_rx_leftover + usb_rx_leftover_len, buf, + remaining_bytes); + pos += remaining_bytes; + + if (remaining_bytes + usb_rx_leftover_len == cmd_len) { + kvaser_usb_hydra_handle_cmd(dev, cmd); + usb_rx_leftover_len = 0; + } else { + /* Command still not complete */ + usb_rx_leftover_len += remaining_bytes; + } + card_data->usb_rx_leftover_len = usb_rx_leftover_len; + } + spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags); + + while (pos < len) { + cmd = buf + pos; + + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + + if (pos + cmd_len > len) { + /* We got first part of a command */ + int leftover_bytes; + + leftover_bytes = len - pos; + /* Make sure we do not overflow usb_rx_leftover */ + if (leftover_bytes > KVASER_USB_HYDRA_MAX_CMD_LEN) { + dev_err(&dev->intf->dev, "Format error\n"); + return; + } + spin_lock_irqsave(usb_rx_leftover_lock, irq_flags); + memcpy(card_data->usb_rx_leftover, buf + pos, + leftover_bytes); + card_data->usb_rx_leftover_len = leftover_bytes; + spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags); + break; + } + + kvaser_usb_hydra_handle_cmd(dev, cmd); + pos += cmd_len; + } +} + +static void * +kvaser_usb_hydra_frame_to_cmd(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + void *buf; + + if (priv->dev->card_data.capabilities & KVASER_USB_HYDRA_CAP_EXT_CMD) + buf = kvaser_usb_hydra_frame_to_cmd_ext(priv, skb, frame_len, + cmd_len, transid); + else + buf = kvaser_usb_hydra_frame_to_cmd_std(priv, skb, frame_len, + cmd_len, transid); + + return buf; +} + +const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops = { + .dev_set_mode = kvaser_usb_hydra_set_mode, + .dev_set_bittiming = kvaser_usb_hydra_set_bittiming, + .dev_set_data_bittiming = kvaser_usb_hydra_set_data_bittiming, + .dev_get_berr_counter = kvaser_usb_hydra_get_berr_counter, + .dev_setup_endpoints = kvaser_usb_hydra_setup_endpoints, + .dev_init_card = kvaser_usb_hydra_init_card, + .dev_get_software_info = kvaser_usb_hydra_get_software_info, + .dev_get_software_details = kvaser_usb_hydra_get_software_details, + .dev_get_card_info = kvaser_usb_hydra_get_card_info, + .dev_get_capabilities = kvaser_usb_hydra_get_capabilities, + .dev_set_opt_mode = kvaser_usb_hydra_set_opt_mode, + .dev_start_chip = kvaser_usb_hydra_start_chip, + .dev_stop_chip = kvaser_usb_hydra_stop_chip, + .dev_reset_chip = NULL, + .dev_flush_queue = kvaser_usb_hydra_flush_queue, + .dev_read_bulk_callback = kvaser_usb_hydra_read_bulk_callback, + .dev_frame_to_cmd = kvaser_usb_hydra_frame_to_cmd, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_kcan = { + .clock = { + .freq = 80000000, + }, + .timestamp_freq = 80, + .bittiming_const = &kvaser_usb_hydra_kcan_bittiming_c, + .data_bittiming_const = &kvaser_usb_hydra_kcan_bittiming_c, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_flexc = { + .clock = { + .freq = 24000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_hydra_flexc_bittiming_c, +}; diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c new file mode 100644 index 000000000..8b5d1add8 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -0,0 +1,1403 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) + * + * Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/netlink.h> + +#include "kvaser_usb.h" + +#define MAX_USBCAN_NET_DEVICES 2 + +/* Command header size */ +#define CMD_HEADER_LEN 2 + +/* Kvaser CAN message flags */ +#define MSG_FLAG_ERROR_FRAME BIT(0) +#define MSG_FLAG_OVERRUN BIT(1) +#define MSG_FLAG_NERR BIT(2) +#define MSG_FLAG_WAKEUP BIT(3) +#define MSG_FLAG_REMOTE_FRAME BIT(4) +#define MSG_FLAG_RESERVED BIT(5) +#define MSG_FLAG_TX_ACK BIT(6) +#define MSG_FLAG_TX_REQUEST BIT(7) + +/* CAN states (M16C CxSTRH register) */ +#define M16C_STATE_BUS_RESET BIT(0) +#define M16C_STATE_BUS_ERROR BIT(4) +#define M16C_STATE_BUS_PASSIVE BIT(5) +#define M16C_STATE_BUS_OFF BIT(6) + +/* Leaf/usbcan command ids */ +#define CMD_RX_STD_MESSAGE 12 +#define CMD_TX_STD_MESSAGE 13 +#define CMD_RX_EXT_MESSAGE 14 +#define CMD_TX_EXT_MESSAGE 15 +#define CMD_SET_BUS_PARAMS 16 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_CTRL_MODE 21 +#define CMD_RESET_CHIP 24 +#define CMD_START_CHIP 26 +#define CMD_START_CHIP_REPLY 27 +#define CMD_STOP_CHIP 28 +#define CMD_STOP_CHIP_REPLY 29 + +#define CMD_USBCAN_CLOCK_OVERFLOW_EVENT 33 + +#define CMD_GET_CARD_INFO 34 +#define CMD_GET_CARD_INFO_REPLY 35 +#define CMD_GET_SOFTWARE_INFO 38 +#define CMD_GET_SOFTWARE_INFO_REPLY 39 +#define CMD_FLUSH_QUEUE 48 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_CAN_ERROR_EVENT 51 +#define CMD_FLUSH_QUEUE_REPLY 68 + +#define CMD_LEAF_LOG_MESSAGE 106 + +/* Leaf frequency options */ +#define KVASER_USB_LEAF_SWOPTION_FREQ_MASK 0x60 +#define KVASER_USB_LEAF_SWOPTION_FREQ_16_MHZ_CLK 0 +#define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5) +#define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6) + +/* error factors */ +#define M16C_EF_ACKE BIT(0) +#define M16C_EF_CRCE BIT(1) +#define M16C_EF_FORME BIT(2) +#define M16C_EF_STFE BIT(3) +#define M16C_EF_BITE0 BIT(4) +#define M16C_EF_BITE1 BIT(5) +#define M16C_EF_RCVE BIT(6) +#define M16C_EF_TRE BIT(7) + +/* Only Leaf-based devices can report M16C error factors, + * thus define our own error status flags for USBCANII + */ +#define USBCAN_ERROR_STATE_NONE 0 +#define USBCAN_ERROR_STATE_TX_ERROR BIT(0) +#define USBCAN_ERROR_STATE_RX_ERROR BIT(1) +#define USBCAN_ERROR_STATE_BUSERROR BIT(2) + +/* bittiming parameters */ +#define KVASER_USB_TSEG1_MIN 1 +#define KVASER_USB_TSEG1_MAX 16 +#define KVASER_USB_TSEG2_MIN 1 +#define KVASER_USB_TSEG2_MAX 8 +#define KVASER_USB_SJW_MAX 4 +#define KVASER_USB_BRP_MIN 1 +#define KVASER_USB_BRP_MAX 64 +#define KVASER_USB_BRP_INC 1 + +/* ctrl modes */ +#define KVASER_CTRL_MODE_NORMAL 1 +#define KVASER_CTRL_MODE_SILENT 2 +#define KVASER_CTRL_MODE_SELFRECEPTION 3 +#define KVASER_CTRL_MODE_OFF 4 + +/* Extended CAN identifier flag */ +#define KVASER_EXTENDED_FRAME BIT(31) + +struct kvaser_cmd_simple { + u8 tid; + u8 channel; +} __packed; + +struct kvaser_cmd_cardinfo { + u8 tid; + u8 nchannels; + __le32 serial_number; + __le32 padding0; + __le32 clock_resolution; + __le32 mfgdate; + u8 ean[8]; + u8 hw_revision; + union { + struct { + u8 usb_hs_mode; + } __packed leaf1; + struct { + u8 padding; + } __packed usbcan1; + } __packed; + __le16 padding1; +} __packed; + +struct leaf_cmd_softinfo { + u8 tid; + u8 padding0; + __le32 sw_options; + __le32 fw_version; + __le16 max_outstanding_tx; + __le16 padding1[9]; +} __packed; + +struct usbcan_cmd_softinfo { + u8 tid; + u8 fw_name[5]; + __le16 max_outstanding_tx; + u8 padding[6]; + __le32 fw_version; + __le16 checksum; + __le16 sw_options; +} __packed; + +struct kvaser_cmd_busparams { + u8 tid; + u8 channel; + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 no_samp; +} __packed; + +struct kvaser_cmd_tx_can { + u8 channel; + u8 tid; + u8 data[14]; + union { + struct { + u8 padding; + u8 flags; + } __packed leaf; + struct { + u8 flags; + u8 padding; + } __packed usbcan; + } __packed; +} __packed; + +struct kvaser_cmd_rx_can_header { + u8 channel; + u8 flag; +} __packed; + +struct leaf_cmd_rx_can { + u8 channel; + u8 flag; + + __le16 time[3]; + u8 data[14]; +} __packed; + +struct usbcan_cmd_rx_can { + u8 channel; + u8 flag; + + u8 data[14]; + __le16 time; +} __packed; + +struct leaf_cmd_chip_state_event { + u8 tid; + u8 channel; + + __le16 time[3]; + u8 tx_errors_count; + u8 rx_errors_count; + + u8 status; + u8 padding[3]; +} __packed; + +struct usbcan_cmd_chip_state_event { + u8 tid; + u8 channel; + + u8 tx_errors_count; + u8 rx_errors_count; + __le16 time; + + u8 status; + u8 padding[3]; +} __packed; + +struct kvaser_cmd_tx_acknowledge_header { + u8 channel; + u8 tid; +} __packed; + +struct leaf_cmd_error_event { + u8 tid; + u8 flags; + __le16 time[3]; + u8 channel; + u8 padding; + u8 tx_errors_count; + u8 rx_errors_count; + u8 status; + u8 error_factor; +} __packed; + +struct usbcan_cmd_error_event { + u8 tid; + u8 padding; + u8 tx_errors_count_ch0; + u8 rx_errors_count_ch0; + u8 tx_errors_count_ch1; + u8 rx_errors_count_ch1; + u8 status_ch0; + u8 status_ch1; + __le16 time; +} __packed; + +struct kvaser_cmd_ctrl_mode { + u8 tid; + u8 channel; + u8 ctrl_mode; + u8 padding[3]; +} __packed; + +struct kvaser_cmd_flush_queue { + u8 tid; + u8 channel; + u8 flags; + u8 padding[3]; +} __packed; + +struct leaf_cmd_log_message { + u8 channel; + u8 flags; + __le16 time[3]; + u8 dlc; + u8 time_offset; + __le32 id; + u8 data[8]; +} __packed; + +struct kvaser_cmd { + u8 len; + u8 id; + union { + struct kvaser_cmd_simple simple; + struct kvaser_cmd_cardinfo cardinfo; + struct kvaser_cmd_busparams busparams; + + struct kvaser_cmd_rx_can_header rx_can_header; + struct kvaser_cmd_tx_acknowledge_header tx_acknowledge_header; + + union { + struct leaf_cmd_softinfo softinfo; + struct leaf_cmd_rx_can rx_can; + struct leaf_cmd_chip_state_event chip_state_event; + struct leaf_cmd_error_event error_event; + struct leaf_cmd_log_message log_message; + } __packed leaf; + + union { + struct usbcan_cmd_softinfo softinfo; + struct usbcan_cmd_rx_can rx_can; + struct usbcan_cmd_chip_state_event chip_state_event; + struct usbcan_cmd_error_event error_event; + } __packed usbcan; + + struct kvaser_cmd_tx_can tx_can; + struct kvaser_cmd_ctrl_mode ctrl_mode; + struct kvaser_cmd_flush_queue flush_queue; + } u; +} __packed; + +/* Summary of a kvaser error event, for a unified Leaf/Usbcan error + * handling. Some discrepancies between the two families exist: + * + * - USBCAN firmware does not report M16C "error factors" + * - USBCAN controllers has difficulties reporting if the raised error + * event is for ch0 or ch1. They leave such arbitration to the OS + * driver by letting it compare error counters with previous values + * and decide the error event's channel. Thus for USBCAN, the channel + * field is only advisory. + */ +struct kvaser_usb_err_summary { + u8 channel, status, txerr, rxerr; + union { + struct { + u8 error_factor; + } leaf; + struct { + u8 other_ch_status; + u8 error_state; + } usbcan; + }; +}; + +static const struct can_bittiming_const kvaser_usb_leaf_bittiming_const = { + .name = "kvaser_usb", + .tseg1_min = KVASER_USB_TSEG1_MIN, + .tseg1_max = KVASER_USB_TSEG1_MAX, + .tseg2_min = KVASER_USB_TSEG2_MIN, + .tseg2_max = KVASER_USB_TSEG2_MAX, + .sjw_max = KVASER_USB_SJW_MAX, + .brp_min = KVASER_USB_BRP_MIN, + .brp_max = KVASER_USB_BRP_MAX, + .brp_inc = KVASER_USB_BRP_INC, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_dev_cfg_8mhz = { + .clock = { + .freq = 8000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_leaf_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_dev_cfg_16mhz = { + .clock = { + .freq = 16000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_leaf_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_dev_cfg_24mhz = { + .clock = { + .freq = 24000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_leaf_bittiming_const, +}; + +static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_dev_cfg_32mhz = { + .clock = { + .freq = 32000000, + }, + .timestamp_freq = 1, + .bittiming_const = &kvaser_usb_leaf_bittiming_const, +}; + +static void * +kvaser_usb_leaf_frame_to_cmd(const struct kvaser_usb_net_priv *priv, + const struct sk_buff *skb, int *frame_len, + int *cmd_len, u16 transid) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + u8 *cmd_tx_can_flags = NULL; /* GCC */ + struct can_frame *cf = (struct can_frame *)skb->data; + + *frame_len = cf->can_dlc; + + cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); + if (cmd) { + cmd->u.tx_can.tid = transid & 0xff; + cmd->len = *cmd_len = CMD_HEADER_LEN + + sizeof(struct kvaser_cmd_tx_can); + cmd->u.tx_can.channel = priv->channel; + + switch (dev->card_data.leaf.family) { + case KVASER_LEAF: + cmd_tx_can_flags = &cmd->u.tx_can.leaf.flags; + break; + case KVASER_USBCAN: + cmd_tx_can_flags = &cmd->u.tx_can.usbcan.flags; + break; + } + + *cmd_tx_can_flags = 0; + + if (cf->can_id & CAN_EFF_FLAG) { + cmd->id = CMD_TX_EXT_MESSAGE; + cmd->u.tx_can.data[0] = (cf->can_id >> 24) & 0x1f; + cmd->u.tx_can.data[1] = (cf->can_id >> 18) & 0x3f; + cmd->u.tx_can.data[2] = (cf->can_id >> 14) & 0x0f; + cmd->u.tx_can.data[3] = (cf->can_id >> 6) & 0xff; + cmd->u.tx_can.data[4] = cf->can_id & 0x3f; + } else { + cmd->id = CMD_TX_STD_MESSAGE; + cmd->u.tx_can.data[0] = (cf->can_id >> 6) & 0x1f; + cmd->u.tx_can.data[1] = cf->can_id & 0x3f; + } + + cmd->u.tx_can.data[5] = cf->can_dlc; + memcpy(&cmd->u.tx_can.data[6], cf->data, cf->can_dlc); + + if (cf->can_id & CAN_RTR_FLAG) + *cmd_tx_can_flags |= MSG_FLAG_REMOTE_FRAME; + } + return cmd; +} + +static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id, + struct kvaser_cmd *cmd) +{ + struct kvaser_cmd *tmp; + void *buf; + int actual_len; + int err; + int pos; + unsigned long to = jiffies + msecs_to_jiffies(KVASER_USB_TIMEOUT); + + buf = kzalloc(KVASER_USB_RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + err = kvaser_usb_recv_cmd(dev, buf, KVASER_USB_RX_BUFFER_SIZE, + &actual_len); + if (err < 0) + goto end; + + pos = 0; + while (pos <= actual_len - CMD_HEADER_LEN) { + tmp = buf + pos; + + /* Handle commands crossing the USB endpoint max packet + * size boundary. Check kvaser_usb_read_bulk_callback() + * for further details. + */ + if (tmp->len == 0) { + pos = round_up(pos, + le16_to_cpu + (dev->bulk_in->wMaxPacketSize)); + continue; + } + + if (pos + tmp->len > actual_len) { + dev_err_ratelimited(&dev->intf->dev, + "Format error\n"); + break; + } + + if (tmp->id == id) { + memcpy(cmd, tmp, tmp->len); + goto end; + } + + pos += tmp->len; + } + } while (time_before(jiffies, to)); + + err = -EINVAL; + +end: + kfree(buf); + + return err; +} + +static int kvaser_usb_leaf_send_simple_cmd(const struct kvaser_usb *dev, + u8 cmd_id, int channel) +{ + struct kvaser_cmd *cmd; + int rc; + + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = cmd_id; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_simple); + cmd->u.simple.channel = channel; + cmd->u.simple.tid = 0xff; + + rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev, + const struct leaf_cmd_softinfo *softinfo) +{ + u32 sw_options = le32_to_cpu(softinfo->sw_options); + + dev->fw_version = le32_to_cpu(softinfo->fw_version); + dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx); + + switch (sw_options & KVASER_USB_LEAF_SWOPTION_FREQ_MASK) { + case KVASER_USB_LEAF_SWOPTION_FREQ_16_MHZ_CLK: + dev->cfg = &kvaser_usb_leaf_dev_cfg_16mhz; + break; + case KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK: + dev->cfg = &kvaser_usb_leaf_dev_cfg_24mhz; + break; + case KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK: + dev->cfg = &kvaser_usb_leaf_dev_cfg_32mhz; + break; + } +} + +static int kvaser_usb_leaf_get_software_info_inner(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_leaf_send_simple_cmd(dev, CMD_GET_SOFTWARE_INFO, 0); + if (err) + return err; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_SOFTWARE_INFO_REPLY, &cmd); + if (err) + return err; + + switch (dev->card_data.leaf.family) { + case KVASER_LEAF: + kvaser_usb_leaf_get_software_info_leaf(dev, &cmd.u.leaf.softinfo); + break; + case KVASER_USBCAN: + dev->fw_version = le32_to_cpu(cmd.u.usbcan.softinfo.fw_version); + dev->max_tx_urbs = + le16_to_cpu(cmd.u.usbcan.softinfo.max_outstanding_tx); + dev->cfg = &kvaser_usb_leaf_dev_cfg_8mhz; + break; + } + + return 0; +} + +static int kvaser_usb_leaf_get_software_info(struct kvaser_usb *dev) +{ + int err; + int retry = 3; + + /* On some x86 laptops, plugging a Kvaser device again after + * an unplug makes the firmware always ignore the very first + * command. For such a case, provide some room for retries + * instead of completely exiting the driver. + */ + do { + err = kvaser_usb_leaf_get_software_info_inner(dev); + } while (--retry && err == -ETIMEDOUT); + + return err; +} + +static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev) +{ + struct kvaser_cmd cmd; + int err; + + err = kvaser_usb_leaf_send_simple_cmd(dev, CMD_GET_CARD_INFO, 0); + if (err) + return err; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CARD_INFO_REPLY, &cmd); + if (err) + return err; + + dev->nchannels = cmd.u.cardinfo.nchannels; + if (dev->nchannels > KVASER_USB_MAX_NET_DEVICES || + (dev->card_data.leaf.family == KVASER_USBCAN && + dev->nchannels > MAX_USBCAN_NET_DEVICES)) + return -EINVAL; + + return 0; +} + +static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct net_device_stats *stats; + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + unsigned long flags; + u8 channel, tid; + + channel = cmd->u.tx_acknowledge_header.channel; + tid = cmd->u.tx_acknowledge_header.tid; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + if (!netif_device_present(priv->netdev)) + return; + + stats = &priv->netdev->stats; + + context = &priv->tx_contexts[tid % dev->max_tx_urbs]; + + /* Sometimes the state change doesn't come after a bus-off event */ + if (priv->can.restart_ms && priv->can.state >= CAN_STATE_BUS_OFF) { + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_RESTARTED; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } else { + netdev_err(priv->netdev, + "No memory left for err_skb\n"); + } + + priv->can.can_stats.restarts++; + netif_carrier_on(priv->netdev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + stats->tx_packets++; + stats->tx_bytes += context->dlc; + + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + + can_get_echo_skb(priv->netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(priv->netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); +} + +static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv, + u8 cmd_id) +{ + struct kvaser_cmd *cmd; + int err; + + cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_simple); + cmd->id = cmd_id; + cmd->u.simple.channel = priv->channel; + + err = kvaser_usb_send_cmd_async(priv, cmd, cmd->len); + if (err) + kfree(cmd); + + return err; +} + +static void +kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, + const struct kvaser_usb_err_summary *es, + struct can_frame *cf) +{ + struct kvaser_usb *dev = priv->dev; + struct net_device_stats *stats = &priv->netdev->stats; + enum can_state cur_state, new_state, tx_state, rx_state; + + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status); + + new_state = priv->can.state; + cur_state = priv->can.state; + + if (es->status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { + new_state = CAN_STATE_BUS_OFF; + } else if (es->status & M16C_STATE_BUS_PASSIVE) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (es->status & M16C_STATE_BUS_ERROR) { + /* Guard against spurious error events after a busoff */ + if (cur_state < CAN_STATE_BUS_OFF) { + if (es->txerr >= 128 || es->rxerr >= 128) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (es->txerr >= 96 || es->rxerr >= 96) + new_state = CAN_STATE_ERROR_WARNING; + else if (cur_state > CAN_STATE_ERROR_ACTIVE) + new_state = CAN_STATE_ERROR_ACTIVE; + } + } + + if (!es->status) + new_state = CAN_STATE_ERROR_ACTIVE; + + if (new_state != cur_state) { + tx_state = (es->txerr >= es->rxerr) ? new_state : 0; + rx_state = (es->txerr <= es->rxerr) ? new_state : 0; + + can_change_state(priv->netdev, cf, tx_state, rx_state); + } + + if (priv->can.restart_ms && + cur_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + priv->can.can_stats.restarts++; + + switch (dev->card_data.leaf.family) { + case KVASER_LEAF: + if (es->leaf.error_factor) { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + } + break; + case KVASER_USBCAN: + if (es->usbcan.error_state & USBCAN_ERROR_STATE_TX_ERROR) + stats->tx_errors++; + if (es->usbcan.error_state & USBCAN_ERROR_STATE_RX_ERROR) + stats->rx_errors++; + if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) + priv->can.can_stats.bus_error++; + break; + } + + priv->bec.txerr = es->txerr; + priv->bec.rxerr = es->rxerr; +} + +static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, + const struct kvaser_usb_err_summary *es) +{ + struct can_frame *cf; + struct can_frame tmp_cf = { .can_id = CAN_ERR_FLAG, + .can_dlc = CAN_ERR_DLC }; + struct sk_buff *skb; + struct net_device_stats *stats; + struct kvaser_usb_net_priv *priv; + enum can_state old_state, new_state; + + if (es->channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", es->channel); + return; + } + + priv = dev->nets[es->channel]; + stats = &priv->netdev->stats; + + /* Update all of the CAN interface's state and error counters before + * trying any memory allocation that can actually fail with -ENOMEM. + * + * We send a temporary stack-allocated error CAN frame to + * can_change_state() for the very same reason. + * + * TODO: Split can_change_state() responsibility between updating the + * CAN interface's state and counters, and the setting up of CAN error + * frame ID and data to userspace. Remove stack allocation afterwards. + */ + old_state = priv->can.state; + kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf); + new_state = priv->can.state; + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + memcpy(cf, &tmp_cf, sizeof(*cf)); + + if (new_state != old_state) { + if (es->status & + (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { + if (!priv->can.restart_ms) + kvaser_usb_leaf_simple_cmd_async(priv, + CMD_STOP_CHIP); + netif_carrier_off(priv->netdev); + } + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_carrier_on(priv->netdev); + } + } + + switch (dev->card_data.leaf.family) { + case KVASER_LEAF: + if (es->leaf.error_factor) { + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + + if (es->leaf.error_factor & M16C_EF_ACKE) + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + if (es->leaf.error_factor & M16C_EF_CRCE) + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + if (es->leaf.error_factor & M16C_EF_FORME) + cf->data[2] |= CAN_ERR_PROT_FORM; + if (es->leaf.error_factor & M16C_EF_STFE) + cf->data[2] |= CAN_ERR_PROT_STUFF; + if (es->leaf.error_factor & M16C_EF_BITE0) + cf->data[2] |= CAN_ERR_PROT_BIT0; + if (es->leaf.error_factor & M16C_EF_BITE1) + cf->data[2] |= CAN_ERR_PROT_BIT1; + if (es->leaf.error_factor & M16C_EF_TRE) + cf->data[2] |= CAN_ERR_PROT_TX; + } + break; + case KVASER_USBCAN: + if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) + cf->can_id |= CAN_ERR_BUSERROR; + break; + } + + cf->data[6] = es->txerr; + cf->data[7] = es->rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +/* For USBCAN, report error to userspace if the channels's errors counter + * has changed, or we're the only channel seeing a bus error state. + */ +static void +kvaser_usb_leaf_usbcan_conditionally_rx_error(const struct kvaser_usb *dev, + struct kvaser_usb_err_summary *es) +{ + struct kvaser_usb_net_priv *priv; + unsigned int channel; + bool report_error; + + channel = es->channel; + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + report_error = false; + + if (es->txerr != priv->bec.txerr) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_TX_ERROR; + report_error = true; + } + if (es->rxerr != priv->bec.rxerr) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_RX_ERROR; + report_error = true; + } + if ((es->status & M16C_STATE_BUS_ERROR) && + !(es->usbcan.other_ch_status & M16C_STATE_BUS_ERROR)) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_BUSERROR; + report_error = true; + } + + if (report_error) + kvaser_usb_leaf_rx_error(dev, es); +} + +static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_err_summary es = { }; + + switch (cmd->id) { + /* Sometimes errors are sent as unsolicited chip state events */ + case CMD_CHIP_STATE_EVENT: + es.channel = cmd->u.usbcan.chip_state_event.channel; + es.status = cmd->u.usbcan.chip_state_event.status; + es.txerr = cmd->u.usbcan.chip_state_event.tx_errors_count; + es.rxerr = cmd->u.usbcan.chip_state_event.rx_errors_count; + kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); + break; + + case CMD_CAN_ERROR_EVENT: + es.channel = 0; + es.status = cmd->u.usbcan.error_event.status_ch0; + es.txerr = cmd->u.usbcan.error_event.tx_errors_count_ch0; + es.rxerr = cmd->u.usbcan.error_event.rx_errors_count_ch0; + es.usbcan.other_ch_status = + cmd->u.usbcan.error_event.status_ch1; + kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); + + /* The USBCAN firmware supports up to 2 channels. + * Now that ch0 was checked, check if ch1 has any errors. + */ + if (dev->nchannels == MAX_USBCAN_NET_DEVICES) { + es.channel = 1; + es.status = cmd->u.usbcan.error_event.status_ch1; + es.txerr = + cmd->u.usbcan.error_event.tx_errors_count_ch1; + es.rxerr = + cmd->u.usbcan.error_event.rx_errors_count_ch1; + es.usbcan.other_ch_status = + cmd->u.usbcan.error_event.status_ch0; + kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); + } + break; + + default: + dev_err(&dev->intf->dev, "Invalid cmd id (%d)\n", cmd->id); + } +} + +static void kvaser_usb_leaf_leaf_rx_error(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_err_summary es = { }; + + switch (cmd->id) { + case CMD_CAN_ERROR_EVENT: + es.channel = cmd->u.leaf.error_event.channel; + es.status = cmd->u.leaf.error_event.status; + es.txerr = cmd->u.leaf.error_event.tx_errors_count; + es.rxerr = cmd->u.leaf.error_event.rx_errors_count; + es.leaf.error_factor = cmd->u.leaf.error_event.error_factor; + break; + case CMD_LEAF_LOG_MESSAGE: + es.channel = cmd->u.leaf.log_message.channel; + es.status = cmd->u.leaf.log_message.data[0]; + es.txerr = cmd->u.leaf.log_message.data[2]; + es.rxerr = cmd->u.leaf.log_message.data[3]; + es.leaf.error_factor = cmd->u.leaf.log_message.data[1]; + break; + case CMD_CHIP_STATE_EVENT: + es.channel = cmd->u.leaf.chip_state_event.channel; + es.status = cmd->u.leaf.chip_state_event.status; + es.txerr = cmd->u.leaf.chip_state_event.tx_errors_count; + es.rxerr = cmd->u.leaf.chip_state_event.rx_errors_count; + es.leaf.error_factor = 0; + break; + default: + dev_err(&dev->intf->dev, "Invalid cmd id (%d)\n", cmd->id); + return; + } + + kvaser_usb_leaf_rx_error(dev, &es); +} + +static void kvaser_usb_leaf_rx_can_err(const struct kvaser_usb_net_priv *priv, + const struct kvaser_cmd *cmd) +{ + if (cmd->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR)) { + struct net_device_stats *stats = &priv->netdev->stats; + + netdev_err(priv->netdev, "Unknown error (flags: 0x%02x)\n", + cmd->u.rx_can_header.flag); + + stats->rx_errors++; + return; + } + + if (cmd->u.rx_can_header.flag & MSG_FLAG_OVERRUN) + kvaser_usb_can_rx_over_error(priv->netdev); +} + +static void kvaser_usb_leaf_rx_can_msg(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + u8 channel = cmd->u.rx_can_header.channel; + const u8 *rx_data = NULL; /* GCC */ + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + stats = &priv->netdev->stats; + + if ((cmd->u.rx_can_header.flag & MSG_FLAG_ERROR_FRAME) && + (dev->card_data.leaf.family == KVASER_LEAF && + cmd->id == CMD_LEAF_LOG_MESSAGE)) { + kvaser_usb_leaf_leaf_rx_error(dev, cmd); + return; + } else if (cmd->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR | + MSG_FLAG_OVERRUN)) { + kvaser_usb_leaf_rx_can_err(priv, cmd); + return; + } else if (cmd->u.rx_can_header.flag & ~MSG_FLAG_REMOTE_FRAME) { + netdev_warn(priv->netdev, + "Unhandled frame (flags: 0x%02x)\n", + cmd->u.rx_can_header.flag); + return; + } + + switch (dev->card_data.leaf.family) { + case KVASER_LEAF: + rx_data = cmd->u.leaf.rx_can.data; + break; + case KVASER_USBCAN: + rx_data = cmd->u.usbcan.rx_can.data; + break; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + if (dev->card_data.leaf.family == KVASER_LEAF && cmd->id == + CMD_LEAF_LOG_MESSAGE) { + cf->can_id = le32_to_cpu(cmd->u.leaf.log_message.id); + if (cf->can_id & KVASER_EXTENDED_FRAME) + cf->can_id &= CAN_EFF_MASK | CAN_EFF_FLAG; + else + cf->can_id &= CAN_SFF_MASK; + + cf->can_dlc = get_can_dlc(cmd->u.leaf.log_message.dlc); + + if (cmd->u.leaf.log_message.flags & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &cmd->u.leaf.log_message.data, + cf->can_dlc); + } else { + cf->can_id = ((rx_data[0] & 0x1f) << 6) | (rx_data[1] & 0x3f); + + if (cmd->id == CMD_RX_EXT_MESSAGE) { + cf->can_id <<= 18; + cf->can_id |= ((rx_data[2] & 0x0f) << 14) | + ((rx_data[3] & 0xff) << 6) | + (rx_data[4] & 0x3f); + cf->can_id |= CAN_EFF_FLAG; + } + + cf->can_dlc = get_can_dlc(rx_data[5]); + + if (cmd->u.rx_can_header.flag & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &rx_data[6], cf->can_dlc); + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +static void kvaser_usb_leaf_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + if (completion_done(&priv->start_comp) && + netif_queue_stopped(priv->netdev)) { + netif_wake_queue(priv->netdev); + } else { + netif_start_queue(priv->netdev); + complete(&priv->start_comp); + } +} + +static void kvaser_usb_leaf_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + switch (cmd->id) { + case CMD_START_CHIP_REPLY: + kvaser_usb_leaf_start_chip_reply(dev, cmd); + break; + + case CMD_STOP_CHIP_REPLY: + kvaser_usb_leaf_stop_chip_reply(dev, cmd); + break; + + case CMD_RX_STD_MESSAGE: + case CMD_RX_EXT_MESSAGE: + kvaser_usb_leaf_rx_can_msg(dev, cmd); + break; + + case CMD_LEAF_LOG_MESSAGE: + if (dev->card_data.leaf.family != KVASER_LEAF) + goto warn; + kvaser_usb_leaf_rx_can_msg(dev, cmd); + break; + + case CMD_CHIP_STATE_EVENT: + case CMD_CAN_ERROR_EVENT: + if (dev->card_data.leaf.family == KVASER_LEAF) + kvaser_usb_leaf_leaf_rx_error(dev, cmd); + else + kvaser_usb_leaf_usbcan_rx_error(dev, cmd); + break; + + case CMD_TX_ACKNOWLEDGE: + kvaser_usb_leaf_tx_acknowledge(dev, cmd); + break; + + /* Ignored commands */ + case CMD_USBCAN_CLOCK_OVERFLOW_EVENT: + if (dev->card_data.leaf.family != KVASER_USBCAN) + goto warn; + break; + + case CMD_FLUSH_QUEUE_REPLY: + if (dev->card_data.leaf.family != KVASER_LEAF) + goto warn; + break; + + default: +warn: dev_warn(&dev->intf->dev, "Unhandled command (%d)\n", cmd->id); + break; + } +} + +static void kvaser_usb_leaf_read_bulk_callback(struct kvaser_usb *dev, + void *buf, int len) +{ + struct kvaser_cmd *cmd; + int pos = 0; + + while (pos <= len - CMD_HEADER_LEN) { + cmd = buf + pos; + + /* The Kvaser firmware can only read and write commands that + * does not cross the USB's endpoint wMaxPacketSize boundary. + * If a follow-up command crosses such boundary, firmware puts + * a placeholder zero-length command in its place then aligns + * the real command to the next max packet size. + * + * Handle such cases or we're going to miss a significant + * number of events in case of a heavy rx load on the bus. + */ + if (cmd->len == 0) { + pos = round_up(pos, le16_to_cpu + (dev->bulk_in->wMaxPacketSize)); + continue; + } + + if (pos + cmd->len > len) { + dev_err_ratelimited(&dev->intf->dev, "Format error\n"); + break; + } + + kvaser_usb_leaf_handle_command(dev, cmd); + pos += cmd->len; + } +} + +static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv) +{ + struct kvaser_cmd *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_SET_CTRL_MODE; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_ctrl_mode); + cmd->u.ctrl_mode.tid = 0xff; + cmd->u.ctrl_mode.channel = priv->channel; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cmd->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT; + else + cmd->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL; + + rc = kvaser_usb_send_cmd(priv->dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->start_comp); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_START_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->start_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_leaf_stop_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->stop_comp); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_STOP_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->stop_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_leaf_reset_chip(struct kvaser_usb *dev, int channel) +{ + return kvaser_usb_leaf_send_simple_cmd(dev, CMD_RESET_CHIP, channel); +} + +static int kvaser_usb_leaf_flush_queue(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_cmd *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_FLUSH_QUEUE; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_flush_queue); + cmd->u.flush_queue.channel = priv->channel; + cmd->u.flush_queue.flags = 0x00; + + rc = kvaser_usb_send_cmd(priv->dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + + card_data->ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + + return 0; +} + +static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct can_bittiming *bt = &priv->can.bittiming; + struct kvaser_usb *dev = priv->dev; + struct kvaser_cmd *cmd; + int rc; + + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_SET_BUS_PARAMS; + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_busparams); + cmd->u.busparams.channel = priv->channel; + cmd->u.busparams.tid = 0xff; + cmd->u.busparams.bitrate = cpu_to_le32(bt->bitrate); + cmd->u.busparams.sjw = bt->sjw; + cmd->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; + cmd->u.busparams.tseg2 = bt->phase_seg2; + + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + cmd->u.busparams.no_samp = 3; + else + cmd->u.busparams.no_samp = 1; + + rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); + + kfree(cmd); + return rc; +} + +static int kvaser_usb_leaf_set_mode(struct net_device *netdev, + enum can_mode mode) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + int err; + + switch (mode) { + case CAN_MODE_START: + err = kvaser_usb_leaf_simple_cmd_async(priv, CMD_START_CHIP); + if (err) + return err; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int kvaser_usb_leaf_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + + *bec = priv->bec; + + return 0; +} + +static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev) +{ + const struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + iface_desc = dev->intf->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in && usb_endpoint_is_bulk_in(endpoint)) + dev->bulk_in = endpoint; + + if (!dev->bulk_out && usb_endpoint_is_bulk_out(endpoint)) + dev->bulk_out = endpoint; + + /* use first bulk endpoint for in and out */ + if (dev->bulk_in && dev->bulk_out) + return 0; + } + + return -ENODEV; +} + +const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { + .dev_set_mode = kvaser_usb_leaf_set_mode, + .dev_set_bittiming = kvaser_usb_leaf_set_bittiming, + .dev_set_data_bittiming = NULL, + .dev_get_berr_counter = kvaser_usb_leaf_get_berr_counter, + .dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints, + .dev_init_card = kvaser_usb_leaf_init_card, + .dev_get_software_info = kvaser_usb_leaf_get_software_info, + .dev_get_software_details = NULL, + .dev_get_card_info = kvaser_usb_leaf_get_card_info, + .dev_get_capabilities = NULL, + .dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode, + .dev_start_chip = kvaser_usb_leaf_start_chip, + .dev_stop_chip = kvaser_usb_leaf_stop_chip, + .dev_reset_chip = kvaser_usb_leaf_reset_chip, + .dev_flush_queue = kvaser_usb_leaf_flush_queue, + .dev_read_bulk_callback = kvaser_usb_leaf_read_bulk_callback, + .dev_frame_to_cmd = kvaser_usb_leaf_frame_to_cmd, +}; diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c new file mode 100644 index 000000000..ea1de0600 --- /dev/null +++ b/drivers/net/can/usb/mcba_usb.c @@ -0,0 +1,925 @@ +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool + * + * Copyright (C) 2017 Mobica Limited + * + * 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. + * + * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c + */ + +#include <asm/unaligned.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/usb.h> + +/* vendor and product id */ +#define MCBA_MODULE_NAME "mcba_usb" +#define MCBA_VENDOR_ID 0x04d8 +#define MCBA_PRODUCT_ID 0x0a30 + +/* driver constants */ +#define MCBA_MAX_RX_URBS 20 +#define MCBA_MAX_TX_URBS 20 +#define MCBA_CTX_FREE MCBA_MAX_TX_URBS + +/* RX buffer must be bigger than msg size since at the + * beggining USB messages are stacked. + */ +#define MCBA_USB_RX_BUFF_SIZE 64 +#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg)) + +/* Microchip command id */ +#define MBCA_CMD_RECEIVE_MESSAGE 0xE3 +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5 +#define MBCA_CMD_I_AM_ALIVE_FROM_USB 0xF7 +#define MBCA_CMD_CHANGE_BIT_RATE 0xA1 +#define MBCA_CMD_TRANSMIT_MESSAGE_EV 0xA3 +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8 +#define MBCA_CMD_READ_FW_VERSION 0xA9 +#define MBCA_CMD_NOTHING_TO_SEND 0xFF +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP 0xE2 + +#define MCBA_VER_REQ_USB 1 +#define MCBA_VER_REQ_CAN 2 + +#define MCBA_SIDL_EXID_MASK 0x8 +#define MCBA_DLC_MASK 0xf +#define MCBA_DLC_RTR_MASK 0x40 + +#define MCBA_CAN_STATE_WRN_TH 95 +#define MCBA_CAN_STATE_ERR_PSV_TH 127 + +#define MCBA_TERMINATION_DISABLED CAN_TERMINATION_DISABLED +#define MCBA_TERMINATION_ENABLED 120 + +struct mcba_usb_ctx { + struct mcba_priv *priv; + u32 ndx; + u8 dlc; + bool can; +}; + +/* Structure to hold all of our device specific stuff */ +struct mcba_priv { + struct can_priv can; /* must be the first member */ + struct sk_buff *echo_skb[MCBA_MAX_TX_URBS]; + struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS]; + struct usb_device *udev; + struct net_device *netdev; + struct usb_anchor tx_submitted; + struct usb_anchor rx_submitted; + struct can_berr_counter bec; + bool usb_ka_first_pass; + bool can_ka_first_pass; + bool can_speed_check; + atomic_t free_ctx_cnt; + void *rxbuf[MCBA_MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS]; + int rx_pipe; + int tx_pipe; +}; + +/* CAN frame */ +struct __packed mcba_usb_msg_can { + u8 cmd_id; + __be16 eid; + __be16 sid; + u8 dlc; + u8 data[8]; + u8 timestamp[4]; + u8 checksum; +}; + +/* command frame */ +struct __packed mcba_usb_msg { + u8 cmd_id; + u8 unused[18]; +}; + +struct __packed mcba_usb_msg_ka_usb { + u8 cmd_id; + u8 termination_state; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 unused[15]; +}; + +struct __packed mcba_usb_msg_ka_can { + u8 cmd_id; + u8 tx_err_cnt; + u8 rx_err_cnt; + u8 rx_buff_ovfl; + u8 tx_bus_off; + __be16 can_bitrate; + __le16 rx_lost; + u8 can_stat; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 debug_mode; + u8 test_complete; + u8 test_result; + u8 unused[4]; +}; + +struct __packed mcba_usb_msg_change_bitrate { + u8 cmd_id; + __be16 bitrate; + u8 unused[16]; +}; + +struct __packed mcba_usb_msg_termination { + u8 cmd_id; + u8 termination; + u8 unused[17]; +}; + +struct __packed mcba_usb_msg_fw_ver { + u8 cmd_id; + u8 pic; + u8 unused[17]; +}; + +static const struct usb_device_id mcba_usb_table[] = { + { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mcba_usb_table); + +static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED, + MCBA_TERMINATION_ENABLED }; + +static const u32 mcba_bitrate[] = { 20000, 33333, 50000, 80000, 83333, + 100000, 125000, 150000, 175000, 200000, + 225000, 250000, 275000, 300000, 500000, + 625000, 800000, 1000000 }; + +static inline void mcba_init_ctx(struct mcba_priv *priv) +{ + int i = 0; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + priv->tx_context[i].ndx = MCBA_CTX_FREE; + priv->tx_context[i].priv = priv; + } + + atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context)); +} + +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv, + struct can_frame *cf) +{ + int i = 0; + struct mcba_usb_ctx *ctx = NULL; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + if (priv->tx_context[i].ndx == MCBA_CTX_FREE) { + ctx = &priv->tx_context[i]; + ctx->ndx = i; + + if (cf) { + ctx->can = true; + ctx->dlc = cf->can_dlc; + } else { + ctx->can = false; + ctx->dlc = 0; + } + + atomic_dec(&priv->free_ctx_cnt); + break; + } + } + + if (!atomic_read(&priv->free_ctx_cnt)) + /* That was the last free ctx. Slow down tx path */ + netif_stop_queue(priv->netdev); + + return ctx; +} + +/* mcba_usb_free_ctx and mcba_usb_get_free_ctx are executed by different + * threads. The order of execution in below function is important. + */ +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx) +{ + /* Increase number of free ctxs before freeing ctx */ + atomic_inc(&ctx->priv->free_ctx_cnt); + + ctx->ndx = MCBA_CTX_FREE; + + /* Wake up the queue once ctx is marked free */ + netif_wake_queue(ctx->priv->netdev); +} + +static void mcba_usb_write_bulk_callback(struct urb *urb) +{ + struct mcba_usb_ctx *ctx = urb->context; + struct net_device *netdev; + + WARN_ON(!ctx); + + netdev = ctx->priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + if (ctx->can) { + if (!netif_device_present(netdev)) + return; + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += ctx->dlc; + + can_led_event(netdev, CAN_LED_EVENT_TX); + can_get_echo_skb(netdev, ctx->ndx); + } + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + /* Release the context */ + mcba_usb_free_ctx(ctx); +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg, + struct mcba_usb_ctx *ctx) +{ + struct urb *urb; + u8 *buf; + int err; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + err = -ENOMEM; + goto nomembuf; + } + + memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE); + + usb_fill_bulk_urb(urb, priv->udev, priv->tx_pipe, buf, MCBA_USB_TX_BUFF_SIZE, + mcba_usb_write_bulk_callback, ctx); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + goto failed; + + /* Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return 0; + +failed: + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf, + urb->transfer_dma); + + if (err == -ENODEV) + netif_device_detach(priv->netdev); + else + netdev_warn(priv->netdev, "failed tx_urb %d\n", err); + +nomembuf: + usb_free_urb(urb); + + return err; +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct mcba_usb_ctx *ctx = NULL; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + int err; + struct mcba_usb_msg_can usb_msg = { + .cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV + }; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + ctx = mcba_usb_get_free_ctx(priv, cf); + if (!ctx) + return NETDEV_TX_BUSY; + + if (cf->can_id & CAN_EFF_FLAG) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + sid = MCBA_SIDL_EXID_MASK; + /* store 28-18 bits */ + sid |= (cf->can_id & 0x1ffc0000) >> 13; + /* store 17-16 bits */ + sid |= (cf->can_id & 0x30000) >> 16; + put_unaligned_be16(sid, &usb_msg.sid); + + /* store 15-0 bits */ + put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5, + &usb_msg.sid); + usb_msg.eid = 0; + } + + usb_msg.dlc = cf->can_dlc; + + memcpy(usb_msg.data, cf->data, usb_msg.dlc); + + if (cf->can_id & CAN_RTR_FLAG) + usb_msg.dlc |= MCBA_DLC_RTR_MASK; + + can_put_echo_skb(skb, priv->netdev, ctx->ndx); + + err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx); + if (err) + goto xmit_failed; + + return NETDEV_TX_OK; + +xmit_failed: + can_free_echo_skb(priv->netdev, ctx->ndx); + mcba_usb_free_ctx(ctx); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +/* Send cmd to device */ +static void mcba_usb_xmit_cmd(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg) +{ + struct mcba_usb_ctx *ctx = NULL; + int err; + + ctx = mcba_usb_get_free_ctx(priv, NULL); + if (!ctx) { + netdev_err(priv->netdev, + "Lack of free ctx. Sending (%d) cmd aborted", + usb_msg->cmd_id); + + return; + } + + err = mcba_usb_xmit(priv, usb_msg, ctx); + if (err) + netdev_err(priv->netdev, "Failed to send cmd (%d)", + usb_msg->cmd_id); +} + +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate) +{ + struct mcba_usb_msg_change_bitrate usb_msg = { + .cmd_id = MBCA_CMD_CHANGE_BIT_RATE + }; + + put_unaligned_be16(bitrate, &usb_msg.bitrate); + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic) +{ + struct mcba_usb_msg_fw_ver usb_msg = { + .cmd_id = MBCA_CMD_READ_FW_VERSION, + .pic = pic + }; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_process_can(struct mcba_priv *priv, + struct mcba_usb_msg_can *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) + return; + + sid = get_unaligned_be16(&msg->sid); + + if (sid & MCBA_SIDL_EXID_MASK) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + cf->can_id = CAN_EFF_FLAG; + + /* store 28-18 bits */ + cf->can_id |= (sid & 0xffe0) << 13; + /* store 17-16 bits */ + cf->can_id |= (sid & 3) << 16; + /* store 15-0 bits */ + cf->can_id |= get_unaligned_be16(&msg->eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + cf->can_id = (sid & 0xffe0) >> 5; + } + + if (msg->dlc & MCBA_DLC_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK); + + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + can_led_event(priv->netdev, CAN_LED_EVENT_RX); + netif_rx(skb); +} + +static void mcba_usb_process_ka_usb(struct mcba_priv *priv, + struct mcba_usb_msg_ka_usb *msg) +{ + if (unlikely(priv->usb_ka_first_pass)) { + netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->usb_ka_first_pass = false; + } + + if (msg->termination_state) + priv->can.termination = MCBA_TERMINATION_ENABLED; + else + priv->can.termination = MCBA_TERMINATION_DISABLED; +} + +static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg) +{ + const u32 bitrate = get_unaligned_be16(&msg->can_bitrate); + + if ((bitrate == 33) || (bitrate == 83)) + return bitrate * 1000 + 333; + else + return bitrate * 1000; +} + +static void mcba_usb_process_ka_can(struct mcba_priv *priv, + struct mcba_usb_msg_ka_can *msg) +{ + if (unlikely(priv->can_ka_first_pass)) { + netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->can_ka_first_pass = false; + } + + if (unlikely(priv->can_speed_check)) { + const u32 bitrate = convert_can2host_bitrate(msg); + + priv->can_speed_check = false; + + if (bitrate != priv->can.bittiming.bitrate) + netdev_err( + priv->netdev, + "Wrong bitrate reported by the device (%u). Expected %u", + bitrate, priv->can.bittiming.bitrate); + } + + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + if (msg->tx_bus_off) + priv->can.state = CAN_STATE_BUS_OFF; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH)) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH)) + priv->can.state = CAN_STATE_ERROR_WARNING; +} + +static void mcba_usb_process_rx(struct mcba_priv *priv, + struct mcba_usb_msg *msg) +{ + switch (msg->cmd_id) { + case MBCA_CMD_I_AM_ALIVE_FROM_CAN: + mcba_usb_process_ka_can(priv, + (struct mcba_usb_msg_ka_can *)msg); + break; + + case MBCA_CMD_I_AM_ALIVE_FROM_USB: + mcba_usb_process_ka_usb(priv, + (struct mcba_usb_msg_ka_usb *)msg); + break; + + case MBCA_CMD_RECEIVE_MESSAGE: + mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg); + break; + + case MBCA_CMD_NOTHING_TO_SEND: + /* Side effect of communication between PIC_USB and PIC_CAN. + * PIC_CAN is telling us that it has nothing to send + */ + break; + + case MBCA_CMD_TRANSMIT_MESSAGE_RSP: + /* Transmission response from the device containing timestamp */ + break; + + default: + netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)", + msg->cmd_id); + break; + } +} + +/* Callback for reading data from device + * + * Check urb status, call read function and resubmit urb read operation. + */ +static void mcba_usb_read_bulk_callback(struct urb *urb) +{ + struct mcba_priv *priv = urb->context; + struct net_device *netdev; + int retval; + int pos = 0; + + netdev = priv->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); + + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct mcba_usb_msg *msg; + + if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) { + netdev_err(priv->netdev, "format error\n"); + break; + } + + msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos); + mcba_usb_process_rx(priv, msg); + + pos += sizeof(struct mcba_usb_msg); + } + +resubmit_urb: + + usb_fill_bulk_urb(urb, priv->udev, + priv->rx_pipe, + urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + + 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); +} + +/* Start USB device */ +static int mcba_usb_start(struct mcba_priv *priv) +{ + struct net_device *netdev = priv->netdev; + int err, i; + + mcba_init_ctx(priv); + + for (i = 0; i < MCBA_MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + GFP_KERNEL, &buf_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + urb->transfer_dma = buf_dma; + + usb_fill_bulk_urb(urb, priv->udev, + priv->rx_pipe, + buf, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + buf, buf_dma); + usb_free_urb(urb); + break; + } + + priv->rxbuf[i] = buf; + priv->rxbuf_dma[i] = buf_dma; + + /* 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 < MCBA_MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB); + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN); + + return err; +} + +/* Open USB device */ +static int mcba_usb_open(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + priv->can_speed_check = true; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + can_led_event(netdev, CAN_LED_EVENT_OPEN); + netif_start_queue(netdev); + + return 0; +} + +static void mcba_urb_unlink(struct mcba_priv *priv) +{ + int i; + + usb_kill_anchored_urbs(&priv->rx_submitted); + + for (i = 0; i < MCBA_MAX_RX_URBS; ++i) + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + priv->rxbuf[i], priv->rxbuf_dma[i]); + + usb_kill_anchored_urbs(&priv->tx_submitted); +} + +/* Close USB device */ +static int mcba_usb_close(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + mcba_urb_unlink(priv); + + close_candev(netdev); + can_led_event(netdev, CAN_LED_EVENT_STOP); + + return 0; +} + +/* Set network device mode + * + * Maybe we should leave this function empty, because the device + * set mode variable with open command. + */ +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode) +{ + return 0; +} + +static int mcba_net_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +static const struct net_device_ops mcba_netdev_ops = { + .ndo_open = mcba_usb_open, + .ndo_stop = mcba_usb_close, + .ndo_start_xmit = mcba_usb_start_xmit, +}; + +/* Microchip CANBUS has hardcoded bittiming values by default. + * This function sends request via USB to change the speed and align bittiming + * values for presentation purposes only + */ +static int mcba_net_set_bittiming(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000; + + mcba_usb_xmit_change_bitrate(priv, bitrate_kbps); + + return 0; +} + +static int mcba_set_termination(struct net_device *netdev, u16 term) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct mcba_usb_msg_termination usb_msg = { + .cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE + }; + + if (term == MCBA_TERMINATION_ENABLED) + usb_msg.termination = 1; + else + usb_msg.termination = 0; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); + + return 0; +} + +static int mcba_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct mcba_priv *priv; + int err = -ENOMEM; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *in, *out; + + err = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL, NULL); + if (err) { + dev_err(&intf->dev, "Can't find endpoints\n"); + return err; + } + + netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + priv->udev = usbdev; + priv->netdev = netdev; + priv->usb_ka_first_pass = true; + priv->can_ka_first_pass = true; + priv->can_speed_check = false; + + init_usb_anchor(&priv->rx_submitted); + init_usb_anchor(&priv->tx_submitted); + + usb_set_intfdata(intf, priv); + + /* Init CAN device */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.termination_const = mcba_termination; + priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination); + priv->can.bitrate_const = mcba_bitrate; + priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate); + + priv->can.do_set_termination = mcba_set_termination; + priv->can.do_set_mode = mcba_net_set_mode; + priv->can.do_get_berr_counter = mcba_net_get_berr_counter; + priv->can.do_set_bittiming = mcba_net_set_bittiming; + + netdev->netdev_ops = &mcba_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register CAN device: %d\n", err); + + goto cleanup_free_candev; + } + + priv->rx_pipe = usb_rcvbulkpipe(priv->udev, in->bEndpointAddress); + priv->tx_pipe = usb_sndbulkpipe(priv->udev, out->bEndpointAddress); + + devm_can_led_init(netdev); + + /* Start USB dev only if we have successfully registered CAN device */ + err = mcba_usb_start(priv); + if (err) { + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", err); + + goto cleanup_unregister_candev; + } + + dev_info(&intf->dev, "Microchip CAN BUS Analyzer connected\n"); + + return 0; + +cleanup_unregister_candev: + unregister_candev(priv->netdev); + +cleanup_free_candev: + free_candev(netdev); + + return err; +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void mcba_usb_disconnect(struct usb_interface *intf) +{ + struct mcba_priv *priv = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + netdev_info(priv->netdev, "device disconnected\n"); + + unregister_candev(priv->netdev); + mcba_urb_unlink(priv); + free_candev(priv->netdev); +} + +static struct usb_driver mcba_usb_driver = { + .name = MCBA_MODULE_NAME, + .probe = mcba_usb_probe, + .disconnect = mcba_usb_disconnect, + .id_table = mcba_usb_table, +}; + +module_usb_driver(mcba_usb_driver); + +MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>"); +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile new file mode 100644 index 000000000..1839e9ca6 --- /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 pcan_usb_fd.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 000000000..215cd7480 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -0,0 +1,917 @@ +/* + * 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_ts_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(struct timer_list *t) +{ + struct pcan_usb *pdev = from_timer(pdev, t, restart_timer); + struct peak_usb_device *dev = &pdev->dev; + + /* notify candev and netdev */ + peak_usb_restart_complete(dev); +} + +/* + * 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) { + __le32 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) +{ + __le16 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) { + __le16 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; + 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; + } + /* else: fall through */ + + 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) */ + new_state = CAN_STATE_ERROR_ACTIVE; + break; + } + 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 warning state) */ + new_state = CAN_STATE_ERROR_WARNING; + break; + } + 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; + mc->pdev->dev.can.can_stats.bus_off++; + 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; + + case CAN_STATE_ERROR_ACTIVE: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_ACTIVE; + 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) { + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + + peak_usb_get_ts_time(&mc->pdev->time_ref, mc->ts16, + &hwts->hwtstamp); + } + + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + netif_rx(skb); + + 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_ts_idx); + + if (err) + return err; + + /* Next packet in the buffer will have a timestamp on a single + * byte + */ + mc->rec_ts_idx++; + } + + 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 skb_shared_hwtstamps *hwts; + + skb = alloc_can_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + if (status_len & PCAN_USB_STATUSLEN_EXT_ID) { + __le32 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 { + __le16 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); + + /* Only first packet timestamp is a word */ + if (pcan_usb_decode_ts(mc, !mc->rec_ts_idx)) + goto decode_failed; + + /* Next packet in the buffer will have a timestamp on a single byte */ + mc->rec_ts_idx++; + + /* 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, cf->can_dlc); + mc->ptr += rec_len; + } + + /* convert timestamp into kernel time */ + hwts = skb_hwtstamps(skb); + peak_usb_get_ts_time(&mc->pdev->time_ref, mc->ts16, &hwts->hwtstamp); + + /* update statistics */ + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + /* push the skb */ + netif_rx(skb); + + 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); + } + } + + 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 */ + timer_setup(&pdev->restart_timer, pcan_usb_restart, 0); + + /* + * 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 + */ +static const struct can_bittiming_const pcan_usb_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, +}; + +const struct peak_usb_adapter pcan_usb = { + .name = "PCAN-USB", + .device_id = PCAN_USB_PRODUCT_ID, + .ctrl_count = 1, + .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_USB_CRYSTAL_HZ / 2 , + }, + .bittiming_const = &pcan_usb_const, + + /* 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 000000000..c235d1e6b --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -0,0 +1,1028 @@ +/* + * 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)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBFD_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPROFD_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBCHIP_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBX6_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, peak_usb_table); + +/* List of supported PCAN-USB adapters (NULL terminated list) */ +static const struct peak_usb_adapter *const peak_usb_adapters_list[] = { + &pcan_usb, + &pcan_usb_pro, + &pcan_usb_fd, + &pcan_usb_pro_fd, + &pcan_usb_chip, + &pcan_usb_x6, +}; + +/* + * dump memory + */ +#define DUMP_WIDTH 16 +void pcan_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, + const struct peak_usb_adapter *adapter) +{ + if (time_ref) { + memset(time_ref, 0, sizeof(struct peak_time_ref)); + time_ref->adapter = adapter; + } +} + +/* + * 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 (ktime_to_ns(time_ref->tv_host) > 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 (ktime_to_ns(time_ref->tv_host_0) == 0) { + /* use monotonic clock to correctly compute further deltas */ + time_ref->tv_host_0 = ktime_get(); + time_ref->tv_host = ktime_set(0, 0); + } else { + /* + * delta_us should not be >= 2^32 => delta should be < 4294s + * handle 32-bits wrapping here: if count of s. reaches 4200, + * reset counters and change time base + */ + if (ktime_to_ns(time_ref->tv_host)) { + ktime_t delta = ktime_sub(time_ref->tv_host, + time_ref->tv_host_0); + if (ktime_to_ns(delta) > (4200ull * NSEC_PER_SEC)) { + time_ref->tv_host_0 = time_ref->tv_host; + time_ref->ts_total = 0; + } + } + + time_ref->tv_host = 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 time according to current ts and time_ref data + */ +void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *time) +{ + /* protect from getting time before setting now */ + if (ktime_to_ns(time_ref->tv_host)) { + u64 delta_us; + s64 delta_ts = 0; + + /* General case: dev_ts_1 < dev_ts_2 < ts, with: + * + * - dev_ts_1 = previous sync timestamp + * - dev_ts_2 = last sync timestamp + * - ts = event timestamp + * - ts_period = known sync period (theoretical) + * ~ dev_ts2 - dev_ts1 + * *but*: + * + * - time counters wrap (see adapter->ts_used_bits) + * - sometimes, dev_ts_1 < ts < dev_ts2 + * + * "normal" case (sync time counters increase): + * must take into account case when ts wraps (tsw) + * + * < ts_period > < > + * | | | + * ---+--------+----+-------0-+--+--> + * ts_dev_1 | ts_dev_2 | + * ts tsw + */ + if (time_ref->ts_dev_1 < time_ref->ts_dev_2) { + /* case when event time (tsw) wraps */ + if (ts < time_ref->ts_dev_1) + delta_ts = BIT_ULL(time_ref->adapter->ts_used_bits); + + /* Otherwise, sync time counter (ts_dev_2) has wrapped: + * handle case when event time (tsn) hasn't. + * + * < ts_period > < > + * | | | + * ---+--------+--0-+---------+--+--> + * ts_dev_1 | ts_dev_2 | + * tsn ts + */ + } else if (time_ref->ts_dev_1 < ts) { + delta_ts = -BIT_ULL(time_ref->adapter->ts_used_bits); + } + + /* add delay between last sync and event timestamps */ + delta_ts += (signed int)(ts - time_ref->ts_dev_2); + + /* add time from beginning to last sync */ + delta_ts += time_ref->ts_total; + + /* convert ticks number into microseconds */ + delta_us = delta_ts * time_ref->adapter->us_per_ts_scale; + delta_us >>= time_ref->adapter->us_per_ts_shift; + + *time = ktime_add_us(time_ref->tv_host_0, delta_us); + } else { + *time = ktime_get(); + } +} + +/* + * post received skb after having set any hw timestamp + */ +int peak_usb_netif_rx(struct sk_buff *skb, + struct peak_time_ref *time_ref, u32 ts_low) +{ + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + + peak_usb_get_ts_time(time_ref, ts_low, &hwts->hwtstamp); + + return netif_rx(skb); +} + +/* + * 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) + pcan_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->data_len; + + /* prevent tx timeout */ + netif_trans_update(netdev); + 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 canfd_frame *cfd = (struct canfd_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; + + /* Note: this works with CANFD frames too */ + context->data_len = cfd->len; + + 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); + /* fall through */ + case -ENOENT: + /* cable unplugged */ + stats->tx_dropped++; + } + } else { + netif_trans_update(netdev); + + /* 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) { + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL); + if (!buf) { + 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) { + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL); + if (!buf) { + 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"); + goto err_tx; + } + + netdev_warn(netdev, "tx performance may be slow\n"); + } + + if (dev->adapter->dev_start) { + err = dev->adapter->dev_start(dev); + if (err) + goto err_adapter; + } + + 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 err_adapter; + } + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +err_adapter: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + usb_free_urb(dev->tx_contexts[i].urb); + dev->tx_contexts[i].urb = NULL; + } +err_tx: + usb_kill_anchored_urbs(&dev->rx_submitted); + + 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; + } + + 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); + + close_candev(netdev); + + dev->can.state = CAN_STATE_STOPPED; + + /* unlink all pending urbs and free used memory */ + peak_usb_unlink_all_urbs(dev); + + if (dev->adapter->dev_stop) + dev->adapter->dev_stop(dev); + + /* 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) + return -ENOMEM; + + /* also allocate enough space for the commands to send */ + buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_ATOMIC); + if (!buf) { + 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; + + 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 nominal/arbitration bitrate. + */ +static int peak_usb_set_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + const struct peak_usb_adapter *pa = dev->adapter; + + if (pa->dev_set_bittiming) { + struct can_bittiming *bt = &dev->can.bittiming; + int err = pa->dev_set_bittiming(dev, bt); + + if (err) + netdev_info(netdev, "couldn't set bitrate (err %d)\n", + err); + return err; + } + + return 0; +} + +/* + * candev callback used to set device data bitrate. + */ +static int peak_usb_set_data_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + const struct peak_usb_adapter *pa = dev->adapter; + + if (pa->dev_set_data_bittiming) { + struct can_bittiming *bt = &dev->can.data_bittiming; + int err = pa->dev_set_data_bittiming(dev, bt); + + if (err) + netdev_info(netdev, + "couldn't set data 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, + .ndo_change_mtu = can_change_mtu, +}; + +/* + * create one device which is attached to CAN controller #ctrl_idx of the + * usb adapter. + */ +static int peak_usb_create_dev(const 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 = kzalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL); + if (!dev->cmd_buf) { + err = -ENOMEM; + goto lbl_free_candev; + } + + 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.data_bittiming_const = peak_usb_adapter->data_bittiming_const; + dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming; + dev->can.do_set_mode = peak_usb_set_mode; + dev->can.do_get_berr_counter = peak_usb_adapter->do_get_berr_counter; + dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported; + + 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); + netdev->dev_id = ctrl_idx; + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + goto lbl_restore_intf_data; + } + + 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_unregister_candev; + } + + /* set bus off */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 0); + if (err) + goto adap_dev_free; + } + + /* 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; + +adap_dev_free: + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + +lbl_unregister_candev: + unregister_candev(netdev); + +lbl_restore_intf_data: + usb_set_intfdata(intf, dev->prev_siblings); + kfree(dev->cmd_buf); + +lbl_free_candev: + 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; + struct peak_usb_device *dev_prev_siblings; + + /* 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_prev_siblings = dev->prev_siblings; + dev->state &= ~PCAN_USB_STATE_CONNECTED; + strlcpy(name, netdev->name, IFNAMSIZ); + + unregister_netdev(netdev); + + kfree(dev->cmd_buf); + dev->next_siblings = NULL; + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + + free_candev(netdev); + 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); + const u16 usb_id_product = le16_to_cpu(usb_dev->descriptor.idProduct); + const struct peak_usb_adapter *peak_usb_adapter = NULL; + int i, err = -ENOMEM; + + /* get corresponding PCAN-USB adapter */ + for (i = 0; i < ARRAY_SIZE(peak_usb_adapters_list); i++) + if (peak_usb_adapters_list[i]->device_id == usb_id_product) { + peak_usb_adapter = peak_usb_adapters_list[i]; + break; + } + + 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_id_product); + 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 000000000..fb23489e1 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -0,0 +1,160 @@ +/* + * 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_USBPROFD_PRODUCT_ID 0x0011 +#define PCAN_USBFD_PRODUCT_ID 0x0012 +#define PCAN_USBCHIP_PRODUCT_ID 0x0013 +#define PCAN_USBX6_PRODUCT_ID 0x0014 + +#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; + u32 ctrlmode_supported; + struct can_clock clock; + const struct can_bittiming_const * const bittiming_const; + const struct can_bittiming_const * const data_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_data_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); + int (*do_get_berr_counter)(const struct net_device *netdev, + struct can_berr_counter *bec); + 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 const struct peak_usb_adapter pcan_usb; +extern const struct peak_usb_adapter pcan_usb_pro; +extern const struct peak_usb_adapter pcan_usb_fd; +extern const struct peak_usb_adapter pcan_usb_chip; +extern const struct peak_usb_adapter pcan_usb_pro_fd; +extern const struct peak_usb_adapter pcan_usb_x6; + +struct peak_time_ref { + ktime_t tv_host_0, tv_host; + u32 ts_dev_1, ts_dev_2; + u64 ts_total; + u32 tick_count; + const struct peak_usb_adapter *adapter; +}; + +struct peak_tx_urb_context { + struct peak_usb_device *dev; + u32 echo_index; + u8 data_len; + 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; + const struct peak_usb_adapter *adapter; + unsigned int ctrl_idx; + 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 pcan_dump_mem(char *prompt, void *p, int l); + +/* common timestamp management */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + const 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_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *tv); +int peak_usb_netif_rx(struct sk_buff *skb, + struct peak_time_ref *time_ref, u32 ts_low); +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_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c new file mode 100644 index 000000000..d27141b9e --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -0,0 +1,1302 @@ +/* + * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter + * + * Copyright (C) 2013-2014 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 <linux/can/dev/peak_canfd.h> + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter"); +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter"); + +#define PCAN_USBPROFD_CHANNEL_COUNT 2 +#define PCAN_USBFD_CHANNEL_COUNT 1 + +/* PCAN-USB Pro FD adapter internal clock (Hz) */ +#define PCAN_UFD_CRYSTAL_HZ 80000000 + +#define PCAN_UFD_CMD_BUFFER_SIZE 512 +#define PCAN_UFD_LOSPD_PKT_SIZE 64 + +/* PCAN-USB Pro FD command timeout (ms.) */ +#define PCAN_UFD_CMD_TIMEOUT_MS 1000 + +/* PCAN-USB Pro FD rx/tx buffers size */ +#define PCAN_UFD_RX_BUFFER_SIZE 2048 +#define PCAN_UFD_TX_BUFFER_SIZE 512 + +/* read some versions info from the hw devcie */ +struct __packed pcan_ufd_fw_info { + __le16 size_of; /* sizeof this */ + __le16 type; /* type of this structure */ + u8 hw_type; /* Type of hardware (HW_TYPE_xxx) */ + u8 bl_version[3]; /* Bootloader version */ + u8 hw_version; /* Hardware version (PCB) */ + u8 fw_version[3]; /* Firmware version */ + __le32 dev_id[2]; /* "device id" per CAN */ + __le32 ser_no; /* S/N */ + __le32 flags; /* special functions */ +}; + +/* handle device specific info used by the netdevices */ +struct pcan_usb_fd_if { + struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL]; + struct pcan_ufd_fw_info fw_info; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_fd_device { + struct peak_usb_device dev; + struct can_berr_counter bec; + struct pcan_usb_fd_if *usb_if; + u8 *cmd_buffer_addr; +}; + +/* Extended USB commands (non uCAN commands) */ + +/* Clock Modes command */ +#define PCAN_UFD_CMD_CLK_SET 0x80 + +#define PCAN_UFD_CLK_80MHZ 0x0 +#define PCAN_UFD_CLK_60MHZ 0x1 +#define PCAN_UFD_CLK_40MHZ 0x2 +#define PCAN_UFD_CLK_30MHZ 0x3 +#define PCAN_UFD_CLK_24MHZ 0x4 +#define PCAN_UFD_CLK_20MHZ 0x5 +#define PCAN_UFD_CLK_DEF PCAN_UFD_CLK_80MHZ + +struct __packed pcan_ufd_clock { + __le16 opcode_channel; + + u8 mode; + u8 unused[5]; +}; + +/* LED control command */ +#define PCAN_UFD_CMD_LED_SET 0x86 + +#define PCAN_UFD_LED_DEV 0x00 +#define PCAN_UFD_LED_FAST 0x01 +#define PCAN_UFD_LED_SLOW 0x02 +#define PCAN_UFD_LED_ON 0x03 +#define PCAN_UFD_LED_OFF 0x04 +#define PCAN_UFD_LED_DEF PCAN_UFD_LED_DEV + +struct __packed pcan_ufd_led { + __le16 opcode_channel; + + u8 mode; + u8 unused[5]; +}; + +/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */ +#define PCAN_UFD_FLTEXT_CALIBRATION 0x8000 + +struct __packed pcan_ufd_options { + __le16 opcode_channel; + + __le16 ucan_mask; + u16 unused; + __le16 usb_mask; +}; + +/* Extended usage of uCAN messages for PCAN-USB Pro FD */ +#define PCAN_UFD_MSG_CALIBRATION 0x100 + +struct __packed pcan_ufd_ts_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + __le16 usb_frame_index; + u16 unused; +}; + +#define PCAN_UFD_MSG_OVERRUN 0x101 + +#define PCAN_UFD_OVMSG_CHANNEL(o) ((o)->channel & 0xf) + +struct __packed pcan_ufd_ovr_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel; + u8 unused[3]; +}; + +static inline int pufd_omsg_get_channel(struct pcan_ufd_ovr_msg *om) +{ + return om->channel & 0xf; +} + +/* Clock mode frequency values */ +static const u32 pcan_usb_fd_clk_freq[6] = { + [PCAN_UFD_CLK_80MHZ] = 80000000, + [PCAN_UFD_CLK_60MHZ] = 60000000, + [PCAN_UFD_CLK_40MHZ] = 40000000, + [PCAN_UFD_CLK_30MHZ] = 30000000, + [PCAN_UFD_CLK_24MHZ] = 24000000, + [PCAN_UFD_CLK_20MHZ] = 20000000 +}; + +/* return a device USB interface */ +static inline +struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + return pdev->usb_if; +} + +/* return a device USB commands buffer */ +static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + return pdev->cmd_buffer_addr; +} + +/* send PCAN-USB Pro FD commands synchronously */ +static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail) +{ + void *cmd_head = pcan_usb_fd_cmd_buffer(dev); + int err = 0; + u8 *packet_ptr; + int packet_len; + ptrdiff_t cmd_len; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* if a packet is not filled completely by commands, the command list + * is terminated with an "end of collection" record. + */ + cmd_len = cmd_tail - cmd_head; + if (cmd_len <= (PCAN_UFD_CMD_BUFFER_SIZE - sizeof(u64))) { + memset(cmd_tail, 0xff, sizeof(u64)); + cmd_len += sizeof(u64); + } + + packet_ptr = cmd_head; + packet_len = cmd_len; + + /* firmware is not able to re-assemble 512 bytes buffer in full-speed */ + if (unlikely(dev->udev->speed != USB_SPEED_HIGH)) + packet_len = min(packet_len, PCAN_UFD_LOSPD_PKT_SIZE); + + do { + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + PCAN_USBPRO_EP_CMDOUT), + packet_ptr, packet_len, + NULL, PCAN_UFD_CMD_TIMEOUT_MS); + if (err) { + netdev_err(dev->netdev, + "sending command failure: %d\n", err); + break; + } + + packet_ptr += packet_len; + cmd_len -= packet_len; + + if (cmd_len < PCAN_UFD_LOSPD_PKT_SIZE) + packet_len = cmd_len; + + } while (packet_len > 0); + + return err; +} + +/* build the commands list in the given buffer, to enter operational mode */ +static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) +{ + struct pucan_wr_err_cnt *prc; + struct pucan_command *cmd; + u8 *pc = buf; + + /* 1st, reset error counters: */ + prc = (struct pucan_wr_err_cnt *)pc; + prc->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_WR_ERR_CNT); + + /* select both counters */ + prc->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE|PUCAN_WRERRCNT_RE); + + /* and reset their values */ + prc->tx_counter = 0; + prc->rx_counter = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_wr_err_cnt); + + /* add command to switch from ISO to non-ISO mode, if fw allows it */ + if (dev->can.ctrlmode_supported & CAN_CTRLMODE_FD_NON_ISO) { + struct pucan_options *puo = (struct pucan_options *)pc; + + puo->opcode_channel = + (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? + pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_CLR_DIS_OPTION) : + pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_SET_EN_OPTION); + + puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO); + + /* to be sure that no other extended bits will be taken into + * account + */ + puo->unused = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_options); + } + + /* next, go back to operational mode */ + cmd = (struct pucan_command *)pc; + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ? + PUCAN_CMD_LISTEN_ONLY_MODE : + PUCAN_CMD_NORMAL_MODE); + pc += sizeof(struct pucan_command); + + return pc - buf; +} + +/* set CAN bus on/off */ +static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 *pc = pcan_usb_fd_cmd_buffer(dev); + int l; + + if (onoff) { + /* build the cmds list to enter operational mode */ + l = pcan_usb_fd_build_restart_cmd(dev, pc); + } else { + struct pucan_command *cmd = (struct pucan_command *)pc; + + /* build cmd to go back to reset mode */ + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_RESET_MODE); + l = sizeof(struct pucan_command); + } + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, pc + l); +} + +/* set filtering masks: + * + * idx in range [0..63] selects a row #idx, all rows otherwise + * mask in range [0..0xffffffff] defines up to 32 CANIDs in the row(s) + * + * Each bit of this 64 x 32 bits array defines a CANID value: + * + * bit[i,j] = 1 implies that CANID=(i x 32)+j will be received, while + * bit[i,j] = 0 implies that CANID=(i x 32)+j will be discarded. + */ +static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, + u32 mask) +{ + struct pucan_filter_std *cmd = pcan_usb_fd_cmd_buffer(dev); + int i, n; + + /* select all rows when idx is out of range [0..63] */ + if ((idx < 0) || (idx >= (1 << PUCAN_FLTSTD_ROW_IDX_BITS))) { + n = 1 << PUCAN_FLTSTD_ROW_IDX_BITS; + idx = 0; + + /* select the row (and only the row) otherwise */ + } else { + n = idx + 1; + } + + for (i = idx; i < n; i++, cmd++) { + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_FILTER_STD); + cmd->idx = cpu_to_le16(i); + cmd->mask = cpu_to_le32(mask); + } + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, cmd); +} + +/* set/unset options + * + * onoff set(1)/unset(0) options + * mask each bit defines a kind of options to set/unset + */ +static int pcan_usb_fd_set_options(struct peak_usb_device *dev, + bool onoff, u16 ucan_mask, u16 usb_mask) +{ + struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + (onoff) ? PUCAN_CMD_SET_EN_OPTION : + PUCAN_CMD_CLR_DIS_OPTION); + + cmd->ucan_mask = cpu_to_le16(ucan_mask); + cmd->usb_mask = cpu_to_le16(usb_mask); + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* setup LED control */ +static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode) +{ + struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PCAN_UFD_CMD_LED_SET); + cmd->mode = led_mode; + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set CAN clock domain */ +static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev, + u8 clk_mode) +{ + struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PCAN_UFD_CMD_CLK_SET); + cmd->mode = clk_mode; + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set bittiming for CAN and CAN-FD header */ +static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_TIMING_SLOW); + cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1, + dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); + + cmd->tseg2 = PUCAN_TSLOW_TSEG2(bt->phase_seg2 - 1); + cmd->tseg1 = PUCAN_TSLOW_TSEG1(bt->prop_seg + bt->phase_seg1 - 1); + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(bt->brp - 1)); + + cmd->ewl = 96; /* default */ + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set CAN-FD bittiming for data */ +static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_TIMING_FAST); + cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1); + cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1); + cmd->tseg1 = PUCAN_TFAST_TSEG1(bt->prop_seg + bt->phase_seg1 - 1); + cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(bt->brp - 1)); + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* handle restart but in asynchronously way + * (uses PCAN-USB Pro code to complete asynchronous request) + */ +static int pcan_usb_fd_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + u8 *pc = buf; + + /* build the entire cmds list in the provided buffer, to go back into + * operational mode. + */ + pc += pcan_usb_fd_build_restart_cmd(dev, pc); + + /* add EOC */ + memset(pc, 0xff, sizeof(struct pucan_command)); + pc += sizeof(struct pucan_command); + + /* complete the URB */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, pc - buf, + pcan_usb_pro_restart_complete, dev); + + /* and submit it. */ + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static int pcan_usb_fd_drv_loaded(struct peak_usb_device *dev, bool loaded) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + pdev->cmd_buffer_addr[0] = 0; + pdev->cmd_buffer_addr[1] = !!loaded; + + return pcan_usb_pro_send_req(dev, + PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, + pdev->cmd_buffer_addr, + PCAN_USBPRO_FCT_DRVLD_REQ_LEN); +} + +static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_rx_msg *rm = (struct pucan_rx_msg *)rx_msg; + struct peak_usb_device *dev; + struct net_device *netdev; + struct canfd_frame *cfd; + struct sk_buff *skb; + const u16 rx_msg_flags = le16_to_cpu(rm->flags); + + if (pucan_msg_get_channel(rm) >= ARRAY_SIZE(usb_if->dev)) + return -ENOMEM; + + dev = usb_if->dev[pucan_msg_get_channel(rm)]; + netdev = dev->netdev; + + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { + /* CANFD frame case */ + skb = alloc_canfd_skb(netdev, &cfd); + if (!skb) + return -ENOMEM; + + if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) + cfd->flags |= CANFD_BRS; + + if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) + cfd->flags |= CANFD_ESI; + + cfd->len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(rm))); + } else { + /* CAN 2.0 frame case */ + skb = alloc_can_skb(netdev, (struct can_frame **)&cfd); + if (!skb) + return -ENOMEM; + + cfd->len = get_can_dlc(pucan_msg_get_dlc(rm)); + } + + cfd->can_id = le32_to_cpu(rm->can_id); + + if (rx_msg_flags & PUCAN_MSG_EXT_ID) + cfd->can_id |= CAN_EFF_FLAG; + + if (rx_msg_flags & PUCAN_MSG_RTR) + cfd->can_id |= CAN_RTR_FLAG; + else + memcpy(cfd->data, rm->d, cfd->len); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cfd->len; + + peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(rm->ts_low)); + + return 0; +} + +/* handle uCAN status message */ +static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_status_msg *sm = (struct pucan_status_msg *)rx_msg; + struct pcan_usb_fd_device *pdev; + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + enum can_state rx_state, tx_state; + struct peak_usb_device *dev; + struct net_device *netdev; + struct can_frame *cf; + struct sk_buff *skb; + + if (pucan_stmsg_get_channel(sm) >= ARRAY_SIZE(usb_if->dev)) + return -ENOMEM; + + dev = usb_if->dev[pucan_stmsg_get_channel(sm)]; + pdev = container_of(dev, struct pcan_usb_fd_device, dev); + netdev = dev->netdev; + + /* nothing should be sent while in BUS_OFF state */ + if (dev->can.state == CAN_STATE_BUS_OFF) + return 0; + + if (sm->channel_p_w_b & PUCAN_BUS_BUSOFF) { + new_state = CAN_STATE_BUS_OFF; + } else if (sm->channel_p_w_b & PUCAN_BUS_PASSIVE) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (sm->channel_p_w_b & PUCAN_BUS_WARNING) { + new_state = CAN_STATE_ERROR_WARNING; + } else { + /* back to (or still in) ERROR_ACTIVE state */ + new_state = CAN_STATE_ERROR_ACTIVE; + pdev->bec.txerr = 0; + pdev->bec.rxerr = 0; + } + + /* state hasn't changed */ + if (new_state == dev->can.state) + return 0; + + /* handle bus state change */ + tx_state = (pdev->bec.txerr >= pdev->bec.rxerr) ? new_state : 0; + rx_state = (pdev->bec.txerr <= pdev->bec.rxerr) ? new_state : 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &cf); + if (skb) + can_change_state(netdev, cf, tx_state, rx_state); + + /* things must be done even in case of OOM */ + if (new_state == CAN_STATE_BUS_OFF) + can_bus_off(netdev); + + if (!skb) + return -ENOMEM; + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cf->can_dlc; + + peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(sm->ts_low)); + + return 0; +} + +/* handle uCAN error message */ +static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_error_msg *er = (struct pucan_error_msg *)rx_msg; + struct pcan_usb_fd_device *pdev; + struct peak_usb_device *dev; + + if (pucan_ermsg_get_channel(er) >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[pucan_ermsg_get_channel(er)]; + pdev = container_of(dev, struct pcan_usb_fd_device, dev); + + /* keep a trace of tx and rx error counters for later use */ + pdev->bec.txerr = er->tx_err_cnt; + pdev->bec.rxerr = er->rx_err_cnt; + + return 0; +} + +/* handle uCAN overrun message */ +static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pcan_ufd_ovr_msg *ov = (struct pcan_ufd_ovr_msg *)rx_msg; + struct peak_usb_device *dev; + struct net_device *netdev; + struct can_frame *cf; + struct sk_buff *skb; + + if (pufd_omsg_get_channel(ov) >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[pufd_omsg_get_channel(ov)]; + netdev = dev->netdev; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) + return -ENOMEM; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + + peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(ov->ts_low)); + + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + + return 0; +} + +/* handle USB calibration message */ +static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pcan_ufd_ts_msg *ts = (struct pcan_ufd_ts_msg *)rx_msg; + + /* 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->ts_low)); +} + +/* callback for bulk IN urb */ +static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pucan_msg *rx_msg; + u8 *msg_ptr, *msg_end; + int err = 0; + + /* loop reading all the records from the incoming message */ + msg_ptr = urb->transfer_buffer; + msg_end = urb->transfer_buffer + urb->actual_length; + for (; msg_ptr < msg_end;) { + u16 rx_msg_type, rx_msg_size; + + rx_msg = (struct pucan_msg *)msg_ptr; + if (!rx_msg->size) { + /* null packet found: end of list */ + break; + } + + rx_msg_size = le16_to_cpu(rx_msg->size); + rx_msg_type = le16_to_cpu(rx_msg->type); + + /* check if the record goes out of current packet */ + if (msg_ptr + rx_msg_size > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf sze\n"); + err = -EBADMSG; + break; + } + + switch (rx_msg_type) { + case PUCAN_MSG_CAN_RX: + err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_UFD_MSG_CALIBRATION: + pcan_usb_fd_decode_ts(usb_if, rx_msg); + break; + + case PUCAN_MSG_ERROR: + err = pcan_usb_fd_decode_error(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PUCAN_MSG_STATUS: + err = pcan_usb_fd_decode_status(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_UFD_MSG_OVERRUN: + err = pcan_usb_fd_decode_overrun(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + default: + netdev_err(netdev, + "unhandled msg type 0x%02x (%d): ignored\n", + rx_msg_type, rx_msg_type); + break; + } + + msg_ptr += rx_msg_size; + } + +fail: + if (err) + pcan_dump_mem("received msg", + urb->transfer_buffer, urb->actual_length); + return err; +} + +/* CAN/CANFD frames encoding callback */ +static int pcan_usb_fd_encode_msg(struct peak_usb_device *dev, + struct sk_buff *skb, u8 *obuf, size_t *size) +{ + struct pucan_tx_msg *tx_msg = (struct pucan_tx_msg *)obuf; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + u16 tx_msg_size, tx_msg_flags; + u8 can_dlc; + + if (cfd->len > CANFD_MAX_DLEN) + return -EINVAL; + + tx_msg_size = ALIGN(sizeof(struct pucan_tx_msg) + cfd->len, 4); + tx_msg->size = cpu_to_le16(tx_msg_size); + tx_msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); + + tx_msg_flags = 0; + if (cfd->can_id & CAN_EFF_FLAG) { + tx_msg_flags |= PUCAN_MSG_EXT_ID; + tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_EFF_MASK); + } else { + tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) { + /* considering a CANFD frame */ + can_dlc = can_len2dlc(cfd->len); + + tx_msg_flags |= PUCAN_MSG_EXT_DATA_LEN; + + if (cfd->flags & CANFD_BRS) + tx_msg_flags |= PUCAN_MSG_BITRATE_SWITCH; + + if (cfd->flags & CANFD_ESI) + tx_msg_flags |= PUCAN_MSG_ERROR_STATE_IND; + } else { + /* CAND 2.0 frames */ + can_dlc = cfd->len; + + if (cfd->can_id & CAN_RTR_FLAG) + tx_msg_flags |= PUCAN_MSG_RTR; + } + + tx_msg->flags = cpu_to_le16(tx_msg_flags); + tx_msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(dev->ctrl_idx, can_dlc); + memcpy(tx_msg->d, cfd->data, cfd->len); + + /* add null size message to tag the end (messages are 32-bits aligned) + */ + tx_msg = (struct pucan_tx_msg *)(obuf + tx_msg_size); + + tx_msg->size = 0; + + /* set the whole size of the USB packet to send */ + *size = tx_msg_size + sizeof(u32); + + return 0; +} + +/* start the interface (last chance before set bus on) */ +static int pcan_usb_fd_start(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + int err; + + /* set filter mode: all acceptance */ + err = pcan_usb_fd_set_filter_std(dev, -1, 0xffffffff); + 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_fd); + + /* enable USB calibration messages */ + err = pcan_usb_fd_set_options(dev, 1, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + } + + pdev->usb_if->dev_opened_count++; + + /* reset cached error counters */ + pdev->bec.txerr = 0; + pdev->bec.rxerr = 0; + + return err; +} + +/* socket callback used to copy berr counters values receieved through USB */ +static int pcan_usb_fd_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + *bec = pdev->bec; + + /* must return 0 */ + return 0; +} + +/* stop interface (last chance before set bus off) */ +static int pcan_usb_fd_stop(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* turn off special msgs for that interface if no other dev opened */ + if (pdev->usb_if->dev_opened_count == 1) + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + pdev->usb_if->dev_opened_count--; + + return 0; +} + +/* called when probing, to initialize a device object */ +static int pcan_usb_fd_init(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + int i, err = -ENOMEM; + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + /* allocate netdevices common structure attached to first one */ + pdev->usb_if = kzalloc(sizeof(*pdev->usb_if), GFP_KERNEL); + if (!pdev->usb_if) + goto err_out; + + /* allocate command buffer once for all for the interface */ + pdev->cmd_buffer_addr = kzalloc(PCAN_UFD_CMD_BUFFER_SIZE, + GFP_KERNEL); + if (!pdev->cmd_buffer_addr) + goto err_out_1; + + /* number of ts msgs to ignore before taking one into account */ + pdev->usb_if->cm_ignore_count = 5; + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_FW, + &pdev->usb_if->fw_info, + sizeof(pdev->usb_if->fw_info)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + dev->adapter->name, err); + goto err_out_2; + } + + /* explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx (channel) device. + */ + dev_info(dev->netdev->dev.parent, + "PEAK-System %s v%u fw v%u.%u.%u (%u channels)\n", + dev->adapter->name, pdev->usb_if->fw_info.hw_version, + pdev->usb_if->fw_info.fw_version[0], + pdev->usb_if->fw_info.fw_version[1], + pdev->usb_if->fw_info.fw_version[2], + dev->adapter->ctrl_count); + + /* check for ability to switch between ISO/non-ISO modes */ + if (pdev->usb_if->fw_info.fw_version[0] >= 2) { + /* firmware >= 2.x supports ISO/non-ISO switching */ + dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO; + } else { + /* firmware < 2.x only supports fixed(!) non-ISO */ + dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO; + } + + /* tell the hardware the can driver is running */ + err = pcan_usb_fd_drv_loaded(dev, 1); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to tell %s driver is loaded (err %d)\n", + dev->adapter->name, err); + goto err_out_2; + } + } else { + /* otherwise, simply copy previous sibling's values */ + struct pcan_usb_fd_device *ppdev = + container_of(dev->prev_siblings, + struct pcan_usb_fd_device, dev); + + pdev->usb_if = ppdev->usb_if; + pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr; + + /* do a copy of the ctrlmode[_supported] too */ + dev->can.ctrlmode = ppdev->dev.can.ctrlmode; + dev->can.ctrlmode_supported = ppdev->dev.can.ctrlmode_supported; + } + + pdev->usb_if->dev[dev->ctrl_idx] = dev; + dev->device_number = + le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]); + + /* set clock domain */ + for (i = 0; i < ARRAY_SIZE(pcan_usb_fd_clk_freq); i++) + if (dev->adapter->clock.freq == pcan_usb_fd_clk_freq[i]) + break; + + if (i >= ARRAY_SIZE(pcan_usb_fd_clk_freq)) { + dev_warn(dev->netdev->dev.parent, + "incompatible clock frequencies\n"); + err = -EINVAL; + goto err_out_2; + } + + pcan_usb_fd_set_clock_domain(dev, i); + + /* set LED in default state (end of init phase) */ + pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF); + + return 0; + +err_out_2: + kfree(pdev->cmd_buffer_addr); +err_out_1: + kfree(pdev->usb_if); +err_out: + return err; +} + +/* called when driver module is being unloaded */ +static void pcan_usb_fd_exit(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_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_fd_set_bus(dev, 0); + } + + /* switch off corresponding CAN LEDs */ + pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_OFF); + + /* 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_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + + /* tell USB adapter that the driver is being unloaded */ + pcan_usb_fd_drv_loaded(dev, 0); + } +} + +/* called when the USB adapter is unplugged */ +static void pcan_usb_fd_free(struct peak_usb_device *dev) +{ + /* last device: can free shared objects now */ + if (!dev->prev_siblings && !dev->next_siblings) { + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* free commands buffer */ + kfree(pdev->cmd_buffer_addr); + + /* free usb interface object */ + kfree(pdev->usb_if); + } +} + +/* describes the PCAN-USB FD adapter */ +static const struct can_bittiming_const pcan_usb_fd_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_fd_data_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_fd = { + .name = "PCAN-USB FD", + .device_id = PCAN_USBFD_PRODUCT_ID, + .ctrl_count = PCAN_USBFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_fd_const, + .data_bittiming_const = &pcan_usb_fd_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_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}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-CHIP USB */ +static const struct can_bittiming_const pcan_usb_chip_const = { + .name = "pcan_chip_usb", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_chip_data_const = { + .name = "pcan_chip_usb", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_chip = { + .name = "PCAN-Chip USB", + .device_id = PCAN_USBCHIP_PRODUCT_ID, + .ctrl_count = PCAN_USBFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_chip_const, + .data_bittiming_const = &pcan_usb_chip_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_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}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-USB Pro FD adapter */ +static const struct can_bittiming_const pcan_usb_pro_fd_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_pro_fd_data_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_pro_fd = { + .name = "PCAN-USB Pro FD", + .device_id = PCAN_USBPROFD_PRODUCT_ID, + .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_pro_fd_const, + .data_bittiming_const = &pcan_usb_pro_fd_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_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_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-USB X6 adapter */ +static const struct can_bittiming_const pcan_usb_x6_const = { + .name = "pcan_usb_x6", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_x6_data_const = { + .name = "pcan_usb_x6", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +const struct peak_usb_adapter pcan_usb_x6 = { + .name = "PCAN-USB X6", + .device_id = PCAN_USBX6_PRODUCT_ID, + .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_x6_const, + .data_bittiming_const = &pcan_usb_x6_data_const, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_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_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; 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 000000000..b304198f0 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -0,0 +1,1058 @@ +/* + * 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"); + +#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 { + __le16 *rec_cnt_rd; + __le32 *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; + /* fall through */ + case PCAN_USBPRO_TXMSG4: + i += 4; + /* fall through */ + case PCAN_USBPRO_TXMSG0: + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *(__le32 *)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; + *(__le32 *)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); + *(__le16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + case PCAN_USBPRO_SETLED: + *pc++ = va_arg(ap, int); + *(__le16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + *(__le32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETTS: + pc++; + *(__le16 *)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(le32_to_cpu(*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"); + pcan_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; +} + +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; + + 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; + memset(req_addr, '\0', req_size); + 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); +} + +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 int pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded) +{ + u8 *buffer; + int err; + + buffer = kzalloc(PCAN_USBPRO_FCT_DRVLD_REQ_LEN, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 0; + buffer[1] = !!loaded; + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, buffer, + PCAN_USBPRO_FCT_DRVLD_REQ_LEN); + kfree(buffer); + + return err; +} + +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 skb_shared_hwtstamps *hwts; + + 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); + + hwts = skb_hwtstamps(skb); + peak_usb_get_ts_time(&usb_if->time_ref, le32_to_cpu(rx->ts32), + &hwts->hwtstamp); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + netif_rx(skb); + + return 0; +} + +static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxstatus *er) +{ + const u16 raw_status = le16_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 skb_shared_hwtstamps *hwts; + + /* 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; + dev->can.can_stats.bus_off++; + 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; + + hwts = skb_hwtstamps(skb); + peak_usb_get_ts_time(&usb_if->time_ref, le32_to_cpu(er->ts32), &hwts->hwtstamp); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + netif_rx(skb); + + 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) + pcan_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_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + struct pcan_usb_pro_interface *usb_if = NULL; + struct pcan_usb_pro_fwinfo *fi = NULL; + struct pcan_usb_pro_blinfo *bi = NULL; + int err; + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + /* allocate netdevices common structure attached to first one */ + usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface), + GFP_KERNEL); + fi = kmalloc(sizeof(struct pcan_usb_pro_fwinfo), GFP_KERNEL); + bi = kmalloc(sizeof(struct pcan_usb_pro_blinfo), GFP_KERNEL); + if (!usb_if || !fi || !bi) { + err = -ENOMEM; + goto err_out; + } + + /* 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) { + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + pcan_usb_pro.name, err); + goto err_out; + } + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_BL, + bi, sizeof(*bi)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s bootloader info (err %d)\n", + pcan_usb_pro.name, err); + goto err_out; + } + + /* tell the device the can driver is running */ + err = pcan_usb_pro_drv_loaded(dev, 1); + if (err) + goto err_out; + + 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); + } 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); + + kfree(bi); + kfree(fi); + + return 0; + + err_out: + kfree(bi); + kfree(fi); + kfree(usb_if); + + return err; +} + +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 + */ +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 + */ +static const struct can_bittiming_const pcan_usb_pro_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, +}; + +const struct peak_usb_adapter pcan_usb_pro = { + .name = "PCAN-USB Pro", + .device_id = PCAN_USBPRO_PRODUCT_ID, + .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_USBPRO_CRYSTAL_HZ, + }, + .bittiming_const = &pcan_usb_pro_const, + + /* 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 000000000..a62f7ab89 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -0,0 +1,192 @@ +/* + * 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 + +/* PCAN-USB Pro (FD) 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) + +/* Vendor Request value for XXX_FCT */ +#define PCAN_USBPRO_FCT_DRVLD 5 /* tell device driver is loaded */ +#define PCAN_USBPRO_FCT_DRVLD_REQ_LEN 16 + +/* PCAN_USBPRO_INFO_BL vendor request record type */ +struct __packed pcan_usb_pro_blinfo { + __le32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + __le32 serial_num_hi; + __le32 serial_num_lo; + __le32 hw_type; + __le32 hw_rev; +}; + +/* PCAN_USBPRO_INFO_FW vendor request record type */ +struct __packed pcan_usb_pro_fwinfo { + __le32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + __le32 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; + __le16 dummy; + __le32 CCBT; +}; + +struct __packed pcan_usb_pro_busact { + u8 data_type; + u8 channel; + __le16 onoff; +}; + +struct __packed pcan_usb_pro_silent { + u8 data_type; + u8 channel; + __le16 onoff; +}; + +struct __packed pcan_usb_pro_filter { + u8 data_type; + u8 dummy; + __le16 filter_mode; +}; + +struct __packed pcan_usb_pro_setts { + u8 data_type; + u8 dummy; + __le16 mode; +}; + +struct __packed pcan_usb_pro_devid { + u8 data_type; + u8 channel; + __le16 dummy; + __le32 serial_num; +}; + +struct __packed pcan_usb_pro_setled { + u8 data_type; + u8 channel; + __le16 mode; + __le32 timeout; +}; + +struct __packed pcan_usb_pro_rxmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + __le32 ts32; + __le32 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; + __le16 status; + __le32 ts32; + __le32 err_frm; +}; + +struct __packed pcan_usb_pro_rxts { + u8 data_type; + u8 dummy[3]; + __le32 ts64[2]; +}; + +struct __packed pcan_usb_pro_txmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + __le32 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; +}; + +int pcan_usb_pro_probe(struct usb_interface *intf); +int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size); +void pcan_usb_pro_restart_complete(struct urb *urb); + +#endif diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c new file mode 100644 index 000000000..43350f56e --- /dev/null +++ b/drivers/net/can/usb/ucan.c @@ -0,0 +1,1610 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Driver for Theobroma Systems UCAN devices, Protocol Version 3 + * + * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH + * + * + * General Description: + * + * The USB Device uses three Endpoints: + * + * CONTROL Endpoint: Is used the setup the device (start, stop, + * info, configure). + * + * IN Endpoint: The device sends CAN Frame Messages and Device + * Information using the IN endpoint. + * + * OUT Endpoint: The driver sends configuration requests, and CAN + * Frames on the out endpoint. + * + * Error Handling: + * + * If error reporting is turned on the device encodes error into CAN + * error frames (see uapi/linux/can/error.h) and sends it using the + * IN Endpoint. The driver updates statistics and forward it. + */ + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/signal.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#define UCAN_DRIVER_NAME "ucan" +#define UCAN_MAX_RX_URBS 8 +/* the CAN controller needs a while to enable/disable the bus */ +#define UCAN_USB_CTL_PIPE_TIMEOUT 1000 +/* this driver currently supports protocol version 3 only */ +#define UCAN_PROTOCOL_VERSION_MIN 3 +#define UCAN_PROTOCOL_VERSION_MAX 3 + +/* UCAN Message Definitions + * ------------------------ + * + * ucan_message_out_t and ucan_message_in_t define the messages + * transmitted on the OUT and IN endpoint. + * + * Multibyte fields are transmitted with little endianness + * + * INTR Endpoint: a single uint32_t storing the current space in the fifo + * + * OUT Endpoint: single message of type ucan_message_out_t is + * transmitted on the out endpoint + * + * IN Endpoint: multiple messages ucan_message_in_t concateted in + * the following way: + * + * m[n].len <=> the length if message n(including the header in bytes) + * m[n] is is aligned to a 4 byte boundary, hence + * offset(m[0]) := 0; + * offset(m[n+1]) := offset(m[n]) + (m[n].len + 3) & 3 + * + * this implies that + * offset(m[n]) % 4 <=> 0 + */ + +/* Device Global Commands */ +enum { + UCAN_DEVICE_GET_FW_STRING = 0, +}; + +/* UCAN Commands */ +enum { + /* start the can transceiver - val defines the operation mode */ + UCAN_COMMAND_START = 0, + /* cancel pending transmissions and stop the can transceiver */ + UCAN_COMMAND_STOP = 1, + /* send can transceiver into low-power sleep mode */ + UCAN_COMMAND_SLEEP = 2, + /* wake up can transceiver from low-power sleep mode */ + UCAN_COMMAND_WAKEUP = 3, + /* reset the can transceiver */ + UCAN_COMMAND_RESET = 4, + /* get piece of info from the can transceiver - subcmd defines what + * piece + */ + UCAN_COMMAND_GET = 5, + /* clear or disable hardware filter - subcmd defines which of the two */ + UCAN_COMMAND_FILTER = 6, + /* Setup bittiming */ + UCAN_COMMAND_SET_BITTIMING = 7, + /* recover from bus-off state */ + UCAN_COMMAND_RESTART = 8, +}; + +/* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap). + * Undefined bits must be set to 0. + */ +enum { + UCAN_MODE_LOOPBACK = BIT(0), + UCAN_MODE_SILENT = BIT(1), + UCAN_MODE_3_SAMPLES = BIT(2), + UCAN_MODE_ONE_SHOT = BIT(3), + UCAN_MODE_BERR_REPORT = BIT(4), +}; + +/* UCAN_COMMAND_GET subcommands */ +enum { + UCAN_COMMAND_GET_INFO = 0, + UCAN_COMMAND_GET_PROTOCOL_VERSION = 1, +}; + +/* UCAN_COMMAND_FILTER subcommands */ +enum { + UCAN_FILTER_CLEAR = 0, + UCAN_FILTER_DISABLE = 1, + UCAN_FILTER_ENABLE = 2, +}; + +/* OUT endpoint message types */ +enum { + UCAN_OUT_TX = 2, /* transmit a CAN frame */ +}; + +/* IN endpoint message types */ +enum { + UCAN_IN_TX_COMPLETE = 1, /* CAN frame transmission completed */ + UCAN_IN_RX = 2, /* CAN frame received */ +}; + +struct ucan_ctl_cmd_start { + __le16 mode; /* OR-ing any of UCAN_MODE_* */ +} __packed; + +struct ucan_ctl_cmd_set_bittiming { + __le32 tq; /* Time quanta (TQ) in nanoseconds */ + __le16 brp; /* TQ Prescaler */ + __le16 sample_point; /* Samplepoint on tenth percent */ + u8 prop_seg; /* Propagation segment in TQs */ + u8 phase_seg1; /* Phase buffer segment 1 in TQs */ + u8 phase_seg2; /* Phase buffer segment 2 in TQs */ + u8 sjw; /* Synchronisation jump width in TQs */ +} __packed; + +struct ucan_ctl_cmd_device_info { + __le32 freq; /* Clock Frequency for tq generation */ + u8 tx_fifo; /* Size of the transmission fifo */ + u8 sjw_max; /* can_bittiming fields... */ + u8 tseg1_min; + u8 tseg1_max; + u8 tseg2_min; + u8 tseg2_max; + __le16 brp_inc; + __le32 brp_min; + __le32 brp_max; /* ...can_bittiming fields */ + __le16 ctrlmodes; /* supported control modes */ + __le16 hwfilter; /* Number of HW filter banks */ + __le16 rxmboxes; /* Number of receive Mailboxes */ +} __packed; + +struct ucan_ctl_cmd_get_protocol_version { + __le32 version; +} __packed; + +union ucan_ctl_payload { + /* Setup Bittiming + * bmRequest == UCAN_COMMAND_START + */ + struct ucan_ctl_cmd_start cmd_start; + /* Setup Bittiming + * bmRequest == UCAN_COMMAND_SET_BITTIMING + */ + struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming; + /* Get Device Information + * bmRequest == UCAN_COMMAND_GET; wValue = UCAN_COMMAND_GET_INFO + */ + struct ucan_ctl_cmd_device_info cmd_get_device_info; + /* Get Protocol Version + * bmRequest == UCAN_COMMAND_GET; + * wValue = UCAN_COMMAND_GET_PROTOCOL_VERSION + */ + struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version; + + u8 raw[128]; +} __packed; + +enum { + UCAN_TX_COMPLETE_SUCCESS = BIT(0), +}; + +/* Transmission Complete within ucan_message_in */ +struct ucan_tx_complete_entry_t { + u8 echo_index; + u8 flags; +} __packed __aligned(0x2); + +/* CAN Data message format within ucan_message_in/out */ +struct ucan_can_msg { + /* note DLC is computed by + * msg.len - sizeof (msg.len) + * - sizeof (msg.type) + * - sizeof (msg.can_msg.id) + */ + __le32 id; + + union { + u8 data[CAN_MAX_DLEN]; /* Data of CAN frames */ + u8 dlc; /* RTR dlc */ + }; +} __packed; + +/* OUT Endpoint, outbound messages */ +struct ucan_message_out { + __le16 len; /* Length of the content include header */ + u8 type; /* UCAN_OUT_TX and friends */ + u8 subtype; /* command sub type */ + + union { + /* Transmit CAN frame + * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0) + * subtype stores the echo id + */ + struct ucan_can_msg can_msg; + } msg; +} __packed __aligned(0x4); + +/* IN Endpoint, inbound messages */ +struct ucan_message_in { + __le16 len; /* Length of the content include header */ + u8 type; /* UCAN_IN_RX and friends */ + u8 subtype; /* command sub type */ + + union { + /* CAN Frame received + * (type == UCAN_IN_RX) + * && ((msg.can_msg.id & CAN_RTR_FLAG) == 0) + */ + struct ucan_can_msg can_msg; + + /* CAN transmission complete + * (type == UCAN_IN_TX_COMPLETE) + */ + struct ucan_tx_complete_entry_t can_tx_complete_msg[0]; + } __aligned(0x4) msg; +} __packed; + +/* Macros to calculate message lengths */ +#define UCAN_OUT_HDR_SIZE offsetof(struct ucan_message_out, msg) + +#define UCAN_IN_HDR_SIZE offsetof(struct ucan_message_in, msg) +#define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member)) + +struct ucan_priv; + +/* Context Information for transmission URBs */ +struct ucan_urb_context { + struct ucan_priv *up; + u8 dlc; + bool allocated; +}; + +/* Information reported by the USB device */ +struct ucan_device_info { + struct can_bittiming_const bittiming_const; + u8 tx_fifo; +}; + +/* Driver private data */ +struct ucan_priv { + /* must be the first member */ + struct can_priv can; + + /* linux USB device structures */ + struct usb_device *udev; + struct usb_interface *intf; + struct net_device *netdev; + + /* lock for can->echo_skb (used around + * can_put/get/free_echo_skb + */ + spinlock_t echo_skb_lock; + + /* usb device information information */ + u8 intf_index; + u8 in_ep_addr; + u8 out_ep_addr; + u16 in_ep_size; + + /* transmission and reception buffers */ + struct usb_anchor rx_urbs; + struct usb_anchor tx_urbs; + + union ucan_ctl_payload *ctl_msg_buffer; + struct ucan_device_info device_info; + + /* transmission control information and locks */ + spinlock_t context_lock; + unsigned int available_tx_urbs; + struct ucan_urb_context *context_array; +}; + +static u8 ucan_get_can_dlc(struct ucan_can_msg *msg, u16 len) +{ + if (le32_to_cpu(msg->id) & CAN_RTR_FLAG) + return get_can_dlc(msg->dlc); + else + return get_can_dlc(len - (UCAN_IN_HDR_SIZE + sizeof(msg->id))); +} + +static void ucan_release_context_array(struct ucan_priv *up) +{ + if (!up->context_array) + return; + + /* lock is not needed because, driver is currently opening or closing */ + up->available_tx_urbs = 0; + + kfree(up->context_array); + up->context_array = NULL; +} + +static int ucan_alloc_context_array(struct ucan_priv *up) +{ + int i; + + /* release contexts if any */ + ucan_release_context_array(up); + + up->context_array = kcalloc(up->device_info.tx_fifo, + sizeof(*up->context_array), + GFP_KERNEL); + if (!up->context_array) { + netdev_err(up->netdev, + "Not enough memory to allocate tx contexts\n"); + return -ENOMEM; + } + + for (i = 0; i < up->device_info.tx_fifo; i++) { + up->context_array[i].allocated = false; + up->context_array[i].up = up; + } + + /* lock is not needed because, driver is currently opening */ + up->available_tx_urbs = up->device_info.tx_fifo; + + return 0; +} + +static struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up) +{ + int i; + unsigned long flags; + struct ucan_urb_context *ret = NULL; + + if (WARN_ON_ONCE(!up->context_array)) + return NULL; + + /* execute context operation atomically */ + spin_lock_irqsave(&up->context_lock, flags); + + for (i = 0; i < up->device_info.tx_fifo; i++) { + if (!up->context_array[i].allocated) { + /* update context */ + ret = &up->context_array[i]; + up->context_array[i].allocated = true; + + /* stop queue if necessary */ + up->available_tx_urbs--; + if (!up->available_tx_urbs) + netif_stop_queue(up->netdev); + + break; + } + } + + spin_unlock_irqrestore(&up->context_lock, flags); + return ret; +} + +static bool ucan_release_context(struct ucan_priv *up, + struct ucan_urb_context *ctx) +{ + unsigned long flags; + bool ret = false; + + if (WARN_ON_ONCE(!up->context_array)) + return false; + + /* execute context operation atomically */ + spin_lock_irqsave(&up->context_lock, flags); + + /* context was not allocated, maybe the device sent garbage */ + if (ctx->allocated) { + ctx->allocated = false; + + /* check if the queue needs to be woken */ + if (!up->available_tx_urbs) + netif_wake_queue(up->netdev); + up->available_tx_urbs++; + + ret = true; + } + + spin_unlock_irqrestore(&up->context_lock, flags); + return ret; +} + +static int ucan_ctrl_command_out(struct ucan_priv *up, + u8 cmd, u16 subcmd, u16 datalen) +{ + return usb_control_msg(up->udev, + usb_sndctrlpipe(up->udev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + subcmd, + up->intf_index, + up->ctl_msg_buffer, + datalen, + UCAN_USB_CTL_PIPE_TIMEOUT); +} + +static int ucan_device_request_in(struct ucan_priv *up, + u8 cmd, u16 subcmd, u16 datalen) +{ + return usb_control_msg(up->udev, + usb_rcvctrlpipe(up->udev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + subcmd, + 0, + up->ctl_msg_buffer, + datalen, + UCAN_USB_CTL_PIPE_TIMEOUT); +} + +/* Parse the device information structure reported by the device and + * setup private variables accordingly + */ +static void ucan_parse_device_info(struct ucan_priv *up, + struct ucan_ctl_cmd_device_info *device_info) +{ + struct can_bittiming_const *bittiming = + &up->device_info.bittiming_const; + u16 ctrlmodes; + + /* store the data */ + up->can.clock.freq = le32_to_cpu(device_info->freq); + up->device_info.tx_fifo = device_info->tx_fifo; + strcpy(bittiming->name, "ucan"); + bittiming->tseg1_min = device_info->tseg1_min; + bittiming->tseg1_max = device_info->tseg1_max; + bittiming->tseg2_min = device_info->tseg2_min; + bittiming->tseg2_max = device_info->tseg2_max; + bittiming->sjw_max = device_info->sjw_max; + bittiming->brp_min = le32_to_cpu(device_info->brp_min); + bittiming->brp_max = le32_to_cpu(device_info->brp_max); + bittiming->brp_inc = le16_to_cpu(device_info->brp_inc); + + ctrlmodes = le16_to_cpu(device_info->ctrlmodes); + + up->can.ctrlmode_supported = 0; + + if (ctrlmodes & UCAN_MODE_LOOPBACK) + up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; + if (ctrlmodes & UCAN_MODE_SILENT) + up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + if (ctrlmodes & UCAN_MODE_3_SAMPLES) + up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + if (ctrlmodes & UCAN_MODE_ONE_SHOT) + up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; + if (ctrlmodes & UCAN_MODE_BERR_REPORT) + up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; +} + +/* Handle a CAN error frame that we have received from the device. + * Returns true if the can state has changed. + */ +static bool ucan_handle_error_frame(struct ucan_priv *up, + struct ucan_message_in *m, + canid_t canid) +{ + enum can_state new_state = up->can.state; + struct net_device_stats *net_stats = &up->netdev->stats; + struct can_device_stats *can_stats = &up->can.can_stats; + + if (canid & CAN_ERR_LOSTARB) + can_stats->arbitration_lost++; + + if (canid & CAN_ERR_BUSERROR) + can_stats->bus_error++; + + if (canid & CAN_ERR_ACK) + net_stats->tx_errors++; + + if (canid & CAN_ERR_BUSOFF) + new_state = CAN_STATE_BUS_OFF; + + /* controller problems, details in data[1] */ + if (canid & CAN_ERR_CRTL) { + u8 d1 = m->msg.can_msg.data[1]; + + if (d1 & CAN_ERR_CRTL_RX_OVERFLOW) + net_stats->rx_over_errors++; + + /* controller state bits: if multiple are set the worst wins */ + if (d1 & CAN_ERR_CRTL_ACTIVE) + new_state = CAN_STATE_ERROR_ACTIVE; + + if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING)) + new_state = CAN_STATE_ERROR_WARNING; + + if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; + } + + /* protocol error, details in data[2] */ + if (canid & CAN_ERR_PROT) { + u8 d2 = m->msg.can_msg.data[2]; + + if (d2 & CAN_ERR_PROT_TX) + net_stats->tx_errors++; + else + net_stats->rx_errors++; + } + + /* no state change - we are done */ + if (up->can.state == new_state) + return false; + + /* we switched into a better state */ + if (up->can.state > new_state) { + up->can.state = new_state; + return true; + } + + /* we switched into a worse state */ + up->can.state = new_state; + switch (new_state) { + case CAN_STATE_BUS_OFF: + can_stats->bus_off++; + can_bus_off(up->netdev); + break; + case CAN_STATE_ERROR_PASSIVE: + can_stats->error_passive++; + break; + case CAN_STATE_ERROR_WARNING: + can_stats->error_warning++; + break; + default: + break; + } + return true; +} + +/* Callback on reception of a can frame via the IN endpoint + * + * This function allocates an skb and transferres it to the Linux + * network stack + */ +static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m) +{ + int len; + canid_t canid; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &up->netdev->stats; + + /* get the contents of the length field */ + len = le16_to_cpu(m->len); + + /* check sanity */ + if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) { + netdev_warn(up->netdev, "invalid input message len: %d\n", len); + return; + } + + /* handle error frames */ + canid = le32_to_cpu(m->msg.can_msg.id); + if (canid & CAN_ERR_FLAG) { + bool busstate_changed = ucan_handle_error_frame(up, m, canid); + + /* if berr-reporting is off only state changes get through */ + if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && + !busstate_changed) + return; + } else { + canid_t canid_mask; + /* compute the mask for canid */ + canid_mask = CAN_RTR_FLAG; + if (canid & CAN_EFF_FLAG) + canid_mask |= CAN_EFF_MASK | CAN_EFF_FLAG; + else + canid_mask |= CAN_SFF_MASK; + + if (canid & ~canid_mask) + netdev_warn(up->netdev, + "unexpected bits set (canid %x, mask %x)", + canid, canid_mask); + + canid &= canid_mask; + } + + /* allocate skb */ + skb = alloc_can_skb(up->netdev, &cf); + if (!skb) + return; + + /* fill the can frame */ + cf->can_id = canid; + + /* compute DLC taking RTR_FLAG into account */ + cf->can_dlc = ucan_get_can_dlc(&m->msg.can_msg, len); + + /* copy the payload of non RTR frames */ + if (!(cf->can_id & CAN_RTR_FLAG) || (cf->can_id & CAN_ERR_FLAG)) + memcpy(cf->data, m->msg.can_msg.data, cf->can_dlc); + + /* don't count error frames as real packets */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + /* pass it to Linux */ + netif_rx(skb); +} + +/* callback indicating completed transmission */ +static void ucan_tx_complete_msg(struct ucan_priv *up, + struct ucan_message_in *m) +{ + unsigned long flags; + u16 count, i; + u8 echo_index, dlc; + u16 len = le16_to_cpu(m->len); + + struct ucan_urb_context *context; + + if (len < UCAN_IN_HDR_SIZE || (len % 2 != 0)) { + netdev_err(up->netdev, "invalid tx complete length\n"); + return; + } + + count = (len - UCAN_IN_HDR_SIZE) / 2; + for (i = 0; i < count; i++) { + /* we did not submit such echo ids */ + echo_index = m->msg.can_tx_complete_msg[i].echo_index; + if (echo_index >= up->device_info.tx_fifo) { + up->netdev->stats.tx_errors++; + netdev_err(up->netdev, + "invalid echo_index %d received\n", + echo_index); + continue; + } + + /* gather information from the context */ + context = &up->context_array[echo_index]; + dlc = READ_ONCE(context->dlc); + + /* Release context and restart queue if necessary. + * Also check if the context was allocated + */ + if (!ucan_release_context(up, context)) + continue; + + spin_lock_irqsave(&up->echo_skb_lock, flags); + if (m->msg.can_tx_complete_msg[i].flags & + UCAN_TX_COMPLETE_SUCCESS) { + /* update statistics */ + up->netdev->stats.tx_packets++; + up->netdev->stats.tx_bytes += dlc; + can_get_echo_skb(up->netdev, echo_index); + } else { + up->netdev->stats.tx_dropped++; + can_free_echo_skb(up->netdev, echo_index); + } + spin_unlock_irqrestore(&up->echo_skb_lock, flags); + } +} + +/* callback on reception of a USB message */ +static void ucan_read_bulk_callback(struct urb *urb) +{ + int ret; + int pos; + struct ucan_priv *up = urb->context; + struct net_device *netdev = up->netdev; + struct ucan_message_in *m; + + /* the device is not up and the driver should not receive any + * data on the bulk in pipe + */ + if (WARN_ON(!up->context_array)) { + usb_free_coherent(up->udev, + up->in_ep_size, + urb->transfer_buffer, + urb->transfer_dma); + return; + } + + /* check URB status */ + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + case -ETIME: + /* urb is not resubmitted -> free dma data */ + usb_free_coherent(up->udev, + up->in_ep_size, + urb->transfer_buffer, + urb->transfer_dma); + netdev_dbg(up->netdev, "not resumbmitting urb; status: %d\n", + urb->status); + return; + default: + goto resubmit; + } + + /* sanity check */ + if (!netif_device_present(netdev)) + return; + + /* iterate over input */ + pos = 0; + while (pos < urb->actual_length) { + int len; + + /* check sanity (length of header) */ + if ((urb->actual_length - pos) < UCAN_IN_HDR_SIZE) { + netdev_warn(up->netdev, + "invalid message (short; no hdr; l:%d)\n", + urb->actual_length); + goto resubmit; + } + + /* setup the message address */ + m = (struct ucan_message_in *) + ((u8 *)urb->transfer_buffer + pos); + len = le16_to_cpu(m->len); + + /* check sanity (length of content) */ + if (urb->actual_length - pos < len) { + netdev_warn(up->netdev, + "invalid message (short; no data; l:%d)\n", + urb->actual_length); + print_hex_dump(KERN_WARNING, + "raw data: ", + DUMP_PREFIX_ADDRESS, + 16, + 1, + urb->transfer_buffer, + urb->actual_length, + true); + + goto resubmit; + } + + switch (m->type) { + case UCAN_IN_RX: + ucan_rx_can_msg(up, m); + break; + case UCAN_IN_TX_COMPLETE: + ucan_tx_complete_msg(up, m); + break; + default: + netdev_warn(up->netdev, + "invalid message (type; t:%d)\n", + m->type); + break; + } + + /* proceed to next message */ + pos += len; + /* align to 4 byte boundary */ + pos = round_up(pos, 4); + } + +resubmit: + /* resubmit urb when done */ + usb_fill_bulk_urb(urb, up->udev, + usb_rcvbulkpipe(up->udev, + up->in_ep_addr), + urb->transfer_buffer, + up->in_ep_size, + ucan_read_bulk_callback, + up); + + usb_anchor_urb(urb, &up->rx_urbs); + ret = usb_submit_urb(urb, GFP_ATOMIC); + + if (ret < 0) { + netdev_err(up->netdev, + "failed resubmitting read bulk urb: %d\n", + ret); + + usb_unanchor_urb(urb); + usb_free_coherent(up->udev, + up->in_ep_size, + urb->transfer_buffer, + urb->transfer_dma); + + if (ret == -ENODEV) + netif_device_detach(netdev); + } +} + +/* callback after transmission of a USB message */ +static void ucan_write_bulk_callback(struct urb *urb) +{ + unsigned long flags; + struct ucan_priv *up; + struct ucan_urb_context *context = urb->context; + + /* get the urb context */ + if (WARN_ON_ONCE(!context)) + return; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, + sizeof(struct ucan_message_out), + urb->transfer_buffer, + urb->transfer_dma); + + up = context->up; + if (WARN_ON_ONCE(!up)) + return; + + /* sanity check */ + if (!netif_device_present(up->netdev)) + return; + + /* transmission failed (USB - the device will not send a TX complete) */ + if (urb->status) { + netdev_warn(up->netdev, + "failed to transmit USB message to device: %d\n", + urb->status); + + /* update counters an cleanup */ + spin_lock_irqsave(&up->echo_skb_lock, flags); + can_free_echo_skb(up->netdev, context - up->context_array); + spin_unlock_irqrestore(&up->echo_skb_lock, flags); + + up->netdev->stats.tx_dropped++; + + /* release context and restart the queue if necessary */ + if (!ucan_release_context(up, context)) + netdev_err(up->netdev, + "urb failed, failed to release context\n"); + } +} + +static void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs) +{ + int i; + + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + if (urbs[i]) { + usb_unanchor_urb(urbs[i]); + usb_free_coherent(up->udev, + up->in_ep_size, + urbs[i]->transfer_buffer, + urbs[i]->transfer_dma); + usb_free_urb(urbs[i]); + } + } + + memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); +} + +static int ucan_prepare_and_anchor_rx_urbs(struct ucan_priv *up, + struct urb **urbs) +{ + int i; + + memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); + + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + void *buf; + + urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!urbs[i]) + goto err; + + buf = usb_alloc_coherent(up->udev, + up->in_ep_size, + GFP_KERNEL, &urbs[i]->transfer_dma); + if (!buf) { + /* cleanup this urb */ + usb_free_urb(urbs[i]); + urbs[i] = NULL; + goto err; + } + + usb_fill_bulk_urb(urbs[i], up->udev, + usb_rcvbulkpipe(up->udev, + up->in_ep_addr), + buf, + up->in_ep_size, + ucan_read_bulk_callback, + up); + + urbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urbs[i], &up->rx_urbs); + } + return 0; + +err: + /* cleanup other unsubmitted urbs */ + ucan_cleanup_rx_urbs(up, urbs); + return -ENOMEM; +} + +/* Submits rx urbs with the semantic: Either submit all, or cleanup + * everything. I case of errors submitted urbs are killed and all urbs in + * the array are freed. I case of no errors every entry in the urb + * array is set to NULL. + */ +static int ucan_submit_rx_urbs(struct ucan_priv *up, struct urb **urbs) +{ + int i, ret; + + /* Iterate over all urbs to submit. On success remove the urb + * from the list. + */ + for (i = 0; i < UCAN_MAX_RX_URBS; i++) { + ret = usb_submit_urb(urbs[i], GFP_KERNEL); + if (ret) { + netdev_err(up->netdev, + "could not submit urb; code: %d\n", + ret); + goto err; + } + + /* Anchor URB and drop reference, USB core will take + * care of freeing it + */ + usb_free_urb(urbs[i]); + urbs[i] = NULL; + } + return 0; + +err: + /* Cleanup unsubmitted urbs */ + ucan_cleanup_rx_urbs(up, urbs); + + /* Kill urbs that are already submitted */ + usb_kill_anchored_urbs(&up->rx_urbs); + + return ret; +} + +/* Open the network device */ +static int ucan_open(struct net_device *netdev) +{ + int ret, ret_cleanup; + u16 ctrlmode; + struct urb *urbs[UCAN_MAX_RX_URBS]; + struct ucan_priv *up = netdev_priv(netdev); + + ret = ucan_alloc_context_array(up); + if (ret) + return ret; + + /* Allocate and prepare IN URBS - allocated and anchored + * urbs are stored in urbs[] for clean + */ + ret = ucan_prepare_and_anchor_rx_urbs(up, urbs); + if (ret) + goto err_contexts; + + /* Check the control mode */ + ctrlmode = 0; + if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + ctrlmode |= UCAN_MODE_LOOPBACK; + if (up->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + ctrlmode |= UCAN_MODE_SILENT; + if (up->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + ctrlmode |= UCAN_MODE_3_SAMPLES; + if (up->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + ctrlmode |= UCAN_MODE_ONE_SHOT; + + /* Enable this in any case - filtering is down within the + * receive path + */ + ctrlmode |= UCAN_MODE_BERR_REPORT; + up->ctl_msg_buffer->cmd_start.mode = cpu_to_le16(ctrlmode); + + /* Driver is ready to receive data - start the USB device */ + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2); + if (ret < 0) { + netdev_err(up->netdev, + "could not start device, code: %d\n", + ret); + goto err_reset; + } + + /* Call CAN layer open */ + ret = open_candev(netdev); + if (ret) + goto err_stop; + + /* Driver is ready to receive data. Submit RX URBS */ + ret = ucan_submit_rx_urbs(up, urbs); + if (ret) + goto err_stop; + + up->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Start the network queue */ + netif_start_queue(netdev); + + return 0; + +err_stop: + /* The device have started already stop it */ + ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); + if (ret_cleanup < 0) + netdev_err(up->netdev, + "could not stop device, code: %d\n", + ret_cleanup); + +err_reset: + /* The device might have received data, reset it for + * consistent state + */ + ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); + if (ret_cleanup < 0) + netdev_err(up->netdev, + "could not reset device, code: %d\n", + ret_cleanup); + + /* clean up unsubmitted urbs */ + ucan_cleanup_rx_urbs(up, urbs); + +err_contexts: + ucan_release_context_array(up); + return ret; +} + +static struct urb *ucan_prepare_tx_urb(struct ucan_priv *up, + struct ucan_urb_context *context, + struct can_frame *cf, + u8 echo_index) +{ + int mlen; + struct urb *urb; + struct ucan_message_out *m; + + /* 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(up->netdev, "no memory left for URBs\n"); + return NULL; + } + + m = usb_alloc_coherent(up->udev, + sizeof(struct ucan_message_out), + GFP_ATOMIC, + &urb->transfer_dma); + if (!m) { + netdev_err(up->netdev, "no memory left for USB buffer\n"); + usb_free_urb(urb); + return NULL; + } + + /* build the USB message */ + m->type = UCAN_OUT_TX; + m->msg.can_msg.id = cpu_to_le32(cf->can_id); + + if (cf->can_id & CAN_RTR_FLAG) { + mlen = UCAN_OUT_HDR_SIZE + + offsetof(struct ucan_can_msg, dlc) + + sizeof(m->msg.can_msg.dlc); + m->msg.can_msg.dlc = cf->can_dlc; + } else { + mlen = UCAN_OUT_HDR_SIZE + + sizeof(m->msg.can_msg.id) + cf->can_dlc; + memcpy(m->msg.can_msg.data, cf->data, cf->can_dlc); + } + m->len = cpu_to_le16(mlen); + + context->dlc = cf->can_dlc; + + m->subtype = echo_index; + + /* build the urb */ + usb_fill_bulk_urb(urb, up->udev, + usb_sndbulkpipe(up->udev, + up->out_ep_addr), + m, mlen, ucan_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return urb; +} + +static void ucan_clean_up_tx_urb(struct ucan_priv *up, struct urb *urb) +{ + usb_free_coherent(up->udev, sizeof(struct ucan_message_out), + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +/* callback when Linux needs to send a can frame */ +static netdev_tx_t ucan_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + unsigned long flags; + int ret; + u8 echo_index; + struct urb *urb; + struct ucan_urb_context *context; + struct ucan_priv *up = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + + /* check skb */ + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* allocate a context and slow down tx path, if fifo state is low */ + context = ucan_alloc_context(up); + echo_index = context - up->context_array; + + if (WARN_ON_ONCE(!context)) + return NETDEV_TX_BUSY; + + /* prepare urb for transmission */ + urb = ucan_prepare_tx_urb(up, context, cf, echo_index); + if (!urb) + goto drop; + + /* put the skb on can loopback stack */ + spin_lock_irqsave(&up->echo_skb_lock, flags); + can_put_echo_skb(skb, up->netdev, echo_index); + spin_unlock_irqrestore(&up->echo_skb_lock, flags); + + /* transmit it */ + usb_anchor_urb(urb, &up->tx_urbs); + ret = usb_submit_urb(urb, GFP_ATOMIC); + + /* cleanup urb */ + if (ret) { + /* on error, clean up */ + usb_unanchor_urb(urb); + ucan_clean_up_tx_urb(up, urb); + if (!ucan_release_context(up, context)) + netdev_err(up->netdev, + "xmit err: failed to release context\n"); + + /* remove the skb from the echo stack - this also + * frees the skb + */ + spin_lock_irqsave(&up->echo_skb_lock, flags); + can_free_echo_skb(up->netdev, echo_index); + spin_unlock_irqrestore(&up->echo_skb_lock, flags); + + if (ret == -ENODEV) { + netif_device_detach(up->netdev); + } else { + netdev_warn(up->netdev, + "xmit err: failed to submit urb %d\n", + ret); + up->netdev->stats.tx_dropped++; + } + return NETDEV_TX_OK; + } + + netif_trans_update(netdev); + + /* release ref, as we do not need the urb anymore */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +drop: + if (!ucan_release_context(up, context)) + netdev_err(up->netdev, + "xmit drop: failed to release context\n"); + dev_kfree_skb(skb); + up->netdev->stats.tx_dropped++; + + return NETDEV_TX_OK; +} + +/* Device goes down + * + * Clean up used resources + */ +static int ucan_close(struct net_device *netdev) +{ + int ret; + struct ucan_priv *up = netdev_priv(netdev); + + up->can.state = CAN_STATE_STOPPED; + + /* stop sending data */ + usb_kill_anchored_urbs(&up->tx_urbs); + + /* stop receiving data */ + usb_kill_anchored_urbs(&up->rx_urbs); + + /* stop and reset can device */ + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); + if (ret < 0) + netdev_err(up->netdev, + "could not stop device, code: %d\n", + ret); + + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); + if (ret < 0) + netdev_err(up->netdev, + "could not reset device, code: %d\n", + ret); + + netif_stop_queue(netdev); + + ucan_release_context_array(up); + + close_candev(up->netdev); + return 0; +} + +/* CAN driver callbacks */ +static const struct net_device_ops ucan_netdev_ops = { + .ndo_open = ucan_open, + .ndo_stop = ucan_close, + .ndo_start_xmit = ucan_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/* Request to set bittiming + * + * This function generates an USB set bittiming message and transmits + * it to the device + */ +static int ucan_set_bittiming(struct net_device *netdev) +{ + int ret; + struct ucan_priv *up = netdev_priv(netdev); + struct ucan_ctl_cmd_set_bittiming *cmd_set_bittiming; + + cmd_set_bittiming = &up->ctl_msg_buffer->cmd_set_bittiming; + cmd_set_bittiming->tq = cpu_to_le32(up->can.bittiming.tq); + cmd_set_bittiming->brp = cpu_to_le16(up->can.bittiming.brp); + cmd_set_bittiming->sample_point = + cpu_to_le16(up->can.bittiming.sample_point); + cmd_set_bittiming->prop_seg = up->can.bittiming.prop_seg; + cmd_set_bittiming->phase_seg1 = up->can.bittiming.phase_seg1; + cmd_set_bittiming->phase_seg2 = up->can.bittiming.phase_seg2; + cmd_set_bittiming->sjw = up->can.bittiming.sjw; + + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_SET_BITTIMING, 0, + sizeof(*cmd_set_bittiming)); + return (ret < 0) ? ret : 0; +} + +/* Restart the device to get it out of BUS-OFF state. + * Called when the user runs "ip link set can1 type can restart". + */ +static int ucan_set_mode(struct net_device *netdev, enum can_mode mode) +{ + int ret; + unsigned long flags; + struct ucan_priv *up = netdev_priv(netdev); + + switch (mode) { + case CAN_MODE_START: + netdev_dbg(up->netdev, "restarting device\n"); + + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESTART, 0, 0); + up->can.state = CAN_STATE_ERROR_ACTIVE; + + /* check if queue can be restarted, + * up->available_tx_urbs must be protected by the + * lock + */ + spin_lock_irqsave(&up->context_lock, flags); + + if (up->available_tx_urbs > 0) + netif_wake_queue(up->netdev); + + spin_unlock_irqrestore(&up->context_lock, flags); + + return ret; + default: + return -EOPNOTSUPP; + } +} + +/* Probe the device, reset it and gather general device information */ +static int ucan_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + int i; + u32 protocol_version; + struct usb_device *udev; + struct net_device *netdev; + struct usb_host_interface *iface_desc; + struct ucan_priv *up; + struct usb_endpoint_descriptor *ep; + u16 in_ep_size; + u16 out_ep_size; + u8 in_ep_addr; + u8 out_ep_addr; + union ucan_ctl_payload *ctl_msg_buffer; + char firmware_str[sizeof(union ucan_ctl_payload) + 1]; + + udev = interface_to_usbdev(intf); + + /* Stage 1 - Interface Parsing + * --------------------------- + * + * Identifie the device USB interface descriptor and its + * endpoints. Probing is aborted on errors. + */ + + /* check if the interface is sane */ + iface_desc = intf->cur_altsetting; + if (!iface_desc) + return -ENODEV; + + dev_info(&udev->dev, + "%s: probing device on interface #%d\n", + UCAN_DRIVER_NAME, + iface_desc->desc.bInterfaceNumber); + + /* interface sanity check */ + if (iface_desc->desc.bNumEndpoints != 2) { + dev_err(&udev->dev, + "%s: invalid EP count (%d)", + UCAN_DRIVER_NAME, iface_desc->desc.bNumEndpoints); + goto err_firmware_needs_update; + } + + /* check interface endpoints */ + in_ep_addr = 0; + out_ep_addr = 0; + in_ep_size = 0; + out_ep_size = 0; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + ep = &iface_desc->endpoint[i].desc; + + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) && + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* In Endpoint */ + in_ep_addr = ep->bEndpointAddress; + in_ep_addr &= USB_ENDPOINT_NUMBER_MASK; + in_ep_size = le16_to_cpu(ep->wMaxPacketSize); + } else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + 0) && + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* Out Endpoint */ + out_ep_addr = ep->bEndpointAddress; + out_ep_addr &= USB_ENDPOINT_NUMBER_MASK; + out_ep_size = le16_to_cpu(ep->wMaxPacketSize); + } + } + + /* check if interface is sane */ + if (!in_ep_addr || !out_ep_addr) { + dev_err(&udev->dev, "%s: invalid endpoint configuration\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + if (in_ep_size < sizeof(struct ucan_message_in)) { + dev_err(&udev->dev, "%s: invalid in_ep MaxPacketSize\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + if (out_ep_size < sizeof(struct ucan_message_out)) { + dev_err(&udev->dev, "%s: invalid out_ep MaxPacketSize\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + + /* Stage 2 - Device Identification + * ------------------------------- + * + * The device interface seems to be a ucan device. Do further + * compatibility checks. On error probing is aborted, on + * success this stage leaves the ctl_msg_buffer with the + * reported contents of a GET_INFO command (supported + * bittimings, tx_fifo depth). This information is used in + * Stage 3 for the final driver initialisation. + */ + + /* Prepare Memory for control transferes */ + ctl_msg_buffer = devm_kzalloc(&udev->dev, + sizeof(union ucan_ctl_payload), + GFP_KERNEL); + if (!ctl_msg_buffer) { + dev_err(&udev->dev, + "%s: failed to allocate control pipe memory\n", + UCAN_DRIVER_NAME); + return -ENOMEM; + } + + /* get protocol version + * + * note: ucan_ctrl_command_* wrappers cannot be used yet + * because `up` is initialised in Stage 3 + */ + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + UCAN_COMMAND_GET, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + UCAN_COMMAND_GET_PROTOCOL_VERSION, + iface_desc->desc.bInterfaceNumber, + ctl_msg_buffer, + sizeof(union ucan_ctl_payload), + UCAN_USB_CTL_PIPE_TIMEOUT); + + /* older firmware version do not support this command - those + * are not supported by this drive + */ + if (ret != 4) { + dev_err(&udev->dev, + "%s: could not read protocol version, ret=%d\n", + UCAN_DRIVER_NAME, ret); + if (ret >= 0) + ret = -EINVAL; + goto err_firmware_needs_update; + } + + /* this driver currently supports protocol version 3 only */ + protocol_version = + le32_to_cpu(ctl_msg_buffer->cmd_get_protocol_version.version); + if (protocol_version < UCAN_PROTOCOL_VERSION_MIN || + protocol_version > UCAN_PROTOCOL_VERSION_MAX) { + dev_err(&udev->dev, + "%s: device protocol version %d is not supported\n", + UCAN_DRIVER_NAME, protocol_version); + goto err_firmware_needs_update; + } + + /* request the device information and store it in ctl_msg_buffer + * + * note: ucan_ctrl_command_* wrappers connot be used yet + * because `up` is initialised in Stage 3 + */ + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + UCAN_COMMAND_GET, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + UCAN_COMMAND_GET_INFO, + iface_desc->desc.bInterfaceNumber, + ctl_msg_buffer, + sizeof(ctl_msg_buffer->cmd_get_device_info), + UCAN_USB_CTL_PIPE_TIMEOUT); + + if (ret < 0) { + dev_err(&udev->dev, "%s: failed to retrieve device info\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + if (ret < sizeof(ctl_msg_buffer->cmd_get_device_info)) { + dev_err(&udev->dev, "%s: device reported invalid device info\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + if (ctl_msg_buffer->cmd_get_device_info.tx_fifo == 0) { + dev_err(&udev->dev, + "%s: device reported invalid tx-fifo size\n", + UCAN_DRIVER_NAME); + goto err_firmware_needs_update; + } + + /* Stage 3 - Driver Initialisation + * ------------------------------- + * + * Register device to Linux, prepare private structures and + * reset the device. + */ + + /* allocate driver resources */ + netdev = alloc_candev(sizeof(struct ucan_priv), + ctl_msg_buffer->cmd_get_device_info.tx_fifo); + if (!netdev) { + dev_err(&udev->dev, + "%s: cannot allocate candev\n", UCAN_DRIVER_NAME); + return -ENOMEM; + } + + up = netdev_priv(netdev); + + /* initialze data */ + up->udev = udev; + up->intf = intf; + up->netdev = netdev; + up->intf_index = iface_desc->desc.bInterfaceNumber; + up->in_ep_addr = in_ep_addr; + up->out_ep_addr = out_ep_addr; + up->in_ep_size = in_ep_size; + up->ctl_msg_buffer = ctl_msg_buffer; + up->context_array = NULL; + up->available_tx_urbs = 0; + + up->can.state = CAN_STATE_STOPPED; + up->can.bittiming_const = &up->device_info.bittiming_const; + up->can.do_set_bittiming = ucan_set_bittiming; + up->can.do_set_mode = &ucan_set_mode; + spin_lock_init(&up->context_lock); + spin_lock_init(&up->echo_skb_lock); + netdev->netdev_ops = &ucan_netdev_ops; + + usb_set_intfdata(intf, up); + SET_NETDEV_DEV(netdev, &intf->dev); + + /* parse device information + * the data retrieved in Stage 2 is still available in + * up->ctl_msg_buffer + */ + ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info); + + /* just print some device information - if available */ + ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, + sizeof(union ucan_ctl_payload)); + if (ret > 0) { + /* copy string while ensuring zero terminiation */ + strncpy(firmware_str, up->ctl_msg_buffer->raw, + sizeof(union ucan_ctl_payload)); + firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; + } else { + strcpy(firmware_str, "unknown"); + } + + /* device is compatible, reset it */ + ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); + if (ret < 0) + goto err_free_candev; + + init_usb_anchor(&up->rx_urbs); + init_usb_anchor(&up->tx_urbs); + + up->can.state = CAN_STATE_STOPPED; + + /* register the device */ + ret = register_candev(netdev); + if (ret) + goto err_free_candev; + + /* initialisation complete, log device info */ + netdev_info(up->netdev, "registered device\n"); + netdev_info(up->netdev, "firmware string: %s\n", firmware_str); + + /* success */ + return 0; + +err_free_candev: + free_candev(netdev); + return ret; + +err_firmware_needs_update: + dev_err(&udev->dev, + "%s: probe failed; try to update the device firmware\n", + UCAN_DRIVER_NAME); + return -ENODEV; +} + +/* disconnect the device */ +static void ucan_disconnect(struct usb_interface *intf) +{ + struct ucan_priv *up = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (up) { + unregister_netdev(up->netdev); + free_candev(up->netdev); + } +} + +static struct usb_device_id ucan_table[] = { + /* Mule (soldered onto compute modules) */ + {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425a, 0)}, + /* Seal (standalone USB stick) */ + {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425b, 0)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ucan_table); +/* driver callbacks */ +static struct usb_driver ucan_driver = { + .name = UCAN_DRIVER_NAME, + .probe = ucan_probe, + .disconnect = ucan_disconnect, + .id_table = ucan_table, +}; + +module_usb_driver(ucan_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Martin Elshuber <martin.elshuber@theobroma-systems.com>"); +MODULE_AUTHOR("Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com>"); +MODULE_DESCRIPTION("Driver for Theobroma Systems UCAN devices"); diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c new file mode 100644 index 000000000..232f45f72 --- /dev/null +++ b/drivers/net/can/usb/usb_8dev.c @@ -0,0 +1,1036 @@ +/* + * CAN driver for "8 devices" USB2CAN converter + * + * Copyright (C) 2012 Bernd Krumboeck (krumboeck@universalnet.at) + * + * 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. + * + * This driver is inspired by the 3.2.0 version of drivers/net/can/usb/ems_usb.c + * and drivers/net/can/usb/esd_usb2.c + * + * Many thanks to Gerhard Bertelsmann (info@gerhard-bertelsmann.de) + * for testing and fixing this driver. Also many thanks to "8 devices", + * who were very cooperative and answered my questions. + */ + +#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 <linux/can/led.h> + +/* driver constants */ +#define MAX_RX_URBS 20 +#define MAX_TX_URBS 20 +#define RX_BUFFER_SIZE 64 + +/* vendor and product id */ +#define USB_8DEV_VENDOR_ID 0x0483 +#define USB_8DEV_PRODUCT_ID 0x1234 + +/* endpoints */ +enum usb_8dev_endpoint { + USB_8DEV_ENDP_DATA_RX = 1, + USB_8DEV_ENDP_DATA_TX, + USB_8DEV_ENDP_CMD_RX, + USB_8DEV_ENDP_CMD_TX +}; + +/* device CAN clock */ +#define USB_8DEV_ABP_CLOCK 32000000 + +/* setup flags */ +#define USB_8DEV_SILENT 0x01 +#define USB_8DEV_LOOPBACK 0x02 +#define USB_8DEV_DISABLE_AUTO_RESTRANS 0x04 +#define USB_8DEV_STATUS_FRAME 0x08 + +/* commands */ +enum usb_8dev_cmd { + USB_8DEV_RESET = 1, + USB_8DEV_OPEN, + USB_8DEV_CLOSE, + USB_8DEV_SET_SPEED, + USB_8DEV_SET_MASK_FILTER, + USB_8DEV_GET_STATUS, + USB_8DEV_GET_STATISTICS, + USB_8DEV_GET_SERIAL, + USB_8DEV_GET_SOFTW_VER, + USB_8DEV_GET_HARDW_VER, + USB_8DEV_RESET_TIMESTAMP, + USB_8DEV_GET_SOFTW_HARDW_VER +}; + +/* command options */ +#define USB_8DEV_BAUD_MANUAL 0x09 +#define USB_8DEV_CMD_START 0x11 +#define USB_8DEV_CMD_END 0x22 + +#define USB_8DEV_CMD_SUCCESS 0 +#define USB_8DEV_CMD_ERROR 255 + +#define USB_8DEV_CMD_TIMEOUT 1000 + +/* frames */ +#define USB_8DEV_DATA_START 0x55 +#define USB_8DEV_DATA_END 0xAA + +#define USB_8DEV_TYPE_CAN_FRAME 0 +#define USB_8DEV_TYPE_ERROR_FRAME 3 + +#define USB_8DEV_EXTID 0x01 +#define USB_8DEV_RTR 0x02 +#define USB_8DEV_ERR_FLAG 0x04 + +/* status */ +#define USB_8DEV_STATUSMSG_OK 0x00 /* Normal condition. */ +#define USB_8DEV_STATUSMSG_OVERRUN 0x01 /* Overrun occured when sending */ +#define USB_8DEV_STATUSMSG_BUSLIGHT 0x02 /* Error counter has reached 96 */ +#define USB_8DEV_STATUSMSG_BUSHEAVY 0x03 /* Error count. has reached 128 */ +#define USB_8DEV_STATUSMSG_BUSOFF 0x04 /* Device is in BUSOFF */ +#define USB_8DEV_STATUSMSG_STUFF 0x20 /* Stuff Error */ +#define USB_8DEV_STATUSMSG_FORM 0x21 /* Form Error */ +#define USB_8DEV_STATUSMSG_ACK 0x23 /* Ack Error */ +#define USB_8DEV_STATUSMSG_BIT0 0x24 /* Bit1 Error */ +#define USB_8DEV_STATUSMSG_BIT1 0x25 /* Bit0 Error */ +#define USB_8DEV_STATUSMSG_CRC 0x27 /* CRC Error */ + +#define USB_8DEV_RP_MASK 0x7F /* Mask for Receive Error Bit */ + + +/* table of devices that work with this driver */ +static const struct usb_device_id usb_8dev_table[] = { + { USB_DEVICE(USB_8DEV_VENDOR_ID, USB_8DEV_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_8dev_table); + +struct usb_8dev_tx_urb_context { + struct usb_8dev_priv *priv; + + u32 echo_index; + u8 dlc; +}; + +/* Structure to hold all of our device specific stuff */ +struct usb_8dev_priv { + struct can_priv can; /* must be the first member */ + + 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 usb_8dev_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct usb_anchor rx_submitted; + + struct can_berr_counter bec; + + u8 *cmd_msg_buffer; + + struct mutex usb_8dev_cmd_lock; + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +/* tx frame */ +struct __packed usb_8dev_tx_msg { + u8 begin; + u8 flags; /* RTR and EXT_ID flag */ + __be32 id; /* upper 3 bits not used */ + u8 dlc; /* data length code 0-8 bytes */ + u8 data[8]; /* 64-bit data */ + u8 end; +}; + +/* rx frame */ +struct __packed usb_8dev_rx_msg { + u8 begin; + u8 type; /* frame type */ + u8 flags; /* RTR and EXT_ID flag */ + __be32 id; /* upper 3 bits not used */ + u8 dlc; /* data length code 0-8 bytes */ + u8 data[8]; /* 64-bit data */ + __be32 timestamp; /* 32-bit timestamp */ + u8 end; +}; + +/* command frame */ +struct __packed usb_8dev_cmd_msg { + u8 begin; + u8 channel; /* unkown - always 0 */ + u8 command; /* command to execute */ + u8 opt1; /* optional parameter / return value */ + u8 opt2; /* optional parameter 2 */ + u8 data[10]; /* optional parameter and data */ + u8 end; +}; + +static int usb_8dev_send_cmd_msg(struct usb_8dev_priv *priv, u8 *msg, int size) +{ + int actual_length; + + return usb_bulk_msg(priv->udev, + usb_sndbulkpipe(priv->udev, USB_8DEV_ENDP_CMD_TX), + msg, size, &actual_length, USB_8DEV_CMD_TIMEOUT); +} + +static int usb_8dev_wait_cmd_msg(struct usb_8dev_priv *priv, u8 *msg, int size, + int *actual_length) +{ + return usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, USB_8DEV_ENDP_CMD_RX), + msg, size, actual_length, USB_8DEV_CMD_TIMEOUT); +} + +/* Send command to device and receive result. + * Command was successful when opt1 = 0. + */ +static int usb_8dev_send_cmd(struct usb_8dev_priv *priv, + struct usb_8dev_cmd_msg *out, + struct usb_8dev_cmd_msg *in) +{ + int err; + int num_bytes_read; + struct net_device *netdev; + + netdev = priv->netdev; + + out->begin = USB_8DEV_CMD_START; + out->end = USB_8DEV_CMD_END; + + mutex_lock(&priv->usb_8dev_cmd_lock); + + memcpy(priv->cmd_msg_buffer, out, + sizeof(struct usb_8dev_cmd_msg)); + + err = usb_8dev_send_cmd_msg(priv, priv->cmd_msg_buffer, + sizeof(struct usb_8dev_cmd_msg)); + if (err < 0) { + netdev_err(netdev, "sending command message failed\n"); + goto failed; + } + + err = usb_8dev_wait_cmd_msg(priv, priv->cmd_msg_buffer, + sizeof(struct usb_8dev_cmd_msg), + &num_bytes_read); + if (err < 0) { + netdev_err(netdev, "no command message answer\n"); + goto failed; + } + + memcpy(in, priv->cmd_msg_buffer, sizeof(struct usb_8dev_cmd_msg)); + + if (in->begin != USB_8DEV_CMD_START || in->end != USB_8DEV_CMD_END || + num_bytes_read != 16 || in->opt1 != 0) + err = -EPROTO; + +failed: + mutex_unlock(&priv->usb_8dev_cmd_lock); + return err; +} + +/* Send open command to device */ +static int usb_8dev_cmd_open(struct usb_8dev_priv *priv) +{ + struct can_bittiming *bt = &priv->can.bittiming; + struct usb_8dev_cmd_msg outmsg; + struct usb_8dev_cmd_msg inmsg; + u32 ctrlmode = priv->can.ctrlmode; + u32 flags = USB_8DEV_STATUS_FRAME; + __be32 beflags; + __be16 bebrp; + + memset(&outmsg, 0, sizeof(outmsg)); + outmsg.command = USB_8DEV_OPEN; + outmsg.opt1 = USB_8DEV_BAUD_MANUAL; + outmsg.data[0] = bt->prop_seg + bt->phase_seg1; + outmsg.data[1] = bt->phase_seg2; + outmsg.data[2] = bt->sjw; + + /* BRP */ + bebrp = cpu_to_be16((u16)bt->brp); + memcpy(&outmsg.data[3], &bebrp, sizeof(bebrp)); + + /* flags */ + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + flags |= USB_8DEV_LOOPBACK; + if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + flags |= USB_8DEV_SILENT; + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) + flags |= USB_8DEV_DISABLE_AUTO_RESTRANS; + + beflags = cpu_to_be32(flags); + memcpy(&outmsg.data[5], &beflags, sizeof(beflags)); + + return usb_8dev_send_cmd(priv, &outmsg, &inmsg); +} + +/* Send close command to device */ +static int usb_8dev_cmd_close(struct usb_8dev_priv *priv) +{ + struct usb_8dev_cmd_msg inmsg; + struct usb_8dev_cmd_msg outmsg = { + .channel = 0, + .command = USB_8DEV_CLOSE, + .opt1 = 0, + .opt2 = 0 + }; + + return usb_8dev_send_cmd(priv, &outmsg, &inmsg); +} + +/* Get firmware and hardware version */ +static int usb_8dev_cmd_version(struct usb_8dev_priv *priv, u32 *res) +{ + struct usb_8dev_cmd_msg inmsg; + struct usb_8dev_cmd_msg outmsg = { + .channel = 0, + .command = USB_8DEV_GET_SOFTW_HARDW_VER, + .opt1 = 0, + .opt2 = 0 + }; + + int err = usb_8dev_send_cmd(priv, &outmsg, &inmsg); + if (err) + return err; + + *res = be32_to_cpup((__be32 *)inmsg.data); + + return err; +} + +/* Set network device mode + * + * Maybe we should leave this function empty, because the device + * set mode variable with open command. + */ +static int usb_8dev_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + int err = 0; + + switch (mode) { + case CAN_MODE_START: + err = usb_8dev_cmd_open(priv); + if (err) + netdev_warn(netdev, "couldn't start device"); + break; + + default: + return -EOPNOTSUPP; + } + + return err; +} + +/* Read error/status frames */ +static void usb_8dev_rx_err_msg(struct usb_8dev_priv *priv, + struct usb_8dev_rx_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + + /* Error message: + * byte 0: Status + * byte 1: bit 7: Receive Passive + * byte 1: bit 0-6: Receive Error Counter + * byte 2: Transmit Error Counter + * byte 3: Always 0 (maybe reserved for future use) + */ + + u8 state = msg->data[0]; + u8 rxerr = msg->data[1] & USB_8DEV_RP_MASK; + u8 txerr = msg->data[2]; + int rx_errors = 0; + int tx_errors = 0; + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) + return; + + switch (state) { + case USB_8DEV_STATUSMSG_OK: + priv->can.state = CAN_STATE_ERROR_ACTIVE; + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + break; + case USB_8DEV_STATUSMSG_BUSOFF: + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; + can_bus_off(priv->netdev); + break; + case USB_8DEV_STATUSMSG_OVERRUN: + case USB_8DEV_STATUSMSG_BUSLIGHT: + case USB_8DEV_STATUSMSG_BUSHEAVY: + cf->can_id |= CAN_ERR_CRTL; + break; + default: + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + priv->can.can_stats.bus_error++; + break; + } + + switch (state) { + case USB_8DEV_STATUSMSG_OK: + case USB_8DEV_STATUSMSG_BUSOFF: + break; + case USB_8DEV_STATUSMSG_ACK: + cf->can_id |= CAN_ERR_ACK; + tx_errors = 1; + break; + case USB_8DEV_STATUSMSG_CRC: + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_BIT0: + cf->data[2] |= CAN_ERR_PROT_BIT0; + tx_errors = 1; + break; + case USB_8DEV_STATUSMSG_BIT1: + cf->data[2] |= CAN_ERR_PROT_BIT1; + tx_errors = 1; + break; + case USB_8DEV_STATUSMSG_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_OVERRUN: + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + rx_errors = 1; + break; + case USB_8DEV_STATUSMSG_BUSLIGHT: + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + priv->can.can_stats.error_warning++; + break; + case USB_8DEV_STATUSMSG_BUSHEAVY: + priv->can.state = CAN_STATE_ERROR_PASSIVE; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + priv->can.can_stats.error_passive++; + break; + default: + netdev_warn(priv->netdev, + "Unknown status/error message (%d)\n", state); + break; + } + + if (tx_errors) { + cf->data[2] |= CAN_ERR_PROT_TX; + stats->tx_errors++; + } + + if (rx_errors) + stats->rx_errors++; + + cf->data[6] = txerr; + cf->data[7] = rxerr; + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} + +/* Read data and status frames */ +static void usb_8dev_rx_can_msg(struct usb_8dev_priv *priv, + struct usb_8dev_rx_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + + if (msg->type == USB_8DEV_TYPE_ERROR_FRAME && + msg->flags == USB_8DEV_ERR_FLAG) { + usb_8dev_rx_err_msg(priv, msg); + } else if (msg->type == USB_8DEV_TYPE_CAN_FRAME) { + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) + return; + + cf->can_id = be32_to_cpu(msg->id); + cf->can_dlc = get_can_dlc(msg->dlc & 0xF); + + if (msg->flags & USB_8DEV_EXTID) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->flags & USB_8DEV_RTR) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(priv->netdev, CAN_LED_EVENT_RX); + } else { + netdev_warn(priv->netdev, "frame type %d unknown", + msg->type); + } + +} + +/* Callback for reading data from device + * + * Check urb status, call read function and resubmit urb read operation. + */ +static void usb_8dev_read_bulk_callback(struct urb *urb) +{ + struct usb_8dev_priv *priv = urb->context; + struct net_device *netdev; + int retval; + int pos = 0; + + netdev = priv->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", + urb->status); + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct usb_8dev_rx_msg *msg; + + if (pos + sizeof(struct usb_8dev_rx_msg) > urb->actual_length) { + netdev_err(priv->netdev, "format error\n"); + break; + } + + msg = (struct usb_8dev_rx_msg *)(urb->transfer_buffer + pos); + usb_8dev_rx_can_msg(priv, msg); + + pos += sizeof(struct usb_8dev_rx_msg); + } + +resubmit_urb: + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, USB_8DEV_ENDP_DATA_RX), + urb->transfer_buffer, RX_BUFFER_SIZE, + usb_8dev_read_bulk_callback, priv); + + 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 handler for write operations + * + * Free allocated buffers, check transmit status and + * calculate statistic. + */ +static void usb_8dev_write_bulk_callback(struct urb *urb) +{ + struct usb_8dev_tx_urb_context *context = urb->context; + struct usb_8dev_priv *priv; + struct net_device *netdev; + + BUG_ON(!context); + + priv = context->priv; + netdev = priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + atomic_dec(&priv->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", + urb->status); + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + can_get_echo_skb(netdev, context->echo_index); + + can_led_event(netdev, CAN_LED_EVENT_TX); + + /* Release context */ + context->echo_index = MAX_TX_URBS; + + netif_wake_queue(netdev); +} + +/* Send data to device */ +static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *) skb->data; + struct usb_8dev_tx_msg *msg; + struct urb *urb; + struct usb_8dev_tx_urb_context *context = NULL; + u8 *buf; + int i, err; + size_t size = sizeof(struct usb_8dev_tx_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) + goto nomem; + + buf = usb_alloc_coherent(priv->udev, size, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + goto nomembuf; + } + + memset(buf, 0, size); + + msg = (struct usb_8dev_tx_msg *)buf; + msg->begin = USB_8DEV_DATA_START; + msg->flags = 0x00; + + if (cf->can_id & CAN_RTR_FLAG) + msg->flags |= USB_8DEV_RTR; + + if (cf->can_id & CAN_EFF_FLAG) + msg->flags |= USB_8DEV_EXTID; + + msg->id = cpu_to_be32(cf->can_id & CAN_ERR_MASK); + msg->dlc = cf->can_dlc; + memcpy(msg->data, cf->data, cf->can_dlc); + msg->end = USB_8DEV_DATA_END; + + for (i = 0; i < MAX_TX_URBS; i++) { + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &priv->tx_contexts[i]; + break; + } + } + + /* May never happen! When this happens we'd more URBs in flight as + * allowed (MAX_TX_URBS). + */ + if (!context) + goto nofreecontext; + + context->priv = priv; + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_fill_bulk_urb(urb, priv->udev, + usb_sndbulkpipe(priv->udev, USB_8DEV_ENDP_DATA_TX), + buf, size, usb_8dev_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_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(priv->udev, size, buf, urb->transfer_dma); + + atomic_dec(&priv->active_tx_urbs); + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "failed tx_urb %d\n", err); + stats->tx_dropped++; + } else if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) + /* Slow down tx path */ + 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; + +nofreecontext: + usb_free_coherent(priv->udev, size, buf, urb->transfer_dma); + usb_free_urb(urb); + + netdev_warn(netdev, "couldn't find free context"); + + return NETDEV_TX_BUSY; + +nomembuf: + usb_free_urb(urb); + +nomem: + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static int usb_8dev_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +/* Start USB device */ +static int usb_8dev_start(struct usb_8dev_priv *priv) +{ + struct net_device *netdev = priv->netdev; + int err, i; + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf; + dma_addr_t buf_dma; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &buf_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + urb->transfer_dma = buf_dma; + + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + USB_8DEV_ENDP_DATA_RX), + buf, RX_BUFFER_SIZE, + usb_8dev_read_bulk_callback, priv); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + usb_free_urb(urb); + break; + } + + priv->rxbuf[i] = buf; + priv->rxbuf_dma[i] = buf_dma; + + /* 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"); + + err = usb_8dev_cmd_open(priv); + if (err) + goto failed; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + return err; +} + +/* Open USB device */ +static int usb_8dev_open(struct net_device *netdev) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + can_led_event(netdev, CAN_LED_EVENT_OPEN); + + /* finally start device */ + err = usb_8dev_start(priv); + if (err) { + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", + err); + + close_candev(netdev); + + return err; + } + + netif_start_queue(netdev); + + return 0; +} + +static void unlink_all_urbs(struct usb_8dev_priv *priv) +{ + int i; + + usb_kill_anchored_urbs(&priv->rx_submitted); + + for (i = 0; i < MAX_RX_URBS; ++i) + usb_free_coherent(priv->udev, RX_BUFFER_SIZE, + priv->rxbuf[i], priv->rxbuf_dma[i]); + + usb_kill_anchored_urbs(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +/* Close USB device */ +static int usb_8dev_close(struct net_device *netdev) +{ + struct usb_8dev_priv *priv = netdev_priv(netdev); + int err = 0; + + /* Send CLOSE command to CAN controller */ + err = usb_8dev_cmd_close(priv); + if (err) + netdev_warn(netdev, "couldn't stop device"); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + unlink_all_urbs(priv); + + close_candev(netdev); + + can_led_event(netdev, CAN_LED_EVENT_STOP); + + return err; +} + +static const struct net_device_ops usb_8dev_netdev_ops = { + .ndo_open = usb_8dev_open, + .ndo_stop = usb_8dev_close, + .ndo_start_xmit = usb_8dev_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct can_bittiming_const usb_8dev_bittiming_const = { + .name = "usb_8dev", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* Probe USB device + * + * Check device and firmware. + * Set supported modes and bittiming constants. + * Allocate some memory. + */ +static int usb_8dev_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct usb_8dev_priv *priv; + int i, err = -ENOMEM; + u32 version; + char buf[18]; + struct usb_device *usbdev = interface_to_usbdev(intf); + + /* product id looks strange, better we also check iProduct string */ + if (usb_string(usbdev, usbdev->descriptor.iProduct, buf, + sizeof(buf)) > 0 && strcmp(buf, "USB2CAN converter")) { + dev_info(&usbdev->dev, "ignoring: not an USB2CAN converter\n"); + return -ENODEV; + } + + netdev = alloc_candev(sizeof(struct usb_8dev_priv), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + priv->udev = usbdev; + priv->netdev = netdev; + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = USB_8DEV_ABP_CLOCK; + priv->can.bittiming_const = &usb_8dev_bittiming_const; + priv->can.do_set_mode = usb_8dev_set_mode; + priv->can.do_get_berr_counter = usb_8dev_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_ONE_SHOT; + + netdev->netdev_ops = &usb_8dev_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&priv->rx_submitted); + + init_usb_anchor(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; + + priv->cmd_msg_buffer = devm_kzalloc(&intf->dev, sizeof(struct usb_8dev_cmd_msg), + GFP_KERNEL); + if (!priv->cmd_msg_buffer) + goto cleanup_candev; + + usb_set_intfdata(intf, priv); + + SET_NETDEV_DEV(netdev, &intf->dev); + + mutex_init(&priv->usb_8dev_cmd_lock); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, + "couldn't register CAN device: %d\n", err); + goto cleanup_candev; + } + + err = usb_8dev_cmd_version(priv, &version); + if (err) { + netdev_err(netdev, "can't get firmware version\n"); + goto cleanup_unregister_candev; + } else { + netdev_info(netdev, + "firmware: %d.%d, hardware: %d.%d\n", + (version>>24) & 0xff, (version>>16) & 0xff, + (version>>8) & 0xff, version & 0xff); + } + + devm_can_led_init(netdev); + + return 0; + +cleanup_unregister_candev: + unregister_netdev(priv->netdev); + +cleanup_candev: + free_candev(netdev); + + return err; + +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void usb_8dev_disconnect(struct usb_interface *intf) +{ + struct usb_8dev_priv *priv = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (priv) { + netdev_info(priv->netdev, "device disconnected\n"); + + unregister_netdev(priv->netdev); + unlink_all_urbs(priv); + free_candev(priv->netdev); + } + +} + +static struct usb_driver usb_8dev_driver = { + .name = "usb_8dev", + .probe = usb_8dev_probe, + .disconnect = usb_8dev_disconnect, + .id_table = usb_8dev_table, +}; + +module_usb_driver(usb_8dev_driver); + +MODULE_AUTHOR("Bernd Krumboeck <krumboeck@universalnet.at>"); +MODULE_DESCRIPTION("CAN driver for 8 devices USB2CAN interfaces"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c new file mode 100644 index 000000000..d200a5b06 --- /dev/null +++ b/drivers/net/can/vcan.c @@ -0,0 +1,190 @@ +/* + * vcan.c - Virtual CAN interface + * + * Copyright (c) 2002-2017 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/can/skb.h> +#include <linux/slab.h> +#include <net/rtnetlink.h> + +#define DRV_NAME "vcan" + +MODULE_DESCRIPTION("virtual CAN interface"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>"); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); + + +/* + * CAN test feature: + * Enable the echo on driver level for testing the CAN core echo modes. + * See Documentation/networking/can.rst for details. + */ + +static bool echo; /* echo testing. Default: 0 (Off) */ +module_param(echo, bool, 0444); +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 canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct net_device_stats *stats = &dev->stats; + + stats->rx_packets++; + stats->rx_bytes += cfd->len; + + 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 canfd_frame *cfd = (struct canfd_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 += cfd->len; + + /* 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 += cfd->len; + } + consume_skb(skb); + return NETDEV_TX_OK; + } + + /* perform standard echo handling for CAN network interfaces */ + + if (loop) { + + skb = can_create_echo_skb(skb); + if (!skb) + return NETDEV_TX_OK; + + /* receive with packet counting */ + vcan_rx(skb, dev); + } else { + /* no looped packets => no counting */ + consume_skb(skb); + } + return NETDEV_TX_OK; +} + +static int vcan_change_mtu(struct net_device *dev, int new_mtu) +{ + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops vcan_netdev_ops = { + .ndo_start_xmit = vcan_tx, + .ndo_change_mtu = vcan_change_mtu, +}; + +static void vcan_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = CANFD_MTU; + 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->needs_free_netdev = true; +} + +static struct rtnl_link_ops vcan_link_ops __read_mostly = { + .kind = DRV_NAME, + .setup = vcan_setup, +}; + +static __init int vcan_init_module(void) +{ + pr_info("vcan: Virtual CAN interface driver\n"); + + 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); diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c new file mode 100644 index 000000000..8197f04aa --- /dev/null +++ b/drivers/net/can/vxcan.c @@ -0,0 +1,319 @@ +/* + * vxcan.c - Virtual CAN Tunnel for cross namespace communication + * + * This code is derived from drivers/net/can/vcan.c for the virtual CAN + * specific parts and from drivers/net/veth.c to implement the netlink API + * for network interface pairs in a common and established way. + * + * Copyright (c) 2017 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#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/can/skb.h> +#include <linux/can/vxcan.h> +#include <linux/slab.h> +#include <net/rtnetlink.h> + +#define DRV_NAME "vxcan" + +MODULE_DESCRIPTION("Virtual CAN Tunnel"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>"); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); + +struct vxcan_priv { + struct net_device __rcu *peer; +}; + +static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct net_device_stats *peerstats, *srcstats = &dev->stats; + u8 len; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + if (unlikely(!peer)) { + kfree_skb(skb); + dev->stats.tx_dropped++; + goto out_unlock; + } + + skb = can_create_echo_skb(skb); + if (!skb) + goto out_unlock; + + /* reset CAN GW hop counter */ + skb->csum_start = 0; + skb->pkt_type = PACKET_BROADCAST; + skb->dev = peer; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + len = cfd->len; + if (netif_rx_ni(skb) == NET_RX_SUCCESS) { + srcstats->tx_packets++; + srcstats->tx_bytes += len; + peerstats = &peer->stats; + peerstats->rx_packets++; + peerstats->rx_bytes += len; + } + +out_unlock: + rcu_read_unlock(); + return NETDEV_TX_OK; +} + + +static int vxcan_open(struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + if (!peer) + return -ENOTCONN; + + if (peer->flags & IFF_UP) { + netif_carrier_on(dev); + netif_carrier_on(peer); + } + return 0; +} + +static int vxcan_close(struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + netif_carrier_off(dev); + if (peer) + netif_carrier_off(peer); + + return 0; +} + +static int vxcan_get_iflink(const struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer; + int iflink; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + iflink = peer ? peer->ifindex : 0; + rcu_read_unlock(); + + return iflink; +} + +static int vxcan_change_mtu(struct net_device *dev, int new_mtu) +{ + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops vxcan_netdev_ops = { + .ndo_open = vxcan_open, + .ndo_stop = vxcan_close, + .ndo_start_xmit = vxcan_xmit, + .ndo_get_iflink = vxcan_get_iflink, + .ndo_change_mtu = vxcan_change_mtu, +}; + +static void vxcan_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = CANFD_MTU; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 0; + dev->flags = IFF_NOARP; + dev->netdev_ops = &vxcan_netdev_ops; + dev->needs_free_netdev = true; +} + +/* forward declaration for rtnl_create_link() */ +static struct rtnl_link_ops vxcan_link_ops; + +static int vxcan_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct vxcan_priv *priv; + struct net_device *peer; + struct net *peer_net; + + struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb; + char ifname[IFNAMSIZ]; + unsigned char name_assign_type; + struct ifinfomsg *ifmp = NULL; + int err; + + /* register peer device */ + if (data && data[VXCAN_INFO_PEER]) { + struct nlattr *nla_peer; + + nla_peer = data[VXCAN_INFO_PEER]; + ifmp = nla_data(nla_peer); + err = rtnl_nla_parse_ifla(peer_tb, + nla_data(nla_peer) + + sizeof(struct ifinfomsg), + nla_len(nla_peer) - + sizeof(struct ifinfomsg), + NULL); + if (err < 0) + return err; + + tbp = peer_tb; + } + + if (ifmp && tbp[IFLA_IFNAME]) { + nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ); + name_assign_type = NET_NAME_USER; + } else { + snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); + name_assign_type = NET_NAME_ENUM; + } + + peer_net = rtnl_link_get_net(net, tbp); + if (IS_ERR(peer_net)) + return PTR_ERR(peer_net); + + peer = rtnl_create_link(peer_net, ifname, name_assign_type, + &vxcan_link_ops, tbp); + if (IS_ERR(peer)) { + put_net(peer_net); + return PTR_ERR(peer); + } + + if (ifmp && dev->ifindex) + peer->ifindex = ifmp->ifi_index; + + err = register_netdevice(peer); + put_net(peer_net); + peer_net = NULL; + if (err < 0) { + free_netdev(peer); + return err; + } + + netif_carrier_off(peer); + + err = rtnl_configure_link(peer, ifmp); + if (err < 0) + goto unregister_network_device; + + /* register first device */ + if (tb[IFLA_IFNAME]) + nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ); + else + snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); + + err = register_netdevice(dev); + if (err < 0) + goto unregister_network_device; + + netif_carrier_off(dev); + + /* cross link the device pair */ + priv = netdev_priv(dev); + rcu_assign_pointer(priv->peer, peer); + + priv = netdev_priv(peer); + rcu_assign_pointer(priv->peer, dev); + + return 0; + +unregister_network_device: + unregister_netdevice(peer); + return err; +} + +static void vxcan_dellink(struct net_device *dev, struct list_head *head) +{ + struct vxcan_priv *priv; + struct net_device *peer; + + priv = netdev_priv(dev); + peer = rtnl_dereference(priv->peer); + + /* Note : dellink() is called from default_device_exit_batch(), + * before a rcu_synchronize() point. The devices are guaranteed + * not being freed before one RCU grace period. + */ + RCU_INIT_POINTER(priv->peer, NULL); + unregister_netdevice_queue(dev, head); + + if (peer) { + priv = netdev_priv(peer); + RCU_INIT_POINTER(priv->peer, NULL); + unregister_netdevice_queue(peer, head); + } +} + +static const struct nla_policy vxcan_policy[VXCAN_INFO_MAX + 1] = { + [VXCAN_INFO_PEER] = { .len = sizeof(struct ifinfomsg) }, +}; + +static struct net *vxcan_get_link_net(const struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + return peer ? dev_net(peer) : dev_net(dev); +} + +static struct rtnl_link_ops vxcan_link_ops = { + .kind = DRV_NAME, + .priv_size = sizeof(struct vxcan_priv), + .setup = vxcan_setup, + .newlink = vxcan_newlink, + .dellink = vxcan_dellink, + .policy = vxcan_policy, + .maxtype = VXCAN_INFO_MAX, + .get_link_net = vxcan_get_link_net, +}; + +static __init int vxcan_init(void) +{ + pr_info("vxcan: Virtual CAN Tunnel driver\n"); + + return rtnl_link_register(&vxcan_link_ops); +} + +static __exit void vxcan_exit(void) +{ + rtnl_link_unregister(&vxcan_link_ops); +} + +module_init(vxcan_init); +module_exit(vxcan_exit); diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c new file mode 100644 index 000000000..90447a420 --- /dev/null +++ b/drivers/net/can/xilinx_can.c @@ -0,0 +1,1646 @@ +/* Xilinx CAN device driver + * + * Copyright (C) 2012 - 2014 Xilinx, Inc. + * Copyright (C) 2009 PetaLogix. All rights reserved. + * Copyright (C) 2017 - 2018 Sandvik Mining and Construction Oy + * + * Description: + * This driver is developed for Axi CAN IP and for Zynq CANPS Controller. + * 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. + */ + +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.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 <linux/can/led.h> +#include <linux/pm_runtime.h> + +#define DRIVER_NAME "xilinx_can" + +/* CAN registers set */ +enum xcan_reg { + XCAN_SRR_OFFSET = 0x00, /* Software reset */ + XCAN_MSR_OFFSET = 0x04, /* Mode select */ + XCAN_BRPR_OFFSET = 0x08, /* Baud rate prescaler */ + XCAN_BTR_OFFSET = 0x0C, /* Bit timing */ + XCAN_ECR_OFFSET = 0x10, /* Error counter */ + XCAN_ESR_OFFSET = 0x14, /* Error status */ + XCAN_SR_OFFSET = 0x18, /* Status */ + XCAN_ISR_OFFSET = 0x1C, /* Interrupt status */ + XCAN_IER_OFFSET = 0x20, /* Interrupt enable */ + XCAN_ICR_OFFSET = 0x24, /* Interrupt clear */ + + /* not on CAN FD cores */ + XCAN_TXFIFO_OFFSET = 0x30, /* TX FIFO base */ + XCAN_RXFIFO_OFFSET = 0x50, /* RX FIFO base */ + XCAN_AFR_OFFSET = 0x60, /* Acceptance Filter */ + + /* only on CAN FD cores */ + XCAN_TRR_OFFSET = 0x0090, /* TX Buffer Ready Request */ + XCAN_AFR_EXT_OFFSET = 0x00E0, /* Acceptance Filter */ + XCAN_FSR_OFFSET = 0x00E8, /* RX FIFO Status */ + XCAN_TXMSG_BASE_OFFSET = 0x0100, /* TX Message Space */ + XCAN_RXMSG_BASE_OFFSET = 0x1100, /* RX Message Space */ +}; + +#define XCAN_FRAME_ID_OFFSET(frame_base) ((frame_base) + 0x00) +#define XCAN_FRAME_DLC_OFFSET(frame_base) ((frame_base) + 0x04) +#define XCAN_FRAME_DW1_OFFSET(frame_base) ((frame_base) + 0x08) +#define XCAN_FRAME_DW2_OFFSET(frame_base) ((frame_base) + 0x0C) + +#define XCAN_CANFD_FRAME_SIZE 0x48 +#define XCAN_TXMSG_FRAME_OFFSET(n) (XCAN_TXMSG_BASE_OFFSET + \ + XCAN_CANFD_FRAME_SIZE * (n)) +#define XCAN_RXMSG_FRAME_OFFSET(n) (XCAN_RXMSG_BASE_OFFSET + \ + XCAN_CANFD_FRAME_SIZE * (n)) + +/* the single TX mailbox used by this driver on CAN FD HW */ +#define XCAN_TX_MAILBOX_IDX 0 + +/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */ +#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */ +#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */ +#define XCAN_MSR_LBACK_MASK 0x00000002 /* Loop back mode select */ +#define XCAN_MSR_SLEEP_MASK 0x00000001 /* Sleep mode select */ +#define XCAN_BRPR_BRP_MASK 0x000000FF /* Baud rate prescaler */ +#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */ +#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */ +#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */ +#define XCAN_BTR_SJW_MASK_CANFD 0x000F0000 /* Synchronous jump width */ +#define XCAN_BTR_TS2_MASK_CANFD 0x00000F00 /* Time segment 2 */ +#define XCAN_BTR_TS1_MASK_CANFD 0x0000003F /* Time segment 1 */ +#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */ +#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */ +#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */ +#define XCAN_ESR_BERR_MASK 0x00000008 /* Bit error */ +#define XCAN_ESR_STER_MASK 0x00000004 /* Stuff error */ +#define XCAN_ESR_FMER_MASK 0x00000002 /* Form error */ +#define XCAN_ESR_CRCER_MASK 0x00000001 /* CRC error */ +#define XCAN_SR_TXFLL_MASK 0x00000400 /* TX FIFO is full */ +#define XCAN_SR_ESTAT_MASK 0x00000180 /* Error status */ +#define XCAN_SR_ERRWRN_MASK 0x00000040 /* Error warning */ +#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */ +#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */ +#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */ +#define XCAN_IXR_RXMNF_MASK 0x00020000 /* RX match not finished */ +#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */ +#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */ +#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */ +#define XCAN_IXR_BSOFF_MASK 0x00000200 /* Bus off interrupt */ +#define XCAN_IXR_ERROR_MASK 0x00000100 /* Error interrupt */ +#define XCAN_IXR_RXNEMP_MASK 0x00000080 /* RX FIFO NotEmpty intr */ +#define XCAN_IXR_RXOFLW_MASK 0x00000040 /* RX FIFO Overflow intr */ +#define XCAN_IXR_RXOK_MASK 0x00000010 /* Message received intr */ +#define XCAN_IXR_TXFLL_MASK 0x00000004 /* Tx FIFO Full intr */ +#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */ +#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */ +#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */ +#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */ +#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */ +#define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */ +#define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */ +#define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */ +#define XCAN_FSR_FL_MASK 0x00003F00 /* RX Fill Level */ +#define XCAN_FSR_IRI_MASK 0x00000080 /* RX Increment Read Index */ +#define XCAN_FSR_RI_MASK 0x0000001F /* RX Read Index */ + +/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */ +#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */ +#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */ +#define XCAN_BTR_SJW_SHIFT_CANFD 16 /* Synchronous jump width */ +#define XCAN_BTR_TS2_SHIFT_CANFD 8 /* Time segment 2 */ +#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ +#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */ +#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */ +#define XCAN_ESR_REC_SHIFT 8 /* Rx Error Count */ + +/* CAN frame length constants */ +#define XCAN_FRAME_MAX_DATA_LEN 8 +#define XCAN_TIMEOUT (1 * HZ) + +/* TX-FIFO-empty interrupt available */ +#define XCAN_FLAG_TXFEMP 0x0001 +/* RX Match Not Finished interrupt available */ +#define XCAN_FLAG_RXMNF 0x0002 +/* Extended acceptance filters with control at 0xE0 */ +#define XCAN_FLAG_EXT_FILTERS 0x0004 +/* TX mailboxes instead of TX FIFO */ +#define XCAN_FLAG_TX_MAILBOXES 0x0008 +/* RX FIFO with each buffer in separate registers at 0x1100 + * instead of the regular FIFO at 0x50 + */ +#define XCAN_FLAG_RX_FIFO_MULTI 0x0010 + +struct xcan_devtype_data { + unsigned int flags; + const struct can_bittiming_const *bittiming_const; + const char *bus_clk_name; + unsigned int btr_ts2_shift; + unsigned int btr_sjw_shift; +}; + +/** + * struct xcan_priv - This definition define CAN driver instance + * @can: CAN private data structure. + * @tx_lock: Lock for synchronizing TX interrupt handling + * @tx_head: Tx CAN packets ready to send on the queue + * @tx_tail: Tx CAN packets successfully sended on the queue + * @tx_max: Maximum number packets the driver can send + * @napi: NAPI structure + * @read_reg: For reading data from CAN registers + * @write_reg: For writing data to CAN registers + * @dev: Network device data structure + * @reg_base: Ioremapped address to registers + * @irq_flags: For request_irq() + * @bus_clk: Pointer to struct clk + * @can_clk: Pointer to struct clk + * @devtype: Device type specific constants + */ +struct xcan_priv { + struct can_priv can; + spinlock_t tx_lock; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_max; + struct napi_struct napi; + u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg); + void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg, + u32 val); + struct device *dev; + void __iomem *reg_base; + unsigned long irq_flags; + struct clk *bus_clk; + struct clk *can_clk; + struct xcan_devtype_data devtype; +}; + +/* CAN Bittiming constants as per Xilinx CAN specs */ +static const struct can_bittiming_const xcan_bittiming_const = { + .name = DRIVER_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, +}; + +static const struct can_bittiming_const xcan_bittiming_const_canfd = { + .name = DRIVER_NAME, + .tseg1_min = 1, + .tseg1_max = 64, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +/** + * xcan_write_reg_le - Write a value to the device register little endian + * @priv: Driver private data structure + * @reg: Register offset + * @val: Value to write at the Register offset + * + * Write data to the paricular CAN register + */ +static void xcan_write_reg_le(const struct xcan_priv *priv, enum xcan_reg reg, + u32 val) +{ + iowrite32(val, priv->reg_base + reg); +} + +/** + * xcan_read_reg_le - Read a value from the device register little endian + * @priv: Driver private data structure + * @reg: Register offset + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static u32 xcan_read_reg_le(const struct xcan_priv *priv, enum xcan_reg reg) +{ + return ioread32(priv->reg_base + reg); +} + +/** + * xcan_write_reg_be - Write a value to the device register big endian + * @priv: Driver private data structure + * @reg: Register offset + * @val: Value to write at the Register offset + * + * Write data to the paricular CAN register + */ +static void xcan_write_reg_be(const struct xcan_priv *priv, enum xcan_reg reg, + u32 val) +{ + iowrite32be(val, priv->reg_base + reg); +} + +/** + * xcan_read_reg_be - Read a value from the device register big endian + * @priv: Driver private data structure + * @reg: Register offset + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static u32 xcan_read_reg_be(const struct xcan_priv *priv, enum xcan_reg reg) +{ + return ioread32be(priv->reg_base + reg); +} + +/** + * xcan_rx_int_mask - Get the mask for the receive interrupt + * @priv: Driver private data structure + * + * Return: The receive interrupt mask used by the driver on this HW + */ +static u32 xcan_rx_int_mask(const struct xcan_priv *priv) +{ + /* RXNEMP is better suited for our use case as it cannot be cleared + * while the FIFO is non-empty, but CAN FD HW does not have it + */ + if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) + return XCAN_IXR_RXOK_MASK; + else + return XCAN_IXR_RXNEMP_MASK; +} + +/** + * set_reset_mode - Resets the CAN device mode + * @ndev: Pointer to net_device structure + * + * This is the driver reset mode routine.The driver + * enters into configuration mode. + * + * Return: 0 on success and failure value on error + */ +static int set_reset_mode(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + unsigned long timeout; + + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + + timeout = jiffies + XCAN_TIMEOUT; + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) { + if (time_after(jiffies, timeout)) { + netdev_warn(ndev, "timed out for config mode\n"); + return -ETIMEDOUT; + } + usleep_range(500, 10000); + } + + /* reset clears FIFOs */ + priv->tx_head = 0; + priv->tx_tail = 0; + + return 0; +} + +/** + * xcan_set_bittiming - CAN set bit timing routine + * @ndev: Pointer to net_device structure + * + * This is the driver set bittiming routine. + * Return: 0 on success and failure value on error + */ +static int xcan_set_bittiming(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 btr0, btr1; + u32 is_config_mode; + + /* Check whether Xilinx CAN is in configuration mode. + * It cannot set bit timing if Xilinx CAN is not in configuration mode. + */ + is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) & + XCAN_SR_CONFIG_MASK; + if (!is_config_mode) { + netdev_alert(ndev, + "BUG! Cannot set bittiming - CAN is not in config mode\n"); + return -EPERM; + } + + /* Setting Baud Rate prescalar value in BRPR Register */ + btr0 = (bt->brp - 1); + + /* Setting Time Segment 1 in BTR Register */ + btr1 = (bt->prop_seg + bt->phase_seg1 - 1); + + /* Setting Time Segment 2 in BTR Register */ + btr1 |= (bt->phase_seg2 - 1) << priv->devtype.btr_ts2_shift; + + /* Setting Synchronous jump width in BTR Register */ + btr1 |= (bt->sjw - 1) << priv->devtype.btr_sjw_shift; + + priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0); + priv->write_reg(priv, XCAN_BTR_OFFSET, btr1); + + netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n", + priv->read_reg(priv, XCAN_BRPR_OFFSET), + priv->read_reg(priv, XCAN_BTR_OFFSET)); + + return 0; +} + +/** + * xcan_chip_start - This the drivers start routine + * @ndev: Pointer to net_device structure + * + * This is the drivers start routine. + * Based on the State of the CAN device it puts + * the CAN device into a proper mode. + * + * Return: 0 on success and failure value on error + */ +static int xcan_chip_start(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + u32 reg_msr, reg_sr_mask; + int err; + unsigned long timeout; + u32 ier; + + /* Check if it is in reset mode */ + err = set_reset_mode(ndev); + if (err < 0) + return err; + + err = xcan_set_bittiming(ndev); + if (err < 0) + return err; + + /* Enable interrupts */ + ier = XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK | + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | + XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | + XCAN_IXR_ARBLST_MASK | xcan_rx_int_mask(priv); + + if (priv->devtype.flags & XCAN_FLAG_RXMNF) + ier |= XCAN_IXR_RXMNF_MASK; + + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + + /* Check whether it is loopback mode or normal mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + reg_msr = XCAN_MSR_LBACK_MASK; + reg_sr_mask = XCAN_SR_LBACK_MASK; + } else { + reg_msr = 0x0; + reg_sr_mask = XCAN_SR_NORMAL_MASK; + } + + /* enable the first extended filter, if any, as cores with extended + * filtering default to non-receipt if all filters are disabled + */ + if (priv->devtype.flags & XCAN_FLAG_EXT_FILTERS) + priv->write_reg(priv, XCAN_AFR_EXT_OFFSET, 0x00000001); + + priv->write_reg(priv, XCAN_MSR_OFFSET, reg_msr); + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK); + + timeout = jiffies + XCAN_TIMEOUT; + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & reg_sr_mask)) { + if (time_after(jiffies, timeout)) { + netdev_warn(ndev, + "timed out for correct mode\n"); + return -ETIMEDOUT; + } + } + netdev_dbg(ndev, "status:#x%08x\n", + priv->read_reg(priv, XCAN_SR_OFFSET)); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +/** + * xcan_do_set_mode - This sets the mode of the driver + * @ndev: Pointer to net_device structure + * @mode: Tells the mode of the driver + * + * This check the drivers state and calls the + * the corresponding modes to set. + * + * Return: 0 on success and failure value on error + */ +static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + ret = xcan_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "xcan_chip_start failed!\n"); + return ret; + } + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +/** + * xcan_write_frame - Write a frame to HW + * @skb: sk_buff pointer that contains data to be Txed + * @frame_offset: Register offset to write the frame to + */ +static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb, + int frame_offset) +{ + u32 id, dlc, data[2] = {0, 0}; + struct can_frame *cf = (struct can_frame *)skb->data; + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CAN ID format */ + id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) & + XCAN_IDR_ID2_MASK; + id |= (((cf->can_id & CAN_EFF_MASK) >> + (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) << + XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK; + + /* The substibute remote TX request bit should be "1" + * for extended frames as in the Xilinx CAN datasheet + */ + id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Extended frames remote TX request */ + id |= XCAN_IDR_RTR_MASK; + } else { + /* Standard CAN ID format */ + id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) & + XCAN_IDR_ID1_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Standard frames remote TX request */ + id |= XCAN_IDR_SRR_MASK; + } + + dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT; + + if (cf->can_dlc > 0) + data[0] = be32_to_cpup((__be32 *)(cf->data + 0)); + if (cf->can_dlc > 4) + data[1] = be32_to_cpup((__be32 *)(cf->data + 4)); + + priv->write_reg(priv, XCAN_FRAME_ID_OFFSET(frame_offset), id); + /* If the CAN frame is RTR frame this write triggers transmission + * (not on CAN FD) + */ + priv->write_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_offset), dlc); + if (!(cf->can_id & CAN_RTR_FLAG)) { + priv->write_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_offset), + data[0]); + /* If the CAN frame is Standard/Extended frame this + * write triggers transmission (not on CAN FD) + */ + priv->write_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_offset), + data[1]); + } +} + +/** + * xcan_start_xmit_fifo - Starts the transmission (FIFO mode) + * + * Return: 0 on success, -ENOSPC if FIFO is full. + */ +static int xcan_start_xmit_fifo(struct sk_buff *skb, struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + unsigned long flags; + + /* Check if the TX buffer is full */ + if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) & + XCAN_SR_TXFLL_MASK)) + return -ENOSPC; + + can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max); + + spin_lock_irqsave(&priv->tx_lock, flags); + + priv->tx_head++; + + xcan_write_frame(priv, skb, XCAN_TXFIFO_OFFSET); + + /* Clear TX-FIFO-empty interrupt for xcan_tx_interrupt() */ + if (priv->tx_max > 1) + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXFEMP_MASK); + + /* Check if the TX buffer is full */ + if ((priv->tx_head - priv->tx_tail) == priv->tx_max) + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + return 0; +} + +/** + * xcan_start_xmit_mailbox - Starts the transmission (mailbox mode) + * + * Return: 0 on success, -ENOSPC if there is no space + */ +static int xcan_start_xmit_mailbox(struct sk_buff *skb, struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + unsigned long flags; + + if (unlikely(priv->read_reg(priv, XCAN_TRR_OFFSET) & + BIT(XCAN_TX_MAILBOX_IDX))) + return -ENOSPC; + + can_put_echo_skb(skb, ndev, 0); + + spin_lock_irqsave(&priv->tx_lock, flags); + + priv->tx_head++; + + xcan_write_frame(priv, skb, + XCAN_TXMSG_FRAME_OFFSET(XCAN_TX_MAILBOX_IDX)); + + /* Mark buffer as ready for transmit */ + priv->write_reg(priv, XCAN_TRR_OFFSET, BIT(XCAN_TX_MAILBOX_IDX)); + + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + return 0; +} + +/** + * xcan_start_xmit - Starts the transmission + * @skb: sk_buff pointer that contains data to be Txed + * @ndev: Pointer to net_device structure + * + * This function is invoked from upper layers to initiate transmission. + * + * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY when the tx queue is full + */ +static netdev_tx_t xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (priv->devtype.flags & XCAN_FLAG_TX_MAILBOXES) + ret = xcan_start_xmit_mailbox(skb, ndev); + else + ret = xcan_start_xmit_fifo(skb, ndev); + + if (ret < 0) { + netdev_err(ndev, "BUG!, TX full when queue awake!\n"); + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + return NETDEV_TX_OK; +} + +/** + * xcan_rx - Is called from CAN isr to complete the received + * frame processing + * @ndev: Pointer to net_device structure + * @frame_base: Register offset to the frame to be read + * + * This function is invoked from the CAN isr(poll) to process the Rx frames. It + * does minimal processing and invokes "netif_receive_skb" to complete further + * processing. + * Return: 1 on success and 0 on failure. + */ +static int xcan_rx(struct net_device *ndev, int frame_base) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 id_xcan, dlc, data[2] = {0, 0}; + + skb = alloc_can_skb(ndev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + /* Read a frame from Xilinx zynq CANPS */ + id_xcan = priv->read_reg(priv, XCAN_FRAME_ID_OFFSET(frame_base)); + dlc = priv->read_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_base)) >> + XCAN_DLCR_DLC_SHIFT; + + /* Change Xilinx CAN data length format to socketCAN data format */ + cf->can_dlc = get_can_dlc(dlc); + + /* Change Xilinx CAN ID format to socketCAN ID format */ + if (id_xcan & XCAN_IDR_IDE_MASK) { + /* The received frame is an Extended format frame */ + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3; + cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >> + XCAN_IDR_ID2_SHIFT; + cf->can_id |= CAN_EFF_FLAG; + if (id_xcan & XCAN_IDR_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* The received frame is a standard format frame */ + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> + XCAN_IDR_ID1_SHIFT; + if (id_xcan & XCAN_IDR_SRR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + /* DW1/DW2 must always be read to remove message from RXFIFO */ + data[0] = priv->read_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_base)); + data[1] = priv->read_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_base)); + + if (!(cf->can_id & CAN_RTR_FLAG)) { + /* Change Xilinx CAN data format to socketCAN data format */ + if (cf->can_dlc > 0) + *(__be32 *)(cf->data) = cpu_to_be32(data[0]); + if (cf->can_dlc > 4) + *(__be32 *)(cf->data + 4) = cpu_to_be32(data[1]); + } + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_receive_skb(skb); + + return 1; +} + +/** + * xcan_current_error_state - Get current error state from HW + * @ndev: Pointer to net_device structure + * + * Checks the current CAN error state from the HW. Note that this + * only checks for ERROR_PASSIVE and ERROR_WARNING. + * + * Return: + * ERROR_PASSIVE or ERROR_WARNING if either is active, ERROR_ACTIVE + * otherwise. + */ +static enum can_state xcan_current_error_state(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + u32 status = priv->read_reg(priv, XCAN_SR_OFFSET); + + if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) + return CAN_STATE_ERROR_PASSIVE; + else if (status & XCAN_SR_ERRWRN_MASK) + return CAN_STATE_ERROR_WARNING; + else + return CAN_STATE_ERROR_ACTIVE; +} + +/** + * xcan_set_error_state - Set new CAN error state + * @ndev: Pointer to net_device structure + * @new_state: The new CAN state to be set + * @cf: Error frame to be populated or NULL + * + * Set new CAN error state for the device, updating statistics and + * populating the error frame if given. + */ +static void xcan_set_error_state(struct net_device *ndev, + enum can_state new_state, + struct can_frame *cf) +{ + struct xcan_priv *priv = netdev_priv(ndev); + u32 ecr = priv->read_reg(priv, XCAN_ECR_OFFSET); + u32 txerr = ecr & XCAN_ECR_TEC_MASK; + u32 rxerr = (ecr & XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT; + enum can_state tx_state = txerr >= rxerr ? new_state : 0; + enum can_state rx_state = txerr <= rxerr ? new_state : 0; + + /* non-ERROR states are handled elsewhere */ + if (WARN_ON(new_state > CAN_STATE_ERROR_PASSIVE)) + return; + + can_change_state(ndev, cf, tx_state, rx_state); + + if (cf) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } +} + +/** + * xcan_update_error_state_after_rxtx - Update CAN error state after RX/TX + * @ndev: Pointer to net_device structure + * + * If the device is in a ERROR-WARNING or ERROR-PASSIVE state, check if + * the performed RX/TX has caused it to drop to a lesser state and set + * the interface state accordingly. + */ +static void xcan_update_error_state_after_rxtx(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + enum can_state old_state = priv->can.state; + enum can_state new_state; + + /* changing error state due to successful frame RX/TX can only + * occur from these states + */ + if (old_state != CAN_STATE_ERROR_WARNING && + old_state != CAN_STATE_ERROR_PASSIVE) + return; + + new_state = xcan_current_error_state(ndev); + + if (new_state != old_state) { + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_err_skb(ndev, &cf); + + xcan_set_error_state(ndev, new_state, skb ? cf : NULL); + + if (skb) { + struct net_device_stats *stats = &ndev->stats; + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } + } +} + +/** + * xcan_err_interrupt - error frame Isr + * @ndev: net_device pointer + * @isr: interrupt status register value + * + * This is the CAN error interrupt and it will + * check the the type of error and forward the error + * frame to upper layers. + */ +static void xcan_err_interrupt(struct net_device *ndev, u32 isr) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 err_status; + + skb = alloc_can_err_skb(ndev, &cf); + + err_status = priv->read_reg(priv, XCAN_ESR_OFFSET); + priv->write_reg(priv, XCAN_ESR_OFFSET, err_status); + + if (isr & XCAN_IXR_BSOFF_MASK) { + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + /* Leave device in Config Mode in bus-off state */ + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } else { + enum can_state new_state = xcan_current_error_state(ndev); + + if (new_state != priv->can.state) + xcan_set_error_state(ndev, new_state, skb ? cf : NULL); + } + + /* Check for Arbitration lost interrupt */ + if (isr & XCAN_IXR_ARBLST_MASK) { + priv->can.can_stats.arbitration_lost++; + if (skb) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = CAN_ERR_LOSTARB_UNSPEC; + } + } + + /* Check for RX FIFO Overflow interrupt */ + if (isr & XCAN_IXR_RXOFLW_MASK) { + stats->rx_over_errors++; + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + /* Check for RX Match Not Finished interrupt */ + if (isr & XCAN_IXR_RXMNF_MASK) { + stats->rx_dropped++; + stats->rx_errors++; + netdev_err(ndev, "RX match not finished, frame discarded\n"); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_UNSPEC; + } + } + + /* Check for error interrupt */ + if (isr & XCAN_IXR_ERROR_MASK) { + if (skb) + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + /* Check for Ack error interrupt */ + if (err_status & XCAN_ESR_ACKER_MASK) { + stats->tx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_ACK; + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + } + } + + /* Check for Bit error interrupt */ + if (err_status & XCAN_ESR_BERR_MASK) { + stats->tx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_BIT; + } + } + + /* Check for Stuff error interrupt */ + if (err_status & XCAN_ESR_STER_MASK) { + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_STUFF; + } + } + + /* Check for Form error interrupt */ + if (err_status & XCAN_ESR_FMER_MASK) { + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_FORM; + } + } + + /* Check for CRC error interrupt */ + if (err_status & XCAN_ESR_CRCER_MASK) { + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + } + } + priv->can.can_stats.bus_error++; + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } + + netdev_dbg(ndev, "%s: error status register:0x%x\n", + __func__, priv->read_reg(priv, XCAN_ESR_OFFSET)); +} + +/** + * xcan_state_interrupt - It will check the state of the CAN device + * @ndev: net_device pointer + * @isr: interrupt status register value + * + * This will checks the state of the CAN device + * and puts the device into appropriate state. + */ +static void xcan_state_interrupt(struct net_device *ndev, u32 isr) +{ + struct xcan_priv *priv = netdev_priv(ndev); + + /* Check for Sleep interrupt if set put CAN device in sleep state */ + if (isr & XCAN_IXR_SLP_MASK) + priv->can.state = CAN_STATE_SLEEPING; + + /* Check for Wake up interrupt if set put CAN device in Active state */ + if (isr & XCAN_IXR_WKUP_MASK) + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +/** + * xcan_rx_fifo_get_next_frame - Get register offset of next RX frame + * + * Return: Register offset of the next frame in RX FIFO. + */ +static int xcan_rx_fifo_get_next_frame(struct xcan_priv *priv) +{ + int offset; + + if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) { + u32 fsr; + + /* clear RXOK before the is-empty check so that any newly + * received frame will reassert it without a race + */ + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXOK_MASK); + + fsr = priv->read_reg(priv, XCAN_FSR_OFFSET); + + /* check if RX FIFO is empty */ + if (!(fsr & XCAN_FSR_FL_MASK)) + return -ENOENT; + + offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK); + + } else { + /* check if RX FIFO is empty */ + if (!(priv->read_reg(priv, XCAN_ISR_OFFSET) & + XCAN_IXR_RXNEMP_MASK)) + return -ENOENT; + + /* frames are read from a static offset */ + offset = XCAN_RXFIFO_OFFSET; + } + + return offset; +} + +/** + * xcan_rx_poll - Poll routine for rx packets (NAPI) + * @napi: napi structure pointer + * @quota: Max number of rx packets to be processed. + * + * This is the poll routine for rx part. + * It will process the packets maximux quota value. + * + * Return: number of packets received + */ +static int xcan_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct xcan_priv *priv = netdev_priv(ndev); + u32 ier; + int work_done = 0; + int frame_offset; + + while ((frame_offset = xcan_rx_fifo_get_next_frame(priv)) >= 0 && + (work_done < quota)) { + work_done += xcan_rx(ndev, frame_offset); + + if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) + /* increment read index */ + priv->write_reg(priv, XCAN_FSR_OFFSET, + XCAN_FSR_IRI_MASK); + else + /* clear rx-not-empty (will actually clear only if + * empty) + */ + priv->write_reg(priv, XCAN_ICR_OFFSET, + XCAN_IXR_RXNEMP_MASK); + } + + if (work_done) { + can_led_event(ndev, CAN_LED_EVENT_RX); + xcan_update_error_state_after_rxtx(ndev); + } + + if (work_done < quota) { + napi_complete_done(napi, work_done); + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier |= xcan_rx_int_mask(priv); + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + } + return work_done; +} + +/** + * xcan_tx_interrupt - Tx Done Isr + * @ndev: net_device pointer + * @isr: Interrupt status register value + */ +static void xcan_tx_interrupt(struct net_device *ndev, u32 isr) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + unsigned int frames_in_fifo; + int frames_sent = 1; /* TXOK => at least 1 frame was sent */ + unsigned long flags; + int retries = 0; + + /* Synchronize with xmit as we need to know the exact number + * of frames in the FIFO to stay in sync due to the TXFEMP + * handling. + * This also prevents a race between netif_wake_queue() and + * netif_stop_queue(). + */ + spin_lock_irqsave(&priv->tx_lock, flags); + + frames_in_fifo = priv->tx_head - priv->tx_tail; + + if (WARN_ON_ONCE(frames_in_fifo == 0)) { + /* clear TXOK anyway to avoid getting back here */ + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK); + spin_unlock_irqrestore(&priv->tx_lock, flags); + return; + } + + /* Check if 2 frames were sent (TXOK only means that at least 1 + * frame was sent). + */ + if (frames_in_fifo > 1) { + WARN_ON(frames_in_fifo > priv->tx_max); + + /* Synchronize TXOK and isr so that after the loop: + * (1) isr variable is up-to-date at least up to TXOK clear + * time. This avoids us clearing a TXOK of a second frame + * but not noticing that the FIFO is now empty and thus + * marking only a single frame as sent. + * (2) No TXOK is left. Having one could mean leaving a + * stray TXOK as we might process the associated frame + * via TXFEMP handling as we read TXFEMP *after* TXOK + * clear to satisfy (1). + */ + while ((isr & XCAN_IXR_TXOK_MASK) && !WARN_ON(++retries == 100)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK); + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + } + + if (isr & XCAN_IXR_TXFEMP_MASK) { + /* nothing in FIFO anymore */ + frames_sent = frames_in_fifo; + } + } else { + /* single frame in fifo, just clear TXOK */ + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK); + } + + while (frames_sent--) { + stats->tx_bytes += can_get_echo_skb(ndev, priv->tx_tail % + priv->tx_max); + priv->tx_tail++; + stats->tx_packets++; + } + + netif_wake_queue(ndev); + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + can_led_event(ndev, CAN_LED_EVENT_TX); + xcan_update_error_state_after_rxtx(ndev); +} + +/** + * xcan_interrupt - CAN Isr + * @irq: irq number + * @dev_id: device id poniter + * + * This is the xilinx CAN Isr. It checks for the type of interrupt + * and invokes the corresponding ISR. + * + * Return: + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise + */ +static irqreturn_t xcan_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct xcan_priv *priv = netdev_priv(ndev); + u32 isr, ier; + u32 isr_errors; + u32 rx_int_mask = xcan_rx_int_mask(priv); + + /* Get the interrupt status from Xilinx CAN */ + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + if (!isr) + return IRQ_NONE; + + /* Check for the type of interrupt and Processing it */ + if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK | + XCAN_IXR_WKUP_MASK)); + xcan_state_interrupt(ndev, isr); + } + + /* Check for Tx interrupt and Processing it */ + if (isr & XCAN_IXR_TXOK_MASK) + xcan_tx_interrupt(ndev, isr); + + /* Check for the type of error interrupt and Processing it */ + isr_errors = isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK | + XCAN_IXR_RXMNF_MASK); + if (isr_errors) { + priv->write_reg(priv, XCAN_ICR_OFFSET, isr_errors); + xcan_err_interrupt(ndev, isr); + } + + /* Check for the type of receive interrupt and Processing it */ + if (isr & rx_int_mask) { + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier &= ~rx_int_mask; + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + napi_schedule(&priv->napi); + } + return IRQ_HANDLED; +} + +/** + * xcan_chip_stop - Driver stop routine + * @ndev: Pointer to net_device structure + * + * This is the drivers stop routine. It will disable the + * interrupts and put the device into configuration mode. + */ +static void xcan_chip_stop(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + + /* Disable interrupts and leave the can in configuration mode */ + set_reset_mode(ndev); + priv->can.state = CAN_STATE_STOPPED; +} + +/** + * xcan_open - Driver open routine + * @ndev: Pointer to net_device structure + * + * This is the driver open routine. + * Return: 0 on success and failure value on error + */ +static int xcan_open(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + return ret; + } + + ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "irq allocation for CAN failed\n"); + goto err; + } + + /* Set chip into reset mode */ + ret = set_reset_mode(ndev); + if (ret < 0) { + netdev_err(ndev, "mode resetting failed!\n"); + goto err_irq; + } + + /* Common open */ + ret = open_candev(ndev); + if (ret) + goto err_irq; + + ret = xcan_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "xcan_chip_start failed!\n"); + goto err_candev; + } + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; + +err_candev: + close_candev(ndev); +err_irq: + free_irq(ndev->irq, ndev); +err: + pm_runtime_put(priv->dev); + + return ret; +} + +/** + * xcan_close - Driver close routine + * @ndev: Pointer to net_device structure + * + * Return: 0 always + */ +static int xcan_close(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + xcan_chip_stop(ndev); + free_irq(ndev->irq, ndev); + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + pm_runtime_put(priv->dev); + + return 0; +} + +/** + * xcan_get_berr_counter - error counter routine + * @ndev: Pointer to net_device structure + * @bec: Pointer to can_berr_counter structure + * + * This is the driver error counter routine. + * Return: 0 on success and failure value on error + */ +static int xcan_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + return ret; + } + + bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK; + bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) & + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT); + + pm_runtime_put(priv->dev); + + return 0; +} + + +static const struct net_device_ops xcan_netdev_ops = { + .ndo_open = xcan_open, + .ndo_stop = xcan_close, + .ndo_start_xmit = xcan_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/** + * xcan_suspend - Suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused xcan_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + xcan_chip_stop(ndev); + } + + return pm_runtime_force_suspend(dev); +} + +/** + * xcan_resume - Resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused xcan_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "pm_runtime_force_resume failed on resume\n"); + return ret; + } + + if (netif_running(ndev)) { + ret = xcan_chip_start(ndev); + if (ret) { + dev_err(dev, "xcan_chip_start failed on resume\n"); + return ret; + } + + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +/** + * xcan_runtime_suspend - Runtime suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused xcan_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct xcan_priv *priv = netdev_priv(ndev); + + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + + return 0; +} + +/** + * xcan_runtime_resume - Runtime resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused xcan_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + return ret; + } + ret = clk_prepare_enable(priv->can_clk); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + clk_disable_unprepare(priv->bus_clk); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops xcan_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xcan_suspend, xcan_resume) + SET_RUNTIME_PM_OPS(xcan_runtime_suspend, xcan_runtime_resume, NULL) +}; + +static const struct xcan_devtype_data xcan_zynq_data = { + .bittiming_const = &xcan_bittiming_const, + .btr_ts2_shift = XCAN_BTR_TS2_SHIFT, + .btr_sjw_shift = XCAN_BTR_SJW_SHIFT, + .bus_clk_name = "pclk", +}; + +static const struct xcan_devtype_data xcan_axi_data = { + .bittiming_const = &xcan_bittiming_const, + .btr_ts2_shift = XCAN_BTR_TS2_SHIFT, + .btr_sjw_shift = XCAN_BTR_SJW_SHIFT, + .bus_clk_name = "s_axi_aclk", +}; + +static const struct xcan_devtype_data xcan_canfd_data = { + .flags = XCAN_FLAG_EXT_FILTERS | + XCAN_FLAG_RXMNF | + XCAN_FLAG_TX_MAILBOXES | + XCAN_FLAG_RX_FIFO_MULTI, + .bittiming_const = &xcan_bittiming_const_canfd, + .btr_ts2_shift = XCAN_BTR_TS2_SHIFT_CANFD, + .btr_sjw_shift = XCAN_BTR_SJW_SHIFT_CANFD, + .bus_clk_name = "s_axi_aclk", +}; + +/* Match table for OF platform binding */ +static const struct of_device_id xcan_of_match[] = { + { .compatible = "xlnx,zynq-can-1.0", .data = &xcan_zynq_data }, + { .compatible = "xlnx,axi-can-1.00.a", .data = &xcan_axi_data }, + { .compatible = "xlnx,canfd-1.0", .data = &xcan_canfd_data }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, xcan_of_match); + +/** + * xcan_probe - Platform registration call + * @pdev: Handle to the platform device structure + * + * This function does all the memory allocation and registration for the CAN + * device. + * + * Return: 0 on success and failure value on error + */ +static int xcan_probe(struct platform_device *pdev) +{ + struct resource *res; /* IO mem resources */ + struct net_device *ndev; + struct xcan_priv *priv; + const struct of_device_id *of_id; + const struct xcan_devtype_data *devtype = &xcan_axi_data; + void __iomem *addr; + int ret; + int rx_max, tx_max; + int hw_tx_max, hw_rx_max; + const char *hw_tx_max_property; + + /* Get the virtual base address for the device */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) { + ret = PTR_ERR(addr); + goto err; + } + + of_id = of_match_device(xcan_of_match, &pdev->dev); + if (of_id && of_id->data) + devtype = of_id->data; + + hw_tx_max_property = devtype->flags & XCAN_FLAG_TX_MAILBOXES ? + "tx-mailbox-count" : "tx-fifo-depth"; + + ret = of_property_read_u32(pdev->dev.of_node, hw_tx_max_property, + &hw_tx_max); + if (ret < 0) { + dev_err(&pdev->dev, "missing %s property\n", + hw_tx_max_property); + goto err; + } + + ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", + &hw_rx_max); + if (ret < 0) { + dev_err(&pdev->dev, + "missing rx-fifo-depth property (mailbox mode is not supported)\n"); + goto err; + } + + /* With TX FIFO: + * + * There is no way to directly figure out how many frames have been + * sent when the TXOK interrupt is processed. If TXFEMP + * is supported, we can have 2 frames in the FIFO and use TXFEMP + * to determine if 1 or 2 frames have been sent. + * Theoretically we should be able to use TXFWMEMP to determine up + * to 3 frames, but it seems that after putting a second frame in the + * FIFO, with watermark at 2 frames, it can happen that TXFWMEMP (less + * than 2 frames in FIFO) is set anyway with no TXOK (a frame was + * sent), which is not a sensible state - possibly TXFWMEMP is not + * completely synchronized with the rest of the bits? + * + * With TX mailboxes: + * + * HW sends frames in CAN ID priority order. To preserve FIFO ordering + * we submit frames one at a time. + */ + if (!(devtype->flags & XCAN_FLAG_TX_MAILBOXES) && + (devtype->flags & XCAN_FLAG_TXFEMP)) + tx_max = min(hw_tx_max, 2); + else + tx_max = 1; + + rx_max = hw_rx_max; + + /* Create a CAN device instance */ + ndev = alloc_candev(sizeof(struct xcan_priv), tx_max); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + priv->dev = &pdev->dev; + priv->can.bittiming_const = devtype->bittiming_const; + priv->can.do_set_mode = xcan_do_set_mode; + priv->can.do_get_berr_counter = xcan_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_BERR_REPORTING; + priv->reg_base = addr; + priv->tx_max = tx_max; + priv->devtype = *devtype; + spin_lock_init(&priv->tx_lock); + + /* Get IRQ for the device */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_free; + + ndev->irq = ret; + + ndev->flags |= IFF_ECHO; /* We support local echo */ + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &xcan_netdev_ops; + + /* Getting the CAN can_clk info */ + priv->can_clk = devm_clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(priv->can_clk)) { + dev_err(&pdev->dev, "Device clock not found.\n"); + ret = PTR_ERR(priv->can_clk); + goto err_free; + } + + priv->bus_clk = devm_clk_get(&pdev->dev, devtype->bus_clk_name); + if (IS_ERR(priv->bus_clk)) { + dev_err(&pdev->dev, "bus clock not found\n"); + ret = PTR_ERR(priv->bus_clk); + goto err_free; + } + + priv->write_reg = xcan_write_reg_le; + priv->read_reg = xcan_read_reg_le; + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + goto err_pmdisable; + } + + if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) { + priv->write_reg = xcan_write_reg_be; + priv->read_reg = xcan_read_reg_be; + } + + priv->can.clock.freq = clk_get_rate(priv->can_clk); + + netif_napi_add(ndev, &priv->napi, xcan_rx_poll, rx_max); + + ret = register_candev(ndev); + if (ret) { + dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret); + goto err_disableclks; + } + + devm_can_led_init(ndev); + + pm_runtime_put(&pdev->dev); + + netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx buffers: actual %d, using %d\n", + priv->reg_base, ndev->irq, priv->can.clock.freq, + hw_tx_max, priv->tx_max); + + return 0; + +err_disableclks: + pm_runtime_put(priv->dev); +err_pmdisable: + pm_runtime_disable(&pdev->dev); +err_free: + free_candev(ndev); +err: + return ret; +} + +/** + * xcan_remove - Unregister the device after releasing the resources + * @pdev: Handle to the platform device structure + * + * This function frees all the resources allocated to the device. + * Return: 0 always + */ +static int xcan_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct xcan_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + pm_runtime_disable(&pdev->dev); + netif_napi_del(&priv->napi); + free_candev(ndev); + + return 0; +} + +static struct platform_driver xcan_driver = { + .probe = xcan_probe, + .remove = xcan_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &xcan_dev_pm_ops, + .of_match_table = xcan_of_match, + }, +}; + +module_platform_driver(xcan_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xilinx Inc"); +MODULE_DESCRIPTION("Xilinx CAN interface"); |