diff options
Diffstat (limited to 'drivers/net/can')
68 files changed, 22286 insertions, 2531 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d5a9db60ade..41688229c57 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -1,10 +1,7 @@ menu "CAN Device Drivers" - depends on CAN config CAN_VCAN tristate "Virtual Local CAN Interface (vcan)" - depends on CAN - default N ---help--- Similar to the network loopback devices, vcan offers a virtual local CAN interface. @@ -14,8 +11,7 @@ config CAN_VCAN config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" - depends on CAN - default N + 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 @@ -23,7 +19,7 @@ config CAN_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.mictronic.com / www.canhack.de + 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 @@ -35,17 +31,17 @@ config CAN_SLCAN config CAN_DEV tristate "Platform CAN drivers with Netlink support" - depends on CAN - default Y + 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" - depends on CAN_DEV - default Y + default y ---help--- If enabled, CAN bit-timing parameters will be calculated for the bit-rate specified via Netlink argument "bitrate" when the device @@ -56,27 +52,33 @@ config CAN_CALC_BITTIMING 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 + 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 CAN_DEV && ARCH_AT91SAM9263 + depends on ARCH_AT91 || COMPILE_TEST ---help--- - This is a driver for the SoC CAN controller in Atmel's AT91SAM9263. + This is a driver for the SoC CAN controller in Atmel's AT91SAM9263 + and AT91SAM9X5 processors. config CAN_TI_HECC - depends on CAN_DEV && ARCH_OMAP3 + 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_MCP251X - tristate "Microchip MCP251x SPI CAN controllers" - depends on CAN_DEV && SPI && HAS_DMA - ---help--- - Driver for the Microchip MCP251x SPI CAN controllers. - config CAN_BFIN - depends on CAN_DEV && (BF534 || BF536 || BF537 || BF538 || BF539 || BF54x) + depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x tristate "Analog Devices Blackfin on-chip CAN" ---help--- Driver for the Analog Devices Blackfin on-chip CAN controllers @@ -86,7 +88,7 @@ config CAN_BFIN config CAN_JANZ_ICAN3 tristate "Janz VMOD-ICAN3 Intelligent CAN controller" - depends on CAN_DEV && MFD_JANZ_CMODIO + depends on MFD_JANZ_CMODIO ---help--- Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which connects to a MODULbus carrier board. @@ -94,33 +96,65 @@ config CAN_JANZ_ICAN3 This driver can also be built as a module. If so, the module will be called janz-ican3.ko. -config HAVE_CAN_FLEXCAN - bool - config CAN_FLEXCAN tristate "Support for Freescale FLEXCAN based chips" - depends on CAN_DEV && HAVE_CAN_FLEXCAN + depends on ARM || PPC ---help--- Say Y here if you want to support for Freescale FlexCAN. config PCH_CAN - tristate "PCH CAN" - depends on CAN_DEV && PCI + tristate "Intel EG20T PCH CAN controller" + depends on PCI && (X86_32 || COMPILE_TEST) ---help--- - This driver is for PCH CAN of Topcliff which is an IOH for x86 - embedded processor. + 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. +config CAN_GRCAN + tristate "Aeroflex Gaisler GRCAN and GRHCAN CAN devices" + depends on OF + ---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_RCAR + tristate "Renesas R-Car CAN controller" + depends on 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_XILINXCAN + tristate "Xilinx CAN" + depends on ARCH_ZYNQ || 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. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" +source "drivers/net/can/c_can/Kconfig" + +source "drivers/net/can/cc770/Kconfig" + +source "drivers/net/can/spi/Kconfig" + source "drivers/net/can/usb/Kconfig" +source "drivers/net/can/softing/Kconfig" + +endif + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" - depends on CAN - default N ---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 diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 07ca159ba3f..1697f22353a 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -8,16 +8,24 @@ obj-$(CONFIG_CAN_SLCAN) += slcan.o obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o +can-dev-$(CONFIG_CAN_LEDS) += led.o + +obj-y += spi/ obj-y += usb/ +obj-y += softing/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ +obj-$(CONFIG_CAN_C_CAN) += c_can/ +obj-$(CONFIG_CAN_CC770) += cc770/ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o -obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_PCH_CAN) += pch_can.o +obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_RCAR) += rcar_can.o +obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 7ef83d06f7e..f07fa89b5fd 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -2,14 +2,12 @@ * at91_can.c - CAN network driver for AT91 SoC CAN controller * * (C) 2007 by Hans J. Koch <hjk@hansjkoch.de> - * (C) 2008, 2009, 2010 by Marc Kleine-Budde <kernel@pengutronix.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. * - * Send feedback to <socketcan-users@lists.berlios.de> - * * * Your platform definition file should specify something like: * @@ -24,47 +22,24 @@ #include <linux/clk.h> #include <linux/errno.h> #include <linux/if_arp.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/netdevice.h> +#include <linux/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/platform_data/atmel.h> #include <linux/can/dev.h> #include <linux/can/error.h> +#include <linux/can/led.h> -#include <mach/board.h> - -#define AT91_NAPI_WEIGHT 12 - -/* - * RX/TX Mailbox split - * don't dare to touch - */ -#define AT91_MB_RX_NUM 12 -#define AT91_MB_TX_SHIFT 2 - -#define AT91_MB_RX_FIRST 0 -#define AT91_MB_RX_LAST (AT91_MB_RX_FIRST + AT91_MB_RX_NUM - 1) - -#define AT91_MB_RX_MASK(i) ((1 << (i)) - 1) -#define AT91_MB_RX_SPLIT 8 -#define AT91_MB_RX_LOW_LAST (AT91_MB_RX_SPLIT - 1) -#define AT91_MB_RX_LOW_MASK (AT91_MB_RX_MASK(AT91_MB_RX_SPLIT)) - -#define AT91_MB_TX_NUM (1 << AT91_MB_TX_SHIFT) -#define AT91_MB_TX_FIRST (AT91_MB_RX_LAST + 1) -#define AT91_MB_TX_LAST (AT91_MB_TX_FIRST + AT91_MB_TX_NUM - 1) - -#define AT91_NEXT_PRIO_SHIFT (AT91_MB_TX_SHIFT) -#define AT91_NEXT_PRIO_MASK (0xf << AT91_MB_TX_SHIFT) -#define AT91_NEXT_MB_MASK (AT91_MB_TX_NUM - 1) -#define AT91_NEXT_MASK ((AT91_MB_TX_NUM - 1) | AT91_NEXT_PRIO_MASK) +#define AT91_MB_MASK(i) ((1 << (i)) - 1) /* Common registers */ enum at91_reg { @@ -126,12 +101,6 @@ enum at91_mb_mode { }; /* Interrupt mask bits */ -#define AT91_IRQ_MB_RX ((1 << (AT91_MB_RX_LAST + 1)) \ - - (1 << AT91_MB_RX_FIRST)) -#define AT91_IRQ_MB_TX ((1 << (AT91_MB_TX_LAST + 1)) \ - - (1 << AT91_MB_TX_FIRST)) -#define AT91_IRQ_MB_ALL (AT91_IRQ_MB_RX | AT91_IRQ_MB_TX) - #define AT91_IRQ_ERRA (1 << 16) #define AT91_IRQ_WARN (1 << 17) #define AT91_IRQ_ERRP (1 << 18) @@ -154,23 +123,55 @@ enum at91_mb_mode { #define AT91_IRQ_ALL (0x1fffffff) +enum at91_devtype { + AT91_DEVTYPE_SAM9263, + AT91_DEVTYPE_SAM9X5, +}; + +struct at91_devtype_data { + unsigned int rx_first; + unsigned int rx_split; + unsigned int rx_last; + unsigned int tx_shift; + enum at91_devtype type; +}; + struct at91_priv { - struct can_priv can; /* must be the first member! */ - struct net_device *dev; - struct napi_struct napi; + struct can_priv can; /* must be the first member! */ + struct net_device *dev; + struct napi_struct napi; + + void __iomem *reg_base; - 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; +}; - u32 reg_sr; - unsigned int tx_next; - unsigned int tx_echo; - unsigned int rx_next; +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, +}; - struct clk *clk; - struct at91_can_data *pdata; +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 struct can_bittiming_const at91_bittiming_const = { +static const struct can_bittiming_const at91_bittiming_const = { .name = KBUILD_MODNAME, .tseg1_min = 4, .tseg1_max = 16, @@ -182,19 +183,111 @@ static struct can_bittiming_const at91_bittiming_const = { .brp_inc = 1, }; -static inline int get_tx_next_mb(const struct at91_priv *priv) +#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 (priv->tx_next & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST; + return AT91_MB_MASK(get_mb_rx_last(priv) + 1) & + ~AT91_MB_MASK(get_mb_rx_first(priv)); } -static inline int get_tx_next_prio(const struct at91_priv *priv) +static inline unsigned int get_irq_mb_tx(const struct at91_priv *priv) { - return (priv->tx_next >> AT91_NEXT_PRIO_SHIFT) & 0xf; + return AT91_MB_MASK(get_mb_tx_last(priv) + 1) & + ~AT91_MB_MASK(get_mb_tx_first(priv)); } -static inline int get_tx_echo_mb(const struct at91_priv *priv) +static inline unsigned int get_tx_next_mb(const struct at91_priv *priv) { - return (priv->tx_echo & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST; + 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) @@ -220,6 +313,18 @@ static inline void set_mb_mode(const struct at91_priv *priv, unsigned int mb, set_mb_mode_prio(priv, mb, mode, 0); } +static inline u32 at91_can_id_to_reg_mid(canid_t can_id) +{ + u32 reg_mid; + + if (can_id & CAN_EFF_FLAG) + reg_mid = (can_id & CAN_EFF_MASK) | AT91_MID_MIDE; + else + reg_mid = (can_id & CAN_SFF_MASK) << 18; + + return reg_mid; +} + /* * Swtich transceiver on or off */ @@ -233,28 +338,39 @@ static void at91_setup_mailboxes(struct net_device *dev) { struct at91_priv *priv = netdev_priv(dev); unsigned int i; + u32 reg_mid; /* - * The first 12 mailboxes are used as a reception FIFO. The - * last mailbox is configured with overwrite option. The - * overwrite flag indicates a FIFO overflow. + * 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. */ - for (i = AT91_MB_RX_FIRST; i < AT91_MB_RX_LAST; i++) + 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, AT91_MB_RX_LAST, AT91_MB_MODE_RX_OVRWR); + set_mb_mode(priv, get_mb_rx_last(priv), AT91_MB_MODE_RX_OVRWR); /* reset acceptance mask and id register */ - for (i = AT91_MB_RX_FIRST; i <= AT91_MB_RX_LAST; i++) { - at91_write(priv, AT91_MAM(i), 0x0 ); + 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 = AT91_MB_TX_FIRST; i <= AT91_MB_TX_LAST; i++) + 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 = priv->rx_next = 0; + priv->tx_next = priv->tx_echo = 0; + priv->rx_next = get_mb_rx_first(priv); } static int at91_set_bittiming(struct net_device *dev) @@ -304,12 +420,16 @@ static void at91_chip_start(struct net_device *dev) at91_transceiver_switch(priv, 1); /* enable chip */ - at91_write(priv, AT91_MR, AT91_MR_CANEN); + 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 = AT91_IRQ_MB_RX | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME; + 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); } @@ -348,8 +468,8 @@ static void at91_chip_stop(struct net_device *dev, enum can_state state) * 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 << AT91_NEXT_PRIO_SHIFT) || - * (mb - AT91_MB_TX_FIRST); + * 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) @@ -372,12 +492,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) netdev_err(dev, "BUG! TX buffer full when queue awake!\n"); return NETDEV_TX_BUSY; } - - if (cf->can_id & CAN_EFF_FLAG) - reg_mid = (cf->can_id & CAN_EFF_MASK) | AT91_MID_MIDE; - else - reg_mid = (cf->can_id & CAN_SFF_MASK) << 18; - + 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; @@ -394,8 +509,8 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_bytes += cf->can_dlc; - /* _NOTE_: substract AT91_MB_TX_FIRST offset from mb! */ - can_put_echo_skb(skb, dev, mb - AT91_MB_TX_FIRST); + /* _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 @@ -408,7 +523,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) priv->tx_next++; if (!(at91_read(priv, AT91_MSR(get_tx_next_mb(priv))) & AT91_MSR_MRDY) || - (priv->tx_next & AT91_NEXT_MASK) == 0) + (priv->tx_next & get_next_mask(priv)) == 0) netif_stop_queue(dev); /* Enable interrupt for this mailbox */ @@ -425,7 +540,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) */ static inline void at91_activate_rx_low(const struct at91_priv *priv) { - u32 mask = AT91_MB_RX_LOW_MASK; + u32 mask = get_mb_rx_low_mask(priv); at91_write(priv, AT91_TCR, mask); } @@ -491,17 +606,19 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb, cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK; reg_msr = at91_read(priv, AT91_MSR(mb)); - if (reg_msr & AT91_MSR_MRTR) - cf->can_id |= CAN_RTR_FLAG; cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf); - *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb)); - *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb)); + 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 == AT91_MB_RX_LAST && reg_msr & AT91_MSR_MMI)) + if (unlikely(mb == get_mb_rx_last(priv) && reg_msr & AT91_MSR_MMI)) at91_rx_overflow_err(dev); } @@ -530,6 +647,8 @@ static void at91_read_msg(struct net_device *dev, unsigned int mb) stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + + can_led_event(dev, CAN_LED_EVENT_RX); } /** @@ -539,27 +658,32 @@ static void at91_read_msg(struct net_device *dev, unsigned int mb) * * Theory of Operation: * - * 12 of the 16 mailboxes on the chip are reserved for RX. we split - * them into 2 groups. The lower group holds 8 and upper 4 mailboxes. + * 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. 0 and issues an interrupt. All + * 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|x|| | | | | + * | |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 @@ -584,23 +708,23 @@ static int at91_poll_rx(struct net_device *dev, int quota) unsigned int mb; int received = 0; - if (priv->rx_next > AT91_MB_RX_LOW_LAST && - reg_sr & AT91_MB_RX_LOW_MASK) + 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, AT91_MB_RX_NUM, priv->rx_next); - mb < AT91_MB_RX_NUM && quota > 0; + 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, AT91_MB_RX_NUM, ++priv->rx_next)) { + mb = find_next_bit(addr, get_mb_tx_first(priv), ++priv->rx_next)) { at91_read_msg(dev, mb); /* reactivate mailboxes */ - if (mb == AT91_MB_RX_LOW_LAST) + if (mb == get_mb_rx_low_last(priv)) /* all lower mailboxed, if just finished it */ at91_activate_rx_low(priv); - else if (mb > AT91_MB_RX_LOW_LAST) + else if (mb > get_mb_rx_low_last(priv)) /* only the mailbox we read */ at91_activate_rx_mb(priv, mb); @@ -609,9 +733,9 @@ static int at91_poll_rx(struct net_device *dev, int quota) } /* upper group completed, look again in lower */ - if (priv->rx_next > AT91_MB_RX_LOW_LAST && - quota > 0 && mb >= AT91_MB_RX_NUM) { - priv->rx_next = 0; + if (priv->rx_next > get_mb_rx_low_last(priv) && + quota > 0 && mb > get_mb_rx_last(priv)) { + priv->rx_next = get_mb_rx_first(priv); goto again; } @@ -694,7 +818,7 @@ static int at91_poll(struct napi_struct *napi, int quota) u32 reg_sr = at91_read(priv, AT91_SR); int work_done = 0; - if (reg_sr & AT91_IRQ_MB_RX) + if (reg_sr & get_irq_mb_rx(priv)) work_done += at91_poll_rx(dev, quota - work_done); /* @@ -708,7 +832,7 @@ static int at91_poll(struct napi_struct *napi, int quota) if (work_done < quota) { /* enable IRQs for frame errors and all mailboxes >= rx_next */ u32 reg_ier = AT91_IRQ_ERR_FRAME; - reg_ier |= AT91_IRQ_MB_RX & ~AT91_MB_RX_MASK(priv->rx_next); + reg_ier |= get_irq_mb_rx(priv) & ~AT91_MB_MASK(priv->rx_next); napi_complete(napi); at91_write(priv, AT91_IER, reg_ier); @@ -756,9 +880,10 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr) reg_msr = at91_read(priv, AT91_MSR(mb)); if (likely(reg_msr & AT91_MSR_MRDY && ~reg_msr & AT91_MSR_MABT)) { - /* _NOTE_: substract AT91_MB_TX_FIRST offset from mb! */ - can_get_echo_skb(dev, mb - AT91_MB_TX_FIRST); + /* _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); } } @@ -767,8 +892,8 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr) * we get a TX int for the last can frame directly before a * wrap around. */ - if ((priv->tx_next & AT91_NEXT_MASK) != 0 || - (priv->tx_echo & AT91_NEXT_MASK) == 0) + if ((priv->tx_next & get_next_mask(priv)) != 0 || + (priv->tx_echo & get_next_mask(priv)) == 0) netif_wake_queue(dev); } @@ -880,6 +1005,29 @@ static void at91_irq_err_state(struct net_device *dev, 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); @@ -887,21 +1035,28 @@ static void at91_irq_err(struct net_device *dev) struct can_frame *cf; enum can_state new_state; u32 reg_sr; + int err; - 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; + 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 */ @@ -942,19 +1097,19 @@ static irqreturn_t at91_irq(int irq, void *dev_id) handled = IRQ_HANDLED; /* Receive or error interrupt? -> napi */ - if (reg_sr & (AT91_IRQ_MB_RX | AT91_IRQ_ERR_FRAME)) { + 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, - AT91_IRQ_MB_RX | AT91_IRQ_ERR_FRAME); + get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME); napi_schedule(&priv->napi); } /* Transmission complete interrupt */ - if (reg_sr & AT91_IRQ_MB_TX) + if (reg_sr & get_irq_mb_tx(priv)) at91_irq_tx(dev, reg_sr); at91_irq_err(dev); @@ -982,6 +1137,8 @@ static int at91_open(struct net_device *dev) goto out_close; } + can_led_event(dev, CAN_LED_EVENT_OPEN); + /* start chip and queuing */ at91_chip_start(dev); napi_enable(&priv->napi); @@ -1013,6 +1170,8 @@ static int at91_close(struct net_device *dev) close_candev(dev); + can_led_event(dev, CAN_LED_EVENT_STOP); + return 0; } @@ -1035,10 +1194,101 @@ 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 int __devinit at91_can_probe(struct platform_device *pdev) +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, S_IWUSR | S_IRUGO, + at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id); + +static struct attribute *at91_sysfs_attrs[] = { + &dev_attr_mb0_id.attr, + NULL, +}; + +static struct attribute_group at91_sysfs_attr_group = { + .attrs = at91_sysfs_attrs, +}; + +#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; @@ -1046,6 +1296,13 @@ static int __devinit at91_can_probe(struct platform_device *pdev) 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"); @@ -1073,7 +1330,8 @@ static int __devinit at91_can_probe(struct platform_device *pdev) goto exit_release; } - dev = alloc_candev(sizeof(struct at91_priv), AT91_MB_TX_NUM); + dev = alloc_candev(sizeof(struct at91_priv), + 1 << devtype_data->tx_shift); if (!dev) { err = -ENOMEM; goto exit_iounmap; @@ -1088,15 +1346,21 @@ static int __devinit at91_can_probe(struct platform_device *pdev) priv->can.bittiming_const = &at91_bittiming_const; priv->can.do_set_mode = at91_set_mode; priv->can.do_get_berr_counter = at91_get_berr_counter; - priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; - priv->reg_base = addr; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LISTENONLY; priv->dev = dev; + priv->reg_base = addr; + priv->devtype_data = *devtype_data; priv->clk = clk; - priv->pdata = pdev->dev.platform_data; + priv->pdata = dev_get_platdata(&pdev->dev); + priv->mb0_id = 0x7ff; - netif_napi_add(dev, &priv->napi, at91_poll, AT91_NAPI_WEIGHT); + netif_napi_add(dev, &priv->napi, at91_poll, get_mb_rx_num(priv)); - dev_set_drvdata(&pdev->dev, dev); + 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); @@ -1105,6 +1369,8 @@ static int __devinit at91_can_probe(struct platform_device *pdev) 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); @@ -1122,7 +1388,7 @@ static int __devinit at91_can_probe(struct platform_device *pdev) return err; } -static int __devexit at91_can_remove(struct platform_device *pdev) +static int at91_can_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct at91_priv *priv = netdev_priv(dev); @@ -1130,8 +1396,6 @@ static int __devexit at91_can_remove(struct platform_device *pdev) unregister_netdev(dev); - platform_set_drvdata(pdev, NULL); - iounmap(priv->reg_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1144,27 +1408,31 @@ static int __devexit at91_can_remove(struct platform_device *pdev) 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 = __devexit_p(at91_can_remove), - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, + .probe = at91_can_probe, + .remove = at91_can_remove, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_can_dt_ids), }, + .id_table = at91_can_id_table, }; -static int __init at91_can_module_init(void) -{ - return platform_driver_register(&at91_can_driver); -} - -static void __exit at91_can_module_exit(void) -{ - platform_driver_unregister(&at91_can_driver); -} - -module_init(at91_can_module_init); -module_exit(at91_can_module_exit); +module_platform_driver(at91_can_driver); MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index b6e890d2836..543ecceb33e 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -9,7 +9,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/bitops.h> #include <linux/interrupt.h> @@ -44,7 +43,7 @@ struct bfin_can_priv { /* * bfin can timing parameters */ -static struct can_bittiming_const bfin_can_bittiming_const = { +static const struct can_bittiming_const bfin_can_bittiming_const = { .name = DRV_NAME, .tseg1_min = 1, .tseg1_max = 16, @@ -79,11 +78,10 @@ static int bfin_can_set_bittiming(struct net_device *dev) if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) timing |= SAM; - bfin_write16(®->clock, clk); - bfin_write16(®->timing, timing); + bfin_write(®->clock, clk); + bfin_write(®->timing, timing); - dev_info(dev->dev.parent, "setting CLOCK=0x%04x TIMING=0x%04x\n", - clk, timing); + netdev_info(dev, "setting CLOCK=0x%04x TIMING=0x%04x\n", clk, timing); return 0; } @@ -96,20 +94,19 @@ static void bfin_can_set_reset_mode(struct net_device *dev) int i; /* disable interrupts */ - bfin_write16(®->mbim1, 0); - bfin_write16(®->mbim2, 0); - bfin_write16(®->gim, 0); + bfin_write(®->mbim1, 0); + bfin_write(®->mbim2, 0); + bfin_write(®->gim, 0); /* reset can and enter configuration mode */ - bfin_write16(®->control, SRS | CCR); + bfin_write(®->control, SRS | CCR); SSYNC(); - bfin_write16(®->control, CCR); + bfin_write(®->control, CCR); SSYNC(); - while (!(bfin_read16(®->control) & CCA)) { + while (!(bfin_read(®->control) & CCA)) { udelay(10); if (--timeout == 0) { - dev_err(dev->dev.parent, - "fail to enter configuration mode\n"); + netdev_err(dev, "fail to enter configuration mode\n"); BUG(); } } @@ -119,33 +116,33 @@ static void bfin_can_set_reset_mode(struct net_device *dev) * by writing to CAN Mailbox Configuration Registers 1 and 2 * For all bits: 0 - Mailbox disabled, 1 - Mailbox enabled */ - bfin_write16(®->mc1, 0); - bfin_write16(®->mc2, 0); + bfin_write(®->mc1, 0); + bfin_write(®->mc2, 0); /* Set Mailbox Direction */ - bfin_write16(®->md1, 0xFFFF); /* mailbox 1-16 are RX */ - bfin_write16(®->md2, 0); /* mailbox 17-32 are TX */ + bfin_write(®->md1, 0xFFFF); /* mailbox 1-16 are RX */ + bfin_write(®->md2, 0); /* mailbox 17-32 are TX */ /* RECEIVE_STD_CHL */ for (i = 0; i < 2; i++) { - bfin_write16(®->chl[RECEIVE_STD_CHL + i].id0, 0); - bfin_write16(®->chl[RECEIVE_STD_CHL + i].id1, AME); - bfin_write16(®->chl[RECEIVE_STD_CHL + i].dlc, 0); - bfin_write16(®->msk[RECEIVE_STD_CHL + i].amh, 0x1FFF); - bfin_write16(®->msk[RECEIVE_STD_CHL + i].aml, 0xFFFF); + bfin_write(®->chl[RECEIVE_STD_CHL + i].id0, 0); + bfin_write(®->chl[RECEIVE_STD_CHL + i].id1, AME); + bfin_write(®->chl[RECEIVE_STD_CHL + i].dlc, 0); + bfin_write(®->msk[RECEIVE_STD_CHL + i].amh, 0x1FFF); + bfin_write(®->msk[RECEIVE_STD_CHL + i].aml, 0xFFFF); } /* RECEIVE_EXT_CHL */ for (i = 0; i < 2; i++) { - bfin_write16(®->chl[RECEIVE_EXT_CHL + i].id0, 0); - bfin_write16(®->chl[RECEIVE_EXT_CHL + i].id1, AME | IDE); - bfin_write16(®->chl[RECEIVE_EXT_CHL + i].dlc, 0); - bfin_write16(®->msk[RECEIVE_EXT_CHL + i].amh, 0x1FFF); - bfin_write16(®->msk[RECEIVE_EXT_CHL + i].aml, 0xFFFF); + bfin_write(®->chl[RECEIVE_EXT_CHL + i].id0, 0); + bfin_write(®->chl[RECEIVE_EXT_CHL + i].id1, AME | IDE); + bfin_write(®->chl[RECEIVE_EXT_CHL + i].dlc, 0); + bfin_write(®->msk[RECEIVE_EXT_CHL + i].amh, 0x1FFF); + bfin_write(®->msk[RECEIVE_EXT_CHL + i].aml, 0xFFFF); } - bfin_write16(®->mc2, BIT(TRANSMIT_CHL - 16)); - bfin_write16(®->mc1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); + bfin_write(®->mc2, BIT(TRANSMIT_CHL - 16)); + bfin_write(®->mc1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); SSYNC(); priv->can.state = CAN_STATE_STOPPED; @@ -160,13 +157,12 @@ static void bfin_can_set_normal_mode(struct net_device *dev) /* * leave configuration mode */ - bfin_write16(®->control, bfin_read16(®->control) & ~CCR); + bfin_write(®->control, bfin_read(®->control) & ~CCR); - while (bfin_read16(®->status) & CCA) { + while (bfin_read(®->status) & CCA) { udelay(10); if (--timeout == 0) { - dev_err(dev->dev.parent, - "fail to leave configuration mode\n"); + netdev_err(dev, "fail to leave configuration mode\n"); BUG(); } } @@ -174,25 +170,25 @@ static void bfin_can_set_normal_mode(struct net_device *dev) /* * clear _All_ tx and rx interrupts */ - bfin_write16(®->mbtif1, 0xFFFF); - bfin_write16(®->mbtif2, 0xFFFF); - bfin_write16(®->mbrif1, 0xFFFF); - bfin_write16(®->mbrif2, 0xFFFF); + bfin_write(®->mbtif1, 0xFFFF); + bfin_write(®->mbtif2, 0xFFFF); + bfin_write(®->mbrif1, 0xFFFF); + bfin_write(®->mbrif2, 0xFFFF); /* * clear global interrupt status register */ - bfin_write16(®->gis, 0x7FF); /* overwrites with '1' */ + bfin_write(®->gis, 0x7FF); /* overwrites with '1' */ /* * Initialize Interrupts * - set bits in the mailbox interrupt mask register * - global interrupt mask */ - bfin_write16(®->mbim1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); - bfin_write16(®->mbim2, BIT(TRANSMIT_CHL - 16)); + bfin_write(®->mbim1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); + bfin_write(®->mbim2, BIT(TRANSMIT_CHL - 16)); - bfin_write16(®->gim, EPIM | BOIM | RMLIM); + bfin_write(®->gim, EPIM | BOIM | RMLIM); SSYNC(); } @@ -224,6 +220,20 @@ static int bfin_can_set_mode(struct net_device *dev, enum can_mode mode) return 0; } +static int bfin_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + + u16 cec = bfin_read(®->cec); + + bec->txerr = cec >> 8; + bec->rxerr = cec; + + return 0; +} + static int bfin_can_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct bfin_can_priv *priv = netdev_priv(dev); @@ -242,37 +252,28 @@ static int bfin_can_start_xmit(struct sk_buff *skb, struct net_device *dev) /* fill id */ if (id & CAN_EFF_FLAG) { - bfin_write16(®->chl[TRANSMIT_CHL].id0, id); - if (id & CAN_RTR_FLAG) - writew(((id & 0x1FFF0000) >> 16) | IDE | AME | RTR, - ®->chl[TRANSMIT_CHL].id1); - else - writew(((id & 0x1FFF0000) >> 16) | IDE | AME, - ®->chl[TRANSMIT_CHL].id1); - - } else { - if (id & CAN_RTR_FLAG) - writew((id << 2) | AME | RTR, - ®->chl[TRANSMIT_CHL].id1); - else - bfin_write16(®->chl[TRANSMIT_CHL].id1, - (id << 2) | AME); - } + bfin_write(®->chl[TRANSMIT_CHL].id0, id); + val = ((id & 0x1FFF0000) >> 16) | IDE; + } else + val = (id << 2); + if (id & CAN_RTR_FLAG) + val |= RTR; + bfin_write(®->chl[TRANSMIT_CHL].id1, val | AME); /* fill payload */ for (i = 0; i < 8; i += 2) { val = ((7 - i) < dlc ? (data[7 - i]) : 0) + ((6 - i) < dlc ? (data[6 - i] << 8) : 0); - bfin_write16(®->chl[TRANSMIT_CHL].data[i], val); + bfin_write(®->chl[TRANSMIT_CHL].data[i], val); } /* fill data length code */ - bfin_write16(®->chl[TRANSMIT_CHL].dlc, dlc); + bfin_write(®->chl[TRANSMIT_CHL].dlc, dlc); can_put_echo_skb(skb, dev, 0); /* set transmit request */ - bfin_write16(®->trs2, BIT(TRANSMIT_CHL - 16)); + bfin_write(®->trs2, BIT(TRANSMIT_CHL - 16)); return 0; } @@ -295,26 +296,26 @@ static void bfin_can_rx(struct net_device *dev, u16 isrc) /* get id */ if (isrc & BIT(RECEIVE_EXT_CHL)) { /* extended frame format (EFF) */ - cf->can_id = ((bfin_read16(®->chl[RECEIVE_EXT_CHL].id1) + cf->can_id = ((bfin_read(®->chl[RECEIVE_EXT_CHL].id1) & 0x1FFF) << 16) - + bfin_read16(®->chl[RECEIVE_EXT_CHL].id0); + + bfin_read(®->chl[RECEIVE_EXT_CHL].id0); cf->can_id |= CAN_EFF_FLAG; obj = RECEIVE_EXT_CHL; } else { /* standard frame format (SFF) */ - cf->can_id = (bfin_read16(®->chl[RECEIVE_STD_CHL].id1) + cf->can_id = (bfin_read(®->chl[RECEIVE_STD_CHL].id1) & 0x1ffc) >> 2; obj = RECEIVE_STD_CHL; } - if (bfin_read16(®->chl[obj].id1) & RTR) + if (bfin_read(®->chl[obj].id1) & RTR) cf->can_id |= CAN_RTR_FLAG; /* get data length code */ - cf->can_dlc = get_can_dlc(bfin_read16(®->chl[obj].dlc) & 0xF); + cf->can_dlc = get_can_dlc(bfin_read(®->chl[obj].dlc) & 0xF); /* get payload */ for (i = 0; i < 8; i += 2) { - val = bfin_read16(®->chl[obj].data[i]); + val = bfin_read(®->chl[obj].data[i]); cf->data[7 - i] = (7 - i) < cf->can_dlc ? val : 0; cf->data[6 - i] = (6 - i) < cf->can_dlc ? (val >> 8) : 0; } @@ -340,7 +341,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) if (isrc & RMLIS) { /* data overrun interrupt */ - dev_dbg(dev->dev.parent, "data overrun interrupt\n"); + 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++; @@ -348,7 +349,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) } if (isrc & BOIS) { - dev_dbg(dev->dev.parent, "bus-off mode interrupt\n"); + netdev_dbg(dev, "bus-off mode interrupt\n"); state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; can_bus_off(dev); @@ -356,19 +357,18 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) if (isrc & EPIS) { /* error passive interrupt */ - dev_dbg(dev->dev.parent, "error passive interrupt\n"); + netdev_dbg(dev, "error passive interrupt\n"); state = CAN_STATE_ERROR_PASSIVE; } if ((isrc & EWTIS) || (isrc & EWRIS)) { - dev_dbg(dev->dev.parent, - "Error Warning Transmit/Receive Interrupt\n"); + netdev_dbg(dev, "Error Warning Transmit/Receive Interrupt\n"); state = CAN_STATE_ERROR_WARNING; } if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING || state == CAN_STATE_ERROR_PASSIVE)) { - u16 cec = bfin_read16(®->cec); + u16 cec = bfin_read(®->cec); u8 rxerr = cec; u8 txerr = cec >> 8; @@ -411,7 +411,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) return 0; } -irqreturn_t bfin_can_interrupt(int irq, void *dev_id) +static irqreturn_t bfin_can_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct bfin_can_priv *priv = netdev_priv(dev); @@ -419,23 +419,23 @@ irqreturn_t bfin_can_interrupt(int irq, void *dev_id) struct net_device_stats *stats = &dev->stats; u16 status, isrc; - if ((irq == priv->tx_irq) && bfin_read16(®->mbtif2)) { + if ((irq == priv->tx_irq) && bfin_read(®->mbtif2)) { /* transmission complete interrupt */ - bfin_write16(®->mbtif2, 0xFFFF); + bfin_write(®->mbtif2, 0xFFFF); stats->tx_packets++; - stats->tx_bytes += bfin_read16(®->chl[TRANSMIT_CHL].dlc); + stats->tx_bytes += bfin_read(®->chl[TRANSMIT_CHL].dlc); can_get_echo_skb(dev, 0); netif_wake_queue(dev); - } else if ((irq == priv->rx_irq) && bfin_read16(®->mbrif1)) { + } else if ((irq == priv->rx_irq) && bfin_read(®->mbrif1)) { /* receive interrupt */ - isrc = bfin_read16(®->mbrif1); - bfin_write16(®->mbrif1, 0xFFFF); + isrc = bfin_read(®->mbrif1); + bfin_write(®->mbrif1, 0xFFFF); bfin_can_rx(dev, isrc); - } else if ((irq == priv->err_irq) && bfin_read16(®->gis)) { + } else if ((irq == priv->err_irq) && bfin_read(®->gis)) { /* error interrupt */ - isrc = bfin_read16(®->gis); - status = bfin_read16(®->esr); - bfin_write16(®->gis, 0x7FF); + isrc = bfin_read(®->gis); + status = bfin_read(®->esr); + bfin_write(®->gis, 0x7FF); bfin_can_err(dev, isrc, status); } else { return IRQ_NONE; @@ -503,7 +503,7 @@ static int bfin_can_close(struct net_device *dev) return 0; } -struct net_device *alloc_bfin_candev(void) +static struct net_device *alloc_bfin_candev(void) { struct net_device *dev; struct bfin_can_priv *priv; @@ -518,6 +518,7 @@ struct net_device *alloc_bfin_candev(void) priv->can.bittiming_const = &bfin_can_bittiming_const; priv->can.do_set_bittiming = bfin_can_set_bittiming; priv->can.do_set_mode = bfin_can_set_mode; + priv->can.do_get_berr_counter = bfin_can_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; return dev; @@ -527,9 +528,10 @@ static const struct net_device_ops bfin_can_netdev_ops = { .ndo_open = bfin_can_open, .ndo_stop = bfin_can_close, .ndo_start_xmit = bfin_can_start_xmit, + .ndo_change_mtu = can_change_mtu, }; -static int __devinit bfin_can_probe(struct platform_device *pdev) +static int bfin_can_probe(struct platform_device *pdev) { int err; struct net_device *dev; @@ -537,7 +539,7 @@ static int __devinit bfin_can_probe(struct platform_device *pdev) struct resource *res_mem, *rx_irq, *tx_irq, *err_irq; unsigned short *pdata; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data provided!\n"); err = -EINVAL; @@ -578,7 +580,7 @@ static int __devinit bfin_can_probe(struct platform_device *pdev) priv->pin_list = pdata; priv->can.clock.freq = get_sclk(); - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); dev->flags |= IFF_ECHO; /* we support local echo */ @@ -595,7 +597,7 @@ static int __devinit bfin_can_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s device registered" "(®_base=%p, rx_irq=%d, tx_irq=%d, err_irq=%d, sclk=%d)\n", - DRV_NAME, (void *)priv->membase, priv->rx_irq, + DRV_NAME, priv->membase, priv->rx_irq, priv->tx_irq, priv->err_irq, priv->can.clock.freq); return 0; @@ -609,9 +611,9 @@ exit: return err; } -static int __devexit bfin_can_remove(struct platform_device *pdev) +static int bfin_can_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct bfin_can_priv *priv = netdev_priv(dev); struct resource *res; @@ -619,8 +621,6 @@ static int __devexit bfin_can_remove(struct platform_device *pdev) unregister_candev(dev); - dev_set_drvdata(&pdev->dev, NULL); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); @@ -633,20 +633,19 @@ static int __devexit bfin_can_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct bfin_can_priv *priv = netdev_priv(dev); struct bfin_can_regs __iomem *reg = priv->membase; int timeout = BFIN_CAN_TIMEOUT; if (netif_running(dev)) { /* enter sleep mode */ - bfin_write16(®->control, bfin_read16(®->control) | SMR); + bfin_write(®->control, bfin_read(®->control) | SMR); SSYNC(); - while (!(bfin_read16(®->intr) & SMACK)) { + while (!(bfin_read(®->intr) & SMACK)) { udelay(10); if (--timeout == 0) { - dev_err(dev->dev.parent, - "fail to enter sleep mode\n"); + netdev_err(dev, "fail to enter sleep mode\n"); BUG(); } } @@ -657,13 +656,13 @@ static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg) static int bfin_can_resume(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct bfin_can_priv *priv = netdev_priv(dev); struct bfin_can_regs __iomem *reg = priv->membase; if (netif_running(dev)) { /* leave sleep mode */ - bfin_write16(®->intr, 0); + bfin_write(®->intr, 0); SSYNC(); } @@ -676,7 +675,7 @@ static int bfin_can_resume(struct platform_device *pdev) static struct platform_driver bfin_can_driver = { .probe = bfin_can_probe, - .remove = __devexit_p(bfin_can_remove), + .remove = bfin_can_remove, .suspend = bfin_can_suspend, .resume = bfin_can_resume, .driver = { @@ -685,18 +684,9 @@ static struct platform_driver bfin_can_driver = { }, }; -static int __init bfin_can_init(void) -{ - return platform_driver_register(&bfin_can_driver); -} -module_init(bfin_can_init); - -static void __exit bfin_can_exit(void) -{ - platform_driver_unregister(&bfin_can_driver); -} -module_exit(bfin_can_exit); +module_platform_driver(bfin_can_driver); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Blackfin on-chip CAN netdevice driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig new file mode 100644 index 00000000000..61ffc12d8fd --- /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 00000000000..ad1cc842170 --- /dev/null +++ b/drivers/net/can/c_can/Makefile @@ -0,0 +1,9 @@ +# +# 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 + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c new file mode 100644 index 00000000000..8e78bb48f5a --- /dev/null +++ b/drivers/net/can/c_can/c_can.c @@ -0,0 +1,1274 @@ +/* + * 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/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_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) + +/* 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_enable(const struct c_can_priv *priv) +{ + if (priv->device) + pm_runtime_enable(priv->device); +} + +static inline void c_can_pm_runtime_disable(const struct c_can_priv *priv) +{ + if (priv->device) + pm_runtime_disable(priv->device); +} + +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); + + 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); + + 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); +} + +/* + * 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); + + /* 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; + + /* 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; + + 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); + 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; + 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; + + __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; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + + switch (lec_type) { + case LEC_STUFF_ERROR: + netdev_dbg(dev, "stuff error\n"); + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case LEC_FORM_ERROR: + netdev_dbg(dev, "form error\n"); + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case LEC_ACK_ERROR: + netdev_dbg(dev, "ack error\n"); + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL); + break; + case LEC_BIT1_ERROR: + netdev_dbg(dev, "bit1 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT1; + break; + case LEC_BIT0_ERROR: + netdev_dbg(dev, "bit0 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT0; + break; + case LEC_CRC_ERROR: + netdev_dbg(dev, "CRC error\n"); + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + 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; + + 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); + + /* 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(napi); + /* 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); + + if (!priv->read_reg(priv, C_CAN_INT_REG)) + return IRQ_NONE; + + /* 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) +{ + struct c_can_priv *priv = netdev_priv(dev); + int err; + + c_can_pm_runtime_enable(priv); + + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &c_can_netdev_ops; + + err = register_candev(dev); + if (err) + c_can_pm_runtime_disable(priv); + else + devm_can_led_init(dev); + + return err; +} +EXPORT_SYMBOL_GPL(register_c_can_dev); + +void unregister_c_can_dev(struct net_device *dev) +{ + struct c_can_priv *priv = netdev_priv(dev); + + unregister_candev(dev); + + c_can_pm_runtime_disable(priv); +} +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 00000000000..99ad1aa576b --- /dev/null +++ b/drivers/net/can/c_can/c_can.h @@ -0,0 +1,207 @@ +/* + * 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, +}; + +/* 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; + 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; + u32 __iomem *raminit_ctrlreg; + int instance; + 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 00000000000..5d11e0e4225 --- /dev/null +++ b/drivers/net/can/c_can/c_can_pci.c @@ -0,0 +1,292 @@ +/* + * 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; + + 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; + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + 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); + + unregister_c_can_dev(dev); + + free_c_can_dev(dev); + + pci_iounmap(pdev, priv->base); + pci_disable_msi(pdev); + pci_clear_master(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static 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 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 DEFINE_PCI_DEVICE_TABLE(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 00000000000..12430be6448 --- /dev/null +++ b/drivers/net/can/c_can/c_can_platform.c @@ -0,0 +1,432 @@ +/* + * Platform CAN bus driver for Bosch C_CAN controller + * + * Copyright (C) 2010 ST Microelectronics + * Bhupesh Sharma <bhupesh.sharma@st.com> + * + * Borrowed heavily from the C_CAN driver originally written by: + * Copyright (C) 2007 + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de> + * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch> + * + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B. + * Bosch C_CAN user manual can be obtained from: + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/ + * users_manual_c_can.pdf + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include <linux/can/dev.h> + +#include "c_can.h" + +#define CAN_RAMINIT_START_MASK(i) (0x001 << (i)) +#define CAN_RAMINIT_DONE_MASK(i) (0x100 << (i)) +#define CAN_RAMINIT_ALL_MASK(i) (0x101 << (i)) +#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_ti(const struct c_can_priv *priv, u32 mask, + u32 val) +{ + /* We look only at the bits of our instance. */ + val &= mask; + while ((readl(priv->raminit_ctrlreg) & mask) != val) + udelay(1); +} + +static void c_can_hw_raminit_ti(const struct c_can_priv *priv, bool enable) +{ + u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance); + u32 ctrl; + + spin_lock(&raminit_lock); + + ctrl = readl(priv->raminit_ctrlreg); + /* We clear the done and start bit first. The start bit is + * looking at the 0 -> transition, but is not self clearing; + * And we clear the init done bit as well. + */ + ctrl &= ~CAN_RAMINIT_START_MASK(priv->instance); + ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance); + writel(ctrl, priv->raminit_ctrlreg); + ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance); + c_can_hw_raminit_wait_ti(priv, ctrl, mask); + + if (enable) { + /* Set start bit and wait for the done bit. */ + ctrl |= CAN_RAMINIT_START_MASK(priv->instance); + writel(ctrl, priv->raminit_ctrlreg); + ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance); + c_can_hw_raminit_wait_ti(priv, ctrl, mask); + } + 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 struct platform_device_id c_can_id_table[] = { + [BOSCH_C_CAN_PLATFORM] = { + .name = KBUILD_MODNAME, + .driver_data = BOSCH_C_CAN, + }, + [BOSCH_C_CAN] = { + .name = "c_can", + .driver_data = BOSCH_C_CAN, + }, + [BOSCH_D_CAN] = { + .name = "d_can", + .driver_data = BOSCH_D_CAN, + }, { + } +}; +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_id_table[BOSCH_C_CAN] }, + { .compatible = "bosch,d_can", .data = &c_can_id_table[BOSCH_D_CAN] }, + { /* 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; + const struct platform_device_id *id; + struct resource *mem, *res; + int irq; + struct clk *clk; + + if (pdev->dev.of_node) { + match = of_match_device(c_can_of_table, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Failed to find matching dt id\n"); + ret = -EINVAL; + goto exit; + } + id = match->data; + } else { + id = platform_get_device_id(pdev); + } + + /* get the appropriate clk */ + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + ret = -ENODEV; + goto exit; + } + + /* get the platform data */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!mem || irq <= 0) { + ret = -ENODEV; + goto exit_free_clk; + } + + if (!request_mem_region(mem->start, resource_size(mem), + KBUILD_MODNAME)) { + dev_err(&pdev->dev, "resource unavailable\n"); + ret = -ENODEV; + goto exit_free_clk; + } + + addr = ioremap(mem->start, resource_size(mem)); + if (!addr) { + dev_err(&pdev->dev, "failed to map can port\n"); + ret = -ENOMEM; + goto exit_release_mem; + } + + /* allocate the c_can device */ + dev = alloc_c_can_dev(); + if (!dev) { + ret = -ENOMEM; + goto exit_iounmap; + } + + priv = netdev_priv(dev); + switch (id->driver_data) { + 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->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + 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; + + if (pdev->dev.of_node) + priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can"); + else + priv->instance = pdev->id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + /* Not all D_CAN modules have a separate register for the D_CAN + * RAM initialization. Use default RAM init bit in D_CAN module + * if not specified in DT. + */ + if (!res) { + priv->raminit = c_can_hw_raminit; + break; + } + + priv->raminit_ctrlreg = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0) + dev_info(&pdev->dev, "control memory is not used for raminit\n"); + else + priv->raminit = c_can_hw_raminit_ti; + 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 = id->driver_data; + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + ret = register_c_can_dev(dev); + if (ret) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + KBUILD_MODNAME, ret); + goto exit_free_device; + } + + dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", + KBUILD_MODNAME, priv->base, dev->irq); + return 0; + +exit_free_device: + free_c_can_dev(dev); +exit_iounmap: + iounmap(addr); +exit_release_mem: + release_mem_region(mem->start, resource_size(mem)); +exit_free_clk: + clk_put(clk); +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); + struct resource *mem; + + unregister_c_can_dev(dev); + + free_c_can_dev(dev); + iounmap(priv->base); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + + clk_put(priv->priv); + + 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, + .owner = THIS_MODULE, + .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 00000000000..6a9a5ba7922 --- /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 00000000000..9fb8321b33e --- /dev/null +++ b/drivers/net/can/cc770/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Bosch CC770 CAN controller drivers. +# + +obj-$(CONFIG_CAN_CC770) += cc770.o +obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o +obj-$(CONFIG_CAN_CC770_PLATFORM) += cc770_platform.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c new file mode 100644 index 00000000000..d8379278d64 --- /dev/null +++ b/drivers/net/can/cc770/cc770.c @@ -0,0 +1,883 @@ +/* + * Core driver for the CC770 and AN82527 CAN controllers + * + * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/delay.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/platform/cc770.h> + +#include "cc770.h" + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver"); + +/* + * The CC770 is a CAN controller from Bosch, which is 100% compatible + * with the AN82527 from Intel, but with "bugs" being fixed and some + * additional functionality, mainly: + * + * 1. RX and TX error counters are readable. + * 2. Support of silent (listen-only) mode. + * 3. Message object 15 can receive all types of frames, also RTR and EFF. + * + * Details are available from Bosch's "CC770_Product_Info_2007-01.pdf", + * which explains in detail the compatibility between the CC770 and the + * 82527. This driver use the additional functionality 3. on real CC770 + * devices. Unfortunately, the CC770 does still not store the message + * identifier of received remote transmission request frames and + * therefore it's set to 0. + * + * The message objects 1..14 can be used for TX and RX while the message + * objects 15 is optimized for RX. It has a shadow register for reliable + * data receiption under heavy bus load. Therefore it makes sense to use + * this message object for the needed use case. The frame type (EFF/SFF) + * for the message object 15 can be defined via kernel module parameter + * "msgobj15_eff". If not equal 0, it will receive 29-bit EFF frames, + * otherwise 11 bit SFF messages. + */ +static int msgobj15_eff; +module_param(msgobj15_eff, int, S_IRUGO); +MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 " + "(default: 11-bit standard frames)"); + +static int i82527_compat; +module_param(i82527_compat, int, S_IRUGO); +MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode " + "without using additional functions"); + +/* + * This driver uses the last 5 message objects 11..15. The definitions + * and structure below allows to configure and assign them to the real + * message object. + */ +static unsigned char cc770_obj_flags[CC770_OBJ_MAX] = { + [CC770_OBJ_RX0] = CC770_OBJ_FLAG_RX, + [CC770_OBJ_RX1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_EFF, + [CC770_OBJ_RX_RTR0] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR, + [CC770_OBJ_RX_RTR1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR | + CC770_OBJ_FLAG_EFF, + [CC770_OBJ_TX] = 0, +}; + +static 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 netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + unsigned int mo = obj2msgobj(CC770_OBJ_TX); + u8 dlc, rtr; + u32 id; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + if ((cc770_read_reg(priv, + msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) { + netdev_err(dev, "TX register is still occupied!\n"); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(dev); + + dlc = cf->can_dlc; + id = cf->can_id; + if (cf->can_id & CAN_RTR_FLAG) + rtr = 0; + else + rtr = MSGCFG_DIR; + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES); + if (id & CAN_EFF_FLAG) { + id &= CAN_EFF_MASK; + cc770_write_reg(priv, msgobj[mo].config, + (dlc << 4) | rtr | MSGCFG_XTD); + cc770_write_reg(priv, msgobj[mo].id[3], id << 3); + cc770_write_reg(priv, msgobj[mo].id[2], id >> 5); + cc770_write_reg(priv, msgobj[mo].id[1], id >> 13); + cc770_write_reg(priv, msgobj[mo].id[0], id >> 21); + } else { + id &= CAN_SFF_MASK; + cc770_write_reg(priv, msgobj[mo].config, (dlc << 4) | rtr); + cc770_write_reg(priv, msgobj[mo].id[0], id >> 3); + cc770_write_reg(priv, msgobj[mo].id[1], id << 5); + } + + for (i = 0; i < dlc; i++) + cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); + + /* Store echo skb before starting the transfer */ + can_put_echo_skb(skb, dev, 0); + + cc770_write_reg(priv, msgobj[mo].ctrl1, + RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); + + stats->tx_bytes += dlc; + + + /* + * HM: We had some cases of repeated IRQs so make sure the + * INT is acknowledged I know it's already further up, but + * doing again fixed the issue + */ + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); + + return NETDEV_TX_OK; +} + +static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 config; + u32 id; + int i; + + skb = alloc_can_skb(dev, &cf); + if (!skb) + return; + + config = cc770_read_reg(priv, msgobj[mo].config); + + if (ctrl1 & RMTPND_SET) { + /* + * Unfortunately, the chip does not store the real message + * identifier of the received remote transmission request + * frame. Therefore we set it to 0. + */ + cf->can_id = CAN_RTR_FLAG; + if (config & MSGCFG_XTD) + cf->can_id |= CAN_EFF_FLAG; + cf->can_dlc = 0; + } else { + if (config & MSGCFG_XTD) { + id = cc770_read_reg(priv, msgobj[mo].id[3]); + id |= cc770_read_reg(priv, msgobj[mo].id[2]) << 8; + id |= cc770_read_reg(priv, msgobj[mo].id[1]) << 16; + id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 24; + id >>= 3; + id |= CAN_EFF_FLAG; + } else { + id = cc770_read_reg(priv, msgobj[mo].id[1]); + id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 8; + id >>= 5; + } + + cf->can_id = id; + cf->can_dlc = get_can_dlc((config & 0xf0) >> 4); + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]); + } + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static int cc770_err(struct net_device *dev, u8 status) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 lec; + + netdev_dbg(dev, "status interrupt (%#x)\n", status); + + skb = alloc_can_err_skb(dev, &cf); + if (!skb) + return -ENOMEM; + + /* Use extended functions of the CC770 */ + if (priv->control_normal_mode & CTRL_EAF) { + cf->data[6] = cc770_read_reg(priv, tx_error_counter); + cf->data[7] = cc770_read_reg(priv, rx_error_counter); + } + + if (status & STAT_BOFF) { + /* Disable interrupts */ + cc770_write_reg(priv, control, CTRL_INI); + cf->can_id |= CAN_ERR_BUSOFF; + priv->can.state = CAN_STATE_BUS_OFF; + can_bus_off(dev); + } else if (status & STAT_WARN) { + cf->can_id |= CAN_ERR_CRTL; + /* Only the CC770 does show error passive */ + if (cf->data[7] > 127) { + cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE | + CAN_ERR_CRTL_TX_PASSIVE; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + } else { + cf->data[1] = CAN_ERR_CRTL_RX_WARNING | + CAN_ERR_CRTL_TX_WARNING; + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + } + } else { + /* Back to error avtive */ + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + lec = status & STAT_LEC_MASK; + if (lec < 7 && lec > 0) { + if (lec == STAT_LEC_ACK) { + cf->can_id |= CAN_ERR_ACK; + } else { + cf->can_id |= CAN_ERR_PROT; + switch (lec) { + case STAT_LEC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case STAT_LEC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case STAT_LEC_BIT1: + cf->data[2] |= CAN_ERR_PROT_BIT1; + break; + case STAT_LEC_BIT0: + cf->data[2] |= CAN_ERR_PROT_BIT0; + break; + case STAT_LEC_CRC: + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + break; + } + } + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +static int cc770_status_interrupt(struct net_device *dev) +{ + struct cc770_priv *priv = netdev_priv(dev); + u8 status; + + status = cc770_read_reg(priv, status); + /* Reset the status register including RXOK and TXOK */ + cc770_write_reg(priv, status, STAT_LEC_MASK); + + if (status & (STAT_WARN | STAT_BOFF) || + (status & STAT_LEC_MASK) != STAT_LEC_MASK) { + cc770_err(dev, status); + return status & STAT_BOFF; + } + + return 0; +} + +static void cc770_rx_interrupt(struct net_device *dev, unsigned int o) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned int mo = obj2msgobj(o); + u8 ctrl1; + int n = CC770_MAX_MSG; + + while (n--) { + ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); + + if (!(ctrl1 & NEWDAT_SET)) { + /* Check for RTR if additional functions are enabled */ + if (priv->control_normal_mode & CTRL_EAF) { + if (!(cc770_read_reg(priv, msgobj[mo].ctrl0) & + INTPND_SET)) + break; + } else { + break; + } + } + + if (ctrl1 & MSGLST_SET) { + stats->rx_over_errors++; + stats->rx_errors++; + } + if (mo < MSGOBJ_LAST) + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_UNC | RMTPND_UNC); + cc770_rx(dev, mo, ctrl1); + + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_RES | + RXIE_SET | INTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | MSGLST_RES | + TXRQST_RES | RMTPND_RES); + } +} + +static void cc770_rtr_interrupt(struct net_device *dev, unsigned int o) +{ + struct cc770_priv *priv = netdev_priv(dev); + unsigned int mo = obj2msgobj(o); + u8 ctrl0, ctrl1; + int n = CC770_MAX_MSG; + + while (n--) { + ctrl0 = cc770_read_reg(priv, msgobj[mo].ctrl0); + if (!(ctrl0 & INTPND_SET)) + break; + + ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); + cc770_rx(dev, mo, ctrl1); + + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_RES | + RXIE_SET | INTPND_RES); + cc770_write_reg(priv, msgobj[mo].ctrl1, + NEWDAT_RES | CPUUPD_SET | + TXRQST_RES | RMTPND_RES); + } +} + +static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) +{ + struct cc770_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned int mo = obj2msgobj(o); + + /* Nothing more to send, switch off interrupts */ + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); + /* + * We had some cases of repeated IRQ so make sure the + * INT is acknowledged + */ + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); + + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); +} + +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; + + 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 00000000000..a1739db98d9 --- /dev/null +++ b/drivers/net/can/cc770/cc770.h @@ -0,0 +1,203 @@ +/* + * Core driver for the CC770 and AN82527 CAN controllers + * + * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CC770_DEV_H +#define CC770_DEV_H + +#include <linux/can/dev.h> + +struct cc770_msgobj { + u8 ctrl0; + u8 ctrl1; + u8 id[4]; + u8 config; + u8 data[8]; + u8 dontuse; /* padding */ +} __packed; + +struct cc770_regs { + union { + struct cc770_msgobj msgobj[16]; /* Message object 1..15 */ + struct { + u8 control; /* Control Register */ + u8 status; /* Status Register */ + u8 cpu_interface; /* CPU Interface Register */ + u8 dontuse1; + u8 high_speed_read[2]; /* High Speed Read */ + u8 global_mask_std[2]; /* Standard Global Mask */ + u8 global_mask_ext[4]; /* Extended Global Mask */ + u8 msg15_mask[4]; /* Message 15 Mask */ + u8 dontuse2[15]; + u8 clkout; /* Clock Out Register */ + u8 dontuse3[15]; + u8 bus_config; /* Bus Configuration Register */ + u8 dontuse4[15]; + u8 bit_timing_0; /* Bit Timing Register byte 0 */ + u8 dontuse5[15]; + u8 bit_timing_1; /* Bit Timing Register byte 1 */ + u8 dontuse6[15]; + u8 interrupt; /* Interrupt Register */ + u8 dontuse7[15]; + u8 rx_error_counter; /* Receive Error Counter */ + u8 dontuse8[15]; + u8 tx_error_counter; /* Transmit Error Counter */ + u8 dontuse9[31]; + u8 p1_conf; + u8 dontuse10[15]; + u8 p2_conf; + u8 dontuse11[15]; + u8 p1_in; + u8 dontuse12[15]; + u8 p2_in; + u8 dontuse13[15]; + u8 p1_out; + u8 dontuse14[15]; + u8 p2_out; + u8 dontuse15[15]; + u8 serial_reset_addr; + }; + }; +} __packed; + +/* Control Register (0x00) */ +#define CTRL_INI 0x01 /* Initialization */ +#define CTRL_IE 0x02 /* Interrupt Enable */ +#define CTRL_SIE 0x04 /* Status Interrupt Enable */ +#define CTRL_EIE 0x08 /* Error Interrupt Enable */ +#define CTRL_EAF 0x20 /* Enable additional functions */ +#define CTRL_CCE 0x40 /* Change Configuration Enable */ + +/* Status Register (0x01) */ +#define STAT_LEC_STUFF 0x01 /* Stuff error */ +#define STAT_LEC_FORM 0x02 /* Form error */ +#define STAT_LEC_ACK 0x03 /* Acknowledgement error */ +#define STAT_LEC_BIT1 0x04 /* Bit1 error */ +#define STAT_LEC_BIT0 0x05 /* Bit0 error */ +#define STAT_LEC_CRC 0x06 /* CRC error */ +#define STAT_LEC_MASK 0x07 /* Last Error Code mask */ +#define STAT_TXOK 0x08 /* Transmit Message Successfully */ +#define STAT_RXOK 0x10 /* Receive Message Successfully */ +#define STAT_WAKE 0x20 /* Wake Up Status */ +#define STAT_WARN 0x40 /* Warning Status */ +#define STAT_BOFF 0x80 /* Bus Off Status */ + +/* + * CPU Interface Register (0x02) + * Clock Out Register (0x1f) + * Bus Configuration Register (0x2f) + * + * see include/linux/can/platform/cc770.h + */ + +/* Message Control Register 0 (Base Address + 0x0) */ +#define INTPND_RES 0x01 /* No Interrupt pending */ +#define INTPND_SET 0x02 /* Interrupt pending */ +#define INTPND_UNC 0x03 +#define RXIE_RES 0x04 /* Receive Interrupt Disable */ +#define RXIE_SET 0x08 /* Receive Interrupt Enable */ +#define RXIE_UNC 0x0c +#define TXIE_RES 0x10 /* Transmit Interrupt Disable */ +#define TXIE_SET 0x20 /* Transmit Interrupt Enable */ +#define TXIE_UNC 0x30 +#define MSGVAL_RES 0x40 /* Message Invalid */ +#define MSGVAL_SET 0x80 /* Message Valid */ +#define MSGVAL_UNC 0xc0 + +/* Message Control Register 1 (Base Address + 0x01) */ +#define NEWDAT_RES 0x01 /* No New Data */ +#define NEWDAT_SET 0x02 /* New Data */ +#define NEWDAT_UNC 0x03 +#define MSGLST_RES 0x04 /* No Message Lost */ +#define MSGLST_SET 0x08 /* Message Lost */ +#define MSGLST_UNC 0x0c +#define CPUUPD_RES 0x04 /* No CPU Updating */ +#define CPUUPD_SET 0x08 /* CPU Updating */ +#define CPUUPD_UNC 0x0c +#define TXRQST_RES 0x10 /* No Transmission Request */ +#define TXRQST_SET 0x20 /* Transmission Request */ +#define TXRQST_UNC 0x30 +#define RMTPND_RES 0x40 /* No Remote Request Pending */ +#define RMTPND_SET 0x80 /* Remote Request Pending */ +#define RMTPND_UNC 0xc0 + +/* Message Configuration Register (Base Address + 0x06) */ +#define MSGCFG_XTD 0x04 /* Extended Identifier */ +#define MSGCFG_DIR 0x08 /* Direction is Transmit */ + +#define MSGOBJ_FIRST 1 +#define MSGOBJ_LAST 15 + +#define CC770_IO_SIZE 0x100 +#define CC770_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ +#define CC770_MAX_MSG 4 /* max. number of messages handled in ISR */ + +#define CC770_ECHO_SKB_MAX 1 + +#define cc770_read_reg(priv, member) \ + priv->read_reg(priv, offsetof(struct cc770_regs, member)) + +#define cc770_write_reg(priv, member, value) \ + priv->write_reg(priv, offsetof(struct cc770_regs, member), value) + +/* + * Message objects and flags used by this driver + */ +#define CC770_OBJ_FLAG_RX 0x01 +#define CC770_OBJ_FLAG_RTR 0x02 +#define CC770_OBJ_FLAG_EFF 0x04 + +enum { + CC770_OBJ_RX0 = 0, /* for receiving normal messages */ + CC770_OBJ_RX1, /* for receiving normal messages */ + CC770_OBJ_RX_RTR0, /* for receiving remote transmission requests */ + CC770_OBJ_RX_RTR1, /* for receiving remote transmission requests */ + CC770_OBJ_TX, /* for sending messages */ + CC770_OBJ_MAX +}; + +#define obj2msgobj(o) (MSGOBJ_LAST - (o)) /* message object 11..15 */ + +/* + * CC770 private data structure + */ +struct cc770_priv { + struct can_priv can; /* must be the first member */ + struct sk_buff *echo_skb; + + /* the lower-layer is responsible for appropriate locking */ + u8 (*read_reg)(const struct cc770_priv *priv, int reg); + void (*write_reg)(const struct cc770_priv *priv, int reg, u8 val); + void (*pre_irq)(const struct cc770_priv *priv); + void (*post_irq)(const struct cc770_priv *priv); + + void *priv; /* for board-specific data */ + struct net_device *dev; + + void __iomem *reg_base; /* ioremap'ed address to registers */ + unsigned long irq_flags; /* for request_irq() */ + + unsigned char obj_flags[CC770_OBJ_MAX]; + u8 control_normal_mode; /* Control register for normal mode */ + u8 cpu_interface; /* CPU interface register */ + u8 clkout; /* Clock out register */ + u8 bus_config; /* Bus conffiguration register */ +}; + +struct net_device *alloc_cc770dev(int sizeof_priv); +void free_cc770dev(struct net_device *dev); +int register_cc770dev(struct net_device *dev); +void unregister_cc770dev(struct net_device *dev); + +#endif /* CC770_DEV_H */ diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c new file mode 100644 index 00000000000..87a47c0cfd4 --- /dev/null +++ b/drivers/net/can/cc770/cc770_isa.c @@ -0,0 +1,380 @@ +/* + * 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_array(port, ulong, NULL, S_IRUGO); +MODULE_PARM_DESC(port, "I/O port number"); + +module_param_array(mem, ulong, NULL, S_IRUGO); +MODULE_PARM_DESC(mem, "I/O memory address"); + +module_param_array(indirect, int, NULL, S_IRUGO); +MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); + +module_param_array(irq, int, NULL, S_IRUGO); +MODULE_PARM_DESC(irq, "IRQ number"); + +module_param_array(clk, int, NULL, S_IRUGO); +MODULE_PARM_DESC(clk, "External oscillator clock frequency " + "(default=16000000 [16 MHz])"); + +module_param_array(cir, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])"); + +module_param_array(cor, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(cor, "Clockout register (default=0x00)"); + +module_param_array(bcr, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])"); + +#define CC770_IOSIZE 0x20 +#define CC770_IOSIZE_INDIRECT 0x02 + +/* Spinlock for cc770_isa_port_write_reg_indirect + * and cc770_isa_port_read_reg_indirect + */ +static DEFINE_SPINLOCK(cc770_isa_port_lock); + +static struct platform_device *cc770_isa_devs[MAXDEV]; + +static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg) +{ + return readb(priv->reg_base + reg); +} + +static void cc770_isa_mem_write_reg(const struct cc770_priv *priv, + int reg, u8 val) +{ + writeb(val, priv->reg_base + reg); +} + +static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg) +{ + return inb((unsigned long)priv->reg_base + reg); +} + +static void cc770_isa_port_write_reg(const struct cc770_priv *priv, + int reg, u8 val) +{ + outb(val, (unsigned long)priv->reg_base + reg); +} + +static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv, + int reg) +{ + unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags; + u8 val; + + spin_lock_irqsave(&cc770_isa_port_lock, flags); + outb(reg, base); + val = inb(base + 1); + spin_unlock_irqrestore(&cc770_isa_port_lock, flags); + + return val; +} + +static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv, + int reg, u8 val) +{ + unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags; + + spin_lock_irqsave(&cc770_isa_port_lock, flags); + outb(reg, base); + outb(val, base + 1); + spin_unlock_irqrestore(&cc770_isa_port_lock, flags); +} + +static int 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, + .owner = THIS_MODULE, + }, +}; + +static int __init cc770_isa_init(void) +{ + int idx, err; + + for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) { + if ((port[idx] || mem[idx]) && irq[idx]) { + cc770_isa_devs[idx] = + platform_device_alloc(KBUILD_MODNAME, idx); + if (!cc770_isa_devs[idx]) { + err = -ENOMEM; + goto exit_free_devices; + } + err = platform_device_add(cc770_isa_devs[idx]); + if (err) { + platform_device_put(cc770_isa_devs[idx]); + goto exit_free_devices; + } + pr_debug("platform device %d: port=%#lx, mem=%#lx, " + "irq=%d\n", + idx, port[idx], mem[idx], irq[idx]); + } else if (idx == 0 || port[idx] || mem[idx]) { + pr_err("insufficient parameters supplied\n"); + err = -EINVAL; + goto exit_free_devices; + } + } + + err = platform_driver_register(&cc770_isa_driver); + if (err) + goto exit_free_devices; + + pr_info("driver for max. %d devices registered\n", MAXDEV); + + return 0; + +exit_free_devices: + while (--idx >= 0) { + if (cc770_isa_devs[idx]) + platform_device_unregister(cc770_isa_devs[idx]); + } + + return err; +} +module_init(cc770_isa_init); + +static void __exit cc770_isa_exit(void) +{ + int idx; + + platform_driver_unregister(&cc770_isa_driver); + for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) { + if (cc770_isa_devs[idx]) + platform_device_unregister(cc770_isa_devs[idx]); + } +} +module_exit(cc770_isa_exit); diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c new file mode 100644 index 00000000000..ad76734b3ec --- /dev/null +++ b/drivers/net/can/cc770/cc770_platform.c @@ -0,0 +1,274 @@ +/* + * 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 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, + .owner = THIS_MODULE, + .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 index d0f8c7e67e7..e318e87e2bf 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -13,8 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/module.h> @@ -24,7 +23,9 @@ #include <linux/if_arp.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 <net/rtnetlink.h> #define MOD_DESC "CAN device driver interface" @@ -33,6 +34,39 @@ 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 */ @@ -65,10 +99,10 @@ static int can_update_spt(const struct can_bittiming_const *btc, return 1000 * (tseg + 1 - *tseg2) / (tseg + 1); } -static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) +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); - const struct can_bittiming_const *btc = priv->bittiming_const; long rate, best_rate = 0; long best_error = 1000000000, error = 0; int best_tseg = 0, best_brp = 0, brp = 0; @@ -76,9 +110,6 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) int spt_error = 1000, spt = 0, sampl_pt; u64 v64; - if (!priv->bittiming_const) - return -ENOTSUPP; - /* Use CIA recommended sample points */ if (bt->sample_point) { sampl_pt = bt->sample_point; @@ -130,13 +161,13 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) /* Error in one-tenth of a percent */ error = (best_error * 1000) / bt->bitrate; if (error > CAN_CALC_MAX_ERROR) { - dev_err(dev->dev.parent, - "bitrate error %ld.%ld%% too high\n", - error / 10, error % 10); + netdev_err(dev, + "bitrate error %ld.%ld%% too high\n", + error / 10, error % 10); return -EDOM; } else { - dev_warn(dev->dev.parent, "bitrate error %ld.%ld%%\n", - error / 10, error % 10); + netdev_warn(dev, "bitrate error %ld.%ld%%\n", + error / 10, error % 10); } } @@ -150,7 +181,19 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) bt->prop_seg = tseg1 / 2; bt->phase_seg1 = tseg1 - bt->prop_seg; bt->phase_seg2 = tseg2; - bt->sjw = 1; + + /* check for sjw user settings */ + if (!bt->sjw || !btc->sjw_max) + bt->sjw = 1; + else { + /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */ + if (bt->sjw > btc->sjw_max) + bt->sjw = btc->sjw_max; + /* bt->sjw must not be higher than tseg2 */ + if (tseg2 < bt->sjw) + bt->sjw = tseg2; + } + bt->brp = best_brp; /* real bit-rate */ bt->bitrate = priv->clock.freq / (bt->brp * (tseg1 + tseg2 + 1)); @@ -158,9 +201,10 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) return 0; } #else /* !CONFIG_CAN_CALC_BITTIMING */ -static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) +static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) { - dev_err(dev->dev.parent, "bit-timing calculation not available\n"); + netdev_err(dev, "bit-timing calculation not available\n"); return -EINVAL; } #endif /* CONFIG_CAN_CALC_BITTIMING */ @@ -171,16 +215,13 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt) * 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) +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); - const struct can_bittiming_const *btc = priv->bittiming_const; int tseg1, alltseg; u64 brp64; - if (!priv->bittiming_const) - return -ENOTSUPP; - tseg1 = bt->prop_seg + bt->phase_seg1; if (!bt->sjw) bt->sjw = 1; @@ -208,26 +249,29 @@ static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt) return 0; } -int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt) +static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) { - struct can_priv *priv = netdev_priv(dev); int err; /* Check if the CAN device has bit-timing parameters */ - if (priv->bittiming_const) { + if (!btc) + return -EOPNOTSUPP; - /* Non-expert mode? Check if the bitrate has been pre-defined */ - if (!bt->tq) - /* Determine bit-timing parameters */ - err = can_calc_bittiming(dev, bt); - else - /* Check bit-timing params and calculate proper brp */ - err = can_fixup_bittiming(dev, bt); - if (err) - return err; - } + /* + * 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) + err = can_calc_bittiming(dev, bt, btc); + else if (bt->tq && !bt->bitrate) + err = can_fixup_bittiming(dev, bt, btc); + else + err = -EINVAL; - return 0; + return err; } /* @@ -271,28 +315,20 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *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) { + 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]) { - struct sock *srcsk = skb->sk; - if (atomic_read(&skb->users) != 1) { - struct sk_buff *old_skb = skb; - - skb = skb_clone(old_skb, GFP_ATOMIC); - kfree_skb(old_skb); - if (!skb) - return; - } else - skb_orphan(skb); - - skb->sk = srcsk; + skb = can_create_echo_skb(skb); + if (!skb) + return; /* make settings for echo to reduce code in irq context */ - skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; skb->dev = dev; @@ -301,8 +337,7 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, priv->echo_skb[idx] = skb; } else { /* locking problem with netif_stop_queue() ?? */ - dev_err(dev->dev.parent, "%s: BUG! echo_skb is occupied!\n", - __func__); + netdev_err(dev, "%s: BUG! echo_skb is occupied!\n", __func__); kfree_skb(skb); } } @@ -315,16 +350,24 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb); * is handled in the device driver. The driver must protect * access to priv->echo_skb, if necessary. */ -void can_get_echo_skb(struct net_device *dev, unsigned int idx) +unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx) { struct can_priv *priv = netdev_priv(dev); BUG_ON(idx >= priv->echo_skb_max); if (priv->echo_skb[idx]) { + struct sk_buff *skb = priv->echo_skb[idx]; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc = cf->can_dlc; + netif_rx(priv->echo_skb[idx]); priv->echo_skb[idx] = NULL; + + return dlc; } + + return 0; } EXPORT_SYMBOL_GPL(can_get_echo_skb); @@ -349,7 +392,7 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb); /* * CAN device restart for bus-off recovery */ -void can_restart(unsigned long data) +static void can_restart(unsigned long data) { struct net_device *dev = (struct net_device *)data; struct can_priv *priv = netdev_priv(dev); @@ -380,7 +423,7 @@ void can_restart(unsigned long data) stats->rx_bytes += cf->can_dlc; restart: - dev_dbg(dev->dev.parent, "restarted\n"); + netdev_dbg(dev, "restarted\n"); priv->can_stats.restarts++; /* Now restart the device */ @@ -388,7 +431,7 @@ restart: netif_carrier_on(dev); if (err) - dev_err(dev->dev.parent, "Error %d during restart", err); + netdev_err(dev, "Error %d during restart", err); } int can_restart_now(struct net_device *dev) @@ -421,7 +464,7 @@ void can_bus_off(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - dev_dbg(dev->dev.parent, "bus-off\n"); + netdev_dbg(dev, "bus-off\n"); netif_carrier_off(dev); priv->can_stats.bus_off++; @@ -435,27 +478,32 @@ EXPORT_SYMBOL_GPL(can_bus_off); static void can_setup(struct net_device *dev) { dev->type = ARPHRD_CAN; - dev->mtu = sizeof(struct can_frame); + dev->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_NO_CSUM; + dev->features = NETIF_F_HW_CSUM; } struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) { struct sk_buff *skb; - skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); + 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; + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); memset(*cf, 0, sizeof(struct can_frame)); @@ -463,6 +511,30 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) } 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; + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + + *cfd = (struct canfd_frame *)skb_put(skb, sizeof(struct canfd_frame)); + memset(*cfd, 0, 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; @@ -523,6 +595,39 @@ void free_candev(struct net_device *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: + priv->ctrlmode &= ~CAN_CTRLMODE_FD; + break; + + case CANFD_MTU: + if (!(priv->ctrlmode_supported & 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 @@ -532,8 +637,16 @@ int open_candev(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - if (!priv->bittiming.tq && !priv->bittiming.bitrate) { - dev_err(dev->dev.parent, "bit-timing not yet defined\n"); + 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; } @@ -557,8 +670,7 @@ void close_candev(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - if (del_timer_sync(&priv->restart_timer)) - dev_put(dev); + del_timer_sync(&priv->restart_timer); can_flush_echo_skb(dev); } EXPORT_SYMBOL_GPL(close_candev); @@ -576,6 +688,10 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { = { .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) }, }; static int can_changelink(struct net_device *dev, @@ -587,19 +703,6 @@ static int can_changelink(struct net_device *dev, /* We need synchronization with dev->stop() */ ASSERT_RTNL(); - if (data[IFLA_CAN_CTRLMODE]) { - struct can_ctrlmode *cm; - - /* Do not allow changing controller mode while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - cm = nla_data(data[IFLA_CAN_CTRLMODE]); - if (cm->flags & ~priv->ctrlmode_supported) - return -EOPNOTSUPP; - priv->ctrlmode &= ~cm->mask; - priv->ctrlmode |= cm->flags; - } - if (data[IFLA_CAN_BITTIMING]) { struct can_bittiming bt; @@ -607,9 +710,7 @@ static int can_changelink(struct net_device *dev, if (dev->flags & IFF_UP) return -EBUSY; memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); - if ((!bt.bitrate && !bt.tq) || (bt.bitrate && bt.tq)) - return -EINVAL; - err = can_get_bittiming(dev, &bt); + err = can_get_bittiming(dev, &bt, priv->bittiming_const); if (err) return err; memcpy(&priv->bittiming, &bt, sizeof(bt)); @@ -622,6 +723,25 @@ static int can_changelink(struct net_device *dev, } } + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm; + + /* Do not allow changing controller mode while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + cm = nla_data(data[IFLA_CAN_CTRLMODE]); + if (cm->flags & ~priv->ctrlmode_supported) + return -EOPNOTSUPP; + priv->ctrlmode &= ~cm->mask; + priv->ctrlmode |= cm->flags; + + /* 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) @@ -638,23 +758,49 @@ static int can_changelink(struct net_device *dev, 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; + memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), + sizeof(dbt)); + err = can_get_bittiming(dev, &dbt, priv->data_bittiming_const); + if (err) + return err; + 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; + } + } + return 0; } static size_t can_get_size(const struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - size_t size; - - size = nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ - size += sizeof(struct can_ctrlmode); /* IFLA_CAN_CTRLMODE */ - size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ - size += sizeof(struct can_bittiming); /* IFLA_CAN_BITTIMING */ - size += sizeof(struct can_clock); /* IFLA_CAN_CLOCK */ - if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ - size += sizeof(struct can_berr_counter); - if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ - size += sizeof(struct can_bittiming_const); + 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)); return size; } @@ -668,22 +814,35 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) if (priv->do_get_state) priv->do_get_state(dev, &state); - NLA_PUT_U32(skb, IFLA_CAN_STATE, state); - NLA_PUT(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm); - NLA_PUT_U32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms); - NLA_PUT(skb, IFLA_CAN_BITTIMING, - sizeof(priv->bittiming), &priv->bittiming); - NLA_PUT(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock); - if (priv->do_get_berr_counter && !priv->do_get_berr_counter(dev, &bec)) - NLA_PUT(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec); - if (priv->bittiming_const) - NLA_PUT(skb, IFLA_CAN_BITTIMING_CONST, - sizeof(*priv->bittiming_const), priv->bittiming_const); - return 0; + if ((priv->bittiming.bitrate && + nla_put(skb, IFLA_CAN_BITTIMING, + sizeof(priv->bittiming), &priv->bittiming)) || -nla_put_failure: - return -EMSGSIZE; + (priv->bittiming_const && + nla_put(skb, IFLA_CAN_BITTIMING_CONST, + sizeof(*priv->bittiming_const), priv->bittiming_const)) || + + nla_put(skb, IFLA_CAN_CLOCK, sizeof(cm), &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))) + return -EMSGSIZE; + + return 0; } static size_t can_get_xstats_size(const struct net_device *dev) @@ -695,9 +854,9 @@ static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - NLA_PUT(skb, IFLA_INFO_XSTATS, - sizeof(priv->can_stats), &priv->can_stats); - + if (nla_put(skb, IFLA_INFO_XSTATS, + sizeof(priv->can_stats), &priv->can_stats)) + goto nla_put_failure; return 0; nla_put_failure: @@ -742,10 +901,25 @@ void unregister_candev(struct net_device *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"); @@ -757,6 +931,8 @@ 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); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index d4990568bae..f425ec2c783 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -23,7 +23,7 @@ #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> -#include <linux/can/platform/flexcan.h> +#include <linux/can/led.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/if_arp.h> @@ -33,9 +33,10 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> - -#include <mach/clock.h> +#include <linux/regulator/consumer.h> #define DRV_NAME "flexcan" @@ -61,7 +62,7 @@ #define FLEXCAN_MCR_BCC BIT(16) #define FLEXCAN_MCR_LPRIO_EN BIT(13) #define FLEXCAN_MCR_AEN BIT(12) -#define FLEXCAN_MCR_MAXMB(x) ((x) & 0xf) +#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x1f) #define FLEXCAN_MCR_IDAM_A (0 << 8) #define FLEXCAN_MCR_IDAM_B (1 << 8) #define FLEXCAN_MCR_IDAM_C (2 << 8) @@ -119,6 +120,9 @@ (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | FLEXCAN_ESR_BOFF_INT) #define FLEXCAN_ESR_ERR_ALL \ (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) +#define FLEXCAN_ESR_ALL_INT \ + (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ + FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) /* FLEXCAN interrupt flag register (IFLAG) bits */ #define FLEXCAN_TX_BUF_ID 8 @@ -140,6 +144,25 @@ #define FLEXCAN_MB_CODE_MASK (0xf0ffffff) +#define FLEXCAN_TIMEOUT_US (50) + +/* + * FLEXCAN hardware feature flags + * + * Below is some version info we got: + * SOC Version IP-Version Glitch- [TR]WRN_INT + * Filter? connected? + * MX25 FlexCAN2 03.00.00.00 no no + * MX28 FlexCAN2 03.00.04.00 yes yes + * MX35 FlexCAN2 03.00.00.00 no no + * MX53 FlexCAN2 03.00.00.00 yes no + * MX6s FlexCAN3 10.00.12.00 yes yes + * + * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected. + */ +#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */ +#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */ + /* Structure of the message buffer */ struct flexcan_mb { u32 can_ctrl; @@ -162,10 +185,21 @@ struct flexcan_regs { u32 imask1; /* 0x28 */ u32 iflag2; /* 0x2c */ u32 iflag1; /* 0x30 */ - u32 _reserved2[19]; + u32 crl2; /* 0x34 */ + u32 esr2; /* 0x38 */ + u32 imeur; /* 0x3c */ + u32 lrfr; /* 0x40 */ + u32 crcr; /* 0x44 */ + u32 rxfgmask; /* 0x48 */ + u32 rxfir; /* 0x4c */ + u32 _reserved3[12]; struct flexcan_mb cantxfg[64]; }; +struct flexcan_devtype_data { + u32 features; /* hardware controller features */ +}; + struct flexcan_priv { struct can_priv can; struct net_device *dev; @@ -175,11 +209,22 @@ struct flexcan_priv { u32 reg_esr; u32 reg_ctrl_default; - struct clk *clk; + struct clk *clk_ipg; + struct clk *clk_per; struct flexcan_platform_data *pdata; + const struct flexcan_devtype_data *devtype_data; + struct regulator *reg_xceiver; }; -static struct can_bittiming_const flexcan_bittiming_const = { +static struct flexcan_devtype_data fsl_p1010_devtype_data = { + .features = FLEXCAN_HAS_BROKEN_ERR_STATE, +}; +static struct flexcan_devtype_data fsl_imx28_devtype_data; +static struct flexcan_devtype_data fsl_imx6q_devtype_data = { + .features = FLEXCAN_HAS_V10_FEATURES, +}; + +static const struct can_bittiming_const flexcan_bittiming_const = { .name = DRV_NAME, .tseg1_min = 4, .tseg1_max = 16, @@ -192,12 +237,47 @@ static struct can_bittiming_const flexcan_bittiming_const = { }; /* - * Swtich transceiver on or off + * Abstract off the read/write for arm versus ppc. This + * assumes that PPC uses big-endian registers and everything + * else uses little-endian registers, independent of CPU + * endianess. */ -static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on) +#if defined(CONFIG_PPC) +static inline u32 flexcan_read(void __iomem *addr) +{ + return in_be32(addr); +} + +static inline void flexcan_write(u32 val, void __iomem *addr) +{ + out_be32(addr, val); +} +#else +static inline u32 flexcan_read(void __iomem *addr) +{ + return readl(addr); +} + +static inline void flexcan_write(u32 val, void __iomem *addr) +{ + writel(val, addr); +} +#endif + +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->pdata && priv->pdata->transceiver_switch) - priv->pdata->transceiver_switch(on); + if (!priv->reg_xceiver) + return 0; + + return regulator_disable(priv->reg_xceiver); } static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, @@ -207,26 +287,95 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, (reg_esr & FLEXCAN_ESR_ERR_BUS); } -static inline void flexcan_chip_enable(struct flexcan_priv *priv) +static int flexcan_chip_enable(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; - reg = readl(®s->mcr); + reg = flexcan_read(®s->mcr); reg &= ~FLEXCAN_MCR_MDIS; - writel(reg, ®s->mcr); + flexcan_write(reg, ®s->mcr); + + while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + usleep_range(10, 20); + + if (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) + return -ETIMEDOUT; - udelay(10); + return 0; } -static inline void flexcan_chip_disable(struct flexcan_priv *priv) +static int flexcan_chip_disable(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; - reg = readl(®s->mcr); + reg = flexcan_read(®s->mcr); reg |= FLEXCAN_MCR_MDIS; - writel(reg, ®s->mcr); + flexcan_write(reg, ®s->mcr); + + while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + usleep_range(10, 20); + + if (!(flexcan_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->base; + unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate; + u32 reg; + + reg = flexcan_read(®s->mcr); + reg |= FLEXCAN_MCR_HALT; + flexcan_write(reg, ®s->mcr); + + while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + usleep_range(100, 200); + + if (!(flexcan_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->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + u32 reg; + + reg = flexcan_read(®s->mcr); + reg &= ~FLEXCAN_MCR_HALT; + flexcan_write(reg, ®s->mcr); + + while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + usleep_range(10, 20); + + if (flexcan_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->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + + flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); + while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST)) + usleep_range(10, 20); + + if (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST) + return -ETIMEDOUT; + + return 0; } static int flexcan_get_berr_counter(const struct net_device *dev, @@ -234,7 +383,7 @@ static int flexcan_get_berr_counter(const struct net_device *dev, { const struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - u32 reg = readl(®s->ecr); + u32 reg = flexcan_read(®s->ecr); bec->txerr = (reg >> 0) & 0xff; bec->rxerr = (reg >> 8) & 0xff; @@ -245,7 +394,6 @@ static int flexcan_get_berr_counter(const struct net_device *dev, static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; struct flexcan_regs __iomem *regs = priv->base; struct can_frame *cf = (struct can_frame *)skb->data; u32 can_id; @@ -268,20 +416,17 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) if (cf->can_dlc > 0) { u32 data = be32_to_cpup((__be32 *)&cf->data[0]); - writel(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); + flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); } if (cf->can_dlc > 3) { u32 data = be32_to_cpup((__be32 *)&cf->data[4]); - writel(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); + flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); } - writel(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); - writel(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); - - kfree_skb(skb); + can_put_echo_skb(skb, dev, 0); - /* tx_packets is incremented in flexcan_irq */ - stats->tx_bytes += cf->can_dlc; + flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); + flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); return NETDEV_TX_OK; } @@ -295,34 +440,34 @@ static void do_bus_err(struct net_device *dev, cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; if (reg_esr & FLEXCAN_ESR_BIT1_ERR) { - dev_dbg(dev->dev.parent, "BIT1_ERR irq\n"); + netdev_dbg(dev, "BIT1_ERR irq\n"); cf->data[2] |= CAN_ERR_PROT_BIT1; tx_errors = 1; } if (reg_esr & FLEXCAN_ESR_BIT0_ERR) { - dev_dbg(dev->dev.parent, "BIT0_ERR irq\n"); + netdev_dbg(dev, "BIT0_ERR irq\n"); cf->data[2] |= CAN_ERR_PROT_BIT0; tx_errors = 1; } if (reg_esr & FLEXCAN_ESR_ACK_ERR) { - dev_dbg(dev->dev.parent, "ACK_ERR irq\n"); + netdev_dbg(dev, "ACK_ERR irq\n"); cf->can_id |= CAN_ERR_ACK; cf->data[3] |= CAN_ERR_PROT_LOC_ACK; tx_errors = 1; } if (reg_esr & FLEXCAN_ESR_CRC_ERR) { - dev_dbg(dev->dev.parent, "CRC_ERR irq\n"); + netdev_dbg(dev, "CRC_ERR irq\n"); cf->data[2] |= CAN_ERR_PROT_BIT; cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; rx_errors = 1; } if (reg_esr & FLEXCAN_ESR_FRM_ERR) { - dev_dbg(dev->dev.parent, "FRM_ERR irq\n"); + netdev_dbg(dev, "FRM_ERR irq\n"); cf->data[2] |= CAN_ERR_PROT_FORM; rx_errors = 1; } if (reg_esr & FLEXCAN_ESR_STF_ERR) { - dev_dbg(dev->dev.parent, "STF_ERR irq\n"); + netdev_dbg(dev, "STF_ERR irq\n"); cf->data[2] |= CAN_ERR_PROT_STUFF; rx_errors = 1; } @@ -369,7 +514,7 @@ static void do_state(struct net_device *dev, */ if (new_state >= CAN_STATE_ERROR_WARNING && new_state <= CAN_STATE_BUS_OFF) { - dev_dbg(dev->dev.parent, "Error Warning IRQ\n"); + netdev_dbg(dev, "Error Warning IRQ\n"); priv->can.can_stats.error_warning++; cf->can_id |= CAN_ERR_CRTL; @@ -385,7 +530,7 @@ static void do_state(struct net_device *dev, */ if (new_state >= CAN_STATE_ERROR_PASSIVE && new_state <= CAN_STATE_BUS_OFF) { - dev_dbg(dev->dev.parent, "Error Passive IRQ\n"); + netdev_dbg(dev, "Error Passive IRQ\n"); priv->can.can_stats.error_passive++; cf->can_id |= CAN_ERR_CRTL; @@ -395,8 +540,8 @@ static void do_state(struct net_device *dev, } break; case CAN_STATE_BUS_OFF: - dev_err(dev->dev.parent, - "BUG! hardware recovered automatically from BUS_OFF\n"); + netdev_err(dev, "BUG! " + "hardware recovered automatically from BUS_OFF\n"); break; default: break; @@ -405,7 +550,7 @@ static void do_state(struct net_device *dev, /* process state changes depending on the new state */ switch (new_state) { case CAN_STATE_ERROR_ACTIVE: - dev_dbg(dev->dev.parent, "Error Active\n"); + netdev_dbg(dev, "Error Active\n"); cf->can_id |= CAN_ERR_PROT; cf->data[2] = CAN_ERR_PROT_ACTIVE; break; @@ -464,8 +609,8 @@ static void flexcan_read_fifo(const struct net_device *dev, struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; u32 reg_ctrl, reg_id; - reg_ctrl = readl(&mb->can_ctrl); - reg_id = readl(&mb->can_id); + reg_ctrl = flexcan_read(&mb->can_ctrl); + reg_id = flexcan_read(&mb->can_id); if (reg_ctrl & FLEXCAN_MB_CNT_IDE) cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; else @@ -475,12 +620,12 @@ static void flexcan_read_fifo(const struct net_device *dev, cf->can_id |= CAN_RTR_FLAG; cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); - *(__be32 *)(cf->data + 0) = cpu_to_be32(readl(&mb->data[0])); - *(__be32 *)(cf->data + 4) = cpu_to_be32(readl(&mb->data[1])); + *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0])); + *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1])); /* mark as read */ - writel(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); - readl(®s->timer); + flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); + flexcan_read(®s->timer); } static int flexcan_read_frame(struct net_device *dev) @@ -501,6 +646,8 @@ static int flexcan_read_frame(struct net_device *dev) stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + can_led_event(dev, CAN_LED_EVENT_RX); + return 1; } @@ -516,17 +663,17 @@ static int flexcan_poll(struct napi_struct *napi, int quota) * The error bits are cleared on read, * use saved value from irq handler. */ - reg_esr = readl(®s->esr) | priv->reg_esr; + reg_esr = flexcan_read(®s->esr) | priv->reg_esr; /* handle state changes */ work_done += flexcan_poll_state(dev, reg_esr); /* handle RX-FIFO */ - reg_iflag1 = readl(®s->iflag1); + reg_iflag1 = flexcan_read(®s->iflag1); while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE && work_done < quota) { work_done += flexcan_read_frame(dev); - reg_iflag1 = readl(®s->iflag1); + reg_iflag1 = flexcan_read(®s->iflag1); } /* report bus errors */ @@ -536,8 +683,8 @@ static int flexcan_poll(struct napi_struct *napi, int quota) if (work_done < quota) { napi_complete(napi); /* enable IRQs */ - writel(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); - writel(priv->reg_ctrl_default, ®s->ctrl); + flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + flexcan_write(priv->reg_ctrl_default, ®s->ctrl); } return work_done; @@ -551,9 +698,11 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) struct flexcan_regs __iomem *regs = priv->base; u32 reg_iflag1, reg_esr; - reg_iflag1 = readl(®s->iflag1); - reg_esr = readl(®s->esr); - writel(FLEXCAN_ESR_ERR_INT, ®s->esr); /* ACK err IRQ */ + reg_iflag1 = flexcan_read(®s->iflag1); + reg_esr = flexcan_read(®s->esr); + /* ACK all bus error and state change IRQ sources */ + if (reg_esr & FLEXCAN_ESR_ALL_INT) + flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); /* * schedule NAPI in case of: @@ -569,25 +718,26 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) * save them for later use. */ priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; - writel(FLEXCAN_IFLAG_DEFAULT & ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, - ®s->imask1); - writel(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + flexcan_write(FLEXCAN_IFLAG_DEFAULT & + ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); + flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, ®s->ctrl); napi_schedule(&priv->napi); } /* FIFO overflow */ if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { - writel(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); + flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); dev->stats.rx_over_errors++; dev->stats.rx_errors++; } /* transmission complete interrupt */ if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) { - /* tx_bytes is incremented in flexcan_start_xmit */ + stats->tx_bytes += can_get_echo_skb(dev, 0); stats->tx_packets++; - writel((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); + can_led_event(dev, CAN_LED_EVENT_TX); + flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); netif_wake_queue(dev); } @@ -601,7 +751,7 @@ static void flexcan_set_bittiming(struct net_device *dev) struct flexcan_regs __iomem *regs = priv->base; u32 reg; - reg = readl(®s->ctrl); + reg = flexcan_read(®s->ctrl); reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) | FLEXCAN_CTRL_RJW(0x3) | FLEXCAN_CTRL_PSEG1(0x7) | @@ -624,12 +774,12 @@ static void flexcan_set_bittiming(struct net_device *dev) if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) reg |= FLEXCAN_CTRL_SMP; - dev_info(dev->dev.parent, "writing ctrl=0x%08x\n", reg); - writel(reg, ®s->ctrl); + netdev_info(dev, "writing ctrl=0x%08x\n", reg); + flexcan_write(reg, ®s->ctrl); /* print chip status */ - dev_dbg(dev->dev.parent, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, - readl(®s->mcr), readl(®s->ctrl)); + netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, + flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); } /* @@ -642,25 +792,18 @@ static int flexcan_chip_start(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - unsigned int i; int err; u32 reg_mcr, reg_ctrl; /* enable module */ - flexcan_chip_enable(priv); + err = flexcan_chip_enable(priv); + if (err) + return err; /* soft reset */ - writel(FLEXCAN_MCR_SOFTRST, ®s->mcr); - udelay(10); - - reg_mcr = readl(®s->mcr); - if (reg_mcr & FLEXCAN_MCR_SOFTRST) { - dev_err(dev->dev.parent, - "Failed to softreset can module (mcr=0x%08x)\n", - reg_mcr); - err = -ENODEV; - goto out; - } + err = flexcan_chip_softreset(priv); + if (err) + goto out_chip_disable; flexcan_set_bittiming(dev); @@ -673,14 +816,17 @@ static int flexcan_chip_start(struct net_device *dev) * only supervisor access * enable warning int * choose format C + * disable local echo * */ - reg_mcr = readl(®s->mcr); + reg_mcr = flexcan_read(®s->mcr); + reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | - FLEXCAN_MCR_IDAM_C; - dev_dbg(dev->dev.parent, "%s: writing mcr=0x%08x", __func__, reg_mcr); - writel(reg_mcr, ®s->mcr); + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | + FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); + netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); + flexcan_write(reg_mcr, ®s->mcr); /* * CTRL @@ -693,55 +839,60 @@ static int flexcan_chip_start(struct net_device *dev) * enable tx and rx warning interrupt * enable bus off interrupt * (== FLEXCAN_CTRL_ERR_STATE) - * - * _note_: we enable the "error interrupt" - * (FLEXCAN_CTRL_ERR_MSK), too. Otherwise we don't get any - * warning or bus passive interrupts. */ - reg_ctrl = readl(®s->ctrl); + reg_ctrl = flexcan_read(®s->ctrl); reg_ctrl &= ~FLEXCAN_CTRL_TSYN; reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | - FLEXCAN_CTRL_ERR_STATE | FLEXCAN_CTRL_ERR_MSK; + 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->features & FLEXCAN_HAS_BROKEN_ERR_STATE || + priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + reg_ctrl |= FLEXCAN_CTRL_ERR_MSK; /* save for later use */ priv->reg_ctrl_default = reg_ctrl; - dev_dbg(dev->dev.parent, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); - writel(reg_ctrl, ®s->ctrl); + netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); + flexcan_write(reg_ctrl, ®s->ctrl); - for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) { - writel(0, ®s->cantxfg[i].can_ctrl); - writel(0, ®s->cantxfg[i].can_id); - writel(0, ®s->cantxfg[i].data[0]); - writel(0, ®s->cantxfg[i].data[1]); - - /* put MB into rx queue */ - writel(FLEXCAN_MB_CNT_CODE(0x4), ®s->cantxfg[i].can_ctrl); - } + /* Abort any pending TX, mark Mailbox as INACTIVE */ + flexcan_write(FLEXCAN_MB_CNT_CODE(0x4), + ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); /* acceptance mask/acceptance code (accept everything) */ - writel(0x0, ®s->rxgmask); - writel(0x0, ®s->rx14mask); - writel(0x0, ®s->rx15mask); + flexcan_write(0x0, ®s->rxgmask); + flexcan_write(0x0, ®s->rx14mask); + flexcan_write(0x0, ®s->rx15mask); + + if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) + flexcan_write(0x0, ®s->rxfgmask); - flexcan_transceiver_switch(priv, 1); + err = flexcan_transceiver_enable(priv); + if (err) + goto out_chip_disable; /* synchronize with the can bus */ - reg_mcr = readl(®s->mcr); - reg_mcr &= ~FLEXCAN_MCR_HALT; - writel(reg_mcr, ®s->mcr); + err = flexcan_chip_unfreeze(priv); + if (err) + goto out_transceiver_disable; priv->can.state = CAN_STATE_ERROR_ACTIVE; /* enable FIFO interrupts */ - writel(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); /* print chip status */ - dev_dbg(dev->dev.parent, "%s: reading mcr=0x%08x ctrl=0x%08x\n", - __func__, readl(®s->mcr), readl(®s->ctrl)); + netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, + flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); return 0; - out: + out_transceiver_disable: + flexcan_transceiver_disable(priv); + out_chip_disable: flexcan_chip_disable(priv); return err; } @@ -756,17 +907,17 @@ static void flexcan_chip_stop(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - u32 reg; - /* Disable all interrupts */ - writel(0, ®s->imask1); + /* freeze + disable module */ + flexcan_chip_freeze(priv); + flexcan_chip_disable(priv); - /* Disable + halt module */ - reg = readl(®s->mcr); - reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT; - writel(reg, ®s->mcr); + /* Disable all interrupts */ + flexcan_write(0, ®s->imask1); + flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + ®s->ctrl); - flexcan_transceiver_switch(priv, 0); + flexcan_transceiver_disable(priv); priv->can.state = CAN_STATE_STOPPED; return; @@ -777,11 +928,17 @@ static int flexcan_open(struct net_device *dev) struct flexcan_priv *priv = netdev_priv(dev); int err; - clk_enable(priv->clk); + 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; + goto out_disable_per; err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev); if (err) @@ -790,16 +947,23 @@ static int flexcan_open(struct net_device *dev) /* start chip and queuing */ err = flexcan_chip_start(dev); if (err) - goto out_close; + goto out_free_irq; + + can_led_event(dev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); netif_start_queue(dev); return 0; + out_free_irq: + free_irq(dev->irq, dev); out_close: close_candev(dev); - out: - clk_disable(priv->clk); + out_disable_per: + clk_disable_unprepare(priv->clk_per); + out_disable_ipg: + clk_disable_unprepare(priv->clk_ipg); return err; } @@ -813,10 +977,13 @@ static int flexcan_close(struct net_device *dev) flexcan_chip_stop(dev); free_irq(dev->irq, dev); - clk_disable(priv->clk); + 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; } @@ -844,106 +1011,145 @@ 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 __devinit register_flexcandev(struct net_device *dev) +static int register_flexcandev(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; u32 reg, err; - clk_enable(priv->clk); + 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 */ - flexcan_chip_disable(priv); - reg = readl(®s->ctrl); + err = flexcan_chip_disable(priv); + if (err) + goto out_disable_per; + reg = flexcan_read(®s->ctrl); reg |= FLEXCAN_CTRL_CLK_SRC; - writel(reg, ®s->ctrl); + flexcan_write(reg, ®s->ctrl); - flexcan_chip_enable(priv); + err = flexcan_chip_enable(priv); + if (err) + goto out_chip_disable; /* set freeze, halt and activate FIFO, restrict register access */ - reg = readl(®s->mcr); + reg = flexcan_read(®s->mcr); reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; - writel(reg, ®s->mcr); + flexcan_write(reg, ®s->mcr); /* * Currently we only support newer versions of this core * featuring a RX FIFO. Older cores found on some Coldfire * derivates are not yet supported. */ - reg = readl(®s->mcr); + reg = flexcan_read(®s->mcr); if (!(reg & FLEXCAN_MCR_FEN)) { - dev_err(dev->dev.parent, - "Could not enable RX FIFO, unsupported core\n"); + netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); err = -ENODEV; - goto out; + goto out_chip_disable; } err = register_candev(dev); - out: /* disable core and turn off clocks */ + out_chip_disable: flexcan_chip_disable(priv); - clk_disable(priv->clk); + out_disable_per: + clk_disable_unprepare(priv->clk_per); + out_disable_ipg: + clk_disable_unprepare(priv->clk_ipg); return err; } -static void __devexit unregister_flexcandev(struct net_device *dev) +static void unregister_flexcandev(struct net_device *dev) { unregister_candev(dev); } -static int __devinit flexcan_probe(struct platform_device *pdev) +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,p1010-flexcan", .data = &fsl_p1010_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 resource *mem; - struct clk *clk; + struct clk *clk_ipg = NULL, *clk_per = NULL; void __iomem *base; - resource_size_t mem_size; int err, irq; + u32 clock_freq = 0; - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "no clock defined\n"); - err = PTR_ERR(clk); - goto failed_clock; - } + if (pdev->dev.of_node) + of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &clock_freq); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!mem || irq <= 0) { - err = -ENODEV; - goto failed_get; - } + 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); + } - mem_size = resource_size(mem); - if (!request_mem_region(mem->start, mem_size, pdev->name)) { - err = -EBUSY; - goto failed_req; + 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); } - base = ioremap(mem->start, mem_size); - if (!base) { - err = -ENOMEM; - goto failed_map; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENODEV; + + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + 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), 0); - if (!dev) { - err = -ENOMEM; - goto failed_alloc; - } + dev = alloc_candev(sizeof(struct flexcan_priv), 1); + if (!dev) + return -ENOMEM; dev->netdev_ops = &flexcan_netdev_ops; dev->irq = irq; - dev->flags |= IFF_ECHO; /* we support local echo in hardware */ + dev->flags |= IFF_ECHO; priv = netdev_priv(dev); - priv->can.clock.freq = clk_get_rate(clk); + 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; @@ -952,12 +1158,18 @@ static int __devinit flexcan_probe(struct platform_device *pdev) CAN_CTRLMODE_BERR_REPORTING; priv->base = base; priv->dev = dev; - priv->clk = clk; - priv->pdata = pdev->dev.platform_data; + priv->clk_ipg = clk_ipg; + priv->clk_per = clk_per; + priv->pdata = dev_get_platdata(&pdev->dev); + priv->devtype_data = devtype_data; + + priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (IS_ERR(priv->reg_xceiver)) + priv->reg_xceiver = NULL; netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); err = register_flexcandev(dev); @@ -966,6 +1178,8 @@ static int __devinit flexcan_probe(struct platform_device *pdev) goto failed_register; } + devm_can_led_init(dev); + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", priv->base, dev->irq); @@ -973,57 +1187,68 @@ static int __devinit flexcan_probe(struct platform_device *pdev) failed_register: free_candev(dev); - failed_alloc: - iounmap(base); - failed_map: - release_mem_region(mem->start, mem_size); - failed_req: - clk_put(clk); - failed_get: - failed_clock: return err; } -static int __devexit flexcan_remove(struct platform_device *pdev) +static int flexcan_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct flexcan_priv *priv = netdev_priv(dev); - struct resource *mem; unregister_flexcandev(dev); - platform_set_drvdata(pdev, NULL); - iounmap(priv->base); - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); - - clk_put(priv->clk); - + netif_napi_del(&priv->napi); free_candev(dev); return 0; } -static struct platform_driver flexcan_driver = { - .driver.name = DRV_NAME, - .probe = flexcan_probe, - .remove = __devexit_p(flexcan_remove), -}; - -static int __init flexcan_init(void) +static int __maybe_unused flexcan_suspend(struct device *device) { - pr_info("%s netdevice driver\n", DRV_NAME); - return platform_driver_register(&flexcan_driver); + struct net_device *dev = dev_get_drvdata(device); + struct flexcan_priv *priv = netdev_priv(dev); + int err; + + err = flexcan_chip_disable(priv); + if (err) + return err; + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + priv->can.state = CAN_STATE_SLEEPING; + + return 0; } -static void __exit flexcan_exit(void) +static int __maybe_unused flexcan_resume(struct device *device) { - platform_driver_unregister(&flexcan_driver); - pr_info("%s: driver removed\n", DRV_NAME); + struct net_device *dev = dev_get_drvdata(device); + struct flexcan_priv *priv = netdev_priv(dev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + return flexcan_chip_enable(priv); } -module_init(flexcan_init); -module_exit(flexcan_exit); +static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume); + +static struct platform_driver flexcan_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .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>"); diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c new file mode 100644 index 00000000000..3fd9fd942c6 --- /dev/null +++ b/drivers/net/can/grcan.c @@ -0,0 +1,1752 @@ +/* + * 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/kernel-parameters.txt" 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 0x4100 +#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 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(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct grcan_priv *priv = netdev_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(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct grcan_priv *priv = netdev_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(&dev->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(&dev->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 = kzalloc(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 = kzalloc(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) { + del_timer_sync(&priv->hang_timer); + del_timer_sync(&priv->rr_timer); + } + 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 int grcan_transmit_catch_up(struct net_device *dev, int budget) +{ + 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, budget, 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); + + return work_done; +} + +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); + } + } + netif_receive_skb(skb); + + /* Update statistics and read pointer */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + 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 tx_work_done, rx_work_done; + int rx_budget = budget / 2; + int tx_budget = budget - rx_budget; + + /* Half of the budget for receiveing messages */ + rx_work_done = grcan_receive(dev, rx_budget); + + /* Half of the budget for transmitting messages as that can trigger echo + * frames being received + */ + tx_work_done = grcan_transmit_catch_up(dev, tx_budget); + + if (rx_work_done < rx_budget && tx_work_done < tx_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 rx_work_done + tx_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, S_IRUGO); \ + 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, S_IRUGO | S_IWUSR, \ + 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->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) { + init_timer(&priv->rr_timer); + priv->rr_timer.function = grcan_running_reset; + priv->rr_timer.data = (unsigned long)dev; + + init_timer(&priv->hang_timer); + priv->hang_timer.function = grcan_initiate_running_reset; + priv->hang_timer.data = (unsigned long)dev; + } + + 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 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) + */ + err = of_property_read_u32(np, "systemid", &sysid); + if (!err && ((sysid & GRLIB_VERSION_MASK) + >= GRCAN_TXBUG_SAFE_GRLIB_VERSION)) + txbug = false; + + 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 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, + .owner = THIS_MODULE, + .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/janz-ican3.c b/drivers/net/can/janz-ican3.c index 6e533dcc36c..2382c04dc78 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> @@ -19,9 +18,11 @@ #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 @@ -115,6 +116,7 @@ #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 @@ -196,9 +198,6 @@ struct ican3_dev { struct net_device *ndev; struct napi_struct napi; - /* Device for printing */ - struct device *dev; - /* module number */ unsigned int num; @@ -219,6 +218,9 @@ struct ican3_dev { /* 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 @@ -234,7 +236,6 @@ struct ican3_dev { /* fast host interface */ unsigned int fastrx_start; - unsigned int fastrx_int; unsigned int fastrx_num; unsigned int fasttx_start; unsigned int fasttx_num; @@ -273,7 +274,7 @@ static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page) */ /* - * Recieve a message from the ICAN3 "old-style" firmware interface + * Receive a message from the ICAN3 "old-style" firmware interface * * LOCKING: must hold mod->lock * @@ -291,7 +292,7 @@ static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) xord = locl ^ peer; if ((xord & MSYNC_RB_MASK) == 0x00) { - dev_dbg(mod->dev, "no mbox for reading\n"); + netdev_dbg(mod->ndev, "no mbox for reading\n"); return -ENOMEM; } @@ -336,7 +337,7 @@ static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) xord = locl ^ peer; if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) { - dev_err(mod->dev, "no mbox for writing\n"); + netdev_err(mod->ndev, "no mbox for writing\n"); return -ENOMEM; } @@ -361,7 +362,7 @@ static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) * ICAN3 "new-style" Host Interface Setup */ -static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod) +static void ican3_init_new_host_interface(struct ican3_dev *mod) { struct ican3_new_desc desc; unsigned long flags; @@ -440,7 +441,7 @@ static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod) * ICAN3 Fast Host Interface Setup */ -static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod) +static void ican3_init_fast_host_interface(struct ican3_dev *mod) { struct ican3_fast_desc desc; unsigned long flags; @@ -453,7 +454,6 @@ static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod) /* save the start recv page */ mod->fastrx_start = mod->free_page; mod->fastrx_num = 0; - mod->fastrx_int = 0; /* build a single fast tohost queue descriptor */ memset(&desc, 0, sizeof(desc)); @@ -539,7 +539,7 @@ static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) memcpy_fromio(&desc, desc_addr, sizeof(desc)); if (!(desc.control & DESC_VALID)) { - dev_dbg(mod->dev, "%s: no free buffers\n", __func__); + netdev_dbg(mod->ndev, "%s: no free buffers\n", __func__); return -ENOMEM; } @@ -570,7 +570,7 @@ static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) memcpy_fromio(&desc, desc_addr, sizeof(desc)); if (!(desc.control & DESC_VALID)) { - dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__); + netdev_dbg(mod->ndev, "%s: no buffers to recv\n", __func__); return -ENOMEM; } @@ -628,7 +628,7 @@ static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) * Quick Pre-constructed Messages */ -static int __devinit ican3_msg_connect(struct ican3_dev *mod) +static int ican3_msg_connect(struct ican3_dev *mod) { struct ican3_msg msg; @@ -639,7 +639,7 @@ static int __devinit ican3_msg_connect(struct ican3_dev *mod) return ican3_send_msg(mod, &msg); } -static int __devexit ican3_msg_disconnect(struct ican3_dev *mod) +static int ican3_msg_disconnect(struct ican3_dev *mod) { struct ican3_msg msg; @@ -650,7 +650,7 @@ static int __devexit ican3_msg_disconnect(struct ican3_dev *mod) return ican3_send_msg(mod, &msg); } -static int __devinit ican3_msg_newhostif(struct ican3_dev *mod) +static int ican3_msg_newhostif(struct ican3_dev *mod) { struct ican3_msg msg; int ret; @@ -671,7 +671,7 @@ static int __devinit ican3_msg_newhostif(struct ican3_dev *mod) return 0; } -static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod) +static int ican3_msg_fasthostif(struct ican3_dev *mod) { struct ican3_msg msg; unsigned int addr; @@ -704,7 +704,7 @@ static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod) * Setup the CAN filter to either accept or reject all * messages from the CAN bus. */ -static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept) +static int ican3_set_id_filter(struct ican3_dev *mod, bool accept) { struct ican3_msg msg; int ret; @@ -812,10 +812,10 @@ static void ican3_to_can_frame(struct ican3_dev *mod, cf->can_id |= desc->data[0] << 3; cf->can_id |= (desc->data[1] & 0xe0) >> 5; - cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK; - memcpy(cf->data, &desc->data[2], sizeof(cf->data)); + 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 = desc->data[0] & ICAN3_CAN_DLC_MASK; + 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; @@ -830,7 +830,7 @@ static void ican3_to_can_frame(struct ican3_dev *mod, cf->can_id |= desc->data[3] >> 5; /* 2-0 */ } - memcpy(cf->data, &desc->data[6], sizeof(cf->data)); + memcpy(cf->data, &desc->data[6], cf->can_dlc); } } @@ -846,6 +846,10 @@ static void can_frame_to_ican3(struct ican3_dev *mod, 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; @@ -862,7 +866,7 @@ static void can_frame_to_ican3(struct ican3_dev *mod, } /* copy the data bits into the descriptor */ - memcpy(&desc->data[6], cf->data, sizeof(cf->data)); + memcpy(&desc->data[6], cf->data, cf->can_dlc); } /* @@ -876,7 +880,7 @@ static void can_frame_to_ican3(struct ican3_dev *mod, */ static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg) { - dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data); + netdev_dbg(mod->ndev, "IDVERS response: %s\n", msg->data); } static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) @@ -892,7 +896,7 @@ static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) * error frame for userspace */ if (msg->spec == MSG_MSGLOST) { - dev_err(mod->dev, "lost %d control messages\n", msg->data[0]); + netdev_err(mod->ndev, "lost %d control messages\n", msg->data[0]); return; } @@ -908,8 +912,8 @@ static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) if (skb) { cf->can_id |= CAN_ERR_CRTL; cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; stats->rx_errors++; - stats->rx_bytes += cf->can_dlc; netif_rx(skb); } } @@ -926,34 +930,72 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) struct net_device *dev = mod->ndev; struct net_device_stats *stats = &dev->stats; enum can_state state = mod->can.state; - u8 status, isrc, rxerr, txerr; + 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) { - dev_err(mod->dev, "unable to handle errors on non-SJA1000\n"); + 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) { - dev_err(mod->dev, "error message too short\n"); + netdev_err(mod->ndev, "error message too short\n"); return -EINVAL; } - skb = alloc_can_err_skb(dev, &cf); - if (skb == NULL) - return -ENOMEM; - 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) { - dev_dbg(mod->dev, "data overrun interrupt\n"); + 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++; @@ -962,7 +1004,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) /* error warning + passive interrupt */ if (isrc == CEVTIND_EI) { - dev_dbg(mod->dev, "error warning + passive interrupt\n"); + netdev_dbg(mod->ndev, "error warning + passive interrupt\n"); if (status & SR_BS) { state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; @@ -979,11 +1021,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) /* bus error interrupt */ if (isrc == CEVTIND_BEI) { - u8 ecc = msg->data[2]; - - dev_dbg(mod->dev, "bus error interrupt\n"); mod->can.can_stats.bus_error++; - stats->rx_errors++; cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; switch (ecc & ECC_MASK) { @@ -1002,7 +1040,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) break; } - if ((ecc & ECC_DIR) == 0) + if (!(ecc & ECC_DIR)) cf->data[2] |= CAN_ERR_PROT_TX; cf->data[6] = txerr; @@ -1029,8 +1067,6 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) } mod->can.state = state; - stats->rx_errors++; - stats->rx_bytes += cf->can_dlc; netif_rx(skb); return 0; } @@ -1049,7 +1085,7 @@ static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) complete(&mod->termination_comp); break; default: - dev_err(mod->dev, "recieved an unknown inquiry response\n"); + netdev_err(mod->ndev, "received an unknown inquiry response\n"); break; } } @@ -1057,7 +1093,7 @@ static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) static void ican3_handle_unknown_message(struct ican3_dev *mod, struct ican3_msg *msg) { - dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n", + netdev_warn(mod->ndev, "received unknown message: spec 0x%.2x length %d\n", msg->spec, le16_to_cpu(msg->len)); } @@ -1066,7 +1102,7 @@ static void ican3_handle_unknown_message(struct ican3_dev *mod, */ static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) { - dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__, + 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) { @@ -1090,6 +1126,77 @@ static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) } /* + * 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 @@ -1099,6 +1206,10 @@ 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)); @@ -1112,12 +1223,7 @@ static bool ican3_txok(struct ican3_dev *mod) } /* - * Recieve one CAN frame from the hardware - * - * This works like the core of a NAPI function, but is intended to be called - * from workqueue context instead. This driver already needs a workqueue to - * process control messages, so we use the workqueue instead of using NAPI. - * This was done to simplify locking. + * Receive one CAN frame from the hardware * * CONTEXT: must be called from user context */ @@ -1154,10 +1260,27 @@ static int ican3_recv_skb(struct ican3_dev *mod) /* convert the ICAN3 frame into Linux CAN format */ ican3_to_can_frame(mod, &desc, cf); - /* receive the skb, update statistics */ - netif_receive_skb(skb); + /* + * 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 */ @@ -1180,13 +1303,13 @@ err_noalloc: static int ican3_napi(struct napi_struct *napi, int budget) { struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi); - struct ican3_msg msg; unsigned long flags; int received = 0; int ret; /* process all communication messages */ while (true) { + struct ican3_msg uninitialized_var(msg); ret = ican3_recv_msg(mod, &msg); if (ret) break; @@ -1251,20 +1374,15 @@ static irqreturn_t ican3_irq(int irq, void *dev_id) * Reset an ICAN module to its power-on state * * CONTEXT: no network device registered - * LOCKING: work function disabled */ static int ican3_reset_module(struct ican3_dev *mod) { - u8 val = 1 << mod->num; unsigned long start; u8 runold, runnew; /* disable interrupts so no more work is scheduled */ iowrite8(1 << mod->num, &mod->ctrl->int_disable); - /* flush any pending work */ - flush_scheduled_work(); - /* the first unallocated page in the DPM is #9 */ mod->free_page = DPM_FREE_START; @@ -1272,8 +1390,7 @@ static int ican3_reset_module(struct ican3_dev *mod) runold = ioread8(mod->dpm + TARGET_RUNNING); /* reset the module */ - iowrite8(val, &mod->ctrl->reset_assert); - iowrite8(val, &mod->ctrl->reset_deassert); + iowrite8(0x00, &mod->dpmctrl->hwreset); /* wait until the module has finished resetting and is running */ start = jiffies; @@ -1286,11 +1403,11 @@ static int ican3_reset_module(struct ican3_dev *mod) msleep(10); } while (time_before(jiffies, start + HZ / 4)); - dev_err(mod->dev, "failed to reset CAN module\n"); + netdev_err(mod->ndev, "failed to reset CAN module\n"); return -ETIMEDOUT; } -static void __devexit ican3_shutdown_module(struct ican3_dev *mod) +static void ican3_shutdown_module(struct ican3_dev *mod) { ican3_msg_disconnect(mod); ican3_reset_module(mod); @@ -1299,13 +1416,13 @@ static void __devexit ican3_shutdown_module(struct ican3_dev *mod) /* * Startup an ICAN module, bringing it into fast mode */ -static int __devinit ican3_startup_module(struct ican3_dev *mod) +static int ican3_startup_module(struct ican3_dev *mod) { int ret; ret = ican3_reset_module(mod); if (ret) { - dev_err(mod->dev, "unable to reset module\n"); + netdev_err(mod->ndev, "unable to reset module\n"); return ret; } @@ -1314,41 +1431,41 @@ static int __devinit ican3_startup_module(struct ican3_dev *mod) ret = ican3_msg_connect(mod); if (ret) { - dev_err(mod->dev, "unable to connect to module\n"); + 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) { - dev_err(mod->dev, "unable to switch to new-style interface\n"); + 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) { - dev_err(mod->dev, "unable to enable termination\n"); + netdev_err(mod->ndev, "unable to enable termination\n"); return ret; } /* default to "bus errors enabled" */ - ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX); + ret = ican3_set_buserror(mod, 1); if (ret) { - dev_err(mod->dev, "unable to set bus-error\n"); + 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) { - dev_err(mod->dev, "unable to switch to fast host interface\n"); + netdev_err(mod->ndev, "unable to switch to fast host interface\n"); return ret; } ret = ican3_set_id_filter(mod, true); if (ret) { - dev_err(mod->dev, "unable to set acceptance filter\n"); + netdev_err(mod->ndev, "unable to set acceptance filter\n"); return ret; } @@ -1362,33 +1479,19 @@ static int __devinit ican3_startup_module(struct ican3_dev *mod) static int ican3_open(struct net_device *ndev) { struct ican3_dev *mod = netdev_priv(ndev); - u8 quota; int ret; /* open the CAN layer */ ret = open_candev(ndev); if (ret) { - dev_err(mod->dev, "unable to start CAN layer\n"); - return ret; - } - - /* set the bus error generation state appropriately */ - if (mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) - quota = ICAN3_BUSERR_QUOTA_MAX; - else - quota = 0; - - ret = ican3_set_buserror(mod, quota); - if (ret) { - dev_err(mod->dev, "unable to set bus-error\n"); - close_candev(ndev); + 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) { - dev_err(mod->dev, "unable to set bus-on\n"); + netdev_err(mod->ndev, "unable to set bus-on\n"); close_candev(ndev); return ret; } @@ -1412,10 +1515,13 @@ static int ican3_stop(struct net_device *ndev) /* bring the bus offline, stop receiving packets */ ret = ican3_set_bus_state(mod, false); if (ret) { - dev_err(mod->dev, "unable to set bus-off\n"); + 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; @@ -1424,18 +1530,19 @@ static int ican3_stop(struct net_device *ndev) static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev) { struct ican3_dev *mod = netdev_priv(ndev); - struct net_device_stats *stats = &ndev->stats; struct can_frame *cf = (struct can_frame *)skb->data; struct ican3_fast_desc desc; void __iomem *desc_addr; unsigned long flags; + 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)) { - dev_err(mod->dev, "no free descriptors, stopping queue\n"); - netif_stop_queue(ndev); + netdev_err(mod->ndev, "BUG: no free descriptors\n"); spin_unlock_irqrestore(&mod->lock, flags); return NETDEV_TX_BUSY; } @@ -1450,6 +1557,14 @@ static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev) 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 @@ -1467,19 +1582,7 @@ static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev) mod->fasttx_num = (desc.control & DESC_WRAP) ? 0 : (mod->fasttx_num + 1); - /* update statistics */ - stats->tx_packets++; - stats->tx_bytes += cf->can_dlc; - kfree_skb(skb); - - /* - * This hardware doesn't have TX-done notifications, so we'll try and - * emulate it the best we can using ECHO skbs. Get the next TX - * descriptor, and see if we have room to send. If not, stop the queue. - * It will be woken when the ECHO skb for the current packet is recv'd. - */ - - /* copy the control bits of the descriptor */ + /* if there is no free descriptor space, stop the transmit queue */ if (!ican3_txok(mod)) netif_stop_queue(ndev); @@ -1491,6 +1594,7 @@ 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, }; /* @@ -1498,7 +1602,7 @@ static const struct net_device_ops ican3_netdev_ops = { */ /* This structure was stolen from drivers/net/can/sja1000/sja1000.c */ -static struct can_bittiming_const ican3_bittiming_const = { +static const struct can_bittiming_const ican3_bittiming_const = { .name = DRV_NAME, .tseg1_min = 1, .tseg1_max = 16, @@ -1551,7 +1655,7 @@ static int ican3_set_mode(struct net_device *ndev, enum can_mode mode) /* bring the bus online */ ret = ican3_set_bus_state(mod, true); if (ret) { - dev_err(mod->dev, "unable to set bus-on\n"); + netdev_err(ndev, "unable to set bus-on\n"); return ret; } @@ -1575,8 +1679,8 @@ static int ican3_get_berr_counter(const struct net_device *ndev, return ret; ret = wait_for_completion_timeout(&mod->buserror_comp, HZ); - if (ret <= 0) { - dev_info(mod->dev, "%s timed out\n", __func__); + if (ret == 0) { + netdev_info(mod->ndev, "%s timed out\n", __func__); return -ETIMEDOUT; } @@ -1601,8 +1705,8 @@ static ssize_t ican3_sysfs_show_term(struct device *dev, return ret; ret = wait_for_completion_timeout(&mod->termination_comp, HZ); - if (ret <= 0) { - dev_info(mod->dev, "%s timed out\n", __func__); + if (ret == 0) { + netdev_info(mod->ndev, "%s timed out\n", __func__); return -ETIMEDOUT; } @@ -1617,7 +1721,7 @@ static ssize_t ican3_sysfs_set_term(struct device *dev, unsigned long enable; int ret; - if (strict_strtoul(buf, 0, &enable)) + if (kstrtoul(buf, 0, &enable)) return -EINVAL; ret = ican3_set_termination(mod, enable); @@ -1627,7 +1731,7 @@ static ssize_t ican3_sysfs_set_term(struct device *dev, return count; } -static DEVICE_ATTR(termination, S_IWUGO | S_IRUGO, ican3_sysfs_show_term, +static DEVICE_ATTR(termination, S_IWUSR | S_IRUGO, ican3_sysfs_show_term, ican3_sysfs_set_term); static struct attribute *ican3_sysfs_attrs[] = { @@ -1643,7 +1747,7 @@ static struct attribute_group ican3_sysfs_attr_group = { * PCI Subsystem */ -static int __devinit ican3_probe(struct platform_device *pdev) +static int ican3_probe(struct platform_device *pdev) { struct janz_platform_data *pdata; struct net_device *ndev; @@ -1652,7 +1756,7 @@ static int __devinit ican3_probe(struct platform_device *pdev) struct device *dev; int ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) return -ENXIO; @@ -1672,9 +1776,9 @@ static int __devinit ican3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ndev); mod = netdev_priv(ndev); mod->ndev = ndev; - mod->dev = &pdev->dev; mod->num = pdata->modno; netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS); + skb_queue_head_init(&mod->echoq); spin_lock_init(&mod->lock); init_completion(&mod->termination_comp); init_completion(&mod->buserror_comp); @@ -1695,7 +1799,8 @@ static int __devinit ican3_probe(struct platform_device *pdev) 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_BERR_REPORTING + | CAN_CTRLMODE_ONE_SHOT; /* find our IRQ number */ mod->irq = platform_get_irq(pdev, 0); @@ -1779,7 +1884,7 @@ out_return: return ret; } -static int __devexit ican3_remove(struct platform_device *pdev) +static int ican3_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct ican3_dev *mod = netdev_priv(ndev); @@ -1808,23 +1913,12 @@ static struct platform_driver ican3_driver = { .owner = THIS_MODULE, }, .probe = ican3_probe, - .remove = __devexit_p(ican3_remove), + .remove = ican3_remove, }; -static int __init ican3_init(void) -{ - return platform_driver_register(&ican3_driver); -} - -static void __exit ican3_exit(void) -{ - platform_driver_unregister(&ican3_driver); -} +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"); - -module_init(ican3_init); -module_exit(ican3_exit); diff --git a/drivers/net/can/led.c b/drivers/net/can/led.c new file mode 100644 index 00000000000..ab7f1b01be4 --- /dev/null +++ b/drivers/net/can/led.c @@ -0,0 +1,127 @@ +/* + * 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); + break; + case CAN_LED_EVENT_STOP: + led_trigger_event(priv->tx_led_trig, LED_OFF); + led_trigger_event(priv->rx_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); + break; + case CAN_LED_EVENT_RX: + if (led_delay) + led_trigger_blink_oneshot(priv->rx_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); +} + +/* 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); + + 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); + + 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) + 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); + } + + 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/mscan/Kconfig b/drivers/net/can/mscan/Kconfig index 27d1d398e25..81c71171949 100644 --- a/drivers/net/can/mscan/Kconfig +++ b/drivers/net/can/mscan/Kconfig @@ -1,5 +1,5 @@ config CAN_MSCAN - depends on CAN_DEV && (PPC || M68K || M68KNOMMU) + depends on PPC tristate "Support for Freescale MSCAN based chips" ---help--- The Motorola Scalable Controller Area Network (MSCAN) definition diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index 312b9c8f4f3..44725296f72 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -16,8 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -40,17 +39,17 @@ 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 struct of_device_id __devinitdata mpc52xx_cdm_ids[] = { +static struct of_device_id mpc52xx_cdm_ids[] = { { .compatible = "fsl,mpc5200-cdm", }, {} }; -static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, - const char *clock_name, - int *mscan_clksrc) +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; @@ -101,156 +100,193 @@ static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, return freq; } #else /* !CONFIG_PPC_MPC52xx */ -static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev, - const char *clock_name, - int *mscan_clksrc) +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 -struct mpc512x_clockctl { - u32 spmr; /* System PLL Mode Reg */ - u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ - u32 scfr1; /* System Clk Freq Reg 1 */ - u32 scfr2; /* System Clk Freq Reg 2 */ - u32 reserved; - u32 bcr; /* Bread Crumb Reg */ - u32 pccr[12]; /* PSC Clk Ctrl Reg 0-11 */ - u32 spccr; /* SPDIF Clk Ctrl Reg */ - u32 cccr; /* CFM Clk Ctrl Reg */ - u32 dccr; /* DIU Clk Cnfg Reg */ - u32 mccr[4]; /* MSCAN Clk Ctrl Reg 1-3 */ -}; - -static struct of_device_id __devinitdata mpc512x_clock_ids[] = { - { .compatible = "fsl,mpc5121-clock", }, - {} -}; - -static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, - const char *clock_name, - int *mscan_clksrc) +static u32 mpc512x_can_get_clock(struct platform_device *ofdev, + const char *clock_source, int *mscan_clksrc) { - struct mpc512x_clockctl __iomem *clockctl; - struct device_node *np_clock; - struct clk *sys_clk, *ref_clk; - int plen, clockidx, clocksrc = -1; - u32 sys_freq, val, clockdiv = 1, freq = 0; - const u32 *pval; - - np_clock = of_find_matching_node(NULL, mpc512x_clock_ids); - if (!np_clock) { - dev_err(&ofdev->dev, "couldn't find clock node\n"); - return 0; + 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); } - clockctl = of_iomap(np_clock, 0); - if (!clockctl) { - dev_err(&ofdev->dev, "couldn't map clock registers\n"); - goto exit_put; + 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); } - /* Determine the MSCAN device index from the physical address */ - pval = of_get_property(ofdev->dev.of_node, "reg", &plen); - BUG_ON(!pval || plen < sizeof(*pval)); - clockidx = (*pval & 0x80) ? 1 : 0; - if (*pval & 0x2000) - clockidx += 2; - - /* - * Clock source and divider selection: 3 different clock sources - * can be selected: "ip", "ref" or "sys". For the latter two, a - * clock divider can be defined as well. If the clock source is - * not specified by the device tree, we first try to find an - * optimal CAN source clock based on the system clock. If that - * is not posslible, the reference clock will be used. + /* 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 */ - if (clock_name && !strcmp(clock_name, "ip")) { + 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; - freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node); - } else { + 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; - - pval = of_get_property(ofdev->dev.of_node, - "fsl,mscan-clock-divider", &plen); - if (pval && plen == sizeof(*pval)) - clockdiv = *pval; - if (!clockdiv) - clockdiv = 1; - - if (!clock_name || !strcmp(clock_name, "sys")) { - sys_clk = clk_get(&ofdev->dev, "sys_clk"); - if (!sys_clk) { - dev_err(&ofdev->dev, "couldn't get sys_clk\n"); - goto exit_unmap; - } - /* Get and round up/down sys clock rate */ - sys_freq = 1000000 * - ((clk_get_rate(sys_clk) + 499999) / 1000000); - - if (!clock_name) { - /* A multiple of 16 MHz would be optimal */ - if ((sys_freq % 16000000) == 0) { - clocksrc = 0; - clockdiv = sys_freq / 16000000; - freq = sys_freq / clockdiv; - } - } else { - clocksrc = 0; - freq = sys_freq / clockdiv; - } - } - - if (clocksrc < 0) { - ref_clk = clk_get(&ofdev->dev, "ref_clk"); - if (!ref_clk) { - dev_err(&ofdev->dev, "couldn't get ref_clk\n"); - goto exit_unmap; - } - clocksrc = 1; - freq = clk_get_rate(ref_clk) / clockdiv; - } + dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + default: + goto err_invalid; } - /* Disable clock */ - out_be32(&clockctl->mccr[clockidx], 0x0); - if (clocksrc >= 0) { - /* Set source and divider */ - val = (clocksrc << 14) | ((clockdiv - 1) << 17); - out_be32(&clockctl->mccr[clockidx], val); - /* Enable clock */ - out_be32(&clockctl->mccr[clockidx], val | 0x10000); - } + /* 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; - /* Enable MSCAN clock domain */ - val = in_be32(&clockctl->sccr[1]); - if (!(val & (1 << 25))) - out_be32(&clockctl->sccr[1], val | (1 << 25)); +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; +} - dev_dbg(&ofdev->dev, "using '%s' with frequency divider %d\n", - *mscan_clksrc == MSCAN_CLKSRC_IPS ? "ips_clk" : - clocksrc == 1 ? "ref_clk" : "sys_clk", clockdiv); +static void mpc512x_can_put_clock(struct platform_device *ofdev) +{ + struct mscan_priv *priv; -exit_unmap: - iounmap(clockctl); -exit_put: - of_node_put(np_clock); - return freq; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + if (priv->clk_ipg) + clk_disable_unprepare(priv->clk_ipg); } #else /* !CONFIG_PPC_MPC512x */ -static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev, - const char *clock_name, - int *mscan_clksrc) +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 int __devinit mpc5xxx_can_probe(struct platform_device *ofdev, - const struct of_device_id *id) +static const struct of_device_id mpc5xxx_can_table[]; +static int mpc5xxx_can_probe(struct platform_device *ofdev) { - struct mpc5xxx_can_data *data = (struct mpc5xxx_can_data *)id->data; + 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; @@ -259,6 +295,11 @@ static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev, 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"); @@ -275,6 +316,8 @@ static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev, 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; @@ -291,8 +334,6 @@ static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev, goto exit_free_mscan; } - SET_NETDEV_DEV(dev, &ofdev->dev); - err = register_mscandev(dev, mscan_clksrc); if (err) { dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", @@ -300,8 +341,6 @@ static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev, goto exit_free_mscan; } - dev_set_drvdata(&ofdev->dev, dev); - dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n", priv->reg_base, dev->irq, priv->can.clock.freq); @@ -317,14 +356,19 @@ exit_unmap_mem: return err; } -static int __devexit mpc5xxx_can_remove(struct platform_device *ofdev) +static int mpc5xxx_can_remove(struct platform_device *ofdev) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + 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); - dev_set_drvdata(&ofdev->dev, NULL); + 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); @@ -336,7 +380,7 @@ static int __devexit mpc5xxx_can_remove(struct platform_device *ofdev) static struct mscan_regs saved_regs; static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct net_device *dev = platform_get_drvdata(ofdev); struct mscan_priv *priv = netdev_priv(dev); struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; @@ -347,7 +391,7 @@ static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state static int mpc5xxx_can_resume(struct platform_device *ofdev) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + 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; @@ -374,48 +418,41 @@ static int mpc5xxx_can_resume(struct platform_device *ofdev) } #endif -static struct mpc5xxx_can_data __devinitdata mpc5200_can_data = { +static const struct mpc5xxx_can_data mpc5200_can_data = { .type = MSCAN_TYPE_MPC5200, .get_clock = mpc52xx_can_get_clock, + /* .put_clock not applicable */ }; -static struct mpc5xxx_can_data __devinitdata mpc5121_can_data = { +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 struct of_device_id __devinitdata mpc5xxx_can_table[] = { +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 of_platform_driver mpc5xxx_can_driver = { +static struct platform_driver mpc5xxx_can_driver = { .driver = { .name = "mpc5xxx_can", .owner = THIS_MODULE, .of_match_table = mpc5xxx_can_table, }, .probe = mpc5xxx_can_probe, - .remove = __devexit_p(mpc5xxx_can_remove), + .remove = mpc5xxx_can_remove, #ifdef CONFIG_PM .suspend = mpc5xxx_can_suspend, .resume = mpc5xxx_can_resume, #endif }; -static int __init mpc5xxx_can_init(void) -{ - return of_register_platform_driver(&mpc5xxx_can_driver); -} -module_init(mpc5xxx_can_init); - -static void __exit mpc5xxx_can_exit(void) -{ - return of_unregister_platform_driver(&mpc5xxx_can_driver); -}; -module_exit(mpc5xxx_can_exit); +module_platform_driver(mpc5xxx_can_driver); MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); MODULE_DESCRIPTION("Freescale MPC5xxx CAN driver"); diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index 74cd880c7e0..e0c9be5e2ab 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -16,8 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -34,7 +33,7 @@ #include "mscan.h" -static struct can_bittiming_const mscan_bittiming_const = { +static const struct can_bittiming_const mscan_bittiming_const = { .name = "mscan", .tseg1_min = 4, .tseg1_max = 16, @@ -62,7 +61,7 @@ static enum can_state state_map[] = { static int mscan_set_mode(struct net_device *dev, u8 mode) { struct mscan_priv *priv = netdev_priv(dev); - struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; int ret = 0; int i; u8 canctl1; @@ -95,9 +94,9 @@ static int mscan_set_mode(struct net_device *dev, u8 mode) * any, at once. */ if (i >= MSCAN_SET_MODE_RETRIES) - dev_dbg(dev->dev.parent, - "device failed to enter sleep mode. " - "We proceed anyhow.\n"); + netdev_dbg(dev, + "device failed to enter sleep mode. " + "We proceed anyhow.\n"); else priv->can.state = CAN_STATE_SLEEPING; } @@ -138,7 +137,7 @@ static int mscan_set_mode(struct net_device *dev, u8 mode) static int mscan_start(struct net_device *dev) { struct mscan_priv *priv = netdev_priv(dev); - struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; u8 canrflg; int err; @@ -178,7 +177,7 @@ static int mscan_restart(struct net_device *dev) struct mscan_priv *priv = netdev_priv(dev); if (priv->type == MSCAN_TYPE_MPC5121) { - struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; priv->can.state = CAN_STATE_ERROR_ACTIVE; WARN(!(in_8(®s->canmisc) & MSCAN_BOHOLD), @@ -199,7 +198,7 @@ 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 *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; int i, rtr, buf_id; u32 can_id; @@ -213,7 +212,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) switch (hweight8(i)) { case 0: netif_stop_queue(dev); - dev_err(dev->dev.parent, "Tx Ring full when queue awake!\n"); + netdev_err(dev, "Tx Ring full when queue awake!\n"); return NETDEV_TX_BUSY; case 1: /* @@ -246,7 +245,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) out_be16(®s->tx.idr3_2, can_id); can_id >>= 16; - /* EFF_FLAGS are inbetween the IDs :( */ + /* EFF_FLAGS are between the IDs :( */ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | MSCAN_EFF_FLAGS; } else { @@ -261,11 +260,13 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) void __iomem *data = ®s->tx.dsr1_0; u16 *payload = (u16 *)frame->data; - /* It is safe to write into dsr[dlc+1] */ - for (i = 0; i < (frame->can_dlc + 1) / 2; i++) { + 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); @@ -305,7 +306,7 @@ static enum can_state check_set_state(struct net_device *dev, u8 canrflg) static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) { struct mscan_priv *priv = netdev_priv(dev); - struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; u32 can_id; int i; @@ -330,10 +331,13 @@ static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) void __iomem *data = ®s->rx.dsr1_0; u16 *payload = (u16 *)frame->data; - for (i = 0; i < (frame->can_dlc + 1) / 2; i++) { + 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); @@ -343,11 +347,11 @@ 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 *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; struct net_device_stats *stats = &dev->stats; enum can_state old_state; - dev_dbg(dev->dev.parent, "error interrupt (canrflg=%#x)\n", canrflg); + netdev_dbg(dev, "error interrupt (canrflg=%#x)\n", canrflg); frame->can_id = CAN_ERR_FLAG; if (canrflg & MSCAN_OVRIF) { @@ -406,7 +410,7 @@ 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 *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; struct net_device_stats *stats = &dev->stats; int npackets = 0; int ret = 1; @@ -422,7 +426,7 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota) skb = alloc_can_skb(dev, &frame); if (!skb) { if (printk_ratelimit()) - dev_notice(dev->dev.parent, "packet dropped\n"); + netdev_notice(dev, "packet dropped\n"); stats->rx_dropped++; out_8(®s->canrflg, canrflg); continue; @@ -453,7 +457,7 @@ 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 *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; struct net_device_stats *stats = &dev->stats; u8 cantier, cantflg, canrflg; irqreturn_t ret = IRQ_NONE; @@ -512,12 +516,8 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) { - struct mscan_priv *priv = netdev_priv(dev); int ret = 0; - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: ret = mscan_restart(dev); @@ -537,7 +537,7 @@ static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) static int mscan_do_set_bittiming(struct net_device *dev) { struct mscan_priv *priv = netdev_priv(dev); - struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; struct can_bittiming *bt = &priv->can.bittiming; u8 btr0, btr1; @@ -546,8 +546,7 @@ static int mscan_do_set_bittiming(struct net_device *dev) BTR1_SET_TSEG2(bt->phase_seg2) | BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)); - dev_info(dev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n", - btr0, btr1); + netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); out_8(®s->canbtr0, btr0); out_8(®s->canbtr1, btr1); @@ -555,28 +554,52 @@ static int mscan_do_set_bittiming(struct net_device *dev) 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 *regs = (struct mscan_regs *)priv->reg_base; + 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) - return ret; + goto exit_dis_can_clock; napi_enable(&priv->napi); ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev); if (ret < 0) { - dev_err(dev->dev.parent, "failed to attach interrupt\n"); + netdev_err(dev, "failed to attach interrupt\n"); goto exit_napi_disable; } - priv->open_time = jiffies; - - clrbits8(®s->canctl1, MSCAN_LISTEN); + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + setbits8(®s->canctl1, MSCAN_LISTEN); + else + clrbits8(®s->canctl1, MSCAN_LISTEN); ret = mscan_start(dev); if (ret) @@ -587,18 +610,24 @@ static int mscan_open(struct net_device *dev) return 0; exit_free_irq: - priv->open_time = 0; free_irq(dev->irq, dev); exit_napi_disable: napi_disable(&priv->napi); close_candev(dev); +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 *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; netif_stop_queue(dev); napi_disable(&priv->napi); @@ -608,21 +637,26 @@ static int mscan_close(struct net_device *dev) mscan_set_mode(dev, MSCAN_INIT_MODE); close_candev(dev); free_irq(dev->irq, dev); - priv->open_time = 0; + + 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_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 *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; u8 ctl1; ctl1 = in_8(®s->canctl1); @@ -631,8 +665,10 @@ int register_mscandev(struct net_device *dev, int mscan_clksrc) else ctl1 &= ~MSCAN_CLKSRC; - if (priv->type == MSCAN_TYPE_MPC5121) + 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); @@ -659,7 +695,7 @@ int register_mscandev(struct net_device *dev, int mscan_clksrc) void unregister_mscandev(struct net_device *dev) { struct mscan_priv *priv = netdev_priv(dev); - struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct mscan_regs __iomem *regs = priv->reg_base; mscan_set_mode(dev, MSCAN_INIT_MODE); clrbits8(®s->canctl1, MSCAN_CANE); unregister_candev(dev); @@ -685,7 +721,8 @@ struct net_device *alloc_mscandev(void) 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; + 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; diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h index b43e9f5d326..ad8e08f9c49 100644 --- a/drivers/net/can/mscan/mscan.h +++ b/drivers/net/can/mscan/mscan.h @@ -14,13 +14,13 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * 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 */ @@ -281,9 +281,10 @@ struct tx_queue_entry { struct mscan_priv { struct can_priv can; /* must be the first member */ unsigned int type; /* MSCAN type variants */ - long open_time; unsigned long flags; void __iomem *reg_base; /* ioremap'ed address to registers */ + struct clk *clk_ipg; /* clock for registers */ + struct clk *clk_can; /* clock for bitrates */ u8 shadow_statflg; u8 shadow_canrier; u8 cur_pri; @@ -295,8 +296,8 @@ struct mscan_priv { struct napi_struct napi; }; -extern struct net_device *alloc_mscandev(void); -extern int register_mscandev(struct net_device *dev, int mscan_clksrc); -extern void unregister_mscandev(struct net_device *dev); +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 index a9b6a6525a6..6472562efed 100644 --- a/drivers/net/can/pch_can.c +++ b/drivers/net/can/pch_can.c @@ -1,6 +1,6 @@ /* * Copyright (C) 1999 - 2010 Intel Corporation. - * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * 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 @@ -12,8 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/interrupt.h> @@ -22,7 +21,6 @@ #include <linux/module.h> #include <linux/sched.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/errno.h> @@ -32,8 +30,6 @@ #include <linux/can/dev.h> #include <linux/can/error.h> -#define PCH_ENABLE 1 /* The enable flag */ -#define PCH_DISABLE 0 /* The disable flag */ #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)) @@ -68,6 +64,7 @@ #define PCH_IF_CREQ_BUSY BIT(15) #define PCH_STATUS_INT 0x8000 +#define PCH_RP 0x00008000 #define PCH_REC 0x00007f00 #define PCH_TEC 0x000000ff @@ -78,11 +75,12 @@ #define PCH_BUS_OFF BIT(7) /* bit position of certain controller bits. */ -#define PCH_BIT_BRP 0 -#define PCH_BIT_SJW 6 -#define PCH_BIT_TSEG1 8 -#define PCH_BIT_TSEG2 12 -#define PCH_BIT_BRPE_BRPE 6 +#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 @@ -90,9 +88,11 @@ #define PCH_CAN_CLK 50000000 /* 50MHz */ -/* Define the number of message object. +/* + * Define the number of message object. * PCH CAN communications are done via Message RAM. - * The Message RAM consists of 32 message objects. */ + * 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 @@ -102,6 +102,10 @@ #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, @@ -123,7 +127,7 @@ enum pch_can_mode { PCH_CAN_ALL, PCH_CAN_NONE, PCH_CAN_STOP, - PCH_CAN_RUN + PCH_CAN_RUN, }; struct pch_can_if_regs { @@ -134,10 +138,7 @@ struct pch_can_if_regs { u32 id1; u32 id2; u32 mcont; - u32 dataa1; - u32 dataa2; - u32 datab1; - u32 datab2; + u32 data[4]; u32 rsv[13]; }; @@ -169,24 +170,21 @@ struct pch_can_regs { struct pch_can_priv { struct can_priv can; - unsigned int can_num; struct pci_dev *dev; - int tx_enable[PCH_TX_OBJ_END]; - int rx_enable[PCH_TX_OBJ_END]; - int rx_link[PCH_TX_OBJ_END]; - unsigned int int_enables; - unsigned int int_stat; + 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; - unsigned int msg_obj[PCH_TX_OBJ_END]; struct pch_can_regs __iomem *regs; struct napi_struct napi; - unsigned int tx_obj; /* Point next Tx Obj index */ - unsigned int use_msi; + int tx_obj; /* Point next Tx Obj index */ + int use_msi; }; -static struct can_bittiming_const pch_can_bittiming_const = { +static const struct can_bittiming_const pch_can_bittiming_const = { .name = KBUILD_MODNAME, - .tseg1_min = 1, + .tseg1_min = 2, .tseg1_max = 16, .tseg2_min = 1, .tseg2_max = 8, @@ -225,7 +223,7 @@ static void pch_can_set_run_mode(struct pch_can_priv *priv, break; default: - dev_err(&priv->ndev->dev, "%s -> Invalid Mode.\n", __func__); + netdev_err(priv->ndev, "%s -> Invalid Mode.\n", __func__); break; } } @@ -244,31 +242,27 @@ static void pch_can_set_optmode(struct pch_can_priv *priv) iowrite32(reg_val, &priv->regs->opt); } -static void pch_can_set_int_custom(struct pch_can_priv *priv) +static void pch_can_rw_msg_obj(void __iomem *creq_addr, u32 num) { - /* 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)); -} + int counter = PCH_COUNTER_LIMIT; + u32 ifx_creq; -/* This function retrieves interrupt enabled for the CAN device. */ -static void pch_can_get_int_enables(struct pch_can_priv *priv, u32 *enables) -{ - /* Obtaining the status of IE, SIE and EIE interrupt bits. */ - *enables = ((ioread32(&priv->regs->cont) & PCH_CTRL_IE_SIE_EIE) >> 1); + 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_ENABLE: - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_IE); - break; - case PCH_CAN_DISABLE: pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE); break; @@ -282,30 +276,13 @@ static void pch_can_set_int_enables(struct pch_can_priv *priv, break; default: - dev_err(&priv->ndev->dev, "Invalid interrupt number.\n"); + netdev_err(priv->ndev, "Invalid interrupt number.\n"); break; } } -static void pch_can_check_if_busy(u32 __iomem *creq_addr, u32 num) -{ - u32 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_rxtx(struct pch_can_priv *priv, u32 buff_num, - u32 set, enum pch_ifreg dir) + int set, enum pch_ifreg dir) { u32 ie; @@ -314,29 +291,28 @@ static void pch_can_set_rxtx(struct pch_can_priv *priv, u32 buff_num, else ie = PCH_IF_MCONT_RXIE; - /* Reading the receive buffer data from RAM to Interface1 registers */ + /* Reading the Msg buffer from Message RAM to IF1/2 registers. */ iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[dir].creq, buff_num); + pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); - /* Setting the IF1MASK1 register to access MsgVal and RxIE bits */ + /* 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 == PCH_ENABLE) { - /* Setting the MsgVal and RxIE bits */ + 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 if (set == PCH_DISABLE) { - /* Resetting the MsgVal and RxIE bits */ + } 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_check_if_busy(&priv->regs->ifregs[dir].creq, buff_num); + pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); } -static void pch_can_set_rx_all(struct pch_can_priv *priv, u32 set) +static void pch_can_set_rx_all(struct pch_can_priv *priv, int set) { int i; @@ -345,7 +321,7 @@ static void pch_can_set_rx_all(struct pch_can_priv *priv, u32 set) pch_can_set_rxtx(priv, i, set, PCH_RX_IFREG); } -static void pch_can_set_tx_all(struct pch_can_priv *priv, u32 set) +static void pch_can_set_tx_all(struct pch_can_priv *priv, int set) { int i; @@ -354,97 +330,30 @@ static void pch_can_set_tx_all(struct pch_can_priv *priv, u32 set) pch_can_set_rxtx(priv, i, set, PCH_TX_IFREG); } -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_check_if_busy(&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 int pch_can_int_pending(struct pch_can_priv *priv) +static u32 pch_can_int_pending(struct pch_can_priv *priv) { return ioread32(&priv->regs->intr) & 0xffff; } -static void pch_can_set_rx_buffer_link(struct pch_can_priv *priv, - u32 buffer_num, u32 set) +static void pch_can_clear_if_buffers(struct pch_can_priv *priv) { - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[0].creq, buffer_num); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - if (set == PCH_ENABLE) - 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); + int i; /* Msg Obj ID (1~32) */ - pch_can_check_if_busy(&priv->regs->ifregs[0].creq, buffer_num); -} - -static void 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_check_if_busy(&priv->regs->ifregs[0].creq, buffer_num); - - if (ioread32(&priv->regs->ifregs[0].mcont) & PCH_IF_MCONT_EOB) - *link = PCH_DISABLE; - else - *link = PCH_ENABLE; -} - -static void pch_can_clear_buffers(struct pch_can_priv *priv) -{ - int i; - - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { + 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].dataa1); - iowrite32(0x0, &priv->regs->ifregs[0].dataa2); - iowrite32(0x0, &priv->regs->ifregs[0].datab1); - iowrite32(0x0, &priv->regs->ifregs[0].datab2); + 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_check_if_busy(&priv->regs->ifregs[0].creq, i); - } - - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_SET, &priv->regs->ifregs[1].cmask); - iowrite32(0xffff, &priv->regs->ifregs[1].mask1); - iowrite32(0xffff, &priv->regs->ifregs[1].mask2); - iowrite32(0x0, &priv->regs->ifregs[1].id1); - iowrite32(0x0, &priv->regs->ifregs[1].id2); - iowrite32(0x0, &priv->regs->ifregs[1].mcont); - iowrite32(0x0, &priv->regs->ifregs[1].dataa1); - iowrite32(0x0, &priv->regs->ifregs[1].dataa2); - iowrite32(0x0, &priv->regs->ifregs[1].datab1); - iowrite32(0x0, &priv->regs->ifregs[1].datab2); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | - PCH_CMASK_ARB | PCH_CMASK_CTRL, - &priv->regs->ifregs[1].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[1].creq, i); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); } } @@ -453,9 +362,8 @@ 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_check_if_busy(&priv->regs->ifregs[0].creq, 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); @@ -463,12 +371,12 @@ static void pch_can_config_rx_tx_buffers(struct pch_can_priv *priv) pch_can_bit_set(&priv->regs->ifregs[0].mcont, PCH_IF_MCONT_UMASK); - /* Set FIFO mode set to 0 except last Rx Obj*/ - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); /* 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); @@ -476,38 +384,32 @@ static void pch_can_config_rx_tx_buffers(struct pch_can_priv *priv) 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); + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | + PCH_CMASK_CTRL, &priv->regs->ifregs[0].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[0].creq, i); + 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_check_if_busy(&priv->regs->ifregs[1].creq, 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(0x0, &priv->regs->ifregs[1].id2); - pch_can_bit_set(&priv->regs->ifregs[1].id2, PCH_ID2_DIR); + iowrite32(PCH_ID2_DIR, &priv->regs->ifregs[1].id2); /* Setting EOB bit for transmitter */ - iowrite32(PCH_IF_MCONT_EOB, &priv->regs->ifregs[1].mcont); - - pch_can_bit_set(&priv->regs->ifregs[1].mcont, - PCH_IF_MCONT_UMASK); + 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); + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | + PCH_CMASK_CTRL, &priv->regs->ifregs[1].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[1].creq, i); + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); } } @@ -517,7 +419,7 @@ static void pch_can_init(struct pch_can_priv *priv) pch_can_set_run_mode(priv, PCH_CAN_STOP); /* Clearing all the message object buffers. */ - pch_can_clear_buffers(priv); + pch_can_clear_if_buffers(priv); /* Configuring the respective message object as either rx/tx object. */ pch_can_config_rx_tx_buffers(priv); @@ -544,11 +446,6 @@ static void pch_can_release(struct pch_can_priv *priv) /* This function clears interrupt(s) from the CAN device. */ static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) { - if (mask == PCH_STATUS_INT) { - ioread32(&priv->regs->stat); - return; - } - /* Clear interrupt for transmit object */ if ((mask >= PCH_RX_OBJ_START) && (mask <= PCH_RX_OBJ_END)) { /* Setting CMASK for clearing the reception interrupts. */ @@ -562,10 +459,11 @@ static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) pch_can_bit_clear(&priv->regs->ifregs[0].mcont, PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND); - pch_can_check_if_busy(&priv->regs->ifregs[0].creq, mask); + 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. */ + /* + * Setting CMASK for clearing interrupts for frame transmission. + */ iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, &priv->regs->ifregs[1].cmask); @@ -578,16 +476,10 @@ static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) pch_can_bit_clear(&priv->regs->ifregs[1].mcont, PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND | PCH_IF_MCONT_TXRQXT); - pch_can_check_if_busy(&priv->regs->ifregs[1].creq, mask); + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, mask); } } -static int pch_can_get_buffer_status(struct pch_can_priv *priv) -{ - return (ioread32(&priv->regs->treq1) & 0xffff) | - ((ioread32(&priv->regs->treq2) & 0xffff) << 16); -} - static void pch_can_reset(struct pch_can_priv *priv) { /* write to sw reset register */ @@ -614,21 +506,19 @@ static void pch_can_error(struct net_device *ndev, u32 status) state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; can_bus_off(ndev); - pch_can_set_run_mode(priv, PCH_CAN_RUN); - dev_err(&ndev->dev, "%s -> Bus Off occurres.\n", __func__); } + 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; - errc = ioread32(&priv->regs->errc); 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; - dev_warn(&ndev->dev, + netdev_dbg(ndev, "%s -> Error Counter is more than 96.\n", __func__); } /* Error passive interrupt. */ @@ -636,12 +526,11 @@ static void pch_can_error(struct net_device *ndev, u32 status) priv->can.can_stats.error_passive++; state = CAN_STATE_ERROR_PASSIVE; cf->can_id |= CAN_ERR_CRTL; - errc = ioread32(&priv->regs->errc); - if (((errc & PCH_REC) >> 8) > 127) + 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; - dev_err(&ndev->dev, + netdev_dbg(ndev, "%s -> CAN controller is ERROR PASSIVE .\n", __func__); } @@ -669,7 +558,7 @@ static void pch_can_error(struct net_device *ndev, u32 status) stats->rx_errors++; break; case PCH_CRC_ERR: - cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL; priv->can.can_stats.bus_error++; stats->rx_errors++; @@ -678,8 +567,11 @@ static void pch_can_error(struct net_device *ndev, u32 status) break; } + cf->data[6] = errc & PCH_TEC; + cf->data[7] = (errc & PCH_REC) >> 8; + priv->can.state = state; - netif_rx(skb); + netif_receive_skb(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; @@ -690,199 +582,202 @@ 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); - pch_can_set_int_enables(priv, PCH_CAN_NONE); + 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 int pch_can_rx_normal(struct net_device *ndev, u32 int_stat) +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; - u32 ide; - u32 rtr; - int i, j, k; 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; - /* Reading the messsage object from the Message RAM */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[0].creq, int_stat); + 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); - reg &= 0xffff; + /* Reading the MCONT register. */ + reg = ioread32(&priv->regs->ifregs[0].mcont); + + if (reg & PCH_IF_MCONT_EOB) + break; - for (k = int_stat; !(reg & PCH_IF_MCONT_EOB); k++) { /* If MsgLost bit set. */ if (reg & PCH_IF_MCONT_MSGLOST) { - dev_err(&priv->ndev->dev, "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_check_if_busy(&priv->regs->ifregs[0].creq, k); - - skb = alloc_can_err_skb(ndev, &cf); - if (!skb) - return -ENOMEM; - - priv->can.can_stats.error_passive++; - priv->can.state = CAN_STATE_ERROR_PASSIVE; - cf->can_id |= CAN_ERR_CRTL; - cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; - cf->data[2] |= CAN_ERR_PROT_OVERLOAD; - stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; - - netif_receive_skb(skb); + pch_can_rx_msg_lost(ndev, obj_num); rcv_pkts++; - goto RX_NEXT; + quota--; + obj_num++; + continue; + } else if (!(reg & PCH_IF_MCONT_NEWDAT)) { + obj_num++; + continue; } - if (!(reg & PCH_IF_MCONT_NEWDAT)) - goto RX_NEXT; skb = alloc_can_skb(priv->ndev, &cf); - if (!skb) - return -ENOMEM; + if (!skb) { + netdev_err(ndev, "alloc_can_skb Failed\n"); + return rcv_pkts; + } /* Get Received data */ - ide = ((ioread32(&priv->regs->ifregs[0].id2)) & PCH_ID2_XTD) >> - 14; - if (ide) { + id2 = ioread32(&priv->regs->ifregs[0].id2); + if (id2 & PCH_ID2_XTD) { id = (ioread32(&priv->regs->ifregs[0].id1) & 0xffff); - id |= (((ioread32(&priv->regs->ifregs[0].id2)) & - 0x1fff) << 16); - cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; + id |= (((id2) & 0x1fff) << 16); + cf->can_id = id | CAN_EFF_FLAG; } else { - id = (((ioread32(&priv->regs->ifregs[0].id2)) & - (CAN_SFF_MASK << 2)) >> 2); - cf->can_id = (id & CAN_SFF_MASK); + id = (id2 >> 2) & CAN_SFF_MASK; + cf->can_id = id; } - rtr = (ioread32(&priv->regs->ifregs[0].id2) & PCH_ID2_DIR); - if (rtr) { - cf->can_dlc = 0; + if (id2 & PCH_ID2_DIR) cf->can_id |= CAN_RTR_FLAG; - } else { - cf->can_dlc = - ((ioread32(&priv->regs->ifregs[0].mcont)) & 0x0f); - } - for (i = 0, j = 0; i < cf->can_dlc; j++) { - reg = ioread32(&priv->regs->ifregs[0].dataa1 + j*4); - cf->data[i++] = cpu_to_le32(reg & 0xff); - if (i == cf->can_dlc) - break; - cf->data[i++] = cpu_to_le32((reg >> 8) & 0xff); + cf->can_dlc = get_can_dlc((ioread32(&priv->regs-> + ifregs[0].mcont)) & 0xF); + + for (i = 0; i < cf->can_dlc; i += 2) { + data_reg = ioread16(&priv->regs->ifregs[0].data[i / 2]); + cf->data[i] = data_reg; + cf->data[i + 1] = data_reg >> 8; } netif_receive_skb(skb); rcv_pkts++; stats->rx_packets++; + quota--; stats->rx_bytes += cf->can_dlc; - if (k < 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_check_if_busy(&priv->regs->ifregs[0].creq, k); - } else if (k > PCH_FIFO_THRESH) { - pch_can_int_clr(priv, k); - } else if (k == PCH_FIFO_THRESH) { - int cnt; - for (cnt = 0; cnt < PCH_FIFO_THRESH; cnt++) - pch_can_int_clr(priv, cnt+1); - } -RX_NEXT: - /* Reading the messsage object from the Message RAM */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[0].creq, k); - reg = ioread32(&priv->regs->ifregs[0].mcont); - } + pch_fifo_thresh(priv, obj_num); + obj_num++; + } while (quota > 0); return rcv_pkts; } -static int pch_can_rx_poll(struct napi_struct *napi, int quota) + +static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat) { - struct net_device *ndev = napi->dev; 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; - int rcv_pkts = 0; u32 reg_stat; + int quota_save = quota; int_stat = pch_can_int_pending(priv); if (!int_stat) - return 0; + goto end; -INT_STAT: if (int_stat == PCH_STATUS_INT) { reg_stat = ioread32(&priv->regs->stat); - if (reg_stat & (PCH_BUS_OFF | PCH_LEC_ALL)) { - if ((reg_stat & PCH_LEC_ALL) != PCH_LEC_ALL) - pch_can_error(ndev, reg_stat); - } - if (reg_stat & PCH_TX_OK) { - iowrite32(PCH_CMASK_RX_TX_GET, - &priv->regs->ifregs[1].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[1].creq, - ioread32(&priv->regs->intr)); - pch_can_bit_clear(&priv->regs->stat, PCH_TX_OK); + 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_RX_OK) - pch_can_bit_clear(&priv->regs->stat, PCH_RX_OK); + 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 (int_stat == PCH_STATUS_INT) - goto INT_STAT; } -MSG_OBJ: + if (quota == 0) + goto end; + if ((int_stat >= PCH_RX_OBJ_START) && (int_stat <= PCH_RX_OBJ_END)) { - rcv_pkts = pch_can_rx_normal(ndev, int_stat); - if (rcv_pkts < 0) - return 0; + 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 */ - 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); - dlc = ioread32(&priv->regs->ifregs[1].mcont) & - PCH_IF_MCONT_DLC; - pch_can_check_if_busy(&priv->regs->ifregs[1].creq, int_stat); - if (dlc > 8) - dlc = 8; - stats->tx_bytes += dlc; - stats->tx_packets++; + pch_can_tx_complete(ndev, int_stat); } - int_stat = pch_can_int_pending(priv); - if (int_stat == PCH_STATUS_INT) - goto INT_STAT; - else if (int_stat >= 1 && int_stat <= 32) - goto MSG_OBJ; - +end: napi_complete(napi); pch_can_set_int_enables(priv, PCH_CAN_ALL); - return rcv_pkts; + return quota_save - quota; } static int pch_set_bittiming(struct net_device *ndev) @@ -891,17 +786,15 @@ static int pch_set_bittiming(struct net_device *ndev) const struct can_bittiming *bt = &priv->can.bittiming; u32 canbit; u32 bepe; - u32 brp; /* Setting the CCE bit for accessing the Can Timing register. */ pch_can_bit_set(&priv->regs->cont, PCH_CTRL_CCE); - brp = (bt->tq) / (1000000000/PCH_CAN_CLK) - 1; - canbit = brp & PCH_MSK_BITT_BRP; - canbit |= (bt->sjw - 1) << PCH_BIT_SJW; - canbit |= (bt->phase_seg1 + bt->prop_seg - 1) << PCH_BIT_TSEG1; - canbit |= (bt->phase_seg2 - 1) << PCH_BIT_TSEG2; - bepe = (brp & PCH_MSK_BRPE_BRPE) >> PCH_BIT_BRPE_BRPE; + 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); @@ -952,27 +845,18 @@ static int pch_can_open(struct net_device *ndev) struct pch_can_priv *priv = netdev_priv(ndev); int retval; - retval = pci_enable_msi(priv->dev); - if (retval) { - dev_info(&ndev->dev, "PCH CAN opened without MSI\n"); - priv->use_msi = 0; - } else { - dev_info(&ndev->dev, "PCH CAN opened with MSI\n"); - priv->use_msi = 1; - } - - /* Regsitering the interrupt. */ + /* Regstering the interrupt. */ retval = request_irq(priv->dev->irq, pch_can_interrupt, IRQF_SHARED, ndev->name, ndev); if (retval) { - dev_err(&ndev->dev, "request_irq failed.\n"); + netdev_err(ndev, "request_irq failed.\n"); goto req_irq_err; } /* Open common can device */ retval = open_candev(ndev); if (retval) { - dev_err(ndev->dev.parent, "open_candev() failed %d\n", retval); + netdev_err(ndev, "open_candev() failed %d\n", retval); goto err_open_candev; } @@ -986,9 +870,6 @@ static int pch_can_open(struct net_device *ndev) err_open_candev: free_irq(priv->dev->irq, ndev); req_irq_err: - if (priv->use_msi) - pci_disable_msi(priv->dev); - pch_can_release(priv); return retval; @@ -1002,97 +883,65 @@ static int pch_close(struct net_device *ndev) napi_disable(&priv->napi); pch_can_release(priv); free_irq(priv->dev->irq, ndev); - if (priv->use_msi) - pci_disable_msi(priv->dev); close_candev(ndev); priv->can.state = CAN_STATE_STOPPED; return 0; } -static int pch_get_msg_obj_sts(struct net_device *ndev, u32 obj_id) -{ - u32 buffer_status = 0; - struct pch_can_priv *priv = netdev_priv(ndev); - - /* Getting the message object status. */ - buffer_status = (u32) pch_can_get_buffer_status(priv); - - return buffer_status & obj_id; -} - - static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev) { - int i, j; struct pch_can_priv *priv = netdev_priv(ndev); struct can_frame *cf = (struct can_frame *)skb->data; - int tx_buffer_avail = 0; + int tx_obj_no; + int i; + u32 id2; if (can_dropped_invalid_skb(ndev, skb)) return NETDEV_TX_OK; - if (priv->tx_obj == PCH_TX_OBJ_END) { /* Point tail Obj */ - while (pch_get_msg_obj_sts(ndev, (((1 << PCH_TX_OBJ_NUM)-1) << - PCH_RX_OBJ_NUM))) - udelay(500); + 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; /* Point head of Tx Obj ID */ - tx_buffer_avail = priv->tx_obj; /* Point Tail of Tx Obj */ + priv->tx_obj = PCH_TX_OBJ_START; } else { - tx_buffer_avail = priv->tx_obj; + priv->tx_obj++; } - priv->tx_obj++; - - /* Reading the Msg Obj from the Msg RAM to the Interface register. */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[1].cmask); - pch_can_check_if_busy(&priv->regs->ifregs[1].creq, tx_buffer_avail); /* Setting the CMASK register. */ pch_can_bit_set(&priv->regs->ifregs[1].cmask, PCH_CMASK_ALL); /* If ID extended is set. */ - pch_can_bit_clear(&priv->regs->ifregs[1].id1, 0xffff); - pch_can_bit_clear(&priv->regs->ifregs[1].id2, 0x1fff | PCH_ID2_XTD); if (cf->can_id & CAN_EFF_FLAG) { - pch_can_bit_set(&priv->regs->ifregs[1].id1, - cf->can_id & 0xffff); - pch_can_bit_set(&priv->regs->ifregs[1].id2, - ((cf->can_id >> 16) & 0x1fff) | PCH_ID2_XTD); + iowrite32(cf->can_id & 0xffff, &priv->regs->ifregs[1].id1); + id2 = ((cf->can_id >> 16) & 0x1fff) | PCH_ID2_XTD; } else { - pch_can_bit_set(&priv->regs->ifregs[1].id1, 0); - pch_can_bit_set(&priv->regs->ifregs[1].id2, - (cf->can_id & CAN_SFF_MASK) << 2); + iowrite32(0, &priv->regs->ifregs[1].id1); + id2 = (cf->can_id & CAN_SFF_MASK) << 2; } - /* If remote frame has to be transmitted.. */ - if (cf->can_id & CAN_RTR_FLAG) - pch_can_bit_clear(&priv->regs->ifregs[1].id2, PCH_ID2_DIR); + id2 |= PCH_ID_MSGVAL; - for (i = 0, j = 0; i < cf->can_dlc; j++) { - iowrite32(le32_to_cpu(cf->data[i++]), - (&priv->regs->ifregs[1].dataa1) + j*4); - if (i == cf->can_dlc) - break; - iowrite32(le32_to_cpu(cf->data[i++] << 8), - (&priv->regs->ifregs[1].dataa1) + j*4); - } + /* If remote frame has to be transmitted.. */ + if (!(cf->can_id & CAN_RTR_FLAG)) + id2 |= PCH_ID2_DIR; - can_put_echo_skb(skb, ndev, tx_buffer_avail - PCH_RX_OBJ_END - 1); + iowrite32(id2, &priv->regs->ifregs[1].id2); - /* Updating the size of the data. */ - pch_can_bit_clear(&priv->regs->ifregs[1].mcont, 0x0f); - pch_can_bit_set(&priv->regs->ifregs[1].mcont, cf->can_dlc); + /* 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]); + } - /* Clearing IntPend, NewDat & TxRqst */ - pch_can_bit_clear(&priv->regs->ifregs[1].mcont, - PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND | - PCH_IF_MCONT_TXRQXT); + can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1); - /* Setting NewDat, TxRqst bits */ - pch_can_bit_set(&priv->regs->ifregs[1].mcont, - PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT); + /* 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_check_if_busy(&priv->regs->ifregs[1].creq, tx_buffer_avail); + pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, tx_obj_no); return NETDEV_TX_OK; } @@ -1101,29 +950,106 @@ 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 __devexit pch_can_remove(struct pci_dev *pdev) +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); - free_candev(priv->ndev); - pci_iounmap(pdev, priv->regs); + if (priv->use_msi) + pci_disable_msi(priv->dev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); pch_can_reset(priv); + pci_iounmap(pdev, priv->regs); + free_candev(priv->ndev); } #ifdef CONFIG_PM +static void pch_can_set_int_custom(struct pch_can_priv *priv) +{ + /* Clearing the IE, SIE and EIE bits of Can control register. */ + pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); + + /* Appropriately setting them. */ + pch_can_bit_set(&priv->regs->cont, + ((priv->int_enables & PCH_MSK_CTRL_IE_SIE_EIE) << 1)); +} + +/* This function retrieves interrupt enabled for the CAN device. */ +static u32 pch_can_get_int_enables(struct pch_can_priv *priv) +{ + /* Obtaining the status of IE, SIE and EIE interrupt bits. */ + return (ioread32(&priv->regs->cont) & PCH_CTRL_IE_SIE_EIE) >> 1; +} + +static u32 pch_can_get_rxtx_ir(struct pch_can_priv *priv, u32 buff_num, + enum pch_ifreg dir) +{ + u32 ie, enable; + + if (dir) + ie = PCH_IF_MCONT_RXIE; + else + ie = PCH_IF_MCONT_TXIE; + + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); + + if (((ioread32(&priv->regs->ifregs[dir].id2)) & PCH_ID_MSGVAL) && + ((ioread32(&priv->regs->ifregs[dir].mcont)) & ie)) + enable = 1; + else + enable = 0; + + return enable; +} + +static void pch_can_set_rx_buffer_link(struct pch_can_priv *priv, + u32 buffer_num, int set) +{ + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); + iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, + &priv->regs->ifregs[0].cmask); + if (set) + pch_can_bit_clear(&priv->regs->ifregs[0].mcont, + PCH_IF_MCONT_EOB); + else + pch_can_bit_set(&priv->regs->ifregs[0].mcont, PCH_IF_MCONT_EOB); + + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); +} + +static u32 pch_can_get_rx_buffer_link(struct pch_can_priv *priv, u32 buffer_num) +{ + u32 link; + + iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); + pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); + + if (ioread32(&priv->regs->ifregs[0].mcont) & PCH_IF_MCONT_EOB) + link = 0; + else + link = 1; + return link; +} + +static int pch_can_get_buffer_status(struct pch_can_priv *priv) +{ + return (ioread32(&priv->regs->treq1) & 0xffff) | + (ioread32(&priv->regs->treq2) << 16); +} + static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state) { - int i; /* Counter variable. */ - int retval; /* Return value. */ + int i; + int retval; u32 buf_stat; /* Variable for reading the transmit buffer status. */ - u32 counter = 0xFFFFFF; + int counter = PCH_COUNTER_LIMIT; struct net_device *dev = pci_get_drvdata(pdev); struct pch_can_priv *priv = netdev_priv(dev); @@ -1132,7 +1058,7 @@ static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state) pch_can_set_run_mode(priv, PCH_CAN_STOP); /* Indicate that we are aboutto/in suspend */ - priv->can.state = CAN_STATE_SLEEPING; + priv->can.state = CAN_STATE_STOPPED; /* Waiting for all transmission to complete. */ while (counter) { @@ -1146,20 +1072,22 @@ static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state) dev_err(&pdev->dev, "%s -> Transmission time out.\n", __func__); /* Save interrupt configuration and then disable them */ - pch_can_get_int_enables(priv, &(priv->int_enables)); + 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] = pch_can_get_rxtx_ir(priv, i, PCH_TX_IFREG); + 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] = pch_can_get_rxtx_ir(priv, i, PCH_RX_IFREG); - pch_can_get_rx_buffer_link(priv, i, &priv->rx_link[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 */ @@ -1178,8 +1106,8 @@ static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state) static int pch_can_resume(struct pci_dev *pdev) { - int i; /* Counter variable. */ - int retval; /* Return variable. */ + int i; + int retval; struct net_device *dev = pci_get_drvdata(pdev); struct pch_can_priv *priv = netdev_priv(dev); @@ -1212,15 +1140,15 @@ static int pch_can_resume(struct pci_dev *pdev) /* 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], PCH_TX_IFREG); + 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]); + 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], PCH_RX_IFREG); + pch_can_set_rxtx(priv, i, priv->rx_enable[i - 1], PCH_RX_IFREG); } /* Enable CAN Interrupts */ @@ -1240,14 +1168,15 @@ 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 = ioread32(&priv->regs->errc) & PCH_TEC; - bec->rxerr = (ioread32(&priv->regs->errc) & PCH_REC) >> 8; + bec->txerr = errc & PCH_TEC; + bec->rxerr = (errc & PCH_REC) >> 8; return 0; } -static int __devinit pch_can_probe(struct pci_dev *pdev, +static int pch_can_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct net_device *ndev; @@ -1300,7 +1229,17 @@ static int __devinit pch_can_probe(struct pci_dev *pdev, ndev->netdev_ops = &pch_can_netdev_ops; priv->can.clock.freq = PCH_CAN_CLK; /* Hz */ - netif_napi_add(ndev, &priv->napi, pch_can_rx_poll, PCH_RX_OBJ_END); + 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) { @@ -1311,6 +1250,8 @@ static int __devinit pch_can_probe(struct pci_dev *pdev, 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); @@ -1326,23 +1267,13 @@ static struct pci_driver pch_can_pci_driver = { .name = "pch_can", .id_table = pch_pci_tbl, .probe = pch_can_probe, - .remove = __devexit_p(pch_can_remove), + .remove = pch_can_remove, .suspend = pch_can_suspend, .resume = pch_can_resume, }; -static int __init pch_can_pci_init(void) -{ - return pci_register_driver(&pch_can_pci_driver); -} -module_init(pch_can_pci_init); - -static void __exit pch_can_pci_exit(void) -{ - pci_unregister_driver(&pch_can_pci_driver); -} -module_exit(pch_can_pci_exit); +module_pci_driver(pch_can_pci_driver); -MODULE_DESCRIPTION("Controller Area Network 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/rcar_can.c b/drivers/net/can/rcar_can.c new file mode 100644 index 00000000000..5268d216ecf --- /dev/null +++ b/drivers/net/can/rcar_can.c @@ -0,0 +1,876 @@ +/* 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> + +#define RCAR_CAN_DRV_NAME "rcar_can" + +/* 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; + 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; + cf->data[2] = CAN_ERR_PROT_UNSPEC; + } + 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); + 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, "clk_prepare_enable() failed, error %d\n", + err); + goto out; + } + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto out_clock; + } + napi_enable(&priv->napi); + err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev); + if (err) { + netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq); + 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_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->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, +}; + +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(napi); + 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 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; + int err = -ENODEV; + int irq; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + goto fail; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "No IRQ resource\n"); + 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, NULL); + if (IS_ERR(priv->clk)) { + err = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "cannot get clock: %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 = pdata->clock_select; + priv->can.clock.freq = clk_get_rate(priv->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 (reg_base=%p, irq=%u)\n", + priv->regs, 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)) { + 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; + + 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; + + if (netif_running(ndev)) { + 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 struct platform_driver rcar_can_driver = { + .driver = { + .name = RCAR_CAN_DRV_NAME, + .owner = THIS_MODULE, + .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/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index 6fdc031daaa..1e65cb6c259 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -1,12 +1,11 @@ menuconfig CAN_SJA1000 tristate "Philips/NXP SJA1000 devices" - depends on CAN_DEV && HAS_IOMEM + depends on HAS_IOMEM if CAN_SJA1000 config CAN_SJA1000_ISA tristate "ISA Bus based legacy SJA1000 driver" - depends on ISA ---help--- This driver adds legacy support for SJA1000 chips connected to the ISA bus using I/O port, memory mapped or indirect access. @@ -18,16 +17,16 @@ config CAN_SJA1000_PLATFORM 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. + 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_SJA1000_OF_PLATFORM - tristate "Generic OF Platform Bus based SJA1000 driver" - depends on PPC_OF +config CAN_EMS_PCMCIA + tristate "EMS CPC-CARD Card" + depends on PCMCIA ---help--- - This driver adds support for the SJA1000 chips connected to - the OpenFirmware "platform bus" found on embedded systems with - OpenFirmware bindings, e.g. if you have a PowerPC based system - you may want to enable this option. + 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" @@ -37,11 +36,40 @@ config CAN_EMS_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 the PCIcanx and PCIcan cards (1, 2 or + This driver is for the PCIcanx and PCIcan cards (1, 2 or 4 channel) from Kvaser (http://www.kvaser.com). config CAN_PLX_PCI @@ -58,16 +86,18 @@ config CAN_PLX_PCI - 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). + 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 index 2c591eb321c..531d5fcc97e 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -5,9 +5,11 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000.o obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o -obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o +obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o +obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o +obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c index 36f4f9780c3..fd13dbf07d9 100644 --- a/drivers/net/can/sja1000/ems_pci.c +++ b/drivers/net/can/sja1000/ems_pci.c @@ -13,8 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -168,12 +167,12 @@ static inline int ems_pci_check_chan(const struct sja1000_priv *priv) unsigned char res; /* Make sure SJA1000 is in reset mode */ - priv->write_reg(priv, REG_MOD, 1); + priv->write_reg(priv, SJA1000_MOD, 1); - priv->write_reg(priv, REG_CDR, CDR_PELICAN); + priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN); /* read reset-values */ - res = priv->read_reg(priv, REG_CDR); + res = priv->read_reg(priv, SJA1000_CDR); if (res == CDR_PELICAN) return 1; @@ -207,7 +206,6 @@ static void ems_pci_del_card(struct pci_dev *pdev) kfree(card); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static void ems_pci_card_reset(struct ems_pci_card *card) @@ -220,8 +218,8 @@ static void ems_pci_card_reset(struct ems_pci_card *card) * Probe PCI device for EMS CAN signature and register each available * CAN channel to SJA1000 Socket-CAN subsystem. */ -static int __devinit ems_pci_add_card(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int ems_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct sja1000_priv *priv; struct net_device *dev; @@ -238,7 +236,6 @@ static int __devinit ems_pci_add_card(struct pci_dev *pdev, /* Allocating card structures to hold addresses, ... */ card = kzalloc(sizeof(struct ems_pci_card), GFP_KERNEL); if (card == NULL) { - dev_err(&pdev->dev, "Unable to allocate memory\n"); pci_disable_device(pdev); return -ENOMEM; } @@ -326,6 +323,7 @@ static int __devinit ems_pci_add_card(struct pci_dev *pdev, priv->cdr = EMS_PCI_CDR; SET_NETDEV_DEV(dev, &pdev->dev); + dev->dev_id = i; if (card->version == 1) /* reset int flag of pita */ @@ -371,16 +369,4 @@ static struct pci_driver ems_pci_driver = { .remove = ems_pci_del_card, }; -static int __init ems_pci_init(void) -{ - return pci_register_driver(&ems_pci_driver); -} - -static void __exit ems_pci_exit(void) -{ - pci_unregister_driver(&ems_pci_driver); -} - -module_init(ems_pci_init); -module_exit(ems_pci_exit); - +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 00000000000..381de998d2f --- /dev/null +++ b/drivers/net/can/sja1000/ems_pcmcia.c @@ -0,0 +1,320 @@ +/* + * 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); + } + + err = request_irq(dev->irq, &ems_pcmcia_interrupt, IRQF_SHARED, + DRV_NAME, card); + if (!err) + return 0; + +failure_cleanup: + ems_pcmcia_del_card(pdev); + return err; +} + +/* + * Setup PCMCIA socket and probe for EMS CPC-CARD + */ +static int 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 index ed004cebd31..23b8e1324e2 100644 --- a/drivers/net/can/sja1000/kvaser_pci.c +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -26,8 +26,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -159,9 +158,9 @@ static int number_of_sja1000_chip(void __iomem *base_addr) for (i = 0; i < MAX_NO_OF_CHANNELS; i++) { /* reset chip */ iowrite8(MOD_RM, base_addr + - (i * KVASER_PCI_PORT_BYTES) + REG_MOD); + (i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD); status = ioread8(base_addr + - (i * KVASER_PCI_PORT_BYTES) + REG_MOD); + (i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD); /* check reset bit */ if (!(status & MOD_RM)) break; @@ -271,6 +270,7 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, 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); @@ -290,8 +290,8 @@ failure: return err; } -static int __devinit kvaser_pci_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int kvaser_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { int err; struct net_device *master_dev = NULL; @@ -379,7 +379,7 @@ failure: } -static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev) +static void kvaser_pci_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -387,25 +387,13 @@ static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static struct pci_driver kvaser_pci_driver = { .name = DRV_NAME, .id_table = kvaser_pci_tbl, .probe = kvaser_pci_init_one, - .remove = __devexit_p(kvaser_pci_remove_one), + .remove = kvaser_pci_remove_one, }; -static int __init kvaser_pci_init(void) -{ - return pci_register_driver(&kvaser_pci_driver); -} - -static void __exit kvaser_pci_exit(void) -{ - pci_unregister_driver(&kvaser_pci_driver); -} - -module_init(kvaser_pci_init); -module_exit(kvaser_pci_exit); +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 00000000000..564933ae218 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pci.c @@ -0,0 +1,761 @@ +/* + * 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_PCI_CHAN_MAX 4 + +static const u16 peak_pci_icr_masks[PEAK_PCI_CHAN_MAX] = { + 0x02, 0x01, 0x40, 0x80 +}; + +static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = { + {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_MPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {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,}, +#ifdef CONFIG_CAN_PEAK_PCIEC + {PEAK_PCI_VENDOR_ID, PEAK_PCIEC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, +#endif + {0,} +}; + +MODULE_DEVICE_TABLE(pci, peak_pci_tbl); + +#ifdef CONFIG_CAN_PEAK_PCIEC +/* + * PCAN-ExpressCard needs I2C bit-banging configuration option. + */ + +/* GPIOICR byte access offsets */ +#define PITA_GPOUT 0x18 /* GPx output value */ +#define PITA_GPIN 0x19 /* GPx input value */ +#define PITA_GPOEN 0x1A /* configure GPx as ouput pin */ + +/* I2C GP bits */ +#define PITA_GPIN_SCL 0x01 /* Serial Clock Line */ +#define PITA_GPIN_SDA 0x04 /* Serial DAta line */ + +#define PCA9553_1_SLAVEADDR (0xC4 >> 1) + +/* PCA9553 LS0 fields values */ +enum { + PCA9553_LOW, + PCA9553_HIGHZ, + PCA9553_PWM0, + PCA9553_PWM1 +}; + +/* LEDs control */ +#define PCA9553_ON PCA9553_LOW +#define PCA9553_OFF PCA9553_HIGHZ +#define PCA9553_SLOW PCA9553_PWM0 +#define PCA9553_FAST PCA9553_PWM1 + +#define PCA9553_LED(c) (1 << (c)) +#define PCA9553_LED_STATE(s, c) ((s) << ((c) << 1)) + +#define PCA9553_LED_ON(c) PCA9553_LED_STATE(PCA9553_ON, c) +#define PCA9553_LED_OFF(c) PCA9553_LED_STATE(PCA9553_OFF, c) +#define PCA9553_LED_SLOW(c) PCA9553_LED_STATE(PCA9553_SLOW, c) +#define PCA9553_LED_FAST(c) PCA9553_LED_STATE(PCA9553_FAST, c) +#define PCA9553_LED_MASK(c) PCA9553_LED_STATE(0x03, c) + +#define PCA9553_LED_OFF_ALL (PCA9553_LED_OFF(0) | PCA9553_LED_OFF(1)) + +#define PCA9553_LS0_INIT 0x40 /* initial value (!= from 0x00) */ + +struct peak_pciec_chan { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; + +struct peak_pciec_card { + void __iomem *cfg_base; /* Common for all channels */ + void __iomem *reg_base; /* first channel base address */ + u8 led_cache; /* leds state cache */ + + /* PCIExpressCard i2c data */ + struct i2c_algo_bit_data i2c_bit; + struct i2c_adapter led_chip; + struct delayed_work led_work; /* led delayed work */ + int chan_count; + struct peak_pciec_chan channel[PEAK_PCI_CHAN_MAX]; +}; + +/* "normal" pci register write callback is overloaded for leds control */ +static void peak_pci_write_reg(const struct sja1000_priv *priv, + int port, u8 val); + +static inline void pita_set_scl_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SCL; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static inline void pita_set_sda_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SDA; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void peak_pciec_init_pita_gpio(struct peak_pciec_card *card) +{ + /* raise SCL & SDA GPIOs to high-Z */ + pita_set_scl_highz(card); + pita_set_sda_highz(card); +} + +static void pita_setsda(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output sda always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SDA; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output sda with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SDA; + else + gp_outen |= PITA_GPIN_SDA; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void pita_setscl(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output scl always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SCL; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output scl with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SCL; + else + gp_outen |= PITA_GPIN_SCL; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static int pita_getsda(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_sda_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SDA) ? 1 : 0; +} + +static int pita_getscl(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_scl_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SCL) ? 1 : 0; +} + +/* + * write commands to the LED chip though the I2C-bus of the PCAN-PCIeC + */ +static int peak_pciec_write_pca9553(struct peak_pciec_card *card, + u8 offset, u8 data) +{ + u8 buffer[2] = { + offset, + data + }; + struct i2c_msg msg = { + .addr = PCA9553_1_SLAVEADDR, + .len = 2, + .buf = buffer, + }; + int ret; + + /* cache led mask */ + if ((offset == 5) && (data == card->led_cache)) + return 0; + + ret = i2c_transfer(&card->led_chip, &msg, 1); + if (ret < 0) + return ret; + + if (offset == 5) + card->led_cache = data; + + return 0; +} + +/* + * delayed work callback used to control the LEDs + */ +static void peak_pciec_led_work(struct work_struct *work) +{ + struct peak_pciec_card *card = + container_of(work, struct peak_pciec_card, led_work.work); + struct net_device *netdev; + u8 new_led = card->led_cache; + int i, up_count = 0; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_ON(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_SLOW(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); + + /* restart timer (except if no more configured channels) */ + if (up_count) + schedule_delayed_work(&card->led_work, HZ); +} + +/* + * set LEDs blinking state + */ +static void peak_pciec_set_leds(struct peak_pciec_card *card, u8 led_mask, u8 s) +{ + u8 new_led = card->led_cache; + int i; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCA9553_LED(i)) { + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_STATE(s, i); + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); +} + +/* + * start one second delayed work to control LEDs + */ +static void peak_pciec_start_led_work(struct peak_pciec_card *card) +{ + 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); + mdelay(5); + /* Leave parport mux mode */ + writeb(0x04, cfg_base + PITA_MISC + 3); + + icr = readw(cfg_base + PITA_ICR + 2); + + for (i = 0; i < channels; i++) { + dev = alloc_sja1000dev(sizeof(struct peak_pci_chan)); + if (!dev) { + err = -ENOMEM; + goto failure_remove_channels; + } + + priv = netdev_priv(dev); + chan = priv->priv; + + chan->cfg_base = cfg_base; + priv->reg_base = reg_base + i * PEAK_PCI_CHAN_SIZE; + + priv->read_reg = peak_pci_read_reg; + priv->write_reg = peak_pci_write_reg; + priv->post_irq = peak_pci_post_irq; + + priv->can.clock.freq = PEAK_PCI_CAN_CLOCK; + priv->ocr = PEAK_PCI_OCR; + priv->cdr = PEAK_PCI_CDR; + /* Neither a slave nor a single device distributes the clock */ + if (channels == 1 || i > 0) + priv->cdr |= CDR_CLK_OFF; + + /* Setup interrupt handling */ + priv->irq_flags = IRQF_SHARED; + dev->irq = pdev->irq; + + chan->icr_mask = peak_pci_icr_masks[i]; + icr |= chan->icr_mask; + + SET_NETDEV_DEV(dev, &pdev->dev); + 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) { + 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); + + return 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); + unregister_sja1000dev(dev); + free_sja1000dev(dev); + dev = prev_dev; + + if (!dev) { + /* do that only for first channel */ + if (chan->pciec_card) + peak_pciec_remove(chan->pciec_card); + break; + } + priv = netdev_priv(dev); + chan = priv->priv; + } + + pci_iounmap(pdev, reg_base); + pci_iounmap(pdev, cfg_base); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +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 00000000000..dd56133cc46 --- /dev/null +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -0,0 +1,744 @@ +/* + * 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(unsigned long arg) +{ + struct pcan_pccard *card = (struct pcan_pccard *)arg; + struct net_device *netdev; + int i, up_count = 0; + u8 ccr; + + ccr = card->ccr; + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_ON_CHAN(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_SLOW_CHAN(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + } + + /* write the new leds state */ + pcan_write_reg(card, PCC_CCR, ccr); + + /* restart timer (except if no more configured channels) */ + if (up_count) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * interrupt service routine + */ +static irqreturn_t pcan_isr(int irq, void *dev_id) +{ + struct pcan_pccard *card = dev_id; + int irq_handled; + + /* prevent from infinite loop */ + for (irq_handled = 0; irq_handled < PCC_ISR_MAX_LOOP; irq_handled++) { + /* handle shared interrupt and next loop */ + int nothing_to_handle = 1; + int i; + + /* check interrupt for each channel */ + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + + /* + * check whether the card is present before calling + * sja1000_interrupt() to speed up hotplug detection + */ + if (!pcan_pccard_present(card)) { + /* card unplugged during isr */ + return IRQ_NONE; + } + + /* + * should check whether all or SJA1000_MAX_IRQ + * interrupts have been handled: loop again to be sure. + */ + netdev = card->channel[i].netdev; + if (netdev && + sja1000_interrupt(irq, netdev) == IRQ_HANDLED) + nothing_to_handle = 0; + } + + if (nothing_to_handle) + break; + } + + return (irq_handled) ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * free all resources used by the channels and switch off leds and can power + */ +static void pcan_free_channels(struct pcan_pccard *card) +{ + int i; + u8 led_mask = 0; + + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + char name[IFNAMSIZ]; + + led_mask |= PCC_LED(i); + + netdev = card->channel[i].netdev; + if (!netdev) + continue; + + strncpy(name, netdev->name, IFNAMSIZ); + + unregister_sja1000dev(netdev); + + free_sja1000dev(netdev); + + dev_info(&card->pdev->dev, "%s removed\n", name); + } + + /* do it only if device not removed */ + if (pcan_pccard_present(card)) { + pcan_set_leds(card, led_mask, PCC_LED_OFF); + pcan_set_can_power(card, 0); + } +} + +/* + * check if a CAN controller is present at the specified location + */ +static inline int pcan_channel_present(struct sja1000_priv *priv) +{ + /* make sure SJA1000 is in reset mode */ + pcan_write_canreg(priv, 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 */ + mdelay(2); + + ccr &= ~PCC_CCR_RST_ALL; + pcan_write_reg(card, PCC_CCR, ccr); + + /* create one network device per channel detected */ + for (i = 0; i < ARRAY_SIZE(card->channel); i++) { + struct net_device *netdev; + struct sja1000_priv *priv; + + netdev = alloc_sja1000dev(0); + if (!netdev) { + err = -ENOMEM; + break; + } + + /* update linkages */ + priv = netdev_priv(netdev); + priv->priv = card; + SET_NETDEV_DEV(netdev, &pdev->dev); + 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 */ + init_timer(&card->led_timer); + card->led_timer.function = pcan_led_timer; + card->led_timer.data = (unsigned long)card; + + /* request the given irq */ + err = request_irq(pdev->irq, &pcan_isr, IRQF_SHARED, PCC_NAME, card); + if (err) { + dev_err(&pdev->dev, "couldn't request irq%d\n", pdev->irq); + goto probe_err_5; + } + + /* power on the connectors */ + pcan_set_can_power(card, 1); + + return 0; + +probe_err_5: + /* unregister can devices from network */ + pcan_free_channels(card); + +probe_err_4: + ioport_unmap(card->ioport_addr); + +probe_err_3: + kfree(card); + pdev->priv = NULL; + +probe_err_2: + pcmcia_disable_device(pdev); + +probe_err_1: + return err; +} + +/* + * release claimed resources + */ +static void pcan_remove(struct pcmcia_device *pdev) +{ + pcan_free(pdev); + pcmcia_disable_device(pdev); +} + +static struct pcmcia_driver pcan_driver = { + .name = PCC_NAME, + .probe = pcan_probe, + .remove = pcan_remove, + .id_table = pcan_table, +}; +module_pcmcia_driver(pcan_driver); diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c index 437b5c716a2..ec39b7cb228 100644 --- a/drivers/net/can/sja1000/plx_pci.c +++ b/drivers/net/can/sja1000/plx_pci.c @@ -16,8 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -43,7 +42,10 @@ MODULE_SUPPORTED_DEVICE("Adlink PCI-7841/cPCI-7841, " "TEWS TECHNOLOGIES TPMC810, " "esd CAN-PCI/CPCI/PCI104/200, " "esd CAN-PCI/PMC/266, " - "esd CAN-PCIe/2000") + "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 @@ -121,11 +123,23 @@ struct plx_pci_card { #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 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 + static void plx_pci_reset_common(struct pci_dev *pdev); static void plx_pci_reset_marathon(struct pci_dev *pdev); static void plx9056_pci_reset_common(struct pci_dev *pdev); @@ -153,7 +167,7 @@ struct plx_pci_card_info { void (*reset_func)(struct pci_dev *pdev); }; -static struct plx_pci_card_info plx_pci_card_info_adlink __devinitdata = { +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} }, @@ -161,7 +175,7 @@ static struct plx_pci_card_info plx_pci_card_info_adlink __devinitdata = { /* based on PLX9052 */ }; -static struct plx_pci_card_info plx_pci_card_info_adlink_se __devinitdata = { +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} }, @@ -169,7 +183,7 @@ static struct plx_pci_card_info plx_pci_card_info_adlink_se __devinitdata = { /* based on PLX9052 */ }; -static struct plx_pci_card_info plx_pci_card_info_esd200 __devinitdata = { +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} }, @@ -177,7 +191,7 @@ static struct plx_pci_card_info plx_pci_card_info_esd200 __devinitdata = { /* based on PLX9030/9050 */ }; -static struct plx_pci_card_info plx_pci_card_info_esd266 __devinitdata = { +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} }, @@ -185,7 +199,7 @@ static struct plx_pci_card_info plx_pci_card_info_esd266 __devinitdata = { /* based on PLX9056 */ }; -static struct plx_pci_card_info plx_pci_card_info_esd2000 __devinitdata = { +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} }, @@ -193,7 +207,15 @@ static struct plx_pci_card_info plx_pci_card_info_esd2000 __devinitdata = { /* based on PEX8311 */ }; -static struct plx_pci_card_info plx_pci_card_info_marathon __devinitdata = { +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 = { "Marathon CAN-bus-PCI", 2, PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {4, 0x00, 0x00} }, @@ -201,7 +223,7 @@ static struct plx_pci_card_info plx_pci_card_info_marathon __devinitdata = { /* based on PLX9052 */ }; -static struct plx_pci_card_info plx_pci_card_info_tews __devinitdata = { +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} }, @@ -209,6 +231,22 @@ static struct plx_pci_card_info plx_pci_card_info_tews __devinitdata = { /* 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 DEFINE_PCI_DEVICE_TABLE(plx_pci_tbl) = { { /* Adlink PCI-7841/cPCI-7841 */ @@ -267,6 +305,13 @@ static DEFINE_PCI_DEVICE_TABLE(plx_pci_tbl) = { (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, @@ -280,6 +325,20 @@ static DEFINE_PCI_DEVICE_TABLE(plx_pci_tbl) = { 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 + }, { 0,} }; MODULE_DEVICE_TABLE(pci, plx_pci_tbl); @@ -309,20 +368,20 @@ static inline int plx_pci_check_sja1000(const struct sja1000_priv *priv) */ if ((priv->read_reg(priv, REG_CR) & REG_CR_BASICCAN_INITIAL_MASK) == REG_CR_BASICCAN_INITIAL && - (priv->read_reg(priv, REG_SR) == REG_SR_BASICCAN_INITIAL) && - (priv->read_reg(priv, REG_IR) == REG_IR_BASICCAN_INITIAL)) + (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, REG_CDR, CDR_PELICAN); + 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, REG_MOD) == REG_MOD_PELICAN_INITIAL && - priv->read_reg(priv, REG_SR) == REG_SR_PELICAN_INITIAL && - priv->read_reg(priv, REG_IR) == REG_IR_PELICAN_INITIAL) + 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; @@ -383,7 +442,7 @@ static void plx_pci_reset_marathon(struct pci_dev *pdev) { void __iomem *reset_addr; int i; - int reset_bar[2] = {3, 5}; + static const int reset_bar[2] = {3, 5}; plx_pci_reset_common(pdev); @@ -408,7 +467,7 @@ static void plx_pci_del_card(struct pci_dev *pdev) struct sja1000_priv *priv; int i = 0; - for (i = 0; i < card->channels; i++) { + for (i = 0; i < PLX_PCI_MAX_CHAN; i++) { dev = card->net_dev[i]; if (!dev) continue; @@ -438,15 +497,14 @@ static void plx_pci_del_card(struct pci_dev *pdev) kfree(card); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } /* * Probe PLX90xx based device for the SJA1000 chips and register each * available CAN channel to SJA1000 Socket-CAN subsystem. */ -static int __devinit plx_pci_add_card(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int plx_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct sja1000_priv *priv; struct net_device *dev; @@ -469,7 +527,6 @@ static int __devinit plx_pci_add_card(struct pci_dev *pdev, /* Allocate card structures to hold addresses, ... */ card = kzalloc(sizeof(*card), GFP_KERNEL); if (!card) { - dev_err(&pdev->dev, "Unable to allocate memory\n"); pci_disable_device(pdev); return -ENOMEM; } @@ -530,13 +587,13 @@ static int __devinit plx_pci_add_card(struct pci_dev *pdev, 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); - free_sja1000dev(dev); goto failure_cleanup; } @@ -549,6 +606,7 @@ static int __devinit plx_pci_add_card(struct pci_dev *pdev, dev_err(&pdev->dev, "Channel #%d not detected\n", i + 1); free_sja1000dev(dev); + card->net_dev[i] = NULL; } } @@ -589,15 +647,4 @@ static struct pci_driver plx_pci_driver = { .remove = plx_pci_del_card, }; -static int __init plx_pci_init(void) -{ - return pci_register_driver(&plx_pci_driver); -} - -static void __exit plx_pci_exit(void) -{ - pci_unregister_driver(&plx_pci_driver); -} - -module_init(plx_pci_init); -module_exit(plx_pci_exit); +module_pci_driver(plx_pci_driver); diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 0a8de01d52f..f31499a32d7 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -40,8 +40,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> @@ -62,6 +60,7 @@ #include <linux/can/dev.h> #include <linux/can/error.h> +#include <linux/can/led.h> #include "sja1000.h" @@ -71,7 +70,7 @@ MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION(DRV_NAME "CAN netdevice driver"); -static struct can_bittiming_const sja1000_bittiming_const = { +static const struct can_bittiming_const sja1000_bittiming_const = { .name = DRV_NAME, .tseg1_min = 1, .tseg1_max = 16, @@ -92,18 +91,22 @@ static void sja1000_write_cmdreg(struct sja1000_priv *priv, u8 val) * the write_reg() operation - especially on SMP systems. */ spin_lock_irqsave(&priv->cmdreg_lock, flags); - priv->write_reg(priv, REG_CMR, val); - priv->read_reg(priv, REG_SR); + 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 && (priv->read_reg(priv, 0) == 0xFF)) { - printk(KERN_INFO "%s: probing @0x%lX failed\n", - DRV_NAME, dev->base_addr); + if (priv->reg_base && sja1000_is_absent(priv)) { + netdev_err(dev, "probing failed\n"); return 0; } return -1; @@ -112,11 +115,11 @@ static int sja1000_probe_chip(struct net_device *dev) static void set_reset_mode(struct net_device *dev) { struct sja1000_priv *priv = netdev_priv(dev); - unsigned char status = priv->read_reg(priv, REG_MOD); + unsigned char status = priv->read_reg(priv, SJA1000_MOD); int i; /* disable interrupts */ - priv->write_reg(priv, REG_IER, IRQ_OFF); + priv->write_reg(priv, SJA1000_IER, IRQ_OFF); for (i = 0; i < 100; i++) { /* check reset bit */ @@ -125,18 +128,19 @@ static void set_reset_mode(struct net_device *dev) return; } - priv->write_reg(priv, REG_MOD, MOD_RM); /* reset chip */ + /* reset chip */ + priv->write_reg(priv, SJA1000_MOD, MOD_RM); udelay(10); - status = priv->read_reg(priv, REG_MOD); + status = priv->read_reg(priv, SJA1000_MOD); } - dev_err(dev->dev.parent, "setting SJA1000 into reset mode failed!\n"); + netdev_err(dev, "setting SJA1000 into reset mode failed!\n"); } static void set_normal_mode(struct net_device *dev) { struct sja1000_priv *priv = netdev_priv(dev); - unsigned char status = priv->read_reg(priv, REG_MOD); + unsigned char status = priv->read_reg(priv, SJA1000_MOD); int i; for (i = 0; i < 100; i++) { @@ -145,20 +149,25 @@ static void set_normal_mode(struct net_device *dev) priv->can.state = CAN_STATE_ERROR_ACTIVE; /* enable interrupts */ if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) - priv->write_reg(priv, REG_IER, IRQ_ALL); + priv->write_reg(priv, SJA1000_IER, IRQ_ALL); else - priv->write_reg(priv, REG_IER, + priv->write_reg(priv, SJA1000_IER, IRQ_ALL & ~IRQ_BEI); return; } /* set chip to normal mode */ - priv->write_reg(priv, REG_MOD, 0x00); + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + priv->write_reg(priv, SJA1000_MOD, MOD_LOM); + else + priv->write_reg(priv, SJA1000_MOD, 0x00); + udelay(10); - status = priv->read_reg(priv, REG_MOD); + + status = priv->read_reg(priv, SJA1000_MOD); } - dev_err(dev->dev.parent, "setting SJA1000 into normal mode failed!\n"); + netdev_err(dev, "setting SJA1000 into normal mode failed!\n"); } static void sja1000_start(struct net_device *dev) @@ -170,9 +179,9 @@ static void sja1000_start(struct net_device *dev) set_reset_mode(dev); /* Clear error counters and error code capture */ - priv->write_reg(priv, REG_TXERR, 0x0); - priv->write_reg(priv, REG_RXERR, 0x0); - priv->read_reg(priv, REG_ECC); + priv->write_reg(priv, SJA1000_TXERR, 0x0); + priv->write_reg(priv, SJA1000_RXERR, 0x0); + priv->read_reg(priv, SJA1000_ECC); /* leave reset mode */ set_normal_mode(dev); @@ -180,11 +189,6 @@ static void sja1000_start(struct net_device *dev) static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) { - struct sja1000_priv *priv = netdev_priv(dev); - - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: sja1000_start(dev); @@ -211,11 +215,10 @@ static int sja1000_set_bittiming(struct net_device *dev) if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) btr1 |= 0x80; - dev_info(dev->dev.parent, - "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); - priv->write_reg(priv, REG_BTR0, btr0); - priv->write_reg(priv, REG_BTR1, btr1); + priv->write_reg(priv, SJA1000_BTR0, btr0); + priv->write_reg(priv, SJA1000_BTR1, btr1); return 0; } @@ -225,8 +228,8 @@ static int sja1000_get_berr_counter(const struct net_device *dev, { struct sja1000_priv *priv = netdev_priv(dev); - bec->txerr = priv->read_reg(priv, REG_TXERR); - bec->rxerr = priv->read_reg(priv, REG_RXERR); + bec->txerr = priv->read_reg(priv, SJA1000_TXERR); + bec->rxerr = priv->read_reg(priv, SJA1000_RXERR); return 0; } @@ -244,20 +247,20 @@ static void chipset_init(struct net_device *dev) struct sja1000_priv *priv = netdev_priv(dev); /* set clock divider and output control register */ - priv->write_reg(priv, REG_CDR, priv->cdr | CDR_PELICAN); + priv->write_reg(priv, SJA1000_CDR, priv->cdr | CDR_PELICAN); /* set acceptance filter (accept all) */ - priv->write_reg(priv, REG_ACCC0, 0x00); - priv->write_reg(priv, REG_ACCC1, 0x00); - priv->write_reg(priv, REG_ACCC2, 0x00); - priv->write_reg(priv, REG_ACCC3, 0x00); + priv->write_reg(priv, 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, REG_ACCM0, 0xFF); - priv->write_reg(priv, REG_ACCM1, 0xFF); - priv->write_reg(priv, REG_ACCM2, 0xFF); - priv->write_reg(priv, REG_ACCM3, 0xFF); + priv->write_reg(priv, 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, REG_OCR, priv->ocr | OCR_MODE_NORMAL); + priv->write_reg(priv, SJA1000_OCR, priv->ocr | OCR_MODE_NORMAL); } /* @@ -286,21 +289,21 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb, id = cf->can_id; if (id & CAN_RTR_FLAG) - fi |= FI_RTR; + fi |= SJA1000_FI_RTR; if (id & CAN_EFF_FLAG) { - fi |= FI_FF; - dreg = EFF_BUF; - priv->write_reg(priv, REG_FI, fi); - priv->write_reg(priv, REG_ID1, (id & 0x1fe00000) >> (5 + 16)); - priv->write_reg(priv, REG_ID2, (id & 0x001fe000) >> (5 + 8)); - priv->write_reg(priv, REG_ID3, (id & 0x00001fe0) >> 5); - priv->write_reg(priv, REG_ID4, (id & 0x0000001f) << 3); + 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 = SFF_BUF; - priv->write_reg(priv, REG_FI, fi); - priv->write_reg(priv, REG_ID1, (id & 0x000007f8) >> 3); - priv->write_reg(priv, REG_ID2, (id & 0x00000007) << 5); + 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++) @@ -308,7 +311,10 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb, can_put_echo_skb(skb, dev, 0); - sja1000_write_cmdreg(priv, CMD_TR); + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + sja1000_write_cmdreg(priv, CMD_TR | CMD_AT); + else + sja1000_write_cmdreg(priv, CMD_TR); return NETDEV_TX_OK; } @@ -329,27 +335,27 @@ static void sja1000_rx(struct net_device *dev) if (skb == NULL) return; - fi = priv->read_reg(priv, REG_FI); + fi = priv->read_reg(priv, SJA1000_FI); - if (fi & FI_FF) { + if (fi & SJA1000_FI_FF) { /* extended frame format (EFF) */ - dreg = EFF_BUF; - id = (priv->read_reg(priv, REG_ID1) << (5 + 16)) - | (priv->read_reg(priv, REG_ID2) << (5 + 8)) - | (priv->read_reg(priv, REG_ID3) << 5) - | (priv->read_reg(priv, REG_ID4) >> 3); + 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 = SFF_BUF; - id = (priv->read_reg(priv, REG_ID1) << 3) - | (priv->read_reg(priv, REG_ID2) >> 5); + dreg = SJA1000_SFF_BUF; + id = (priv->read_reg(priv, SJA1000_ID1) << 3) + | (priv->read_reg(priv, SJA1000_ID2) >> 5); } - if (fi & FI_RTR) { + cf->can_dlc = get_can_dlc(fi & 0x0F); + if (fi & SJA1000_FI_RTR) { id |= CAN_RTR_FLAG; } else { - cf->can_dlc = get_can_dlc(fi & 0x0F); for (i = 0; i < cf->can_dlc; i++) cf->data[i] = priv->read_reg(priv, dreg++); } @@ -363,6 +369,8 @@ static void sja1000_rx(struct net_device *dev) stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + + can_led_event(dev, CAN_LED_EVENT_RX); } static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) @@ -380,7 +388,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) if (isrc & IRQ_DOI) { /* data overrun interrupt */ - dev_dbg(dev->dev.parent, "data overrun interrupt\n"); + 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++; @@ -390,7 +398,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) if (isrc & IRQ_EI) { /* error warning interrupt */ - dev_dbg(dev->dev.parent, "error warning interrupt\n"); + netdev_dbg(dev, "error warning interrupt\n"); if (status & SR_BS) { state = CAN_STATE_BUS_OFF; @@ -406,7 +414,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) priv->can.can_stats.bus_error++; stats->rx_errors++; - ecc = priv->read_reg(priv, REG_ECC); + ecc = priv->read_reg(priv, SJA1000_ECC); cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; @@ -425,13 +433,13 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) cf->data[3] = ecc & ECC_SEG; break; } - /* Error occured during transmission? */ + /* Error occurred during transmission? */ if ((ecc & ECC_DIR) == 0) cf->data[2] |= CAN_ERR_PROT_TX; } if (isrc & IRQ_EPI) { /* error passive interrupt */ - dev_dbg(dev->dev.parent, "error passive interrupt\n"); + netdev_dbg(dev, "error passive interrupt\n"); if (status & SR_ES) state = CAN_STATE_ERROR_PASSIVE; else @@ -439,8 +447,8 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) } if (isrc & IRQ_ALI) { /* arbitration lost interrupt */ - dev_dbg(dev->dev.parent, "arbitration lost interrupt\n"); - alc = priv->read_reg(priv, REG_ALC); + 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; @@ -449,8 +457,8 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING || state == CAN_STATE_ERROR_PASSIVE)) { - uint8_t rxerr = priv->read_reg(priv, REG_RXERR); - uint8_t txerr = priv->read_reg(priv, REG_TXERR); + uint8_t rxerr = priv->read_reg(priv, SJA1000_RXERR); + uint8_t txerr = priv->read_reg(priv, SJA1000_TXERR); cf->can_id |= CAN_ERR_CRTL; if (state == CAN_STATE_ERROR_WARNING) { priv->can.can_stats.error_warning++; @@ -485,32 +493,48 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) uint8_t isrc, status; int n = 0; - /* Shared interrupts and IRQ off? */ - if (priv->read_reg(priv, REG_IER) == IRQ_OFF) - return IRQ_NONE; - if (priv->pre_irq) priv->pre_irq(priv); - while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) { - n++; - status = priv->read_reg(priv, REG_SR); + /* 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) - dev_warn(dev->dev.parent, "wakeup interrupt\n"); + netdev_warn(dev, "wakeup interrupt\n"); if (isrc & IRQ_TI) { - /* transmission complete interrupt */ - stats->tx_bytes += priv->read_reg(priv, REG_FI) & 0xf; - stats->tx_packets++; - can_get_echo_skb(dev, 0); + /* 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, REG_SR); + 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)) { @@ -518,13 +542,14 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) if (sja1000_err(dev, isrc, status)) break; } + n++; } - +out: if (priv->post_irq) priv->post_irq(priv); if (n >= SJA1000_MAX_IRQ) - dev_dbg(dev->dev.parent, "%d messages handled in ISR", n); + netdev_dbg(dev, "%d messages handled in ISR", n); return (n) ? IRQ_HANDLED : IRQ_NONE; } @@ -555,7 +580,8 @@ static int sja1000_open(struct net_device *dev) /* init and start chi */ sja1000_start(dev); - priv->open_time = jiffies; + + can_led_event(dev, CAN_LED_EVENT_OPEN); netif_start_queue(dev); @@ -574,7 +600,7 @@ static int sja1000_close(struct net_device *dev) close_candev(dev); - priv->open_time = 0; + can_led_event(dev, CAN_LED_EVENT_STOP); return 0; } @@ -597,7 +623,8 @@ struct net_device *alloc_sja1000dev(int sizeof_priv) priv->can.do_set_mode = sja1000_set_mode; priv->can.do_get_berr_counter = sja1000_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | - CAN_CTRLMODE_BERR_REPORTING; + CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_ONE_SHOT; spin_lock_init(&priv->cmdreg_lock); @@ -615,13 +642,16 @@ void free_sja1000dev(struct net_device *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_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; @@ -631,7 +661,12 @@ int register_sja1000dev(struct net_device *dev) set_reset_mode(dev); chipset_init(dev); - return register_candev(dev); + ret = register_candev(dev); + + if (!ret) + devm_can_led_init(dev); + + return ret; } EXPORT_SYMBOL_GPL(register_sja1000dev); diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h index de8e778f683..9d46398f815 100644 --- a/drivers/net/can/sja1000/sja1000.h +++ b/drivers/net/can/sja1000/sja1000.h @@ -40,13 +40,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #ifndef SJA1000_DEV_H #define SJA1000_DEV_H +#include <linux/irqreturn.h> #include <linux/can/dev.h> #include <linux/can/platform/sja1000.h> @@ -55,46 +54,46 @@ #define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ /* SJA1000 registers - manual section 6.4 (Pelican Mode) */ -#define REG_MOD 0x00 -#define REG_CMR 0x01 -#define REG_SR 0x02 -#define REG_IR 0x03 -#define REG_IER 0x04 -#define REG_ALC 0x0B -#define REG_ECC 0x0C -#define REG_EWL 0x0D -#define REG_RXERR 0x0E -#define REG_TXERR 0x0F -#define REG_ACCC0 0x10 -#define REG_ACCC1 0x11 -#define REG_ACCC2 0x12 -#define REG_ACCC3 0x13 -#define REG_ACCM0 0x14 -#define REG_ACCM1 0x15 -#define REG_ACCM2 0x16 -#define REG_ACCM3 0x17 -#define REG_RMC 0x1D -#define REG_RBSA 0x1E +#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 REG_BTR0 0x06 -#define REG_BTR1 0x07 -#define REG_OCR 0x08 -#define REG_CDR 0x1F +#define SJA1000_BTR0 0x06 +#define SJA1000_BTR1 0x07 +#define SJA1000_OCR 0x08 +#define SJA1000_CDR 0x1F -#define REG_FI 0x10 -#define SFF_BUF 0x13 -#define EFF_BUF 0x15 +#define SJA1000_FI 0x10 +#define SJA1000_SFF_BUF 0x13 +#define SJA1000_EFF_BUF 0x15 -#define FI_FF 0x80 -#define FI_RTR 0x40 +#define SJA1000_FI_FF 0x80 +#define SJA1000_FI_RTR 0x40 -#define REG_ID1 0x11 -#define REG_ID2 0x12 -#define REG_ID3 0x13 -#define REG_ID4 0x14 +#define SJA1000_ID1 0x11 +#define SJA1000_ID2 0x12 +#define SJA1000_ID3 0x13 +#define SJA1000_ID4 0x14 -#define CAN_RAM 0x20 +#define SJA1000_CAN_RAM 0x20 /* mode register */ #define MOD_RM 0x01 @@ -153,7 +152,6 @@ */ struct sja1000_priv { struct can_priv can; /* must be the first member */ - int open_time; struct sk_buff *echo_skb; /* the lower-layer is responsible for appropriate locking */ diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c index 496223e9e2f..014695d7e6a 100644 --- a/drivers/net/can/sja1000/sja1000_isa.c +++ b/drivers/net/can/sja1000/sja1000_isa.c @@ -11,13 +11,12 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/isa.h> +#include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/netdevice.h> #include <linux/delay.h> @@ -42,11 +41,12 @@ MODULE_LICENSE("GPL v2"); static unsigned long port[MAXDEV]; static unsigned long mem[MAXDEV]; -static int __devinitdata irq[MAXDEV]; -static int __devinitdata clk[MAXDEV]; -static char __devinitdata cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; -static char __devinitdata ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; -static char __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; +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_array(port, ulong, NULL, S_IRUGO); MODULE_PARM_DESC(port, "I/O port number"); @@ -54,7 +54,7 @@ MODULE_PARM_DESC(port, "I/O port number"); module_param_array(mem, ulong, NULL, S_IRUGO); MODULE_PARM_DESC(mem, "I/O memory address"); -module_param_array(indirect, byte, NULL, S_IRUGO); +module_param_array(indirect, int, NULL, S_IRUGO); MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); module_param_array(irq, int, NULL, S_IRUGO); @@ -75,6 +75,8 @@ MODULE_PARM_DESC(ocr, "Output control register " #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); @@ -100,41 +102,40 @@ static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv, static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv, int reg) { - unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags, base = (unsigned long)priv->reg_base; + u8 readval; + spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); outb(reg, base); - return inb(base + 1); + 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 base = (unsigned long)priv->reg_base; + 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 __devinit sja1000_isa_match(struct device *pdev, unsigned int idx) -{ - if (port[idx] || mem[idx]) { - if (irq[idx]) - return 1; - } else if (idx) - return 0; - - dev_err(pdev, "insufficient parameters supplied\n"); - return 0; -} - -static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx) +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; @@ -176,6 +177,7 @@ static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int 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; @@ -189,31 +191,32 @@ static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx) else priv->can.clock.freq = CLK_DEFAULT / 2; - if (ocr[idx] != -1) - priv->ocr = ocr[idx] & 0xff; - else if (ocr[0] != -1) - priv->ocr = ocr[0] & 0xff; + 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] != -1) - priv->cdr = cdr[idx] & 0xff; - else if (cdr[0] != -1) - priv->cdr = cdr[0] & 0xff; + if (cdr[idx] != 0xff) + priv->cdr = cdr[idx]; + else if (cdr[0] != 0xff) + priv->cdr = cdr[0]; else priv->cdr = CDR_DEFAULT; - dev_set_drvdata(pdev, dev); - SET_NETDEV_DEV(dev, pdev); + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + dev->dev_id = idx; err = register_sja1000dev(dev); if (err) { - dev_err(pdev, "registering %s failed (err=%d)\n", + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", DRV_NAME, err); goto exit_unmap; } - dev_info(pdev, "%s device registered (reg_base=0x%p, irq=%d)\n", + dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n", DRV_NAME, priv->reg_base, dev->irq); return 0; @@ -229,13 +232,13 @@ static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx) return err; } -static int __devexit sja1000_isa_remove(struct device *pdev, unsigned int idx) +static int sja1000_isa_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(pdev); + struct net_device *dev = platform_get_drvdata(pdev); struct sja1000_priv *priv = netdev_priv(dev); + int idx = pdev->id; unregister_sja1000dev(dev); - dev_set_drvdata(pdev, NULL); if (mem[idx]) { iounmap(priv->reg_base); @@ -251,29 +254,70 @@ static int __devexit sja1000_isa_remove(struct device *pdev, unsigned int idx) return 0; } -static struct isa_driver sja1000_isa_driver = { - .match = sja1000_isa_match, +static struct platform_driver sja1000_isa_driver = { .probe = sja1000_isa_probe, - .remove = __devexit_p(sja1000_isa_remove), + .remove = sja1000_isa_remove, .driver = { .name = DRV_NAME, + .owner = THIS_MODULE, }, }; static int __init sja1000_isa_init(void) { - int err = isa_register_driver(&sja1000_isa_driver, MAXDEV); + 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]); + } - if (!err) - printk(KERN_INFO - "Legacy %s driver for max. %d devices registered\n", - DRV_NAME, MAXDEV); return err; } static void __exit sja1000_isa_exit(void) { - isa_unregister_driver(&sja1000_isa_driver); + 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); diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c deleted file mode 100644 index 09c3e9db931..00000000000 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Driver for SJA1000 CAN controllers on the OpenFirmware platform bus - * - * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the version 2 of the GNU General Public License - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* This is a generic driver for SJA1000 chips on the OpenFirmware platform - * bus found on embedded PowerPC systems. You need a SJA1000 CAN node - * definition in your flattened device tree source (DTS) file similar to: - * - * can@3,100 { - * compatible = "nxp,sja1000"; - * reg = <3 0x100 0x80>; - * interrupts = <2 0>; - * interrupt-parent = <&mpic>; - * nxp,external-clock-frequency = <16000000>; - * }; - * - * See "Documentation/powerpc/dts-bindings/can/sja1000.txt" for further - * information. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/netdevice.h> -#include <linux/delay.h> -#include <linux/can/dev.h> - -#include <linux/of_platform.h> -#include <asm/prom.h> - -#include "sja1000.h" - -#define DRV_NAME "sja1000_of_platform" - -MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); -MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus"); -MODULE_LICENSE("GPL v2"); - -#define SJA1000_OFP_CAN_CLOCK (16000000 / 2) - -#define SJA1000_OFP_OCR OCR_TX0_PULLDOWN -#define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF) - -static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg) -{ - return in_8(priv->reg_base + reg); -} - -static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, - int reg, u8 val) -{ - out_8(priv->reg_base + reg, val); -} - -static int __devexit sja1000_ofp_remove(struct platform_device *ofdev) -{ - struct net_device *dev = dev_get_drvdata(&ofdev->dev); - struct sja1000_priv *priv = netdev_priv(dev); - struct device_node *np = ofdev->dev.of_node; - struct resource res; - - dev_set_drvdata(&ofdev->dev, NULL); - - unregister_sja1000dev(dev); - free_sja1000dev(dev); - iounmap(priv->reg_base); - irq_dispose_mapping(dev->irq); - - of_address_to_resource(np, 0, &res); - release_mem_region(res.start, resource_size(&res)); - - return 0; -} - -static int __devinit sja1000_ofp_probe(struct platform_device *ofdev, - const struct of_device_id *id) -{ - struct device_node *np = ofdev->dev.of_node; - struct net_device *dev; - struct sja1000_priv *priv; - struct resource res; - const u32 *prop; - int err, irq, res_size, prop_size; - void __iomem *base; - - err = of_address_to_resource(np, 0, &res); - if (err) { - dev_err(&ofdev->dev, "invalid address\n"); - return err; - } - - res_size = resource_size(&res); - - if (!request_mem_region(res.start, res_size, DRV_NAME)) { - dev_err(&ofdev->dev, "couldn't request %pR\n", &res); - return -EBUSY; - } - - base = ioremap_nocache(res.start, res_size); - if (!base) { - dev_err(&ofdev->dev, "couldn't ioremap %pR\n", &res); - err = -ENOMEM; - goto exit_release_mem; - } - - irq = irq_of_parse_and_map(np, 0); - if (irq == NO_IRQ) { - dev_err(&ofdev->dev, "no irq found\n"); - err = -ENODEV; - goto exit_unmap_mem; - } - - dev = alloc_sja1000dev(0); - if (!dev) { - err = -ENOMEM; - goto exit_dispose_irq; - } - - priv = netdev_priv(dev); - - priv->read_reg = sja1000_ofp_read_reg; - priv->write_reg = sja1000_ofp_write_reg; - - prop = of_get_property(np, "nxp,external-clock-frequency", &prop_size); - if (prop && (prop_size == sizeof(u32))) - priv->can.clock.freq = *prop / 2; - else - priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */ - - prop = of_get_property(np, "nxp,tx-output-mode", &prop_size); - if (prop && (prop_size == sizeof(u32))) - priv->ocr |= *prop & OCR_MODE_MASK; - else - priv->ocr |= OCR_MODE_NORMAL; /* default */ - - prop = of_get_property(np, "nxp,tx-output-config", &prop_size); - if (prop && (prop_size == sizeof(u32))) - priv->ocr |= (*prop << OCR_TX_SHIFT) & OCR_TX_MASK; - else - priv->ocr |= OCR_TX0_PULLDOWN; /* default */ - - prop = of_get_property(np, "nxp,clock-out-frequency", &prop_size); - if (prop && (prop_size == sizeof(u32)) && *prop) { - u32 divider = priv->can.clock.freq * 2 / *prop; - - if (divider > 1) - priv->cdr |= divider / 2 - 1; - else - priv->cdr |= CDR_CLKOUT_MASK; - } else { - priv->cdr |= CDR_CLK_OFF; /* default */ - } - - prop = of_get_property(np, "nxp,no-comparator-bypass", NULL); - if (!prop) - priv->cdr |= CDR_CBP; /* default */ - - priv->irq_flags = IRQF_SHARED; - priv->reg_base = base; - - dev->irq = irq; - - dev_info(&ofdev->dev, - "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n", - priv->reg_base, dev->irq, priv->can.clock.freq, - priv->ocr, priv->cdr); - - dev_set_drvdata(&ofdev->dev, dev); - SET_NETDEV_DEV(dev, &ofdev->dev); - - err = register_sja1000dev(dev); - if (err) { - dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", - DRV_NAME, err); - goto exit_free_sja1000; - } - - return 0; - -exit_free_sja1000: - free_sja1000dev(dev); -exit_dispose_irq: - irq_dispose_mapping(irq); -exit_unmap_mem: - iounmap(base); -exit_release_mem: - release_mem_region(res.start, res_size); - - return err; -} - -static struct of_device_id __devinitdata sja1000_ofp_table[] = { - {.compatible = "nxp,sja1000"}, - {}, -}; -MODULE_DEVICE_TABLE(of, sja1000_ofp_table); - -static struct of_platform_driver sja1000_ofp_driver = { - .driver = { - .owner = THIS_MODULE, - .name = DRV_NAME, - .of_match_table = sja1000_ofp_table, - }, - .probe = sja1000_ofp_probe, - .remove = __devexit_p(sja1000_ofp_remove), -}; - -static int __init sja1000_ofp_init(void) -{ - return of_register_platform_driver(&sja1000_ofp_driver); -} -module_init(sja1000_ofp_init); - -static void __exit sja1000_ofp_exit(void) -{ - return of_unregister_platform_driver(&sja1000_ofp_driver); -}; -module_exit(sja1000_ofp_exit); diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index d9fadc489b3..95a844a7ee7 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -12,8 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -27,13 +26,18 @@ #include <linux/can/dev.h> #include <linux/can/platform/sja1000.h> #include <linux/io.h> +#include <linux/of.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"); static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg) @@ -66,57 +70,16 @@ static void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val) iowrite8(val, priv->reg_base + reg * 4); } -static int sp_probe(struct platform_device *pdev) +static void sp_populate(struct sja1000_priv *priv, + struct sja1000_platform_data *pdata, + unsigned long resource_mem_flags) { - int err; - void __iomem *addr; - struct net_device *dev; - struct sja1000_priv *priv; - struct resource *res_mem, *res_irq; - struct sja1000_platform_data *pdata; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "No platform data provided!\n"); - err = -ENODEV; - goto exit; - } - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res_mem || !res_irq) { - err = -ENODEV; - goto exit; - } - - if (!request_mem_region(res_mem->start, resource_size(res_mem), - DRV_NAME)) { - err = -EBUSY; - goto exit; - } - - addr = ioremap_nocache(res_mem->start, resource_size(res_mem)); - if (!addr) { - err = -ENOMEM; - goto exit_release; - } - - dev = alloc_sja1000dev(0); - if (!dev) { - err = -ENOMEM; - goto exit_iounmap; - } - priv = netdev_priv(dev); - - dev->irq = res_irq->start; - priv->irq_flags = res_irq->flags & (IRQF_TRIGGER_MASK | IRQF_SHARED); - priv->reg_base = addr; /* The CAN clock frequency is half the oscillator clock frequency */ priv->can.clock.freq = pdata->osc_freq / 2; priv->ocr = pdata->ocr; priv->cdr = pdata->cdr; - switch (res_mem->flags & IORESOURCE_MEM_TYPE_MASK) { + switch (resource_mem_flags & IORESOURCE_MEM_TYPE_MASK) { case IORESOURCE_MEM_32BIT: priv->read_reg = sp_read_reg32; priv->write_reg = sp_write_reg32; @@ -131,8 +94,126 @@ static int sp_probe(struct platform_device *pdev) 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 */ - dev_set_drvdata(&pdev->dev, dev); + 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 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; + + 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; + + dev = alloc_sja1000dev(0); + 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); + else + sp_populate(priv, pdata, res_mem->flags); + + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); err = register_sja1000dev(dev); @@ -148,52 +229,33 @@ static int sp_probe(struct platform_device *pdev) exit_free: free_sja1000dev(dev); - exit_iounmap: - iounmap(addr); - exit_release: - release_mem_region(res_mem->start, resource_size(res_mem)); - exit: return err; } static int sp_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); - struct sja1000_priv *priv = netdev_priv(dev); - struct resource *res; + struct net_device *dev = platform_get_drvdata(pdev); unregister_sja1000dev(dev); - dev_set_drvdata(&pdev->dev, NULL); - - if (priv->reg_base) - iounmap(priv->reg_base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - free_sja1000dev(dev); return 0; } +static struct of_device_id sp_of_table[] = { + {.compatible = "nxp,sja1000"}, + {}, +}; +MODULE_DEVICE_TABLE(of, sp_of_table); + static struct platform_driver sp_driver = { .probe = sp_probe, .remove = sp_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = sp_of_table, }, }; -static int __init sp_init(void) -{ - return platform_driver_register(&sp_driver); -} - -static void __exit sp_exit(void) -{ - platform_driver_unregister(&sp_driver); -} - -module_init(sp_init); -module_exit(sp_exit); +module_platform_driver(sp_driver); diff --git a/drivers/net/can/sja1000/tscan1.c b/drivers/net/can/sja1000/tscan1.c index 9756099a883..76513dd780c 100644 --- a/drivers/net/can/sja1000/tscan1.c +++ b/drivers/net/can/sja1000/tscan1.c @@ -71,7 +71,7 @@ MODULE_LICENSE("GPL"); #define TSCAN1_SJA1000_XTAL 16000000 /* SJA1000 IO base addresses */ -static const unsigned short tscan1_sja1000_addresses[] __devinitconst = { +static const unsigned short tscan1_sja1000_addresses[] = { 0x100, 0x120, 0x180, 0x1a0, 0x200, 0x240, 0x280, 0x320 }; @@ -88,7 +88,7 @@ static void tscan1_write(const struct sja1000_priv *priv, int reg, u8 val) } /* Probe for a TS-CAN1 board with JP2:JP1 jumper setting ID */ -static int __devinit tscan1_probe(struct device *dev, unsigned id) +static int tscan1_probe(struct device *dev, unsigned id) { struct net_device *netdev; struct sja1000_priv *priv; @@ -171,7 +171,7 @@ static int __devinit tscan1_probe(struct device *dev, unsigned id) return -ENXIO; } -static int __devexit tscan1_remove(struct device *dev, unsigned id /*unused*/) +static int tscan1_remove(struct device *dev, unsigned id /*unused*/) { struct net_device *netdev; struct sja1000_priv *priv; @@ -197,7 +197,7 @@ static int __devexit tscan1_remove(struct device *dev, unsigned id /*unused*/) static struct isa_driver tscan1_isa_driver = { .probe = tscan1_probe, - .remove = __devexit_p(tscan1_remove), + .remove = tscan1_remove, .driver = { .name = "tscan1", }, diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 420e95ecc19..ea4d4f1a641 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -1,7 +1,7 @@ /* * slcan.c - serial line CAN interface driver (using tty line discipline) * - * This file is derived from linux/drivers/net/slip.c + * 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> @@ -18,9 +18,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307. You can also get it - * at http://www.gnu.org/licenses/gpl.html + * 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 @@ -35,14 +33,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> #include <linux/moduleparam.h> -#include <asm/system.h> #include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/string.h> @@ -53,11 +48,15 @@ #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> -static __initdata const char banner[] = +static __initconst const char banner[] = KERN_INFO "slcan: serial line CAN interface driver\n"; MODULE_ALIAS_LDISC(N_SLCAN); @@ -76,6 +75,10 @@ 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; @@ -83,6 +86,7 @@ struct slcan { 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 */ @@ -94,10 +98,6 @@ struct slcan { unsigned long flags; /* Flag values/ mode etc */ #define SLF_INUSE 0 /* Channel in use */ #define SLF_ERROR 1 /* Parity, etc. error */ - - unsigned char leased; - dev_t line; - pid_t pid; }; static struct net_device **slcan_devs; @@ -141,72 +141,72 @@ static struct net_device **slcan_devs; * STANDARD SLCAN DECAPSULATION * ************************************************************************/ -static int asc2nibble(char c) -{ - - if ((c >= '0') && (c <= '9')) - return c - '0'; - - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - - return 16; /* error */ -} - /* Send one completely decapsulated can_frame to the network layer */ static void slc_bump(struct slcan *sl) { struct sk_buff *skb; struct can_frame cf; - int i, dlc_pos, tmp; - unsigned long ultmp; - char cmd = sl->rbuff[0]; - - if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R')) + int i, tmp; + u32 tmpid; + char *cmd = sl->rbuff; + + cf.can_id = 0; + + 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 (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */ - dlc_pos = 4; /* dlc position tiiid */ - else - dlc_pos = 9; /* dlc position Tiiiiiiiid */ - - if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9'))) + if (kstrtou32(sl->rbuff + SLC_CMD_LEN, 16, &tmpid)) return; - cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */ - - sl->rbuff[dlc_pos] = 0; /* terminate can_id string */ + cf.can_id |= tmpid; - if (strict_strtoul(sl->rbuff+1, 16, &ultmp)) + /* get can_dlc from sanitized ASCII value */ + if (cf.can_dlc >= '0' && cf.can_dlc < '9') + cf.can_dlc -= '0'; + else return; - cf.can_id = ultmp; - - if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */ - cf.can_id |= CAN_EFF_FLAG; - - if ((cmd | 0x20) == 'r') /* RTR frame */ - cf.can_id |= CAN_RTR_FLAG; - *(u64 *) (&cf.data) = 0; /* clear payload */ - for (i = 0, dlc_pos++; i < cf.can_dlc; i++) { - - tmp = asc2nibble(sl->rbuff[dlc_pos++]); - if (tmp > 0x0F) - return; - cf.data[i] = (tmp << 4); - tmp = asc2nibble(sl->rbuff[dlc_pos++]); - if (tmp > 0x0F) - return; - cf.data[i] |= tmp; + /* 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)); + skb = dev_alloc_skb(sizeof(struct can_frame) + + sizeof(struct can_skb_priv)); if (!skb) return; @@ -214,9 +214,13 @@ static void slc_bump(struct slcan *sl) 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; + memcpy(skb_put(skb, sizeof(struct can_frame)), &cf, sizeof(struct can_frame)); - netif_rx(skb); + netif_rx_ni(skb); sl->dev->stats.rx_packets++; sl->dev->stats.rx_bytes += cf.can_dlc; @@ -225,7 +229,6 @@ static void slc_bump(struct slcan *sl) /* 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)) { @@ -252,27 +255,46 @@ static void slcan_unesc(struct slcan *sl, unsigned char s) /* Encapsulate one can_frame and stuff into a TTY queue. */ static void slc_encaps(struct slcan *sl, struct can_frame *cf) { - int actual, idx, i; - char cmd; + 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) - cmd = 'R'; /* becomes 'r' in standard frame format */ + *pos = 'R'; /* becomes 'r' in standard frame format (SFF) */ else - cmd = 'T'; /* becomes 't' in standard frame format */ + *pos = 'T'; /* becomes 't' in standard frame format (SSF) */ - if (cf->can_id & CAN_EFF_FLAG) - sprintf(sl->xbuff, "%c%08X%d", cmd, - cf->can_id & CAN_EFF_MASK, cf->can_dlc); - else - sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20, - cf->can_id & CAN_SFF_MASK, cf->can_dlc); + /* 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; + } - idx = strlen(sl->xbuff); + pos += (cf->can_id & CAN_EFF_FLAG) ? SLC_EFF_ID_LEN : SLC_SFF_ID_LEN; - for (i = 0; i < cf->can_dlc; i++) - sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]); + *pos++ = cf->can_dlc + '0'; - strcat(sl->xbuff, "\r"); /* add terminating character */ + /* 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, @@ -283,37 +305,50 @@ static void slc_encaps(struct slcan *sl, struct can_frame *cf) * 14 Oct 1994 Dmitry Gorodchanin. */ set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); - actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); - sl->xleft = strlen(sl->xbuff) - actual; + 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; } -/* - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void slcan_write_wakeup(struct tty_struct *tty) +/* 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; - struct slcan *sl = (struct slcan *) tty->disc_data; + spin_lock_bh(&sl->lock); /* First make sure we're connected. */ - if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) + 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, &tty->flags); + clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); + spin_unlock_bh(&sl->lock); netif_wake_queue(sl->dev); return; } - actual = tty->ops->write(tty, sl->xhead, sl->xleft); + 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 = tty->disc_data; + + schedule_work(&sl->tx_work); } /* Send a can_frame to a TTY queue. */ @@ -388,10 +423,16 @@ static void slc_free_netdev(struct net_device *dev) 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) @@ -408,7 +449,7 @@ static void slc_setup(struct net_device *dev) /* New-style flags. */ dev->flags = IFF_NOARP; - dev->features = NETIF_F_NO_CSUM; + dev->features = NETIF_F_HW_CSUM; } /****************************************** @@ -461,7 +502,7 @@ static void slc_sync(void) break; sl = netdev_priv(dev); - if (sl->tty || sl->leased) + if (sl->tty) continue; if (dev->flags & IFF_UP) dev_close(dev); @@ -472,12 +513,10 @@ static void slc_sync(void) static struct slcan *slc_alloc(dev_t line) { int i; + char name[IFNAMSIZ]; struct net_device *dev = NULL; struct slcan *sl; - if (slcan_devs == NULL) - return NULL; /* Master array missing ! */ - for (i = 0; i < maxdev; i++) { dev = slcan_devs[i]; if (dev == NULL) @@ -489,31 +528,19 @@ static struct slcan *slc_alloc(dev_t line) if (i >= maxdev) return NULL; - if (dev) { - sl = netdev_priv(dev); - if (test_bit(SLF_INUSE, &sl->flags)) { - unregister_netdevice(dev); - dev = NULL; - slcan_devs[i] = NULL; - } - } - - if (!dev) { - char name[IFNAMSIZ]; - sprintf(name, "slcan%d", i); - - dev = alloc_netdev(sizeof(*sl), name, slc_setup); - if (!dev) - return NULL; - dev->base_addr = i; - } + sprintf(name, "slcan%d", i); + dev = alloc_netdev(sizeof(*sl), name, slc_setup); + if (!dev) + return NULL; + dev->base_addr = i; sl = netdev_priv(dev); /* Initialize channel control data */ sl->magic = SLCAN_MAGIC; sl->dev = dev; spin_lock_init(&sl->lock); + INIT_WORK(&sl->tx_work, slcan_transmit); slcan_devs[i] = dev; return sl; @@ -564,8 +591,6 @@ static int slcan_open(struct tty_struct *tty) sl->tty = tty; tty->disc_data = sl; - sl->line = tty_devnum(tty); - sl->pid = current->pid; if (!test_bit(SLF_INUSE, &sl->flags)) { /* Perform the low-level SLCAN initialization. */ @@ -582,7 +607,9 @@ static int slcan_open(struct tty_struct *tty) /* Done. We have linked the TTY line to a channel. */ rtnl_unlock(); tty->receive_room = 65536; /* We don't flow control */ - return sl->dev->base_addr; + + /* TTY layer expects 0 on success */ + return 0; err_free_chan: sl->tty = NULL; @@ -612,10 +639,12 @@ static void slcan_close(struct tty_struct *tty) if (!sl || sl->magic != SLCAN_MAGIC || sl->tty != tty) return; + spin_lock_bh(&sl->lock); tty->disc_data = NULL; sl->tty = NULL; - if (!sl->leased) - sl->line = 0; + spin_unlock_bh(&sl->lock); + + flush_work(&sl->tx_work); /* Flush network side */ unregister_netdev(sl->dev); @@ -677,10 +706,8 @@ static int __init slcan_init(void) printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev); slcan_devs = kzalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL); - if (!slcan_devs) { - printk(KERN_ERR "slcan: can't allocate slcan device array!\n"); + if (!slcan_devs) return -ENOMEM; - } /* Fill in our line protocol discipline, and register it */ status = tty_register_ldisc(N_SLCAN, &slc_ldisc); diff --git a/drivers/net/can/softing/Kconfig b/drivers/net/can/softing/Kconfig new file mode 100644 index 00000000000..96b6fe158b5 --- /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 00000000000..c5e5016c742 --- /dev/null +++ b/drivers/net/can/softing/Makefile @@ -0,0 +1,6 @@ + +softing-y := softing_main.o softing_fw.o +obj-$(CONFIG_CAN_SOFTING) += softing.o +obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h new file mode 100644 index 00000000000..35f062282db --- /dev/null +++ b/drivers/net/can/softing/softing.h @@ -0,0 +1,167 @@ +/* + * softing common interfaces + * + * by Kurt Van Dijck, 2008-2010 + */ + +#include <linux/atomic.h> +#include <linux/netdevice.h> +#include <linux/ktime.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "softing_platform.h" + +struct softing; + +struct softing_priv { + struct can_priv can; /* must be the first member! */ + struct net_device *netdev; + struct softing *card; + struct { + int pending; + /* variables which hold the circular buffer */ + int echo_put; + int echo_get; + } tx; + struct can_bittiming_const btr_const; + int index; + uint8_t output; + uint16_t chip; +}; +#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev)) + +struct softing { + const struct softing_platform_data *pdat; + struct platform_device *pdev; + struct net_device *net[2]; + spinlock_t spin; /* protect this structure & DPRAM access */ + ktime_t ts_ref; + ktime_t ts_overflow; /* timestamp overflow value, in ktime */ + + struct { + /* indication of firmware status */ + int up; + /* protection of the 'up' variable */ + struct mutex lock; + } fw; + struct { + int nr; + int requested; + int svc_count; + unsigned int dpram_position; + } irq; + struct { + int pending; + int last_bus; + /* + * keep the bus that last tx'd a message, + * in order to let every netdev queue resume + */ + } tx; + __iomem uint8_t *dpram; + unsigned long dpram_phys; + unsigned long dpram_size; + struct { + uint16_t fw_version, hw_version, license, serial; + uint16_t chip[2]; + unsigned int freq; /* remote cpu's operating frequency */ + } id; +}; + +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 00000000000..cdc0c7433a4 --- /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: + kfree(dev); +mem_failed: +pcmcia_bad: +pcmcia_failed: + pcmcia_disable_device(pcmcia); + pcmcia->priv = NULL; + return ret ?: -ENODEV; +} + +static const struct pcmcia_device_id softingcs_ids[] = { + /* softing */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005), + /* vector, manufacturer? */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085), + /* EDIC */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, softingcs_ids); + +static struct pcmcia_driver softingcs_driver = { + .owner = THIS_MODULE, + .name = "softingcs", + .id_table = softingcs_ids, + .probe = softingcs_probe, + .remove = 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 00000000000..52fe50725d7 --- /dev/null +++ b/drivers/net/can/softing/softing_fw.c @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/firmware.h> +#include <linux/sched.h> +#include <asm/div64.h> +#include <asm/io.h> + +#include "softing.h" + +/* + * low level DPRAM command. + * Make sure that card->dpram[DPRAM_FCT_HOST] is preset + */ +static int _softing_fct_cmd(struct softing *card, int16_t cmd, uint16_t vector, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(cmd, &card->dpram[DPRAM_FCT_PARAM]); + iowrite8(vector >> 8, &card->dpram[DPRAM_FCT_HOST + 1]); + iowrite8(vector, &card->dpram[DPRAM_FCT_HOST]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 1 * HZ; + /* wait for card */ + do { + /* DPRAM_FCT_HOST is _not_ aligned */ + ret = ioread8(&card->dpram[DPRAM_FCT_HOST]) + + (ioread8(&card->dpram[DPRAM_FCT_HOST + 1]) << 8); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + /* read return-value now */ + return ioread16(&card->dpram[DPRAM_FCT_RESULT]); + + if ((ret != vector) || time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (1); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "firmware %s failed (%i)\n", msg, ret); + return ret; +} + +static int softing_fct_cmd(struct softing *card, int16_t cmd, const char *msg) +{ + int ret; + + ret = _softing_fct_cmd(card, cmd, 0, msg); + if (ret > 0) { + dev_alert(&card->pdev->dev, "%s returned %u\n", msg, ret); + ret = -EIO; + } + return ret; +} + +int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(RES_NONE, &card->dpram[DPRAM_RECEIPT]); + iowrite16(cmd, &card->dpram[DPRAM_COMMAND]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 3 * HZ; + /* wait for card */ + do { + ret = ioread16(&card->dpram[DPRAM_RECEIPT]); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + return 0; + if (time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (!signal_pending(current)); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "bootloader %s failed (%i)\n", msg, ret); + return ret; +} + +static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, + uint16_t *plen, const uint8_t **pdat) +{ + uint16_t checksum[2]; + const uint8_t *mem; + const uint8_t *end; + + /* + * firmware records are a binary, unaligned stream composed of: + * uint16_t type; + * uint32_t addr; + * uint16_t len; + * uint8_t dat[len]; + * uint16_t checksum; + * all values in little endian. + * We could define a struct for this, with __attribute__((packed)), + * but would that solve the alignment in _all_ cases (cfr. the + * struct itself may be an odd address)? + * + * I chose to use leXX_to_cpup() since this solves both + * endianness & alignment. + */ + mem = *pmem; + *ptype = le16_to_cpup((void *)&mem[0]); + *paddr = le32_to_cpup((void *)&mem[2]); + *plen = le16_to_cpup((void *)&mem[6]); + *pdat = &mem[8]; + /* verify checksum */ + end = &mem[8 + *plen]; + checksum[0] = le16_to_cpup((void *)end); + for (checksum[1] = 0; mem < end; ++mem) + checksum[1] += *mem; + if (checksum[0] != checksum[1]) + return -EINVAL; + /* increment */ + *pmem += 10 + *plen; + return 0; +} + +int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *dpram, unsigned int size, int offset) +{ + const struct firmware *fw; + int ret; + const uint8_t *mem, *end, *dat; + uint16_t type, len; + uint32_t addr; + uint8_t *buf = NULL, *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(ktime_set(0, 0), ovf); +} + +ktime_t softing_raw2ktime(struct softing *card, u32 raw) +{ + uint64_t rawl; + ktime_t now, real_offset; + ktime_t target; + ktime_t tmp; + + now = ktime_get(); + real_offset = ktime_sub(ktime_get_real(), now); + + /* find nsec from card */ + rawl = raw * 16; + do_div(rawl, card->pdat->freq ?: 16); + target = ktime_add_us(card->ts_ref, rawl); + /* test for overflows */ + tmp = ktime_add(target, card->ts_overflow); + while (unlikely(ktime_to_ns(tmp) > ktime_to_ns(now))) { + card->ts_ref = ktime_add(card->ts_ref, card->ts_overflow); + target = tmp; + tmp = ktime_add(target, card->ts_overflow); + } + return ktime_add(target, real_offset); +} + +static inline int softing_error_reporting(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + ? 1 : 0; +} + +int softing_startstop(struct net_device *dev, int up) +{ + int ret; + struct softing *card; + struct softing_priv *priv; + struct net_device *netdev; + int bus_bitmask_start; + int j, error_reporting; + struct can_frame msg; + const struct can_bittiming *bt; + + priv = netdev_priv(dev); + card = priv->card; + + if (!card->fw.up) + return -EIO; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return ret; + + bus_bitmask_start = 0; + if (dev && up) + /* prepare to start this bus as well */ + bus_bitmask_start |= (1 << priv->index); + /* bring netdevs down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + + if (dev != netdev) + netif_stop_queue(netdev); + + if (netif_running(netdev)) { + if (dev != netdev) + bus_bitmask_start |= (1 << j); + priv->tx.pending = 0; + priv->tx.echo_put = 0; + priv->tx.echo_get = 0; + /* + * this bus' may just have called open_candev() + * which is rather stupid to call close_candev() + * already + * but we may come here from busoff recovery too + * in which case the echo_skb _needs_ flushing too. + * just be sure to call open_candev() again + */ + close_candev(netdev); + } + priv->can.state = CAN_STATE_STOPPED; + } + card->tx.pending = 0; + + softing_enable_irq(card, 0); + ret = softing_reset_chip(card); + if (ret) + goto failed; + if (!bus_bitmask_start) + /* no busses to be brought up */ + goto card_done; + + if ((bus_bitmask_start & 1) && (bus_bitmask_start & 2) + && (softing_error_reporting(card->net[0]) + != softing_error_reporting(card->net[1]))) { + dev_alert(&card->pdev->dev, + "err_reporting flag differs for busses\n"); + goto invalid; + } + error_reporting = 0; + if (bus_bitmask_start & 1) { + netdev = card->net[0]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip 1 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 1, "initialize_chip[0]"); + if (ret < 0) + goto failed; + /* set mode */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 3, "set_mode[0]"); + if (ret < 0) + goto failed; + /* set filter */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 7, "set_filter[0]"); + if (ret < 0) + goto failed; + /* set output control */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 5, "set_output[0]"); + if (ret < 0) + goto failed; + } + if (bus_bitmask_start & 2) { + netdev = card->net[1]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip2 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 2, "initialize_chip[1]"); + if (ret < 0) + goto failed; + /* set mode2 */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 4, "set_mode[1]"); + if (ret < 0) + goto failed; + /* set filter2 */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 8, "set_filter[1]"); + if (ret < 0) + goto failed; + /* set output control2 */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 6, "set_output[1]"); + if (ret < 0) + goto failed; + } + /* enable_error_frame */ + /* + * Error reporting is switched off at the moment since + * the receiving of them is not yet 100% verified + * This should be enabled sooner or later + * + if (error_reporting) { + ret = softing_fct_cmd(card, 51, "enable_error_frame"); + if (ret < 0) + goto failed; + } + */ + /* initialize interface */ + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 12]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 14]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 16]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 18]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 20]); + ret = softing_fct_cmd(card, 17, "initialize_interface"); + if (ret < 0) + goto failed; + /* enable_fifo */ + ret = softing_fct_cmd(card, 36, "enable_fifo"); + if (ret < 0) + goto failed; + /* enable fifo tx ack */ + ret = softing_fct_cmd(card, 13, "fifo_tx_ack[0]"); + if (ret < 0) + goto failed; + /* enable fifo tx ack2 */ + ret = softing_fct_cmd(card, 14, "fifo_tx_ack[1]"); + if (ret < 0) + goto failed; + /* start_chip */ + ret = softing_fct_cmd(card, 11, "start_chip"); + if (ret < 0) + goto failed; + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE]); + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE2]); + if (card->pdat->generation < 2) { + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + /* flush the DPRAM caches */ + wmb(); + } + + softing_initialize_timestamp(card); + + /* + * do socketcan notifications/status changes + * from here, no errors should occur, or the failed: part + * must be reviewed + */ + memset(&msg, 0, sizeof(msg)); + msg.can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED; + msg.can_dlc = CAN_ERR_DLC; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!(bus_bitmask_start & (1 << j))) + continue; + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + open_candev(netdev); + if (dev != netdev) { + /* notify other busses on the restart */ + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + ++priv->can.can_stats.restarts; + } + netif_wake_queue(netdev); + } + + /* enable interrupts */ + ret = softing_enable_irq(card, 1); + if (ret) + goto failed; +card_done: + mutex_unlock(&card->fw.lock); + return 0; +invalid: + ret = -EINVAL; +failed: + softing_enable_irq(card, 0); + softing_reset_chip(card); + mutex_unlock(&card->fw.lock); + /* bring all other interfaces down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + dev_close(netdev); + } + return ret; +} + +int softing_default_output(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + struct softing *card = priv->card; + + switch (priv->chip) { + case 1000: + return (card->pdat->generation < 2) ? 0xfb : 0xfa; + case 5: + return 0x60; + default: + return 0x40; + } +} diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c new file mode 100644 index 00000000000..bacd236ce30 --- /dev/null +++ b/drivers/net/can/softing/softing_main.c @@ -0,0 +1,870 @@ +/* + * 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, ktime_set(0, 0)); + } + /* prepare for other use */ + memset(&msg, 0, sizeof(msg)); + ++cnt; + } + + fifo_rd = ioread8(&card->dpram[DPRAM_RX_RD]); + fifo_wr = ioread8(&card->dpram[DPRAM_RX_WR]); + + if (++fifo_rd >= DPRAM_RX_CNT) + fifo_rd = 0; + if (fifo_wr == fifo_rd) + return cnt; + + memcpy_fromio(buf, &card->dpram[DPRAM_RX + DPRAM_RX_SIZE*fifo_rd], + DPRAM_RX_SIZE); + mb(); + /* trigger dual port RAM */ + iowrite8(fifo_rd, &card->dpram[DPRAM_RX_RD]); + + ptr = buf; + cmd = *ptr++; + if (cmd == 0xff) + /* not quite useful, probably the card has got out */ + return 0; + netdev = card->net[0]; + if (cmd & CMD_BUS2) + netdev = card->net[1]; + priv = netdev_priv(netdev); + + if (cmd & CMD_ERR) { + uint8_t can_state, state; + + state = *ptr++; + + msg.can_id = CAN_ERR_FLAG; + msg.can_dlc = CAN_ERR_DLC; + + if (state & SF_MASK_BUSOFF) { + can_state = CAN_STATE_BUS_OFF; + msg.can_id |= CAN_ERR_BUSOFF; + state = STATE_BUSOFF; + } else if (state & SF_MASK_EPASSIVE) { + can_state = CAN_STATE_ERROR_PASSIVE; + msg.can_id |= CAN_ERR_CRTL; + msg.data[1] = CAN_ERR_CRTL_TX_PASSIVE; + state = STATE_EPASSIVE; + } else { + can_state = CAN_STATE_ERROR_ACTIVE; + msg.can_id |= CAN_ERR_CRTL; + state = STATE_EACTIVE; + } + /* update DPRAM */ + iowrite8(state, &card->dpram[priv->index ? + DPRAM_INFO_BUSSTATE2 : DPRAM_INFO_BUSSTATE]); + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + + ++netdev->stats.rx_errors; + /* update internal status */ + if (can_state != priv->can.state) { + priv->can.state = can_state; + if (can_state == CAN_STATE_ERROR_PASSIVE) + ++priv->can.can_stats.error_passive; + else if (can_state == CAN_STATE_BUS_OFF) { + /* this calls can_close_cleanup() */ + can_bus_off(netdev); + netif_stop_queue(netdev); + } + /* trigger socketcan */ + softing_netdev_rx(netdev, &msg, ktime); + } + + } else { + if (cmd & CMD_RTR) + msg.can_id |= CAN_RTR_FLAG; + msg.can_dlc = get_can_dlc(*ptr++); + if (cmd & CMD_XTD) { + msg.can_id |= CAN_EFF_FLAG; + msg.can_id |= le32_to_cpup((void *)ptr); + ptr += 4; + } else { + msg.can_id |= le16_to_cpup((void *)ptr); + ptr += 2; + } + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + if (!(msg.can_id & CAN_RTR_FLAG)) + memcpy(&msg.data[0], ptr, 8); + ptr += 8; + /* update socket */ + if (cmd & CMD_ACK) { + /* acknowledge, was tx msg */ + struct sk_buff *skb; + skb = priv->can.echo_skb[priv->tx.echo_get]; + if (skb) + skb->tstamp = ktime; + can_get_echo_skb(netdev, priv->tx.echo_get); + ++priv->tx.echo_get; + if (priv->tx.echo_get >= TX_ECHO_SKB_MAX) + priv->tx.echo_get = 0; + if (priv->tx.pending) + --priv->tx.pending; + if (card->tx.pending) + --card->tx.pending; + ++netdev->stats.tx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.tx_bytes += msg.can_dlc; + } else { + int ret; + + ret = softing_netdev_rx(netdev, &msg, ktime); + if (ret == NET_RX_SUCCESS) { + ++netdev->stats.rx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.rx_bytes += msg.can_dlc; + } else { + ++netdev->stats.rx_dropped; + } + } + } + ++cnt; + return cnt; +} + +/* + * real interrupt handler + */ +static irqreturn_t softing_irq_thread(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + struct net_device *netdev; + struct softing_priv *priv; + int j, offset, work_done; + + work_done = 0; + spin_lock_bh(&card->spin); + while (softing_handle_1(card) > 0) { + ++card->irq.svc_count; + ++work_done; + } + spin_unlock_bh(&card->spin); + /* resume tx queue's */ + offset = card->tx.last_bus; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->tx.pending >= TXMAX) + break; + netdev = card->net[(j + offset + 1) % card->pdat->nbus]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + if (!canif_is_active(netdev)) + /* it makes no sense to wake dead busses */ + continue; + if (priv->tx.pending >= TX_ECHO_SKB_MAX) + continue; + ++work_done; + netif_wake_queue(netdev); + } + return work_done ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * interrupt routines: + * schedule the 'real interrupt handler' + */ +static irqreturn_t softing_irq_v2(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_V2_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + return (1 == ir) ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +static irqreturn_t softing_irq_v1(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_IRQ_TOHOST]); + return ir ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +/* + * netdev/candev inter-operability + */ +static int softing_netdev_open(struct net_device *ndev) +{ + int ret; + + /* check or determine and set bittime */ + ret = open_candev(ndev); + if (!ret) + ret = softing_startstop(ndev, 1); + return ret; +} + +static int softing_netdev_stop(struct net_device *ndev) +{ + int ret; + + netif_stop_queue(ndev); + + /* softing cycle does close_candev() */ + ret = softing_startstop(ndev, 0); + return ret; +} + +static int softing_candev_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + /* softing_startstop does close_candev() */ + ret = softing_startstop(ndev, 1); + return ret; + case CAN_MODE_STOP: + case CAN_MODE_SLEEP: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Softing device management helpers + */ +int softing_enable_irq(struct softing *card, int enable) +{ + int ret; + + if (!card->irq.nr) { + return 0; + } else if (card->irq.requested && !enable) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } else if (!card->irq.requested && enable) { + ret = request_threaded_irq(card->irq.nr, + (card->pdat->generation >= 2) ? + softing_irq_v2 : softing_irq_v1, + softing_irq_thread, IRQF_SHARED, + dev_name(&card->pdev->dev), card); + if (ret) { + dev_alert(&card->pdev->dev, + "request_threaded_irq(%u) failed\n", + card->irq.nr); + return ret; + } + card->irq.requested = 1; + } + return 0; +} + +static void softing_card_shutdown(struct softing *card) +{ + int fw_up = 0; + + if (mutex_lock_interruptible(&card->fw.lock)) + /* return -ERESTARTSYS */; + fw_up = card->fw.up; + card->fw.up = 0; + + if (card->irq.requested && card->irq.nr) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } + if (fw_up) { + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + } + mutex_unlock(&card->fw.lock); +} + +static 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, S_IRUGO, show_chip, NULL); +static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output); + +static const struct attribute *const netdev_sysfs_attrs[] = { + &dev_attr_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", + .owner = THIS_MODULE, + }, + .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 00000000000..ebbf6981562 --- /dev/null +++ b/drivers/net/can/softing/softing_platform.h @@ -0,0 +1,40 @@ + +#include <linux/platform_device.h> + +#ifndef _SOFTING_DEVICE_H_ +#define _SOFTING_DEVICE_H_ + +/* softing firmware directory prefix */ +#define fw_dir "softing-4.6/" + +struct softing_platform_data { + unsigned int manf; + unsigned int prod; + /* + * generation + * 1st with NEC or SJA1000 + * 8bit, exclusive interrupt, ... + * 2nd only SJA1000 + * 16bit, shared interrupt + */ + int generation; + int nbus; /* # busses on device */ + unsigned int freq; /* operating frequency in Hz */ + unsigned int max_brp; + unsigned int max_sjw; + unsigned long dpram_size; + const char *name; + struct { + unsigned long offs; + unsigned long addr; + const char *fw; + } boot, load, app; + /* + * reset() function + * bring pdev in or out of reset, depending on value + */ + int (*reset)(struct platform_device *pdev, int value); + int (*enable_irq)(struct platform_device *pdev, int value); +}; + +#endif diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig new file mode 100644 index 00000000000..148cae5871a --- /dev/null +++ b/drivers/net/can/spi/Kconfig @@ -0,0 +1,10 @@ +menu "CAN SPI interfaces" + depends on SPI + +config CAN_MCP251X + tristate "Microchip MCP251x SPI CAN controllers" + depends on HAS_DMA + ---help--- + Driver for the Microchip MCP251x SPI CAN controllers. + +endmenu diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile new file mode 100644 index 00000000000..90bcacffbc6 --- /dev/null +++ b/drivers/net/can/spi/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Linux Controller Area Network SPI drivers. +# + + +obj-$(CONFIG_CAN_MCP251X) += mcp251x.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/spi/mcp251x.c index 7ab534aee45..5df239e6881 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -28,8 +28,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * * * @@ -37,9 +36,6 @@ * * static struct mcp251x_platform_data mcp251x_info = { * .oscillator_frequency = 8000000, - * .board_specific_setup = &mcp251x_setup, - * .power_enable = mcp251x_power_enable, - * .transceiver_enable = NULL, * }; * * static struct spi_board_info spi_board_info[] = { @@ -60,7 +56,9 @@ #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> @@ -71,10 +69,13 @@ #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 @@ -83,6 +84,11 @@ #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 @@ -208,13 +214,15 @@ #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, S_IRUGO); MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)"); -static struct can_bittiming_const mcp251x_bittiming_const = { +static const struct can_bittiming_const mcp251x_bittiming_const = { .name = DEVICE_NAME, .tseg1_min = 3, .tseg1_max = 16, @@ -258,12 +266,15 @@ struct mcp251x_priv { #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 = dev_get_drvdata(&spi->dev); \ + struct mcp251x_priv *priv = spi_get_drvdata(spi); \ return priv->model == CAN_MCP251X_MCP##_model; \ } @@ -299,7 +310,7 @@ static void mcp251x_clean(struct net_device *net) */ static int mcp251x_spi_trans(struct spi_device *spi, int len) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); struct spi_transfer t = { .tx_buf = priv->spi_tx_buf, .rx_buf = priv->spi_rx_buf, @@ -327,7 +338,7 @@ static int mcp251x_spi_trans(struct spi_device *spi, int len) static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); u8 val = 0; priv->spi_tx_buf[0] = INSTRUCTION_READ; @@ -342,7 +353,7 @@ static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg) static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg, uint8_t *v1, uint8_t *v2) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); priv->spi_tx_buf[0] = INSTRUCTION_READ; priv->spi_tx_buf[1] = reg; @@ -355,7 +366,7 @@ static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg, static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); priv->spi_tx_buf[0] = INSTRUCTION_WRITE; priv->spi_tx_buf[1] = reg; @@ -367,7 +378,7 @@ static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val) static void mcp251x_write_bits(struct spi_device *spi, u8 reg, u8 mask, uint8_t val) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY; priv->spi_tx_buf[1] = reg; @@ -380,7 +391,7 @@ static void mcp251x_write_bits(struct spi_device *spi, u8 reg, static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf, int len, int tx_buf_idx) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); if (mcp251x_is_2510(spi)) { int i; @@ -397,6 +408,7 @@ static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf, 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]; @@ -418,13 +430,16 @@ static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame, buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc; memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc); mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx); - mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ); + + /* 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 = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); if (mcp251x_is_2510(spi)) { int i, len; @@ -444,7 +459,7 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); struct sk_buff *skb; struct can_frame *frame; u8 buf[SPI_TRANSFER_BUF_LEN]; @@ -485,6 +500,9 @@ static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) 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); } @@ -537,7 +555,7 @@ static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode) static int mcp251x_set_normal_mode(struct spi_device *spi) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); unsigned long timeout; /* Enable interrupts */ @@ -585,10 +603,10 @@ static int mcp251x_do_set_bittiming(struct net_device *net) (bt->prop_seg - 1)); mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK, (bt->phase_seg2 - 1)); - dev_info(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n", - mcp251x_read_reg(spi, CNF1), - mcp251x_read_reg(spi, CNF2), - mcp251x_read_reg(spi, CNF3)); + 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; } @@ -607,63 +625,67 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv, static int mcp251x_hw_reset(struct spi_device *spi) { - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); + u8 reg; int ret; - unsigned long timeout; + + /* Wait for oscillator startup timer after power up */ + mdelay(MCP251X_OST_DELAY_MS); priv->spi_tx_buf[0] = INSTRUCTION_RESET; - ret = spi_write(spi, priv->spi_tx_buf, 1); - if (ret) { - dev_err(&spi->dev, "reset failed: ret = %d\n", ret); - return -EIO; - } + ret = mcp251x_spi_trans(spi, 1); + if (ret) + return ret; + + /* Wait for oscillator startup timer after reset */ + mdelay(MCP251X_OST_DELAY_MS); + + reg = mcp251x_read_reg(spi, CANSTAT); + if ((reg & CANCTRL_REQOP_MASK) != CANCTRL_REQOP_CONF) + return -ENODEV; - /* Wait for reset to finish */ - timeout = jiffies + HZ; - mdelay(10); - while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) - != CANCTRL_REQOP_CONF) { - schedule(); - if (time_after(jiffies, timeout)) { - dev_err(&spi->dev, "MCP251x didn't" - " enter in conf mode after reset\n"); - return -EBUSY; - } - } return 0; } static int mcp251x_hw_probe(struct spi_device *spi) { - int st1, st2; + u8 ctrl; + int ret; - mcp251x_hw_reset(spi); + ret = mcp251x_hw_reset(spi); + if (ret) + return ret; - /* - * Please note that these are "magic values" based on after - * reset defaults taken from data sheet which allows us to see - * if we really have a chip on the bus (we avoid common all - * zeroes or all ones situations) - */ - st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE; - st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17; + 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; +} - dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2); +static int mcp251x_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR_OR_NULL(reg)) + return 0; - /* Check for power up default values */ - return (st1 == 0x80 && st2 == 0x07) ? 1 : 0; + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); } static void mcp251x_open_clean(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; free_irq(spi->irq, priv); mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); close_candev(net); } @@ -671,7 +693,6 @@ static int mcp251x_stop(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; close_candev(net); @@ -691,13 +712,14 @@ static int mcp251x_stop(struct net_device *net) mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + 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; } @@ -712,8 +734,7 @@ static void mcp251x_error_skb(struct net_device *net, int can_id, int data1) frame->data[1] = data1; netif_rx_ni(skb); } else { - dev_err(&net->dev, - "cannot allocate error skb\n"); + netdev_err(net, "cannot allocate error skb\n"); } } @@ -752,7 +773,6 @@ static void mcp251x_restart_work_handler(struct work_struct *ws) mutex_lock(&priv->mcp_lock); if (priv->after_suspend) { - mdelay(10); mcp251x_hw_reset(spi); mcp251x_setup(net, priv, spi); if (priv->after_suspend & AFTER_SUSPEND_RESTART) { @@ -897,6 +917,7 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) 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; @@ -913,7 +934,7 @@ static int mcp251x_open(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_FALLING; int ret; ret = open_candev(net); @@ -923,24 +944,22 @@ static int mcp251x_open(struct net_device *net) } mutex_lock(&priv->mcp_lock); - if (pdata->transceiver_enable) - pdata->transceiver_enable(1); + 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, - IRQF_TRIGGER_FALLING, DEVICE_NAME, priv); + flags | IRQF_ONESHOT, DEVICE_NAME, priv); if (ret) { dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); close_candev(net); goto open_unlock; } - priv->wq = create_freezeable_workqueue("mcp251x_wq"); + priv->wq = create_freezable_workqueue("mcp251x_wq"); INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler); @@ -959,6 +978,9 @@ static int mcp251x_open(struct net_device *net) mcp251x_open_clean(net); goto open_unlock; } + + can_led_event(net, CAN_LED_EVENT_OPEN); + netif_wake_queue(net); open_unlock: @@ -970,24 +992,68 @@ 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, + }, + { } }; +MODULE_DEVICE_TABLE(of, mcp251x_of_match); -static int __devinit mcp251x_can_probe(struct spi_device *spi) +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, + }, + { } +}; +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 mcp251x_platform_data *pdata = spi->dev.platform_data; - int ret = -ENODEV; + 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); + } - if (!pdata) - /* Platform data is required for osc freq */ - goto error_out; + /* 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) { - ret = -ENOMEM; - goto error_alloc; + if (!net) + return -ENOMEM; + + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; } net->netdev_ops = &mcp251x_netdev_ops; @@ -996,12 +1062,39 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) priv = netdev_priv(net); priv->can.bittiming_const = &mcp251x_bittiming_const; priv->can.do_set_mode = mcp251x_do_set_mode; - priv->can.clock.freq = pdata->oscillator_frequency / 2; + priv->can.clock.freq = freq / 2; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; - priv->model = spi_get_device_id(spi)->driver_data; + if (of_id) + priv->model = (enum mcp251x_model)of_id->data; + else + priv->model = spi_get_device_id(spi)->driver_data; priv->net = net; - dev_set_drvdata(&spi->dev, priv); + 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(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get(&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); @@ -1020,8 +1113,7 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) GFP_DMA); if (priv->spi_tx_buf) { - priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf + - (PAGE_SIZE / 2)); + 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 { @@ -1032,94 +1124,79 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) /* Allocate non-DMA buffers */ if (!mcp251x_enable_dma) { - priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); + priv->spi_tx_buf = devm_kzalloc(&spi->dev, SPI_TRANSFER_BUF_LEN, + GFP_KERNEL); if (!priv->spi_tx_buf) { ret = -ENOMEM; - goto error_tx_buf; + goto error_probe; } - priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); + priv->spi_rx_buf = devm_kzalloc(&spi->dev, SPI_TRANSFER_BUF_LEN, + GFP_KERNEL); if (!priv->spi_rx_buf) { ret = -ENOMEM; - goto error_rx_buf; + goto error_probe; } } - if (pdata->power_enable) - pdata->power_enable(1); - - /* Call out to platform specific setup */ - if (pdata->board_specific_setup) - pdata->board_specific_setup(spi); - SET_NETDEV_DEV(net, &spi->dev); - /* Configure the SPI bus */ - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - spi_setup(spi); - /* Here is OK to not lock the MCP, no one knows about it yet */ - if (!mcp251x_hw_probe(spi)) { - dev_info(&spi->dev, "Probe failed\n"); + ret = mcp251x_hw_probe(spi); + if (ret) goto error_probe; - } - mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_hw_sleep(spi); ret = register_candev(net); - if (!ret) { - dev_info(&spi->dev, "probed\n"); - return ret; - } + if (ret) + goto error_probe; + + devm_can_led_init(net); + + return 0; + error_probe: - if (!mcp251x_enable_dma) - kfree(priv->spi_rx_buf); -error_rx_buf: - if (!mcp251x_enable_dma) - kfree(priv->spi_tx_buf); -error_tx_buf: - free_candev(net); if (mcp251x_enable_dma) dma_free_coherent(&spi->dev, PAGE_SIZE, priv->spi_tx_buf, priv->spi_tx_dma); -error_alloc: - if (pdata->power_enable) - pdata->power_enable(0); - dev_err(&spi->dev, "probe failed\n"); -error_out: + mcp251x_power_enable(priv->power, 0); + +out_clk: + if (!IS_ERR(clk)) + clk_disable_unprepare(clk); + +out_free: + free_candev(net); + return ret; } -static int __devexit mcp251x_can_remove(struct spi_device *spi) +static int mcp251x_can_remove(struct spi_device *spi) { - struct mcp251x_platform_data *pdata = spi->dev.platform_data; - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); struct net_device *net = priv->net; unregister_candev(net); - free_candev(net); if (mcp251x_enable_dma) { dma_free_coherent(&spi->dev, PAGE_SIZE, priv->spi_tx_buf, priv->spi_tx_dma); - } else { - kfree(priv->spi_tx_buf); - kfree(priv->spi_rx_buf); } - if (pdata->power_enable) - pdata->power_enable(0); + mcp251x_power_enable(priv->power, 0); + + if (!IS_ERR(priv->clk)) + clk_disable_unprepare(priv->clk); + + free_candev(net); return 0; } -#ifdef CONFIG_PM -static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state) +static int __maybe_unused mcp251x_can_suspend(struct device *dev) { - struct mcp251x_platform_data *pdata = spi->dev.platform_data; - struct mcp251x_priv *priv = dev_get_drvdata(&spi->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; @@ -1132,33 +1209,31 @@ static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state) netif_device_detach(net); mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); priv->after_suspend = AFTER_SUSPEND_UP; } else { priv->after_suspend = AFTER_SUSPEND_DOWN; } - if (pdata->power_enable) { - pdata->power_enable(0); + if (!IS_ERR_OR_NULL(priv->power)) { + regulator_disable(priv->power); priv->after_suspend |= AFTER_SUSPEND_POWER; } return 0; } -static int mcp251x_can_resume(struct spi_device *spi) +static int __maybe_unused mcp251x_can_resume(struct device *dev) { - struct mcp251x_platform_data *pdata = spi->dev.platform_data; - struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct spi_device *spi = to_spi_device(dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); if (priv->after_suspend & AFTER_SUSPEND_POWER) { - pdata->power_enable(1); + mcp251x_power_enable(priv->power, 1); queue_work(priv->wq, &priv->restart_work); } else { if (priv->after_suspend & AFTER_SUSPEND_UP) { - if (pdata->transceiver_enable) - pdata->transceiver_enable(1); + mcp251x_power_enable(priv->transceiver, 1); queue_work(priv->wq, &priv->restart_work); } else { priv->after_suspend = 0; @@ -1168,45 +1243,22 @@ static int mcp251x_can_resume(struct spi_device *spi) enable_irq(spi->irq); return 0; } -#else -#define mcp251x_can_suspend NULL -#define mcp251x_can_resume NULL -#endif -static const struct spi_device_id mcp251x_id_table[] = { - { "mcp2510", CAN_MCP251X_MCP2510 }, - { "mcp2515", CAN_MCP251X_MCP2515 }, - { }, -}; - -MODULE_DEVICE_TABLE(spi, mcp251x_id_table); +static 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, - .bus = &spi_bus_type, .owner = THIS_MODULE, + .of_match_table = mcp251x_of_match, + .pm = &mcp251x_can_pm_ops, }, - .id_table = mcp251x_id_table, .probe = mcp251x_can_probe, - .remove = __devexit_p(mcp251x_can_remove), - .suspend = mcp251x_can_suspend, - .resume = mcp251x_can_resume, + .remove = mcp251x_can_remove, }; - -static int __init mcp251x_can_init(void) -{ - return spi_register_driver(&mcp251x_can_driver); -} - -static void __exit mcp251x_can_exit(void) -{ - spi_unregister_driver(&mcp251x_can_driver); -} - -module_init(mcp251x_can_init); -module_exit(mcp251x_can_exit); +module_spi_driver(mcp251x_can_driver); MODULE_AUTHOR("Chris Elston <celston@katalix.com>, " "Christian Pellegrin <chripell@evolware.org>"); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 4d07f1ee716..258b9c4856e 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -37,7 +37,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/interrupt.h> @@ -46,9 +45,11 @@ #include <linux/skbuff.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/io.h> #include <linux/can/dev.h> #include <linux/can/error.h> +#include <linux/can/led.h> #include <linux/can/platform/ti_hecc.h> #define DRV_NAME "ti_hecc" @@ -195,7 +196,7 @@ MODULE_VERSION(HECC_MODULE_VERSION); #define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */ /* CAN Bittiming constants as per HECC specs */ -static struct can_bittiming_const ti_hecc_bittiming_const = { +static const struct can_bittiming_const ti_hecc_bittiming_const = { .name = DRV_NAME, .tseg1_min = 1, .tseg1_max = 16, @@ -284,15 +285,6 @@ static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; } -static int ti_hecc_get_state(const struct net_device *ndev, - enum can_state *state) -{ - struct ti_hecc_priv *priv = netdev_priv(ndev); - - *state = priv->can.state; - return 0; -} - static int ti_hecc_set_btc(struct ti_hecc_priv *priv) { struct can_bittiming *bit_timing = &priv->can.bittiming; @@ -305,7 +297,7 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) if (bit_timing->brp > 4) can_btc |= HECC_CANBTC_SAM; else - dev_warn(priv->ndev->dev.parent, "WARN: Triple" \ + netdev_warn(priv->ndev, "WARN: Triple" "sampling not set due to h/w limitations"); } can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8; @@ -314,7 +306,7 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) /* ERM being set to 0 by default meaning resync at falling edge */ hecc_write(priv, HECC_CANBTC, can_btc); - dev_info(priv->ndev->dev.parent, "setting CANBTC=%#x\n", can_btc); + netdev_info(priv->ndev, "setting CANBTC=%#x\n", can_btc); return 0; } @@ -331,7 +323,7 @@ static void ti_hecc_reset(struct net_device *ndev) u32 cnt; struct ti_hecc_priv *priv = netdev_priv(ndev); - dev_dbg(ndev->dev.parent, "resetting hecc ...\n"); + netdev_dbg(ndev, "resetting hecc ...\n"); hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES); /* Set change control request and wait till enabled */ @@ -457,6 +449,17 @@ static int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode) 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 * @@ -495,7 +498,7 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) { spin_unlock_irqrestore(&priv->mbx_lock, flags); netif_stop_queue(ndev); - dev_err(priv->ndev->dev.parent, + 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; @@ -503,9 +506,9 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) 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; - data |= get_tx_head_prio(priv) << 8; hecc_write_mbx(priv, mbxno, HECC_CANMCF, data); if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ @@ -514,10 +517,10 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) data = (cf->can_id & CAN_SFF_MASK) << 18; hecc_write_mbx(priv, mbxno, HECC_CANMID, data); hecc_write_mbx(priv, mbxno, HECC_CANMDL, - be32_to_cpu(*(u32 *)(cf->data))); + be32_to_cpu(*(__be32 *)(cf->data))); if (cf->can_dlc > 4) hecc_write_mbx(priv, mbxno, HECC_CANMDH, - be32_to_cpu(*(u32 *)(cf->data + 4))); + be32_to_cpu(*(__be32 *)(cf->data + 4))); else *(u32 *)(cf->data + 4) = 0; can_put_echo_skb(skb, ndev, mbxno); @@ -549,7 +552,7 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) skb = alloc_can_skb(priv->ndev, &cf); if (!skb) { if (printk_ratelimit()) - dev_err(priv->ndev->dev.parent, + netdev_err(priv->ndev, "ti_hecc_rx_pkt: alloc_can_skb() failed\n"); return -ENOMEM; } @@ -565,12 +568,10 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) cf->can_id |= CAN_RTR_FLAG; cf->can_dlc = get_can_dlc(data & 0xF); data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); - *(u32 *)(cf->data) = cpu_to_be32(data); + *(__be32 *)(cf->data) = cpu_to_be32(data); if (cf->can_dlc > 4) { data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); - *(u32 *)(cf->data + 4) = cpu_to_be32(data); - } else { - *(u32 *)(cf->data + 4) = 0; + *(__be32 *)(cf->data + 4) = cpu_to_be32(data); } spin_lock_irqsave(&priv->mbx_lock, flags); hecc_clear_bit(priv, HECC_CANME, mbx_mask); @@ -581,6 +582,7 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) 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++; @@ -663,11 +665,11 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, struct can_frame *cf; struct sk_buff *skb; - /* propogate the error condition to the can stack */ + /* propagate the error condition to the can stack */ skb = alloc_can_err_skb(ndev, &cf); if (!skb) { if (printk_ratelimit()) - dev_err(priv->ndev->dev.parent, + netdev_err(priv->ndev, "ti_hecc_error: alloc_can_err_skb() failed\n"); return -ENOMEM; } @@ -683,7 +685,7 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; } hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW); - dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n"); + netdev_dbg(priv->ndev, "Error Warning interrupt\n"); hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); } @@ -698,7 +700,7 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; } hecc_set_bit(priv, HECC_CANES, HECC_CANES_EP); - dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + netdev_dbg(priv->ndev, "Error passive interrupt\n"); hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); } @@ -734,19 +736,20 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, } if (err_status & HECC_CANES_CRCE) { hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE); - cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL; } if (err_status & HECC_CANES_ACKE) { hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); - cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + cf->data[3] |= CAN_ERR_PROT_LOC_ACK | CAN_ERR_PROT_LOC_ACK_DEL; } } - netif_receive_skb(skb); + netif_rx(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + return 0; } @@ -783,6 +786,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) 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; } @@ -823,7 +827,7 @@ static int ti_hecc_open(struct net_device *ndev) err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED, ndev->name, ndev); if (err) { - dev_err(ndev->dev.parent, "error requesting interrupt\n"); + netdev_err(ndev, "error requesting interrupt\n"); return err; } @@ -832,12 +836,14 @@ static int ti_hecc_open(struct net_device *ndev) /* Open common can device */ err = open_candev(ndev); if (err) { - dev_err(ndev->dev.parent, "open_candev() failed %d\n", 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); @@ -856,6 +862,8 @@ static int ti_hecc_close(struct net_device *ndev) close_candev(ndev); ti_hecc_transceiver_switch(priv, 0); + can_led_event(ndev, CAN_LED_EVENT_STOP); + return 0; } @@ -863,6 +871,7 @@ 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 int ti_hecc_probe(struct platform_device *pdev) @@ -874,7 +883,7 @@ static int ti_hecc_probe(struct platform_device *pdev) void __iomem *addr; int err = -ENODEV; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data\n"); goto probe_exit; @@ -920,9 +929,10 @@ static int ti_hecc_probe(struct platform_device *pdev) priv->can.bittiming_const = &ti_hecc_bittiming_const; priv->can.do_set_mode = ti_hecc_do_set_mode; - priv->can.do_get_state = ti_hecc_get_state; + priv->can.do_get_berr_counter = ti_hecc_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + spin_lock_init(&priv->mbx_lock); ndev->irq = irq->start; ndev->flags |= IFF_ECHO; platform_set_drvdata(pdev, ndev); @@ -946,6 +956,9 @@ static int ti_hecc_probe(struct platform_device *pdev) 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); @@ -963,20 +976,19 @@ probe_exit: return err; } -static int __devexit ti_hecc_remove(struct platform_device *pdev) +static int ti_hecc_remove(struct platform_device *pdev) { struct resource *res; struct net_device *ndev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(ndev); + unregister_candev(ndev); clk_disable(priv->clk); clk_put(priv->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(priv->base); release_mem_region(res->start, resource_size(res)); - unregister_candev(ndev); free_candev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } @@ -1030,26 +1042,14 @@ static struct platform_driver ti_hecc_driver = { .owner = THIS_MODULE, }, .probe = ti_hecc_probe, - .remove = __devexit_p(ti_hecc_remove), + .remove = ti_hecc_remove, .suspend = ti_hecc_suspend, .resume = ti_hecc_resume, }; -static int __init ti_hecc_init_driver(void) -{ - printk(KERN_INFO DRV_DESC "\n"); - return platform_driver_register(&ti_hecc_driver); -} - -static void __exit ti_hecc_exit_driver(void) -{ - printk(KERN_INFO DRV_DESC " unloaded\n"); - platform_driver_unregister(&ti_hecc_driver); -} - -module_exit(ti_hecc_exit_driver); -module_init(ti_hecc_init_driver); +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 index 04525495b15..a77db919363 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -1,5 +1,5 @@ menu "CAN USB interfaces" - depends on USB && CAN_DEV + depends on USB config CAN_EMS_USB tristate "EMS CPC-USB/ARM7 CAN/USB interface" @@ -13,4 +13,55 @@ config CAN_ESD_USB2 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 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. + + 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 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called kvaser_usb. + +config CAN_PEAK_USB + tristate "PEAK PCAN-USB/USB Pro interfaces" + ---help--- + This driver supports the PCAN-USB and PCAN-USB Pro adapters + from PEAK-System Technik (http://www.peak-system.com). + +config CAN_8DEV_USB + tristate "8 devices USB2CAN interface" + ---help--- + This driver supports the USB2CAN interface + from 8 devices (http://www.8devices.com). + endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index fce3cf11719..7b9a393b1ac 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -4,5 +4,9 @@ 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.o +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ +obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index e75f1a87697..00f2534dde7 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -16,7 +16,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <linux/init.h> #include <linux/signal.h> #include <linux/slab.h> #include <linux/module.h> @@ -245,7 +244,6 @@ struct ems_tx_urb_context { struct ems_usb { struct can_priv can; /* must be the first member */ - int open_time; struct sk_buff *echo_skb[MAX_TX_URBS]; @@ -288,8 +286,7 @@ static void ems_usb_read_interrupt_callback(struct urb *urb) return; default: - dev_info(netdev->dev.parent, "Rx interrupt aborted %d\n", - urb->status); + netdev_info(netdev, "Rx interrupt aborted %d\n", urb->status); break; } @@ -298,8 +295,7 @@ static void ems_usb_read_interrupt_callback(struct urb *urb) if (err == -ENODEV) netif_device_detach(netdev); else if (err) - dev_err(netdev->dev.parent, - "failed resubmitting intr urb: %d\n", 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) @@ -386,7 +382,7 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) break; } - /* Error occured during transmission? */ + /* Error occurred during transmission? */ if ((ecc & SJA1000_ECC_DIR) == 0) cf->data[2] |= CAN_ERR_PROT_TX; @@ -431,8 +427,7 @@ static void ems_usb_read_bulk_callback(struct urb *urb) return; default: - dev_info(netdev->dev.parent, "Rx URB aborted (%d)\n", - urb->status); + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); goto resubmit_urb; } @@ -477,7 +472,7 @@ static void ems_usb_read_bulk_callback(struct urb *urb) msg_count--; if (start > urb->transfer_buffer_length) { - dev_err(netdev->dev.parent, "format error\n"); + netdev_err(netdev, "format error\n"); break; } } @@ -493,8 +488,8 @@ resubmit_urb: if (retval == -ENODEV) netif_device_detach(netdev); else if (retval) - dev_err(netdev->dev.parent, - "failed resubmitting read bulk urb: %d\n", retval); + netdev_err(netdev, + "failed resubmitting read bulk urb: %d\n", retval); } /* @@ -521,8 +516,7 @@ static void ems_usb_write_bulk_callback(struct urb *urb) return; if (urb->status) - dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n", - urb->status); + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); netdev->trans_start = jiffies; @@ -605,18 +599,18 @@ static int ems_usb_start(struct ems_usb *dev) /* create a URB, and a buffer for it */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { - dev_err(netdev->dev.parent, - "No memory left for URBs\n"); - return -ENOMEM; + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; } buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, &urb->transfer_dma); if (!buf) { - dev_err(netdev->dev.parent, - "No memory left for USB buffer\n"); + netdev_err(netdev, "No memory left for USB buffer\n"); usb_free_urb(urb); - return -ENOMEM; + err = -ENOMEM; + break; } usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), @@ -627,12 +621,10 @@ static int ems_usb_start(struct ems_usb *dev) err = usb_submit_urb(urb, GFP_KERNEL); if (err) { - if (err == -ENODEV) - netif_device_detach(dev->netdev); - usb_unanchor_urb(urb); usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, urb->transfer_dma); + usb_free_urb(urb); break; } @@ -642,13 +634,13 @@ static int ems_usb_start(struct ems_usb *dev) /* Did we submit any URBs */ if (i == 0) { - dev_warn(netdev->dev.parent, "couldn't setup read URBs\n"); + 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) - dev_warn(netdev->dev.parent, "rx performance may be slow\n"); + netdev_warn(netdev, "rx performance may be slow\n"); /* Setup and start interrupt URB */ usb_fill_int_urb(dev->intr_urb, dev->udev, @@ -659,11 +651,7 @@ static int ems_usb_start(struct ems_usb *dev) err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); if (err) { - if (err == -ENODEV) - netif_device_detach(dev->netdev); - - dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n", - err); + netdev_warn(netdev, "intr URB submit failed: %d\n", err); return err; } @@ -692,10 +680,7 @@ static int ems_usb_start(struct ems_usb *dev) return 0; failed: - if (err == -ENODEV) - netif_device_detach(dev->netdev); - - dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err); + netdev_warn(netdev, "couldn't submit control: %d\n", err); return err; } @@ -735,15 +720,13 @@ static int ems_usb_open(struct net_device *netdev) if (err == -ENODEV) netif_device_detach(dev->netdev); - dev_warn(netdev->dev.parent, "couldn't start device: %d\n", - err); + netdev_warn(netdev, "couldn't start device: %d\n", err); close_candev(netdev); return err; } - dev->open_time = jiffies; netif_start_queue(netdev); @@ -769,13 +752,13 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne /* create a URB, and a buffer for it, and copy the data to the URB */ urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { - dev_err(netdev->dev.parent, "No memory left for URBs\n"); + netdev_err(netdev, "No memory left for URBs\n"); goto nomem; } buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); if (!buf) { - dev_err(netdev->dev.parent, "No memory left for USB buffer\n"); + netdev_err(netdev, "No memory left for USB buffer\n"); usb_free_urb(urb); goto nomem; } @@ -815,10 +798,10 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne * allowed (MAX_TX_URBS). */ if (!context) { - usb_unanchor_urb(urb); usb_free_coherent(dev->udev, size, buf, urb->transfer_dma); + usb_free_urb(urb); - dev_warn(netdev->dev.parent, "couldn't find free context\n"); + netdev_warn(netdev, "couldn't find free context\n"); return NETDEV_TX_BUSY; } @@ -849,7 +832,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne if (err == -ENODEV) { netif_device_detach(netdev); } else { - dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err); + netdev_warn(netdev, "failed tx_urb %d\n", err); stats->tx_dropped++; } @@ -889,12 +872,10 @@ static int ems_usb_close(struct net_device *netdev) /* Set CAN controller to reset mode */ if (ems_usb_write_mode(dev, SJA1000_MOD_RM)) - dev_warn(netdev->dev.parent, "couldn't stop device"); + netdev_warn(netdev, "couldn't stop device"); close_candev(netdev); - dev->open_time = 0; - return 0; } @@ -902,9 +883,10 @@ 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 struct can_bittiming_const ems_usb_bittiming_const = { +static const struct can_bittiming_const ems_usb_bittiming_const = { .name = "ems_usb", .tseg1_min = 1, .tseg1_max = 16, @@ -920,13 +902,10 @@ static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) { struct ems_usb *dev = netdev_priv(netdev); - if (!dev->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) - dev_warn(netdev->dev.parent, "couldn't start device"); + netdev_warn(netdev, "couldn't start device"); if (netif_queue_stopped(netdev)) netif_wake_queue(netdev); @@ -951,8 +930,7 @@ static int ems_usb_set_bittiming(struct net_device *netdev) if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) btr1 |= 0x80; - dev_info(netdev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n", - btr0, btr1); + 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; @@ -1037,17 +1015,13 @@ static int ems_usb_probe(struct usb_interface *intf, } dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL); - if (!dev->intr_in_buffer) { - dev_err(&intf->dev, "Couldn't alloc Intr buffer\n"); + 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) { - dev_err(&intf->dev, "Couldn't alloc Tx buffer\n"); + if (!dev->tx_msg_buffer) goto cleanup_intr_in_buffer; - } usb_set_intfdata(intf, dev); @@ -1057,15 +1031,13 @@ static int ems_usb_probe(struct usb_interface *intf, err = ems_usb_command_msg(dev, &dev->active_params); if (err) { - dev_err(netdev->dev.parent, - "couldn't initialize controller: %d\n", err); + netdev_err(netdev, "couldn't initialize controller: %d\n", err); goto cleanup_tx_msg_buffer; } err = register_candev(netdev); if (err) { - dev_err(netdev->dev.parent, - "couldn't register CAN device: %d\n", err); + netdev_err(netdev, "couldn't register CAN device: %d\n", err); goto cleanup_tx_msg_buffer; } @@ -1115,28 +1087,4 @@ static struct usb_driver ems_usb_driver = { .id_table = ems_usb_table, }; -static int __init ems_usb_init(void) -{ - int err; - - printk(KERN_INFO "CPC-USB kernel driver loaded\n"); - - /* register this driver with the USB subsystem */ - err = usb_register(&ems_usb_driver); - - if (err) { - err("usb_register failed. Error number %d\n", err); - return err; - } - - return 0; -} - -static void __exit ems_usb_exit(void) -{ - /* deregister this driver with the USB subsystem */ - usb_deregister(&ems_usb_driver); -} - -module_init(ems_usb_init); -module_exit(ems_usb_exit); +module_usb_driver(ems_usb_driver); diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 05a52754f48..b7c9e8b1146 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -1,7 +1,7 @@ /* - * CAN driver for esd CAN-USB/2 + * CAN driver for esd CAN-USB/2 and CAN-USB/Micro * - * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * 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 @@ -16,7 +16,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <linux/init.h> #include <linux/signal.h> #include <linux/slab.h> #include <linux/module.h> @@ -28,14 +27,16 @@ #include <linux/can/error.h> MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd.eu>"); -MODULE_DESCRIPTION("CAN driver for esd CAN-USB/2 interfaces"); +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 */ @@ -69,6 +70,7 @@ MODULE_LICENSE("GPL v2"); #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 @@ -183,6 +185,7 @@ struct __attribute__ ((packed)) esd_usb2_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); @@ -213,7 +216,6 @@ struct esd_usb2_net_priv { struct usb_anchor tx_submitted; struct esd_tx_urb_context tx_contexts[MAX_TX_URBS]; - int open_time; struct esd_usb2 *usb2; struct net_device *netdev; int index; @@ -284,7 +286,7 @@ static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv, break; } - /* Error occured during transmission? */ + /* Error occurred during transmission? */ if (!(ecc & SJA1000_ECC_DIR)) cf->data[2] |= CAN_ERR_PROT_TX; @@ -409,10 +411,20 @@ static void esd_usb2_read_bulk_callback(struct urb *urb) 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; @@ -470,8 +482,7 @@ static void esd_usb2_write_bulk_callback(struct urb *urb) return; if (urb->status) - dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n", - urb->status); + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); netdev->trans_start = jiffies; } @@ -610,9 +621,15 @@ 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; + 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). @@ -626,40 +643,39 @@ static int esd_usb2_start(struct esd_usb2_net_priv *priv) * 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 */ + 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); + 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); + msg->msg.filter.mask[ESD_MAX_ID_SEGMENT] = cpu_to_le32(0x00000001); - err = esd_usb2_send_msg(dev, &msg); + err = esd_usb2_send_msg(dev, msg); if (err) - goto failed; + goto out; err = esd_usb2_setup_rx_urbs(dev); if (err) - goto failed; + goto out; priv->can.state = CAN_STATE_ERROR_ACTIVE; - return 0; - -failed: +out: if (err == -ENODEV) netif_device_detach(netdev); + if (err) + netdev_err(netdev, "couldn't start device: %d\n", err); - dev_err(netdev->dev.parent, "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; + int i, j; usb_kill_anchored_urbs(&dev->rx_submitted); for (i = 0; i < dev->net_count; i++) { @@ -668,8 +684,8 @@ static void unlink_all_urbs(struct esd_usb2 *dev) usb_kill_anchored_urbs(&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; + for (j = 0; j < MAX_TX_URBS; j++) + priv->tx_contexts[j].echo_index = MAX_TX_URBS; } } } @@ -687,14 +703,11 @@ static int esd_usb2_open(struct net_device *netdev) /* finally start device */ err = esd_usb2_start(priv); if (err) { - dev_warn(netdev->dev.parent, - "couldn't start device: %d\n", err); + netdev_warn(netdev, "couldn't start device: %d\n", err); close_candev(netdev); return err; } - priv->open_time = jiffies; - netif_start_queue(netdev); return 0; @@ -721,7 +734,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, /* create a URB, and a buffer for it, and copy the data to the URB */ urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { - dev_err(netdev->dev.parent, "No memory left for URBs\n"); + netdev_err(netdev, "No memory left for URBs\n"); stats->tx_dropped++; dev_kfree_skb(skb); goto nourbmem; @@ -730,7 +743,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); if (!buf) { - dev_err(netdev->dev.parent, "No memory left for USB buffer\n"); + netdev_err(netdev, "No memory left for USB buffer\n"); stats->tx_dropped++; dev_kfree_skb(skb); goto nobufmem; @@ -766,7 +779,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, * This may never happen. */ if (!context) { - dev_warn(netdev->dev.parent, "couldn't find free context\n"); + netdev_warn(netdev, "couldn't find free context\n"); ret = NETDEV_TX_BUSY; goto releasebuf; } @@ -806,7 +819,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, if (err == -ENODEV) netif_device_detach(netdev); else - dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err); + netdev_warn(netdev, "failed tx_urb %d\n", err); goto releasebuf; } @@ -834,27 +847,31 @@ nourbmem: static int esd_usb2_close(struct net_device *netdev) { struct esd_usb2_net_priv *priv = netdev_priv(netdev); - struct esd_usb2_msg msg; + 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 */ + 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) - dev_err(netdev->dev.parent, "sending idadd message failed\n"); + 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) - dev_err(netdev->dev.parent, "sending setbaud message failed\n"); + 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; @@ -862,7 +879,7 @@ static int esd_usb2_close(struct net_device *netdev) close_candev(netdev); - priv->open_time = 0; + kfree(msg); return 0; } @@ -871,9 +888,10 @@ 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 struct can_bittiming_const esd_usb2_bittiming_const = { +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, @@ -889,13 +907,25 @@ 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; + 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)) - << ESD_USB2_SJW_SHIFT; + << sjw_shift; canbtr |= ((bt->prop_seg + bt->phase_seg1 - 1) & (ESD_USB2_TSEG1_MAX - 1)) << ESD_USB2_TSEG1_SHIFT; @@ -904,15 +934,22 @@ static int esd_usb2_set_bittiming(struct net_device *netdev) if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) canbtr |= ESD_USB2_3_SAMPLES; - msg.msg.hdr.len = 2; - msg.msg.hdr.cmd = CMD_SETBAUD; - msg.msg.setbaud.net = priv->index; - msg.msg.setbaud.rsvd = 0; - msg.msg.setbaud.baud = cpu_to_le32(canbtr); + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; - dev_info(netdev->dev.parent, "setting BTR=%#x\n", canbtr); + 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); - return esd_usb2_send_msg(priv->usb2, &msg); + 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, @@ -928,11 +965,6 @@ static int esd_usb2_get_berr_counter(const struct net_device *netdev, static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode) { - struct esd_usb2_net_priv *priv = netdev_priv(netdev); - - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: netif_wake_queue(netdev); @@ -973,30 +1005,38 @@ static int esd_usb2_probe_one_net(struct usb_interface *intf, int index) priv->index = index; priv->can.state = CAN_STATE_STOPPED; - priv->can.clock.freq = ESD_USB2_CAN_CLOCK; + 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; - priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; netdev->flags |= IFF_ECHO; /* we support local echo */ netdev->netdev_ops = &esd_usb2_netdev_ops; SET_NETDEV_DEV(netdev, &intf->dev); + netdev->dev_id = index; err = register_candev(netdev); if (err) { - dev_err(&intf->dev, - "couldn't register CAN device: %d\n", err); + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); free_candev(netdev); err = -ENOMEM; goto done; } dev->nets[index] = priv; - dev_info(netdev->dev.parent, "device %s registered\n", netdev->name); + netdev_info(netdev, "device %s registered\n", netdev->name); done: return err; @@ -1012,7 +1052,7 @@ static int esd_usb2_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct esd_usb2 *dev; - struct esd_usb2_msg msg; + struct esd_usb2_msg *msg; int i, err; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -1027,27 +1067,33 @@ static int esd_usb2_probe(struct usb_interface *intf, 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; + 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); + err = esd_usb2_send_msg(dev, msg); if (err < 0) { dev_err(&intf->dev, "sending version message failed\n"); - goto free_dev; + goto free_msg; } - err = esd_usb2_wait_msg(dev, &msg); + err = esd_usb2_wait_msg(dev, msg); if (err < 0) { dev_err(&intf->dev, "no version message answer\n"); - goto free_dev; + goto free_msg; } - dev->net_count = (int)msg.msg.version_reply.nets; - dev->version = le32_to_cpu(msg.msg.version_reply.version); + 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, @@ -1065,10 +1111,10 @@ static int esd_usb2_probe(struct usb_interface *intf, for (i = 0; i < dev->net_count; i++) esd_usb2_probe_one_net(intf, i); - return 0; - -free_dev: - kfree(dev); +free_msg: + kfree(msg); + if (err) + kfree(dev); done: return err; } @@ -1108,25 +1154,4 @@ static struct usb_driver esd_usb2_driver = { .id_table = esd_usb2_table, }; -static int __init esd_usb2_init(void) -{ - int err; - - /* register this driver with the USB subsystem */ - err = usb_register(&esd_usb2_driver); - - if (err) { - err("usb_register failed. Error number %d\n", err); - return err; - } - - return 0; -} -module_init(esd_usb2_init); - -static void __exit esd_usb2_exit(void) -{ - /* deregister this driver with the USB subsystem */ - usb_deregister(&esd_usb2_driver); -} -module_exit(esd_usb2_exit); +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 00000000000..04b0f84612f --- /dev/null +++ b/drivers/net/can/usb/gs_usb.c @@ -0,0 +1,971 @@ +/* CAN driver for Geschwister Schneider USB/CAN devices. + * + * Copyright (C) 2013 Geschwister Schneider Technologie-, + * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). + * + * 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 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 +}; + +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 +}; + +/* data types passed between host and device */ +struct gs_host_config { + u32 byte_order; +} __packed; +/* All data exchanged between host and device is exchanged in host byte order, + * thanks to the struct gs_host_config byte_order member, which is sent first + * to indicate the desired byte order. + */ + +struct gs_device_config { + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 icount; + u32 sw_version; + u32 hw_version; +} __packed; + +#define GS_CAN_MODE_NORMAL 0 +#define GS_CAN_MODE_LISTEN_ONLY (1<<0) +#define GS_CAN_MODE_LOOP_BACK (1<<1) +#define GS_CAN_MODE_TRIPLE_SAMPLE (1<<2) +#define GS_CAN_MODE_ONE_SHOT (1<<3) + +struct gs_device_mode { + u32 mode; + u32 flags; +} __packed; + +struct gs_device_state { + u32 state; + u32 rxerr; + u32 txerr; +} __packed; + +struct gs_device_bittiming { + u32 prop_seg; + u32 phase_seg1; + u32 phase_seg2; + u32 sjw; + u32 brp; +} __packed; + +#define GS_CAN_FEATURE_LISTEN_ONLY (1<<0) +#define GS_CAN_FEATURE_LOOP_BACK (1<<1) +#define GS_CAN_FEATURE_TRIPLE_SAMPLE (1<<2) +#define GS_CAN_FEATURE_ONE_SHOT (1<<3) + +struct gs_device_bt_const { + u32 feature; + u32 fclk_can; + u32 tseg1_min; + u32 tseg1_max; + u32 tseg2_min; + u32 tseg2_max; + u32 sjw_max; + u32 brp_min; + u32 brp_max; + u32 brp_inc; +} __packed; + +#define GS_CAN_FLAG_OVERFLOW 1 + +struct gs_host_frame { + u32 echo_id; + u32 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 recieve. */ + 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; + atomic_t active_channels; + struct usb_device *udev; +}; + +/* '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_usb *gsusb, 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); + + 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_recieve_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 resubmit_urb; + + 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 = 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 (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); + + 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_recieve_bulk_callback, + usbcan + ); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + + /* USB failure take down all interfaces */ + if (rc == -ENODEV) { + 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 = bt->prop_seg; + dbt->phase_seg1 = bt->phase_seg1; + dbt->phase_seg2 = bt->phase_seg2; + dbt->sjw = bt->sjw; + dbt->brp = 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; +} + +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); + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); +} + +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) { + netdev_err(netdev, "No memory left for URB\n"); + 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; + + cf = (struct can_frame *)skb->data; + + hf->can_id = 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; + + rc = open_candev(netdev); + if (rc) + return rc; + + if (atomic_add_return(1, &parent->active_channels) == 1) { + 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) { + netdev_err(netdev, + "No memory left for URB\n"); + 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_recieve_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); + 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; + dm->flags = 0; + + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + dm->flags |= GS_CAN_MODE_LOOP_BACK; + else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + dm->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) + dm->flags |= GS_CAN_MODE_ONE_SHOT; + + if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) + dm->flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + + /* finally start device */ + dm->mode = GS_CAN_MODE_START; + 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; + + 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 */ + if (atomic_dec_and_test(&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(parent, 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, +}; + +static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf) +{ + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct gs_device_bt_const *bt_const; + + 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 = bt_const->tseg1_min; + dev->bt_const.tseg1_max = bt_const->tseg1_max; + dev->bt_const.tseg2_min = bt_const->tseg2_min; + dev->bt_const.tseg2_max = bt_const->tseg2_max; + dev->bt_const.sjw_max = bt_const->sjw_max; + dev->bt_const.brp_min = bt_const->brp_min; + dev->bt_const.brp_max = bt_const->brp_max; + dev->bt_const.brp_inc = 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 = 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; + + if (bt_const->feature & GS_CAN_FEATURE_LISTEN_ONLY) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + if (bt_const->feature & GS_CAN_FEATURE_LOOP_BACK) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; + + if (bt_const->feature & GS_CAN_FEATURE_TRIPLE_SAMPLE) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + + if (bt_const->feature & GS_CAN_FEATURE_ONE_SHOT) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; + + kfree(bt_const); + + SET_NETDEV_DEV(netdev, &intf->dev); + + 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); + free_candev(dev->netdev); + usb_kill_anchored_urbs(&dev->tx_submitted); + kfree(dev); +} + +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 = 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->altsetting[0].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->altsetting[0].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; + + kfree(dconf); + + 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); + return -EINVAL; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + init_usb_anchor(&dev->rx_submitted); + + atomic_set(&dev->active_channels, 0); + + 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); + if (IS_ERR_OR_NULL(dev->canch[i])) { + /* on failure destroy previously created candevs */ + icount = i; + for (i = 0; i < icount; i++) { + gs_destroy_candev(dev->canch[i]); + dev->canch[i] = NULL; + } + kfree(dev); + return rc; + } + dev->canch[i]->parent = dev; + } + + 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++) { + struct gs_can *can = dev->canch[i]; + + if (!can) + continue; + + gs_destroy_candev(can); + } + + usb_kill_anchored_urbs(&dev->rx_submitted); +} + +static const struct usb_device_id gs_usb_table[] = { + {USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)}, + {} /* 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."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c new file mode 100644 index 00000000000..541fb7a0562 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb.c @@ -0,0 +1,1665 @@ +/* + * 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. + * + * Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * + * Copyright (C) 2002-2006 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> + */ + +#include <linux/completion.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> + +#define MAX_TX_URBS 16 +#define MAX_RX_URBS 4 +#define START_TIMEOUT 1000 /* msecs */ +#define STOP_TIMEOUT 1000 /* msecs */ +#define USB_SEND_TIMEOUT 1000 /* msecs */ +#define USB_RECV_TIMEOUT 1000 /* msecs */ +#define RX_BUFFER_SIZE 3072 +#define CAN_USB_CLOCK 8000000 +#define MAX_NET_DEVICES 3 + +/* Kvaser USB devices */ +#define KVASER_VENDOR_ID 0x0bfd +#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 + +/* USB devices features */ +#define KVASER_HAS_SILENT_MODE BIT(0) +#define KVASER_HAS_TXRX_ERRORS BIT(1) + +/* Message header size */ +#define MSG_HEADER_LEN 2 + +/* 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 */ +#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) + +/* Can msg 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_GET_BUS_PARAMS 17 +#define CMD_GET_BUS_PARAMS_REPLY 18 +#define CMD_GET_CHIP_STATE 19 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_CTRL_MODE 21 +#define CMD_GET_CTRL_MODE 22 +#define CMD_GET_CTRL_MODE_REPLY 23 +#define CMD_RESET_CHIP 24 +#define CMD_RESET_CARD 25 +#define CMD_START_CHIP 26 +#define CMD_START_CHIP_REPLY 27 +#define CMD_STOP_CHIP 28 +#define CMD_STOP_CHIP_REPLY 29 +#define CMD_GET_CARD_INFO2 32 +#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_ERROR_EVENT 45 +#define CMD_FLUSH_QUEUE 48 +#define CMD_RESET_ERROR_COUNTER 49 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_CAN_ERROR_EVENT 51 +#define CMD_USB_THROTTLE 77 +#define CMD_LOG_MESSAGE 106 + +/* 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) + +/* 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 + +/* log message */ +#define KVASER_EXTENDED_FRAME BIT(31) + +struct kvaser_msg_simple { + u8 tid; + u8 channel; +} __packed; + +struct kvaser_msg_cardinfo { + u8 tid; + u8 nchannels; + __le32 serial_number; + __le32 padding; + __le32 clock_resolution; + __le32 mfgdate; + u8 ean[8]; + u8 hw_revision; + u8 usb_hs_mode; + __le16 padding2; +} __packed; + +struct kvaser_msg_cardinfo2 { + u8 tid; + u8 channel; + u8 pcb_id[24]; + __le32 oem_unlock_code; +} __packed; + +struct kvaser_msg_softinfo { + u8 tid; + u8 channel; + __le32 sw_options; + __le32 fw_version; + __le16 max_outstanding_tx; + __le16 padding[9]; +} __packed; + +struct kvaser_msg_busparams { + u8 tid; + u8 channel; + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 no_samp; +} __packed; + +struct kvaser_msg_tx_can { + u8 channel; + u8 tid; + u8 msg[14]; + u8 padding; + u8 flags; +} __packed; + +struct kvaser_msg_rx_can { + u8 channel; + u8 flag; + __le16 time[3]; + u8 msg[14]; +} __packed; + +struct kvaser_msg_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 kvaser_msg_tx_acknowledge { + u8 channel; + u8 tid; + __le16 time[3]; + u8 flags; + u8 time_offset; +} __packed; + +struct kvaser_msg_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 kvaser_msg_ctrl_mode { + u8 tid; + u8 channel; + u8 ctrl_mode; + u8 padding[3]; +} __packed; + +struct kvaser_msg_flush_queue { + u8 tid; + u8 channel; + u8 flags; + u8 padding[3]; +} __packed; + +struct kvaser_msg_log_message { + u8 channel; + u8 flags; + __le16 time[3]; + u8 dlc; + u8 time_offset; + __le32 id; + u8 data[8]; +} __packed; + +struct kvaser_msg { + u8 len; + u8 id; + union { + struct kvaser_msg_simple simple; + struct kvaser_msg_cardinfo cardinfo; + struct kvaser_msg_cardinfo2 cardinfo2; + struct kvaser_msg_softinfo softinfo; + struct kvaser_msg_busparams busparams; + struct kvaser_msg_tx_can tx_can; + struct kvaser_msg_rx_can rx_can; + struct kvaser_msg_chip_state_event chip_state_event; + struct kvaser_msg_tx_acknowledge tx_acknowledge; + struct kvaser_msg_error_event error_event; + struct kvaser_msg_ctrl_mode ctrl_mode; + struct kvaser_msg_flush_queue flush_queue; + struct kvaser_msg_log_message log_message; + } u; +} __packed; + +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 kvaser_usb_net_priv *nets[MAX_NET_DEVICES]; + + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + struct usb_anchor rx_submitted; + + u32 fw_version; + unsigned int nchannels; + + bool rxinitdone; + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +struct kvaser_usb_net_priv { + struct can_priv can; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct completion start_comp, stop_comp; + + struct kvaser_usb *dev; + struct net_device *netdev; + int channel; + + struct can_berr_counter bec; +}; + +static const struct usb_device_id kvaser_usb_table[] = { + { 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_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID), + .driver_info = KVASER_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_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID), + .driver_info = KVASER_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) }, + { } +}; +MODULE_DEVICE_TABLE(usb, kvaser_usb_table); + +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev, + struct kvaser_msg *msg) +{ + int actual_len; + + return usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + msg, msg->len, &actual_len, + USB_SEND_TIMEOUT); +} + +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, + struct kvaser_msg *msg) +{ + struct kvaser_msg *tmp; + void *buf; + int actual_len; + int err; + int pos; + unsigned long to = jiffies + msecs_to_jiffies(USB_RECV_TIMEOUT); + + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + buf, RX_BUFFER_SIZE, &actual_len, + USB_RECV_TIMEOUT); + if (err < 0) + goto end; + + pos = 0; + while (pos <= actual_len - MSG_HEADER_LEN) { + tmp = buf + pos; + + if (!tmp->len) + break; + + if (pos + tmp->len > actual_len) { + dev_err(dev->udev->dev.parent, + "Format error\n"); + break; + } + + if (tmp->id == id) { + memcpy(msg, tmp, tmp->len); + goto end; + } + + pos += tmp->len; + } + } while (time_before(jiffies, to)); + + err = -EINVAL; + +end: + kfree(buf); + + return err; +} + +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev, + u8 msg_id, int channel) +{ + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = msg_id; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple); + msg->u.simple.channel = channel; + msg->u.simple.tid = 0xff; + + rc = kvaser_usb_send_msg(dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_get_software_info(struct kvaser_usb *dev) +{ + struct kvaser_msg msg; + int err; + + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0); + if (err) + return err; + + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg); + if (err) + return err; + + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version); + + return 0; +} + +static int kvaser_usb_get_card_info(struct kvaser_usb *dev) +{ + struct kvaser_msg msg; + int err; + + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0); + if (err) + return err; + + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg); + if (err) + return err; + + dev->nchannels = msg.u.cardinfo.nchannels; + if (dev->nchannels > MAX_NET_DEVICES) + return -EINVAL; + + return 0; +} + +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct net_device_stats *stats; + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + struct sk_buff *skb; + struct can_frame *cf; + u8 channel = msg->u.tx_acknowledge.channel; + u8 tid = msg->u.tx_acknowledge.tid; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "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 % 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)) { + skb = alloc_can_err_skb(priv->netdev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } 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; + can_get_echo_skb(priv->netdev, context->echo_index); + + context->echo_index = MAX_TX_URBS; + atomic_dec(&priv->active_tx_urbs); + + netif_wake_queue(priv->netdev); +} + +static void kvaser_usb_simple_msg_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); +} + +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, + u8 msg_id) +{ + struct kvaser_usb *dev = priv->dev; + struct net_device *netdev = priv->netdev; + struct kvaser_msg *msg; + struct urb *urb; + void *buf; + int err; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + return -ENOMEM; + } + + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + msg = (struct kvaser_msg *)buf; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple); + msg->id = msg_id; + msg->u.simple.channel = priv->channel; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + buf, msg->len, + kvaser_usb_simple_msg_callback, priv); + 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); + kfree(buf); + return err; + } + + usb_free_urb(urb); + + return 0; +} + +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) +{ + int 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; +} + +static void kvaser_usb_rx_error(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + struct kvaser_usb_net_priv *priv; + unsigned int new_state; + u8 channel, status, txerr, rxerr, error_factor; + + switch (msg->id) { + case CMD_CAN_ERROR_EVENT: + channel = msg->u.error_event.channel; + status = msg->u.error_event.status; + txerr = msg->u.error_event.tx_errors_count; + rxerr = msg->u.error_event.rx_errors_count; + error_factor = msg->u.error_event.error_factor; + break; + case CMD_LOG_MESSAGE: + channel = msg->u.log_message.channel; + status = msg->u.log_message.data[0]; + txerr = msg->u.log_message.data[2]; + rxerr = msg->u.log_message.data[3]; + error_factor = msg->u.log_message.data[1]; + break; + case CMD_CHIP_STATE_EVENT: + channel = msg->u.chip_state_event.channel; + status = msg->u.chip_state_event.status; + txerr = msg->u.chip_state_event.tx_errors_count; + rxerr = msg->u.chip_state_event.rx_errors_count; + error_factor = 0; + break; + default: + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", + msg->id); + return; + } + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + stats = &priv->netdev->stats; + + if (status & M16C_STATE_BUS_RESET) { + kvaser_usb_unlink_tx_urbs(priv); + return; + } + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + new_state = priv->can.state; + + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status); + + if (status & M16C_STATE_BUS_OFF) { + cf->can_id |= CAN_ERR_BUSOFF; + + priv->can.can_stats.bus_off++; + if (!priv->can.restart_ms) + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP); + + netif_carrier_off(priv->netdev); + + new_state = CAN_STATE_BUS_OFF; + } else if (status & M16C_STATE_BUS_PASSIVE) { + if (priv->can.state != CAN_STATE_ERROR_PASSIVE) { + cf->can_id |= CAN_ERR_CRTL; + + if (txerr || rxerr) + cf->data[1] = (txerr > rxerr) + ? CAN_ERR_CRTL_TX_PASSIVE + : CAN_ERR_CRTL_RX_PASSIVE; + else + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE | + CAN_ERR_CRTL_RX_PASSIVE; + + priv->can.can_stats.error_passive++; + } + + new_state = CAN_STATE_ERROR_PASSIVE; + } + + if (status == M16C_STATE_BUS_ERROR) { + if ((priv->can.state < CAN_STATE_ERROR_WARNING) && + ((txerr >= 96) || (rxerr >= 96))) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) + ? CAN_ERR_CRTL_TX_WARNING + : CAN_ERR_CRTL_RX_WARNING; + + priv->can.can_stats.error_warning++; + new_state = CAN_STATE_ERROR_WARNING; + } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + + new_state = CAN_STATE_ERROR_ACTIVE; + } + } + + if (!status) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + + new_state = CAN_STATE_ERROR_ACTIVE; + } + + if (priv->can.restart_ms && + (priv->can.state >= CAN_STATE_BUS_OFF) && + (new_state < CAN_STATE_BUS_OFF)) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_carrier_on(priv->netdev); + + priv->can.can_stats.restarts++; + } + + if (error_factor) { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + + if (error_factor & M16C_EF_ACKE) + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK); + if (error_factor & M16C_EF_CRCE) + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + if (error_factor & M16C_EF_FORME) + cf->data[2] |= CAN_ERR_PROT_FORM; + if (error_factor & M16C_EF_STFE) + cf->data[2] |= CAN_ERR_PROT_STUFF; + if (error_factor & M16C_EF_BITE0) + cf->data[2] |= CAN_ERR_PROT_BIT0; + if (error_factor & M16C_EF_BITE1) + cf->data[2] |= CAN_ERR_PROT_BIT1; + if (error_factor & M16C_EF_TRE) + cf->data[2] |= CAN_ERR_PROT_TX; + } + + cf->data[6] = txerr; + cf->data[7] = rxerr; + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + priv->can.state = new_state; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, + const struct kvaser_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR)) { + netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n", + msg->u.rx_can.flag); + + stats->rx_errors++; + return; + } + + if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) { + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_over_errors++; + stats->rx_errors++; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } +} + +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_net_priv *priv; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + u8 channel = msg->u.rx_can.channel; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + stats = &priv->netdev->stats; + + if ((msg->u.rx_can.flag & MSG_FLAG_ERROR_FRAME) && + (msg->id == CMD_LOG_MESSAGE)) { + kvaser_usb_rx_error(dev, msg); + return; + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR | + MSG_FLAG_OVERRUN)) { + kvaser_usb_rx_can_err(priv, msg); + return; + } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) { + netdev_warn(priv->netdev, + "Unhandled frame (flags: 0x%02x)", + msg->u.rx_can.flag); + return; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->tx_dropped++; + return; + } + + if (msg->id == CMD_LOG_MESSAGE) { + cf->can_id = le32_to_cpu(msg->u.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(msg->u.log_message.dlc); + + if (msg->u.log_message.flags & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &msg->u.log_message.data, + cf->can_dlc); + } else { + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) | + (msg->u.rx_can.msg[1] & 0x3f); + + if (msg->id == CMD_RX_EXT_MESSAGE) { + cf->can_id <<= 18; + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) | + ((msg->u.rx_can.msg[3] & 0xff) << 6) | + (msg->u.rx_can.msg[4] & 0x3f); + cf->can_id |= CAN_EFF_FLAG; + } + + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]); + + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &msg->u.rx_can.msg[6], + cf->can_dlc); + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = msg->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "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_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = msg->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_handle_message(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + switch (msg->id) { + case CMD_START_CHIP_REPLY: + kvaser_usb_start_chip_reply(dev, msg); + break; + + case CMD_STOP_CHIP_REPLY: + kvaser_usb_stop_chip_reply(dev, msg); + break; + + case CMD_RX_STD_MESSAGE: + case CMD_RX_EXT_MESSAGE: + case CMD_LOG_MESSAGE: + kvaser_usb_rx_can_msg(dev, msg); + break; + + case CMD_CHIP_STATE_EVENT: + case CMD_CAN_ERROR_EVENT: + kvaser_usb_rx_error(dev, msg); + break; + + case CMD_TX_ACKNOWLEDGE: + kvaser_usb_tx_acknowledge(dev, msg); + break; + + default: + dev_warn(dev->udev->dev.parent, + "Unhandled message (%d)\n", msg->id); + break; + } +} + +static void kvaser_usb_read_bulk_callback(struct urb *urb) +{ + struct kvaser_usb *dev = urb->context; + struct kvaser_msg *msg; + int pos = 0; + int err, i; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + dev_info(dev->udev->dev.parent, "Rx URB aborted (%d)\n", + urb->status); + goto resubmit_urb; + } + + while (pos <= urb->actual_length - MSG_HEADER_LEN) { + msg = urb->transfer_buffer + pos; + + if (!msg->len) + break; + + if (pos + msg->len > urb->actual_length) { + dev_err(dev->udev->dev.parent, "Format error\n"); + break; + } + + kvaser_usb_handle_message(dev, msg); + + pos += msg->len; + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + urb->transfer_buffer, 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->udev->dev.parent, + "Failed resubmitting read bulk urb: %d\n", err); + } + + return; +} + +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *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; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_warn(dev->udev->dev.parent, + "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, + GFP_KERNEL, &buf_dma); + if (!buf) { + dev_warn(dev->udev->dev.parent, + "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, 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, 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->udev->dev.parent, + "Cannot setup read URBs, error %d\n", err); + return err; + } else if (i < MAX_RX_URBS) { + dev_warn(dev->udev->dev.parent, + "RX performances may be slow\n"); + } + + dev->rxinitdone = true; + + return 0; +} + +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv) +{ + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = CMD_SET_CTRL_MODE; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode); + msg->u.ctrl_mode.tid = 0xff; + msg->u.ctrl_mode.channel = priv->channel; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT; + else + msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL; + + rc = kvaser_usb_send_msg(priv->dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->start_comp); + + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->start_comp, + msecs_to_jiffies(START_TIMEOUT))) + return -ETIMEDOUT; + + 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 = kvaser_usb_set_opt_mode(priv); + if (err) + goto error; + + err = kvaser_usb_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_unlink_all_urbs(struct kvaser_usb *dev) +{ + int i; + + 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 < MAX_NET_DEVICES; i++) { + struct kvaser_usb_net_priv *priv = dev->nets[i]; + + if (priv) + kvaser_usb_unlink_tx_urbs(priv); + } +} + +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->stop_comp); + + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->stop_comp, + msecs_to_jiffies(STOP_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = CMD_FLUSH_QUEUE; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue); + msg->u.flush_queue.channel = priv->channel; + msg->u.flush_queue.flags = 0x00; + + rc = kvaser_usb_send_msg(priv->dev, msg); + + kfree(msg); + return rc; +} + +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 = kvaser_usb_flush_queue(priv); + if (err) + netdev_warn(netdev, "Cannot flush queue, error %d\n", err); + + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel)) + netdev_warn(netdev, "Cannot reset card, error %d\n", err); + + err = kvaser_usb_stop_chip(priv); + if (err) + netdev_warn(netdev, "Cannot stop device, error %d\n", err); + + 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 can_frame *cf = (struct can_frame *)skb->data; + struct kvaser_usb_tx_urb_context *context = NULL; + struct urb *urb; + void *buf; + struct kvaser_msg *msg; + int i, err; + int ret = NETDEV_TX_OK; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + stats->tx_dropped++; + goto nourbmem; + } + + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC); + if (!buf) { + stats->tx_dropped++; + goto nobufmem; + } + + msg = buf; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can); + msg->u.tx_can.flags = 0; + msg->u.tx_can.channel = priv->channel; + + if (cf->can_id & CAN_EFF_FLAG) { + msg->id = CMD_TX_EXT_MESSAGE; + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f; + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f; + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f; + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff; + msg->u.tx_can.msg[4] = cf->can_id & 0x3f; + } else { + msg->id = CMD_TX_STD_MESSAGE; + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f; + msg->u.tx_can.msg[1] = cf->can_id & 0x3f; + } + + msg->u.tx_can.msg[5] = cf->can_dlc; + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc); + + if (cf->can_id & CAN_RTR_FLAG) + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME; + + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) { + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &priv->tx_contexts[i]; + break; + } + } + + if (!context) { + netdev_warn(netdev, "cannot find free context\n"); + ret = NETDEV_TX_BUSY; + goto releasebuf; + } + + context->priv = priv; + context->echo_index = i; + context->dlc = cf->can_dlc; + + msg->u.tx_can.tid = context->echo_index; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + buf, msg->len, + kvaser_usb_write_bulk_callback, context); + usb_anchor_urb(urb, &priv->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&priv->active_tx_urbs); + + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) + netif_stop_queue(netdev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + skb = NULL; /* set to NULL to avoid double free in + * dev_kfree_skb(skb) */ + + atomic_dec(&priv->active_tx_urbs); + 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; + } + + usb_free_urb(urb); + + return NETDEV_TX_OK; + +releasebuf: + kfree(buf); +nobufmem: + usb_free_urb(urb); +nourbmem: + dev_kfree_skb(skb); + 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 const struct can_bittiming_const kvaser_usb_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 int kvaser_usb_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_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = CMD_SET_BUS_PARAMS; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams); + msg->u.busparams.channel = priv->channel; + msg->u.busparams.tid = 0xff; + msg->u.busparams.bitrate = cpu_to_le32(bt->bitrate); + msg->u.busparams.sjw = bt->sjw; + msg->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; + msg->u.busparams.tseg2 = bt->phase_seg2; + + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + msg->u.busparams.no_samp = 3; + else + msg->u.busparams.no_samp = 1; + + rc = kvaser_usb_send_msg(dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_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_simple_msg_async(priv, CMD_START_CHIP); + if (err) + return err; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int kvaser_usb_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 void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) +{ + int i; + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + unregister_netdev(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 usb_interface *intf, + const struct usb_device_id *id, int channel) +{ + struct kvaser_usb *dev = usb_get_intfdata(intf); + struct net_device *netdev; + struct kvaser_usb_net_priv *priv; + int i, err; + + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Cannot alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + init_completion(&priv->start_comp); + init_completion(&priv->stop_comp); + + init_usb_anchor(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; + + priv->dev = dev; + priv->netdev = netdev; + priv->channel = channel; + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = CAN_USB_CLOCK; + priv->can.bittiming_const = &kvaser_usb_bittiming_const; + priv->can.do_set_bittiming = kvaser_usb_set_bittiming; + priv->can.do_set_mode = kvaser_usb_set_mode; + if (id->driver_info & KVASER_HAS_TXRX_ERRORS) + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + if (id->driver_info & KVASER_HAS_SILENT_MODE) + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + netdev->flags |= IFF_ECHO; + + netdev->netdev_ops = &kvaser_usb_netdev_ops; + + SET_NETDEV_DEV(netdev, &intf->dev); + netdev->dev_id = channel; + + dev->nets[channel] = priv; + + err = register_candev(netdev); + if (err) { + dev_err(&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_get_endpoints(const struct usb_interface *intf, + struct usb_endpoint_descriptor **in, + struct usb_endpoint_descriptor **out) +{ + const struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + iface_desc = &intf->altsetting[0]; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!*in && usb_endpoint_is_bulk_in(endpoint)) + *in = endpoint; + + if (!*out && usb_endpoint_is_bulk_out(endpoint)) + *out = endpoint; + + /* use first bulk endpoint for in and out */ + if (*in && *out) + return 0; + } + + return -ENODEV; +} + +static int kvaser_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct kvaser_usb *dev; + int err = -ENOMEM; + int i; + + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out); + 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); + + for (i = 0; i < MAX_NET_DEVICES; i++) + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i); + + err = kvaser_usb_get_software_info(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get software infos, error %d\n", err); + return err; + } + + err = kvaser_usb_get_card_info(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get card infos, error %d\n", err); + return err; + } + + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", + ((dev->fw_version >> 24) & 0xff), + ((dev->fw_version >> 16) & 0xff), + (dev->fw_version & 0xffff)); + + for (i = 0; i < dev->nchannels; i++) { + err = kvaser_usb_init_one(intf, 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_DESCRIPTION("CAN driver for Kvaser CAN/USB devices"); +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 00000000000..1aefbc88d64 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o +peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c new file mode 100644 index 00000000000..925ab8ec932 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -0,0 +1,903 @@ +/* + * CAN driver for PEAK System PCAN-USB adapter + * Derived from the PCAN project file driver/src/pcan_usb.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); + +/* PCAN-USB Endpoints */ +#define PCAN_USB_EP_CMDOUT 1 +#define PCAN_USB_EP_CMDIN (PCAN_USB_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USB_EP_MSGOUT 2 +#define PCAN_USB_EP_MSGIN (PCAN_USB_EP_MSGOUT | USB_DIR_IN) + +/* PCAN-USB command struct */ +#define PCAN_USB_CMD_FUNC 0 +#define PCAN_USB_CMD_NUM 1 +#define PCAN_USB_CMD_ARGS 2 +#define PCAN_USB_CMD_ARGS_LEN 14 +#define PCAN_USB_CMD_LEN (PCAN_USB_CMD_ARGS + \ + PCAN_USB_CMD_ARGS_LEN) + +/* PCAN-USB command timeout (ms.) */ +#define PCAN_USB_COMMAND_TIMEOUT 1000 + +/* PCAN-USB startup timeout (ms.) */ +#define PCAN_USB_STARTUP_TIMEOUT 10 + +/* PCAN-USB rx/tx buffers size */ +#define PCAN_USB_RX_BUFFER_SIZE 64 +#define PCAN_USB_TX_BUFFER_SIZE 64 + +#define PCAN_USB_MSG_HEADER_LEN 2 + +/* PCAN-USB adapter internal clock (MHz) */ +#define PCAN_USB_CRYSTAL_HZ 16000000 + +/* PCAN-USB USB message record status/len field */ +#define PCAN_USB_STATUSLEN_TIMESTAMP (1 << 7) +#define PCAN_USB_STATUSLEN_INTERNAL (1 << 6) +#define PCAN_USB_STATUSLEN_EXT_ID (1 << 5) +#define PCAN_USB_STATUSLEN_RTR (1 << 4) +#define PCAN_USB_STATUSLEN_DLC (0xf) + +/* PCAN-USB error flags */ +#define PCAN_USB_ERROR_TXFULL 0x01 +#define PCAN_USB_ERROR_RXQOVR 0x02 +#define PCAN_USB_ERROR_BUS_LIGHT 0x04 +#define PCAN_USB_ERROR_BUS_HEAVY 0x08 +#define PCAN_USB_ERROR_BUS_OFF 0x10 +#define PCAN_USB_ERROR_RXQEMPTY 0x20 +#define PCAN_USB_ERROR_QOVR 0x40 +#define PCAN_USB_ERROR_TXQFULL 0x80 + +/* SJA1000 modes */ +#define SJA1000_MODE_NORMAL 0x00 +#define SJA1000_MODE_INIT 0x01 + +/* + * tick duration = 42.666 us => + * (tick_number * 44739243) >> 20 ~ (tick_number * 42666) / 1000 + * accuracy = 10^-7 + */ +#define PCAN_USB_TS_DIV_SHIFTER 20 +#define PCAN_USB_TS_US_PER_TICK 44739243 + +/* PCAN-USB messages record types */ +#define PCAN_USB_REC_ERROR 1 +#define PCAN_USB_REC_ANALOG 2 +#define PCAN_USB_REC_BUSLOAD 3 +#define PCAN_USB_REC_TS 4 +#define PCAN_USB_REC_BUSEVT 5 + +/* private to PCAN-USB adapter */ +struct pcan_usb { + struct peak_usb_device dev; + struct peak_time_ref time_ref; + struct timer_list restart_timer; +}; + +/* incoming message context for decoding */ +struct pcan_usb_msg_context { + u16 ts16; + u8 prev_ts8; + u8 *ptr; + u8 *end; + u8 rec_cnt; + u8 rec_idx; + u8 rec_data_idx; + struct net_device *netdev; + struct pcan_usb *pdev; +}; + +/* + * send a command + */ +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + dev->cmd_buf[PCAN_USB_CMD_FUNC] = f; + dev->cmd_buf[PCAN_USB_CMD_NUM] = n; + + if (p) + memcpy(dev->cmd_buf + PCAN_USB_CMD_ARGS, + p, PCAN_USB_CMD_ARGS_LEN); + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "sending cmd f=0x%x n=0x%x failure: %d\n", + f, n, err); + return err; +} + +/* + * send a command then wait for its response + */ +static int pcan_usb_wait_rsp(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* first, send command */ + err = pcan_usb_send_cmd(dev, f, n, NULL); + if (err) + return err; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USB_EP_CMDIN), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "waiting rsp f=0x%x n=0x%x failure: %d\n", f, n, err); + else if (p) + memcpy(p, dev->cmd_buf + PCAN_USB_CMD_ARGS, + PCAN_USB_CMD_ARGS_LEN); + + return err; +} + +static int pcan_usb_set_sja1000(struct peak_usb_device *dev, u8 mode) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [1] = mode, + }; + + return pcan_usb_send_cmd(dev, 9, 2, args); +} + +static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 3, 2, args); +} + +static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 3, 3, args); +} + +static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 10, 2, args); +} + +/* + * set bittiming value to can + */ +static int pcan_usb_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(dev->netdev, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + args[0] = btr1; + args[1] = btr0; + + return pcan_usb_send_cmd(dev, 1, 2, args); +} + +/* + * init/reset can + */ +static int pcan_usb_write_mode(struct peak_usb_device *dev, u8 onoff) +{ + int err; + + err = pcan_usb_set_bus(dev, onoff); + if (err) + return err; + + if (!onoff) { + err = pcan_usb_set_sja1000(dev, SJA1000_MODE_INIT); + } else { + /* the PCAN-USB needs time to init */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + } + + return err; +} + +/* + * handle end of waiting for the device to reset + */ +static void pcan_usb_restart(unsigned long arg) +{ + /* notify candev and netdev */ + peak_usb_restart_complete((struct peak_usb_device *)arg); +} + +/* + * handle the submission of the restart urb + */ +static void pcan_usb_restart_pending(struct urb *urb) +{ + struct pcan_usb *pdev = urb->context; + + /* the PCAN-USB needs time to restart */ + mod_timer(&pdev->restart_timer, + jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + + /* can delete usb resources */ + peak_usb_async_complete(urb); +} + +/* + * handle asynchronous restart + */ +static int pcan_usb_restart_async(struct peak_usb_device *dev, struct urb *urb, + u8 *buf) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + if (timer_pending(&pdev->restart_timer)) + return -EBUSY; + + /* set bus on */ + buf[PCAN_USB_CMD_FUNC] = 3; + buf[PCAN_USB_CMD_NUM] = 2; + buf[PCAN_USB_CMD_ARGS] = 1; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + buf, PCAN_USB_CMD_LEN, + pcan_usb_restart_pending, pdev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * read serial number from device + */ +static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, 6, 1, args); + if (err) { + netdev_err(dev->netdev, "getting serial failure: %d\n", err); + } else if (serial_number) { + u32 tmp32; + + memcpy(&tmp32, args, 4); + *serial_number = le32_to_cpu(tmp32); + } + + return err; +} + +/* + * read device id from device + */ +static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, 4, 1, args); + if (err) + netdev_err(dev->netdev, "getting device id failure: %d\n", err); + else if (device_id) + *device_id = args[0]; + + return err; +} + +/* + * update current time ref with received timestamp + */ +static int pcan_usb_update_ts(struct pcan_usb_msg_context *mc) +{ + u16 tmp16; + + if ((mc->ptr+2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + + mc->ts16 = le16_to_cpu(tmp16); + + if (mc->rec_idx > 0) + peak_usb_update_ts_now(&mc->pdev->time_ref, mc->ts16); + else + peak_usb_set_ts_now(&mc->pdev->time_ref, mc->ts16); + + return 0; +} + +/* + * decode received timestamp + */ +static int pcan_usb_decode_ts(struct pcan_usb_msg_context *mc, u8 first_packet) +{ + /* only 1st packet supplies a word timestamp */ + if (first_packet) { + u16 tmp16; + + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + mc->ts16 = le16_to_cpu(tmp16); + mc->prev_ts8 = mc->ts16 & 0x00ff; + } else { + u8 ts8; + + if ((mc->ptr + 1) > mc->end) + return -EINVAL; + + ts8 = *mc->ptr++; + + if (ts8 < mc->prev_ts8) + mc->ts16 += 0x100; + + mc->ts16 &= 0xff00; + mc->ts16 |= ts8; + mc->prev_ts8 = ts8; + } + + return 0; +} + +static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, + u8 status_len) +{ + struct sk_buff *skb; + struct can_frame *cf; + struct timeval tv; + enum can_state new_state; + + /* ignore this error until 1st ts received */ + if (n == PCAN_USB_ERROR_QOVR) + if (!mc->pdev->time_ref.tick_count) + return 0; + + new_state = mc->pdev->dev.can.state; + + switch (mc->pdev->dev.can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + + case CAN_STATE_ERROR_WARNING: + if (n & PCAN_USB_ERROR_BUS_HEAVY) { + new_state = CAN_STATE_ERROR_PASSIVE; + break; + } + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + if ((n & PCAN_USB_ERROR_BUS_LIGHT) == 0) { + /* no error (back to active state) */ + mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + break; + + case CAN_STATE_ERROR_PASSIVE: + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + + if ((n & PCAN_USB_ERROR_BUS_HEAVY) == 0) { + /* no error (back to active state) */ + mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + break; + + default: + /* do nothing waiting for restart */ + return 0; + } + + /* donot post any error if current state didn't change */ + if (mc->pdev->dev.can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(mc->netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE | + CAN_ERR_CRTL_RX_PASSIVE; + mc->pdev->dev.can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING | + CAN_ERR_CRTL_RX_WARNING; + mc->pdev->dev.can.can_stats.error_warning++; + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + mc->netdev->stats.rx_over_errors++; + mc->netdev->stats.rx_errors++; + + new_state = mc->pdev->dev.can.state; + break; + } + + mc->pdev->dev.can.state = new_state; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); + hwts->hwtstamp = timeval_to_ktime(tv); + } + + netif_rx(skb); + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + + return 0; +} + +/* + * decode non-data usb message + */ +static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc, + u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + u8 f, n; + int err; + + /* check whether function and number can be read */ + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + f = mc->ptr[PCAN_USB_CMD_FUNC]; + n = mc->ptr[PCAN_USB_CMD_NUM]; + mc->ptr += PCAN_USB_CMD_ARGS; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + int err = pcan_usb_decode_ts(mc, !mc->rec_idx); + + if (err) + return err; + } + + switch (f) { + case PCAN_USB_REC_ERROR: + err = pcan_usb_decode_error(mc, n, status_len); + if (err) + return err; + break; + + case PCAN_USB_REC_ANALOG: + /* analog values (ignored) */ + rec_len = 2; + break; + + case PCAN_USB_REC_BUSLOAD: + /* bus load (ignored) */ + rec_len = 1; + break; + + case PCAN_USB_REC_TS: + /* only timestamp */ + if (pcan_usb_update_ts(mc)) + return -EINVAL; + break; + + case PCAN_USB_REC_BUSEVT: + /* error frame/bus event */ + if (n & PCAN_USB_ERROR_TXQFULL) + netdev_dbg(mc->netdev, "device Tx queue full)\n"); + break; + default: + netdev_err(mc->netdev, "unexpected function %u\n", f); + break; + } + + if ((mc->ptr + rec_len) > mc->end) + return -EINVAL; + + mc->ptr += rec_len; + + return 0; +} + +/* + * decode data usb message + */ +static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + struct sk_buff *skb; + struct can_frame *cf; + struct timeval tv; + struct skb_shared_hwtstamps *hwts; + + skb = alloc_can_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + if (status_len & PCAN_USB_STATUSLEN_EXT_ID) { + u32 tmp32; + + if ((mc->ptr + 4) > mc->end) + goto decode_failed; + + memcpy(&tmp32, mc->ptr, 4); + mc->ptr += 4; + + cf->can_id = le32_to_cpu(tmp32 >> 3) | CAN_EFF_FLAG; + } else { + u16 tmp16; + + if ((mc->ptr + 2) > mc->end) + goto decode_failed; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + cf->can_id = le16_to_cpu(tmp16 >> 5); + } + + cf->can_dlc = get_can_dlc(rec_len); + + /* first data packet timestamp is a word */ + if (pcan_usb_decode_ts(mc, !mc->rec_data_idx)) + goto decode_failed; + + /* read data */ + memset(cf->data, 0x0, sizeof(cf->data)); + if (status_len & PCAN_USB_STATUSLEN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if ((mc->ptr + rec_len) > mc->end) + goto decode_failed; + + memcpy(cf->data, mc->ptr, cf->can_dlc); + mc->ptr += rec_len; + } + + /* convert timestamp into kernel time */ + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); + hwts = skb_hwtstamps(skb); + hwts->hwtstamp = timeval_to_ktime(tv); + + /* push the skb */ + netif_rx(skb); + + /* update statistics */ + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + + return 0; + +decode_failed: + dev_kfree_skb(skb); + return -EINVAL; +} + +/* + * process incoming message + */ +static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf) +{ + struct pcan_usb_msg_context mc = { + .rec_cnt = ibuf[1], + .ptr = ibuf + PCAN_USB_MSG_HEADER_LEN, + .end = ibuf + lbuf, + .netdev = dev->netdev, + .pdev = container_of(dev, struct pcan_usb, dev), + }; + int err; + + for (err = 0; mc.rec_idx < mc.rec_cnt && !err; mc.rec_idx++) { + u8 sl = *mc.ptr++; + + /* handle status and error frames here */ + if (sl & PCAN_USB_STATUSLEN_INTERNAL) { + err = pcan_usb_decode_status(&mc, sl); + /* handle normal can frames here */ + } else { + err = pcan_usb_decode_data(&mc, sl); + mc.rec_data_idx++; + } + } + + return err; +} + +/* + * process any incoming buffer + */ +static int pcan_usb_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + int err = 0; + + if (urb->actual_length > PCAN_USB_MSG_HEADER_LEN) { + err = pcan_usb_decode_msg(dev, urb->transfer_buffer, + urb->actual_length); + + } else if (urb->actual_length > 0) { + netdev_err(dev->netdev, "usb message length error (%u)\n", + urb->actual_length); + err = -EINVAL; + } + + return err; +} + +/* + * process outgoing packet + */ +static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size) +{ + struct net_device *netdev = dev->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 *pc; + + obuf[0] = 2; + obuf[1] = 1; + + pc = obuf + PCAN_USB_MSG_HEADER_LEN; + + /* status/len byte */ + *pc = cf->can_dlc; + if (cf->can_id & CAN_RTR_FLAG) + *pc |= PCAN_USB_STATUSLEN_RTR; + + /* can id */ + if (cf->can_id & CAN_EFF_FLAG) { + __le32 tmp32 = cpu_to_le32((cf->can_id & CAN_ERR_MASK) << 3); + + *pc |= PCAN_USB_STATUSLEN_EXT_ID; + memcpy(++pc, &tmp32, 4); + pc += 4; + } else { + __le16 tmp16 = cpu_to_le16((cf->can_id & CAN_ERR_MASK) << 5); + + memcpy(++pc, &tmp16, 2); + pc += 2; + } + + /* can data */ + if (!(cf->can_id & CAN_RTR_FLAG)) { + memcpy(pc, cf->data, cf->can_dlc); + pc += cf->can_dlc; + } + + obuf[(*size)-1] = (u8)(stats->tx_packets & 0xff); + + return 0; +} + +/* + * start interface + */ +static int pcan_usb_start(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + /* number of bits used in timestamps read from adapter struct */ + peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb); + + /* if revision greater than 3, can put silent mode on/off */ + if (dev->device_rev > 3) { + int err; + + err = pcan_usb_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + } + + return pcan_usb_set_ext_vcc(dev, 0); +} + +static int pcan_usb_init(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + u32 serial_number; + int err; + + /* initialize a timer needed to wait for hardware restart */ + init_timer(&pdev->restart_timer); + pdev->restart_timer.function = pcan_usb_restart; + pdev->restart_timer.data = (unsigned long)dev; + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevice. + */ + err = pcan_usb_get_serial(dev, &serial_number); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s serial number (err %d)\n", + pcan_usb.name, err); + return err; + } + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s adapter hwrev %u serial %08X (%u channel)\n", + pcan_usb.name, dev->device_rev, serial_number, + pcan_usb.ctrl_count); + + return 0; +} + +/* + * probe function for new PCAN-USB usb interface + */ +static int pcan_usb_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + switch (ep->bEndpointAddress) { + case PCAN_USB_EP_CMDOUT: + case PCAN_USB_EP_CMDIN: + case PCAN_USB_EP_MSGOUT: + case PCAN_USB_EP_MSGIN: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB adapter + */ +struct peak_usb_adapter pcan_usb = { + .name = "PCAN-USB", + .device_id = PCAN_USB_PRODUCT_ID, + .ctrl_count = 1, + .clock = { + .freq = PCAN_USB_CRYSTAL_HZ / 2 , + }, + .bittiming_const = { + .name = "pcan_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb), + + /* timestamps usage */ + .ts_used_bits = 16, + .ts_period = 24575, /* calibration period in ts. */ + .us_per_ts_scale = PCAN_USB_TS_US_PER_TICK, /* us=(ts*scale) */ + .us_per_ts_shift = PCAN_USB_TS_DIV_SHIFTER, /* >> shift */ + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USB_EP_MSGIN, + .ep_msg_out = {PCAN_USB_EP_MSGOUT}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USB_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USB_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_probe, + .dev_init = pcan_usb_init, + .dev_set_bus = pcan_usb_write_mode, + .dev_set_bittiming = pcan_usb_set_bittiming, + .dev_get_device_id = pcan_usb_get_device_id, + .dev_decode_buf = pcan_usb_decode_buf, + .dev_encode_msg = pcan_usb_encode_msg, + .dev_start = pcan_usb_start, + .dev_restart_async = pcan_usb_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c new file mode 100644 index 00000000000..644e6ab8a48 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -0,0 +1,950 @@ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters"); +MODULE_LICENSE("GPL v2"); + +/* Table of devices that work with this driver */ +static struct usb_device_id peak_usb_table[] = { + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, peak_usb_table); + +/* List of supported PCAN-USB adapters (NULL terminated list) */ +static struct peak_usb_adapter *peak_usb_adapters_list[] = { + &pcan_usb, + &pcan_usb_pro, + NULL, +}; + +/* + * dump memory + */ +#define DUMP_WIDTH 16 +void 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, + struct peak_usb_adapter *adapter) +{ + if (time_ref) { + memset(time_ref, 0, sizeof(struct peak_time_ref)); + time_ref->adapter = adapter; + } +} + +static void peak_usb_add_us(struct timeval *tv, u32 delta_us) +{ + /* number of s. to add to final time */ + u32 delta_s = delta_us / 1000000; + + delta_us -= delta_s * 1000000; + + tv->tv_usec += delta_us; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + delta_s++; + } + tv->tv_sec += delta_s; +} + +/* + * sometimes, another now may be more recent than current one... + */ +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + time_ref->ts_dev_2 = ts_now; + + /* should wait at least two passes before computing */ + if (time_ref->tv_host.tv_sec > 0) { + u32 delta_ts = time_ref->ts_dev_2 - time_ref->ts_dev_1; + + if (time_ref->ts_dev_2 < time_ref->ts_dev_1) + delta_ts &= (1 << time_ref->adapter->ts_used_bits) - 1; + + time_ref->ts_total += delta_ts; + } +} + +/* + * register device timestamp as now + */ +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + if (time_ref->tv_host_0.tv_sec == 0) { + /* use monotonic clock to correctly compute further deltas */ + time_ref->tv_host_0 = ktime_to_timeval(ktime_get()); + time_ref->tv_host.tv_sec = 0; + } else { + /* + * delta_us should not be >= 2^32 => delta_s should be < 4294 + * handle 32-bits wrapping here: if count of s. reaches 4200, + * reset counters and change time base + */ + if (time_ref->tv_host.tv_sec != 0) { + u32 delta_s = time_ref->tv_host.tv_sec + - time_ref->tv_host_0.tv_sec; + if (delta_s > 4200) { + time_ref->tv_host_0 = time_ref->tv_host; + time_ref->ts_total = 0; + } + } + + time_ref->tv_host = ktime_to_timeval(ktime_get()); + time_ref->tick_count++; + } + + time_ref->ts_dev_1 = time_ref->ts_dev_2; + peak_usb_update_ts_now(time_ref, ts_now); +} + +/* + * compute timeval according to current ts and time_ref data + */ +void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, + struct timeval *tv) +{ + /* protect from getting timeval before setting now */ + if (time_ref->tv_host.tv_sec > 0) { + u64 delta_us; + + delta_us = ts - time_ref->ts_dev_2; + if (ts < time_ref->ts_dev_2) + delta_us &= (1 << time_ref->adapter->ts_used_bits) - 1; + + delta_us += time_ref->ts_total; + + delta_us *= time_ref->adapter->us_per_ts_scale; + delta_us >>= time_ref->adapter->us_per_ts_shift; + + *tv = time_ref->tv_host_0; + peak_usb_add_us(tv, (u32)delta_us); + } else { + *tv = ktime_to_timeval(ktime_get()); + } +} + +/* + * callback for bulk Rx urb + */ +static void peak_usb_read_bulk_callback(struct urb *urb) +{ + struct peak_usb_device *dev = urb->context; + struct net_device *netdev; + int err; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + /* check reception status */ + switch (urb->status) { + case 0: + /* success */ + break; + + case -EILSEQ: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + + default: + if (net_ratelimit()) + netdev_err(netdev, + "Rx urb aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + /* protect from any incoming empty msgs */ + if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) { + /* handle these kinds of msgs only if _start callback called */ + if (dev->state & PCAN_USB_STATE_STARTED) { + err = dev->adapter->dev_decode_buf(dev, urb); + if (err) + 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->dlc; + + /* prevent tx timeout */ + netdev->trans_start = jiffies; + break; + + default: + if (net_ratelimit()) + netdev_err(netdev, "Tx urb aborted (%d)\n", + urb->status); + case -EPROTO: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + + break; + } + + /* should always release echo skb and corresponding context */ + can_get_echo_skb(netdev, context->echo_index); + context->echo_index = PCAN_USB_MAX_TX_URBS; + + /* do wakeup tx queue in case of success only */ + if (!urb->status) + netif_wake_queue(netdev); +} + +/* + * called by netdev to send one skb on the CAN interface. + */ +static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct peak_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct urb *urb; + u8 *obuf; + int i, err; + size_t size = dev->adapter->tx_buffer_size; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + if (dev->tx_contexts[i].echo_index == PCAN_USB_MAX_TX_URBS) { + context = dev->tx_contexts + i; + break; + } + + if (!context) { + /* should not occur except during restart */ + return NETDEV_TX_BUSY; + } + + urb = context->urb; + obuf = urb->transfer_buffer; + + err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size); + if (err) { + if (net_ratelimit()) + netdev_err(netdev, "packet dropped\n"); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; + } + + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + + /* this context is not used in fact */ + context->echo_index = PCAN_USB_MAX_TX_URBS; + + atomic_dec(&dev->active_tx_urbs); + + switch (err) { + case -ENODEV: + netif_device_detach(netdev); + break; + default: + netdev_warn(netdev, "tx urb submitting failed err=%d\n", + err); + case -ENOENT: + /* cable unplugged */ + stats->tx_dropped++; + } + } else { + netdev->trans_start = jiffies; + + /* slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= PCAN_USB_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + return NETDEV_TX_OK; +} + +/* + * start the CAN interface. + * Rx and Tx urbs are allocated here. Rx urbs are submitted here. + */ +static int peak_usb_start(struct peak_usb_device *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + for (i = 0; i < PCAN_USB_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + /* create a URB, and a buffer for it, to receive usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + buf, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + usb_unanchor_urb(urb); + kfree(buf); + usb_free_urb(urb); + break; + } + + /* drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* did we submit any URBs? Warn if we was not able to submit all urbs */ + if (i < PCAN_USB_MAX_RX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any rx URB\n"); + return err; + } + + netdev_warn(netdev, "rx performance may be slow\n"); + } + + /* pre-alloc tx buffers and corresponding urbs */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct peak_tx_urb_context *context; + struct urb *urb; + u8 *buf; + + /* create a URB and a buffer for it, to transmit usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL); + if (!buf) { + 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); + + /* unlink all pending urbs and free used memory */ + peak_usb_unlink_all_urbs(dev); + + if (dev->adapter->dev_stop) + dev->adapter->dev_stop(dev); + + close_candev(netdev); + + dev->can.state = CAN_STATE_STOPPED; + + /* can set bus off now */ + if (dev->adapter->dev_set_bus) { + int err = dev->adapter->dev_set_bus(dev, 0); + if (err) + return err; + } + + return 0; +} + +/* + * handle end of waiting for the device to reset + */ +void peak_usb_restart_complete(struct peak_usb_device *dev) +{ + /* finally MUST update can state */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + /* netdev queue can be awaken now */ + netif_wake_queue(dev->netdev); +} + +void peak_usb_async_complete(struct urb *urb) +{ + kfree(urb->transfer_buffer); + usb_free_urb(urb); +} + +/* + * device (auto-)restart mechanism runs in a timer context => + * MUST handle restart with asynchronous usb transfers + */ +static int peak_usb_restart(struct peak_usb_device *dev) +{ + struct urb *urb; + int err; + u8 *buf; + + /* + * if device doesn't define any asynchronous restart handler, simply + * wake the netdev queue up + */ + if (!dev->adapter->dev_restart_async) { + peak_usb_restart_complete(dev); + return 0; + } + + /* first allocate a urb to handle the asynchronous steps */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(dev->netdev, "no memory left for urb\n"); + return -ENOMEM; + } + + /* also allocate enough space for the commands to send */ + buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_ATOMIC); + if (!buf) { + 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 bitrate. + */ +static int peak_usb_set_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + + if (dev->adapter->dev_set_bittiming) { + int err = dev->adapter->dev_set_bittiming(dev, bt); + + if (err) + netdev_info(netdev, "couldn't set bitrate (err %d)\n", + err); + return err; + } + + return 0; +} + +static const struct net_device_ops peak_usb_netdev_ops = { + .ndo_open = peak_usb_ndo_open, + .ndo_stop = peak_usb_ndo_stop, + .ndo_start_xmit = peak_usb_ndo_start_xmit, + .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(struct peak_usb_adapter *peak_usb_adapter, + struct usb_interface *intf, int ctrl_idx) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + int sizeof_candev = peak_usb_adapter->sizeof_dev_private; + struct peak_usb_device *dev; + struct net_device *netdev; + int i, err; + u16 tmp16; + + if (sizeof_candev < sizeof(struct peak_usb_device)) + sizeof_candev = sizeof(struct peak_usb_device); + + netdev = alloc_candev(sizeof_candev, PCAN_USB_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "%s: couldn't alloc candev\n", + PCAN_USB_DRIVER_NAME); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + /* allocate a buffer large enough to send commands */ + dev->cmd_buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL); + if (!dev->cmd_buf) { + err = -ENOMEM; + goto lbl_set_intf_data; + } + + dev->udev = usb_dev; + dev->netdev = netdev; + dev->adapter = peak_usb_adapter; + dev->ctrl_idx = ctrl_idx; + dev->state = PCAN_USB_STATE_CONNECTED; + + dev->ep_msg_in = peak_usb_adapter->ep_msg_in; + dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx]; + + dev->can.clock = peak_usb_adapter->clock; + dev->can.bittiming_const = &peak_usb_adapter->bittiming_const; + dev->can.do_set_bittiming = peak_usb_set_bittiming; + dev->can.do_set_mode = peak_usb_set_mode; + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LISTENONLY; + + netdev->netdev_ops = &peak_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = PCAN_USB_MAX_TX_URBS; + + dev->prev_siblings = usb_get_intfdata(intf); + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + 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_free_cmd_buf; + } + + if (dev->prev_siblings) + (dev->prev_siblings)->next_siblings = dev; + + /* keep hw revision into the netdevice */ + tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice); + dev->device_rev = tmp16 >> 8; + + if (dev->adapter->dev_init) { + err = dev->adapter->dev_init(dev); + if (err) + goto lbl_free_cmd_buf; + } + + /* set bus off */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 0); + if (err) + goto lbl_free_cmd_buf; + } + + /* get device number early */ + if (dev->adapter->dev_get_device_id) + dev->adapter->dev_get_device_id(dev, &dev->device_number); + + netdev_info(netdev, "attached to %s channel %u (device %u)\n", + peak_usb_adapter->name, ctrl_idx, dev->device_number); + + return 0; + +lbl_free_cmd_buf: + kfree(dev->cmd_buf); + +lbl_set_intf_data: + usb_set_intfdata(intf, dev->prev_siblings); + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is unplugged from the system + */ +static void peak_usb_disconnect(struct usb_interface *intf) +{ + struct peak_usb_device *dev; + + /* unregister as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + char name[IFNAMSIZ]; + + dev->state &= ~PCAN_USB_STATE_CONNECTED; + strncpy(name, netdev->name, IFNAMSIZ); + + unregister_netdev(netdev); + free_candev(netdev); + + kfree(dev->cmd_buf); + dev->next_siblings = NULL; + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + + dev_info(&intf->dev, "%s removed\n", name); + } + + usb_set_intfdata(intf, NULL); +} + +/* + * probe function for new PEAK-System devices + */ +static int peak_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct peak_usb_adapter *peak_usb_adapter, **pp; + int i, err = -ENOMEM; + + usb_dev = interface_to_usbdev(intf); + + /* get corresponding PCAN-USB adapter */ + for (pp = peak_usb_adapters_list; *pp; pp++) + if ((*pp)->device_id == usb_dev->descriptor.idProduct) + break; + + peak_usb_adapter = *pp; + if (!peak_usb_adapter) { + /* should never come except device_id bad usage in this file */ + pr_err("%s: didn't find device id. 0x%x in devices list\n", + PCAN_USB_DRIVER_NAME, usb_dev->descriptor.idProduct); + return -ENODEV; + } + + /* got corresponding adapter: check if it handles current interface */ + if (peak_usb_adapter->intf_probe) { + err = peak_usb_adapter->intf_probe(intf); + if (err) + return err; + } + + for (i = 0; i < peak_usb_adapter->ctrl_count; i++) { + err = peak_usb_create_dev(peak_usb_adapter, intf, i); + if (err) { + /* deregister already created devices */ + peak_usb_disconnect(intf); + break; + } + } + + return err; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver peak_usb_driver = { + .name = PCAN_USB_DRIVER_NAME, + .disconnect = peak_usb_disconnect, + .probe = peak_usb_probe, + .id_table = peak_usb_table, +}; + +static int __init peak_usb_init(void) +{ + int err; + + /* register this driver with the USB subsystem */ + err = usb_register(&peak_usb_driver); + if (err) + pr_err("%s: usb_register failed (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + return err; +} + +static int peak_usb_do_device_exit(struct device *d, void *arg) +{ + struct usb_interface *intf = to_usb_interface(d); + struct peak_usb_device *dev; + + /* stop as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + + if (netif_device_present(netdev)) + if (dev->adapter->dev_exit) + dev->adapter->dev_exit(dev); + } + + return 0; +} + +static void __exit peak_usb_exit(void) +{ + int err; + + /* last chance do send any synchronous commands here */ + err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL, + NULL, peak_usb_do_device_exit); + if (err) + pr_err("%s: failed to stop all can devices (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + /* deregister this driver with the USB subsystem */ + usb_deregister(&peak_usb_driver); + + pr_info("%s: PCAN-USB interfaces driver unloaded\n", + PCAN_USB_DRIVER_NAME); +} + +module_init(peak_usb_init); +module_exit(peak_usb_exit); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h new file mode 100644 index 00000000000..073b47ff8ee --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -0,0 +1,145 @@ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef PCAN_USB_CORE_H +#define PCAN_USB_CORE_H + +/* PEAK-System vendor id. */ +#define PCAN_USB_VENDOR_ID 0x0c72 + +/* supported device ids. */ +#define PCAN_USB_PRODUCT_ID 0x000c +#define PCAN_USBPRO_PRODUCT_ID 0x000d + +#define PCAN_USB_DRIVER_NAME "peak_usb" + +/* number of urbs that are submitted for rx/tx per channel */ +#define PCAN_USB_MAX_RX_URBS 4 +#define PCAN_USB_MAX_TX_URBS 10 + +/* usb adapters maximum channels per usb interface */ +#define PCAN_USB_MAX_CHANNEL 2 + +/* maximum length of the usb commands sent to/received from the devices */ +#define PCAN_USB_MAX_CMD_LEN 32 + +struct peak_usb_device; + +/* PEAK-System USB adapter descriptor */ +struct peak_usb_adapter { + char *name; + u32 device_id; + struct can_clock clock; + const struct can_bittiming_const bittiming_const; + unsigned int ctrl_count; + + int (*intf_probe)(struct usb_interface *intf); + + int (*dev_init)(struct peak_usb_device *dev); + void (*dev_exit)(struct peak_usb_device *dev); + void (*dev_free)(struct peak_usb_device *dev); + int (*dev_open)(struct peak_usb_device *dev); + int (*dev_close)(struct peak_usb_device *dev); + int (*dev_set_bittiming)(struct peak_usb_device *dev, + struct can_bittiming *bt); + int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff); + int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id); + int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb); + int (*dev_encode_msg)(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size); + int (*dev_start)(struct peak_usb_device *dev); + int (*dev_stop)(struct peak_usb_device *dev); + int (*dev_restart_async)(struct peak_usb_device *dev, struct urb *urb, + u8 *buf); + u8 ep_msg_in; + u8 ep_msg_out[PCAN_USB_MAX_CHANNEL]; + u8 ts_used_bits; + u32 ts_period; + u8 us_per_ts_shift; + u32 us_per_ts_scale; + + int rx_buffer_size; + int tx_buffer_size; + int sizeof_dev_private; +}; + +extern struct peak_usb_adapter pcan_usb; +extern struct peak_usb_adapter pcan_usb_pro; + +struct peak_time_ref { + struct timeval tv_host_0, tv_host; + u32 ts_dev_1, ts_dev_2; + u64 ts_total; + u32 tick_count; + struct peak_usb_adapter *adapter; +}; + +struct peak_tx_urb_context { + struct peak_usb_device *dev; + u32 echo_index; + u8 dlc; + struct urb *urb; +}; + +#define PCAN_USB_STATE_CONNECTED 0x00000001 +#define PCAN_USB_STATE_STARTED 0x00000002 + +/* PEAK-System USB device */ +struct peak_usb_device { + struct can_priv can; + struct peak_usb_adapter *adapter; + unsigned int ctrl_idx; + 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, + struct peak_usb_adapter *adapter); +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, + struct timeval *tv); + +void peak_usb_async_complete(struct urb *urb); +void peak_usb_restart_complete(struct peak_usb_device *dev); +#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c new file mode 100644 index 00000000000..263dd921edc --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -0,0 +1,1064 @@ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro.c + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro adapter"); + +/* PCAN-USB Pro Endpoints */ +#define PCAN_USBPRO_EP_CMDOUT 1 +#define PCAN_USBPRO_EP_CMDIN (PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_0 2 +#define PCAN_USBPRO_EP_MSGIN (PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_1 3 +#define PCAN_USBPRO_EP_UNUSED (PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN) + +#define PCAN_USBPRO_CHANNEL_COUNT 2 + +/* PCAN-USB Pro adapter internal clock (MHz) */ +#define PCAN_USBPRO_CRYSTAL_HZ 56000000 + +/* PCAN-USB Pro command timeout (ms.) */ +#define PCAN_USBPRO_COMMAND_TIMEOUT 1000 + +/* PCAN-USB Pro rx/tx buffers size */ +#define PCAN_USBPRO_RX_BUFFER_SIZE 1024 +#define PCAN_USBPRO_TX_BUFFER_SIZE 64 + +#define PCAN_USBPRO_MSG_HEADER_LEN 4 + +/* some commands responses need to be re-submitted */ +#define PCAN_USBPRO_RSP_SUBMIT_MAX 2 + +#define PCAN_USBPRO_RTR 0x01 +#define PCAN_USBPRO_EXT 0x02 + +#define PCAN_USBPRO_CMD_BUFFER_SIZE 512 + +/* handle device specific info used by the netdevices */ +struct pcan_usb_pro_interface { + struct peak_usb_device *dev[PCAN_USBPRO_CHANNEL_COUNT]; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_pro_device { + struct peak_usb_device dev; + struct pcan_usb_pro_interface *usb_if; + u32 cached_ccbt; +}; + +/* internal structure used to handle messages sent to bulk urb */ +struct pcan_usb_pro_msg { + u8 *rec_ptr; + int rec_buffer_size; + int rec_buffer_len; + union { + u16 *rec_cnt_rd; + u32 *rec_cnt; + u8 *rec_buffer; + } u; +}; + +/* records sizes table indexed on message id. (8-bits value) */ +static u16 pcan_usb_pro_sizeof_rec[256] = { + [PCAN_USBPRO_SETBTR] = sizeof(struct pcan_usb_pro_btr), + [PCAN_USBPRO_SETBUSACT] = sizeof(struct pcan_usb_pro_busact), + [PCAN_USBPRO_SETSILENT] = sizeof(struct pcan_usb_pro_silent), + [PCAN_USBPRO_SETFILTR] = sizeof(struct pcan_usb_pro_filter), + [PCAN_USBPRO_SETTS] = sizeof(struct pcan_usb_pro_setts), + [PCAN_USBPRO_GETDEVID] = sizeof(struct pcan_usb_pro_devid), + [PCAN_USBPRO_SETLED] = sizeof(struct pcan_usb_pro_setled), + [PCAN_USBPRO_RXMSG8] = sizeof(struct pcan_usb_pro_rxmsg), + [PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4, + [PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXRTR] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXSTATUS] = sizeof(struct pcan_usb_pro_rxstatus), + [PCAN_USBPRO_RXTS] = sizeof(struct pcan_usb_pro_rxts), + [PCAN_USBPRO_TXMSG8] = sizeof(struct pcan_usb_pro_txmsg), + [PCAN_USBPRO_TXMSG4] = sizeof(struct pcan_usb_pro_txmsg) - 4, + [PCAN_USBPRO_TXMSG0] = sizeof(struct pcan_usb_pro_txmsg) - 8, +}; + +/* + * initialize PCAN-USB Pro message data structure + */ +static u8 *pcan_msg_init(struct pcan_usb_pro_msg *pm, void *buffer_addr, + int buffer_size) +{ + if (buffer_size < PCAN_USBPRO_MSG_HEADER_LEN) + return NULL; + + pm->u.rec_buffer = (u8 *)buffer_addr; + pm->rec_buffer_size = pm->rec_buffer_len = buffer_size; + pm->rec_ptr = pm->u.rec_buffer + PCAN_USBPRO_MSG_HEADER_LEN; + + return pm->rec_ptr; +} + +static u8 *pcan_msg_init_empty(struct pcan_usb_pro_msg *pm, + void *buffer_addr, int buffer_size) +{ + u8 *pr = pcan_msg_init(pm, buffer_addr, buffer_size); + + if (pr) { + pm->rec_buffer_len = PCAN_USBPRO_MSG_HEADER_LEN; + *pm->u.rec_cnt = 0; + } + return pr; +} + +/* + * add one record to a message being built + */ +static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...) +{ + int len, i; + u8 *pc; + va_list ap; + + va_start(ap, id); + + pc = pm->rec_ptr + 1; + + i = 0; + switch (id) { + case PCAN_USBPRO_TXMSG8: + i += 4; + case PCAN_USBPRO_TXMSG4: + i += 4; + case PCAN_USBPRO_TXMSG0: + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + memcpy(pc, va_arg(ap, int *), i); + pc += i; + break; + + case PCAN_USBPRO_SETBTR: + case PCAN_USBPRO_GETDEVID: + *pc++ = va_arg(ap, int); + pc += 2; + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETFILTR: + case PCAN_USBPRO_SETBUSACT: + case PCAN_USBPRO_SETSILENT: + *pc++ = va_arg(ap, int); + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + case PCAN_USBPRO_SETLED: + *pc++ = va_arg(ap, int); + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETTS: + pc++; + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + default: + pr_err("%s: %s(): unknown data type %02Xh (%d)\n", + PCAN_USB_DRIVER_NAME, __func__, id, id); + pc--; + break; + } + + len = pc - pm->rec_ptr; + if (len > 0) { + *pm->u.rec_cnt = cpu_to_le32(*pm->u.rec_cnt+1); + *pm->rec_ptr = id; + + pm->rec_ptr = pc; + pm->rec_buffer_len += len; + } + + va_end(ap); + + return len; +} + +/* + * send PCAN-USB Pro command synchronously + */ +static int pcan_usb_pro_send_cmd(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + int actual_length; + int err; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, "sending command failure: %d\n", err); + + return err; +} + +/* + * wait for PCAN-USB Pro command response + */ +static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + u8 req_data_type, req_channel; + int actual_length; + int i, err = 0; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + req_data_type = pum->u.rec_buffer[4]; + req_channel = pum->u.rec_buffer[5]; + + *pum->u.rec_cnt = 0; + for (i = 0; !err && i < PCAN_USBPRO_RSP_SUBMIT_MAX; i++) { + struct pcan_usb_pro_msg rsp; + union pcan_usb_pro_rec *pr; + u32 r, rec_cnt; + u16 rec_len; + u8 *pc; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDIN), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) { + netdev_err(dev->netdev, "waiting rsp error %d\n", err); + break; + } + + if (actual_length == 0) + continue; + + err = -EBADMSG; + if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) { + netdev_err(dev->netdev, + "got abnormal too small rsp (len=%d)\n", + actual_length); + break; + } + + pc = pcan_msg_init(&rsp, pum->u.rec_buffer, + actual_length); + + rec_cnt = le32_to_cpu(*rsp.u.rec_cnt); + + /* loop on records stored into message */ + for (r = 0; r < rec_cnt; r++) { + pr = (union pcan_usb_pro_rec *)pc; + rec_len = pcan_usb_pro_sizeof_rec[pr->data_type]; + if (!rec_len) { + netdev_err(dev->netdev, + "got unprocessed record in msg\n"); + 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; +} + +static int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size) +{ + int err; + u8 req_type; + unsigned int p; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + memset(req_addr, '\0', req_size); + + req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER; + + switch (req_id) { + case PCAN_USBPRO_REQ_FCT: + p = usb_sndctrlpipe(dev->udev, 0); + break; + + default: + p = usb_rcvctrlpipe(dev->udev, 0); + req_type |= USB_DIR_IN; + break; + } + + err = usb_control_msg(dev->udev, p, req_id, req_type, req_value, 0, + req_addr, req_size, 2 * USB_CTRL_GET_TIMEOUT); + if (err < 0) { + netdev_info(dev->netdev, + "unable to request usb[type=%d value=%d] err=%d\n", + req_id, req_value, err); + return err; + } + + return 0; +} + +static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETTS, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bitrate(struct peak_usb_device *dev, u32 ccbt) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt); + + /* cache the CCBT value to reuse it before next buson */ + pdev->cached_ccbt = ccbt; + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + /* if bus=on, be sure the bitrate being set before! */ + if (onoff) { + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + pcan_usb_pro_set_bitrate(dev, pdev->cached_ccbt); + } + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETSILENT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_filter(struct peak_usb_device *dev, u16 filter_mode) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETFILTR, dev->ctrl_idx, filter_mode); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_led(struct peak_usb_device *dev, u8 mode, + u32 timeout) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETLED, dev->ctrl_idx, mode, timeout); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev, + u32 *device_id) +{ + struct pcan_usb_pro_devid *pdn; + struct pcan_usb_pro_msg um; + int err; + u8 *pc; + + pc = pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_GETDEVID, dev->ctrl_idx); + + err = pcan_usb_pro_send_cmd(dev, &um); + if (err) + return err; + + err = pcan_usb_pro_wait_rsp(dev, &um); + if (err) + return err; + + pdn = (struct pcan_usb_pro_devid *)pc; + if (device_id) + *device_id = le32_to_cpu(pdn->serial_num); + + return err; +} + +static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u32 ccbt; + + ccbt = (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 0x00800000 : 0; + ccbt |= (bt->sjw - 1) << 24; + ccbt |= (bt->phase_seg2 - 1) << 20; + ccbt |= (bt->prop_seg + bt->phase_seg1 - 1) << 16; /* = tseg1 */ + ccbt |= bt->brp - 1; + + netdev_info(dev->netdev, "setting ccbt=0x%08x\n", ccbt); + + return pcan_usb_pro_set_bitrate(dev, ccbt); +} + +static void pcan_usb_pro_restart_complete(struct urb *urb) +{ + /* can delete usb resources */ + peak_usb_async_complete(urb); + + /* notify candev and netdev */ + peak_usb_restart_complete(urb->context); +} + +/* + * handle restart but in asynchronously way + */ +static int pcan_usb_pro_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, 1); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, PCAN_USB_MAX_CMD_LEN, + pcan_usb_pro_restart_complete, dev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static int pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded) +{ + u8 *buffer; + int err; + + buffer = kmalloc(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 timeval tv; + 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); + + peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(rx->ts32), &tv); + hwts = skb_hwtstamps(skb); + hwts->hwtstamp = timeval_to_ktime(tv); + + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + + return 0; +} + +static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxstatus *er) +{ + const u32 raw_status = le32_to_cpu(er->status); + const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + u8 err_mask = 0; + struct sk_buff *skb; + struct timeval tv; + 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; + can_bus_off(netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_warning++; + break; + + case CAN_STATE_ERROR_ACTIVE: + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + if (raw_status & PCAN_USBPRO_STATUS_OVERRUN) { + can_frame->can_id |= CAN_ERR_PROT; + can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + if (raw_status & PCAN_USBPRO_STATUS_QOVERRUN) { + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + new_state = CAN_STATE_ERROR_ACTIVE; + break; + } + + dev->can.state = new_state; + + peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(er->ts32), &tv); + hwts = skb_hwtstamps(skb); + hwts->hwtstamp = timeval_to_ktime(tv); + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + + return 0; +} + +static void pcan_usb_pro_handle_ts(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxts *ts) +{ + /* should wait until clock is stabilized */ + if (usb_if->cm_ignore_count > 0) + usb_if->cm_ignore_count--; + else + peak_usb_set_ts_now(&usb_if->time_ref, + le32_to_cpu(ts->ts64[1])); +} + +/* + * callback for bulk IN urb + */ +static int pcan_usb_pro_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_pro_interface *usb_if = pcan_usb_pro_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pcan_usb_pro_msg usb_msg; + u8 *rec_ptr, *msg_end; + u16 rec_cnt; + int err = 0; + + rec_ptr = pcan_msg_init(&usb_msg, urb->transfer_buffer, + urb->actual_length); + if (!rec_ptr) { + netdev_err(netdev, "bad msg hdr len %d\n", urb->actual_length); + return -EINVAL; + } + + /* loop reading all the records from the incoming message */ + msg_end = urb->transfer_buffer + urb->actual_length; + rec_cnt = le16_to_cpu(*usb_msg.u.rec_cnt_rd); + for (; rec_cnt > 0; rec_cnt--) { + union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr; + u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type]; + + if (!sizeof_rec) { + netdev_err(netdev, + "got unsupported rec in usb msg:\n"); + err = -ENOTSUPP; + break; + } + + /* check if the record goes out of current packet */ + if (rec_ptr + sizeof_rec > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf size\n"); + err = -EBADMSG; + break; + } + + switch (pr->data_type) { + case PCAN_USBPRO_RXMSG8: + case PCAN_USBPRO_RXMSG4: + case PCAN_USBPRO_RXMSG0: + case PCAN_USBPRO_RXRTR: + err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXSTATUS: + err = pcan_usb_pro_handle_error(usb_if, &pr->rx_status); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXTS: + pcan_usb_pro_handle_ts(usb_if, &pr->rx_ts); + break; + + default: + netdev_err(netdev, + "unhandled rec type 0x%02x (%d): ignored\n", + pr->data_type, pr->data_type); + break; + } + + rec_ptr += sizeof_rec; + } + +fail: + if (err) + 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 + */ +static int pcan_usb_pro_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + /* + * below is the list of valid ep addreses. Any other ep address + * is considered as not-CAN interface address => no dev created + */ + switch (ep->bEndpointAddress) { + case PCAN_USBPRO_EP_CMDOUT: + case PCAN_USBPRO_EP_CMDIN: + case PCAN_USBPRO_EP_MSGOUT_0: + case PCAN_USBPRO_EP_MSGOUT_1: + case PCAN_USBPRO_EP_MSGIN: + case PCAN_USBPRO_EP_UNUSED: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB Pro adapter + */ +struct peak_usb_adapter pcan_usb_pro = { + .name = "PCAN-USB Pro", + .device_id = PCAN_USBPRO_PRODUCT_ID, + .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT, + .clock = { + .freq = PCAN_USBPRO_CRYSTAL_HZ, + }, + .bittiming_const = { + .name = "pcan_usb_pro", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_pro_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USBPRO_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USBPRO_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, + .dev_init = pcan_usb_pro_init, + .dev_exit = pcan_usb_pro_exit, + .dev_free = pcan_usb_pro_free, + .dev_set_bus = pcan_usb_pro_set_bus, + .dev_set_bittiming = pcan_usb_pro_set_bittiming, + .dev_get_device_id = pcan_usb_pro_get_device_id, + .dev_decode_buf = pcan_usb_pro_decode_buf, + .dev_encode_msg = pcan_usb_pro_encode_msg, + .dev_start = pcan_usb_pro_start, + .dev_stop = pcan_usb_pro_stop, + .dev_restart_async = pcan_usb_pro_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h new file mode 100644 index 00000000000..32275af547e --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -0,0 +1,179 @@ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef PCAN_USB_PRO_H +#define PCAN_USB_PRO_H + +/* + * USB Vendor request data types + */ +#define PCAN_USBPRO_REQ_INFO 0 +#define PCAN_USBPRO_REQ_FCT 2 + +/* Vendor Request value for XXX_INFO */ +#define PCAN_USBPRO_INFO_BL 0 +#define PCAN_USBPRO_INFO_FW 1 + +/* Vendor Request value for XXX_FCT */ +#define PCAN_USBPRO_FCT_DRVLD 5 /* tell device driver is loaded */ +#define PCAN_USBPRO_FCT_DRVLD_REQ_LEN 16 + +/* PCAN_USBPRO_INFO_BL vendor request record type */ +struct __packed pcan_usb_pro_blinfo { + u32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + u32 serial_num_hi; + u32 serial_num_lo; + u32 hw_type; + u32 hw_rev; +}; + +/* PCAN_USBPRO_INFO_FW vendor request record type */ +struct __packed pcan_usb_pro_fwinfo { + u32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + u32 fw_type; +}; + +/* + * USB Command record types + */ +#define PCAN_USBPRO_SETBTR 0x02 +#define PCAN_USBPRO_SETBUSACT 0x04 +#define PCAN_USBPRO_SETSILENT 0x05 +#define PCAN_USBPRO_SETFILTR 0x0a +#define PCAN_USBPRO_SETTS 0x10 +#define PCAN_USBPRO_GETDEVID 0x12 +#define PCAN_USBPRO_SETLED 0x1C +#define PCAN_USBPRO_RXMSG8 0x80 +#define PCAN_USBPRO_RXMSG4 0x81 +#define PCAN_USBPRO_RXMSG0 0x82 +#define PCAN_USBPRO_RXRTR 0x83 +#define PCAN_USBPRO_RXSTATUS 0x84 +#define PCAN_USBPRO_RXTS 0x85 +#define PCAN_USBPRO_TXMSG8 0x41 +#define PCAN_USBPRO_TXMSG4 0x42 +#define PCAN_USBPRO_TXMSG0 0x43 + +/* record structures */ +struct __packed pcan_usb_pro_btr { + u8 data_type; + u8 channel; + u16 dummy; + u32 CCBT; +}; + +struct __packed pcan_usb_pro_busact { + u8 data_type; + u8 channel; + u16 onoff; +}; + +struct __packed pcan_usb_pro_silent { + u8 data_type; + u8 channel; + u16 onoff; +}; + +struct __packed pcan_usb_pro_filter { + u8 data_type; + u8 dummy; + u16 filter_mode; +}; + +struct __packed pcan_usb_pro_setts { + u8 data_type; + u8 dummy; + u16 mode; +}; + +struct __packed pcan_usb_pro_devid { + u8 data_type; + u8 channel; + u16 dummy; + u32 serial_num; +}; + +struct __packed pcan_usb_pro_setled { + u8 data_type; + u8 channel; + u16 mode; + u32 timeout; +}; + +struct __packed pcan_usb_pro_rxmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + u32 ts32; + u32 id; + + u8 data[8]; +}; + +#define PCAN_USBPRO_STATUS_ERROR 0x0001 +#define PCAN_USBPRO_STATUS_BUS 0x0002 +#define PCAN_USBPRO_STATUS_OVERRUN 0x0004 +#define PCAN_USBPRO_STATUS_QOVERRUN 0x0008 + +struct __packed pcan_usb_pro_rxstatus { + u8 data_type; + u8 channel; + u16 status; + u32 ts32; + u32 err_frm; +}; + +struct __packed pcan_usb_pro_rxts { + u8 data_type; + u8 dummy[3]; + u32 ts64[2]; +}; + +struct __packed pcan_usb_pro_txmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + u32 id; + u8 data[8]; +}; + +union pcan_usb_pro_rec { + u8 data_type; + struct pcan_usb_pro_btr btr; + struct pcan_usb_pro_busact bus_act; + struct pcan_usb_pro_silent silent_mode; + struct pcan_usb_pro_filter filter_mode; + struct pcan_usb_pro_setts ts; + struct pcan_usb_pro_devid dev_id; + struct pcan_usb_pro_setled set_led; + struct pcan_usb_pro_rxmsg rx_msg; + struct pcan_usb_pro_rxstatus rx_status; + struct pcan_usb_pro_rxts rx_ts; + struct pcan_usb_pro_txmsg tx_msg; +}; + +#endif diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c new file mode 100644 index 00000000000..ef674ecb82f --- /dev/null +++ b/drivers/net/can/usb/usb_8dev.c @@ -0,0 +1,1035 @@ +/* + * 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; + +}; + +/* 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; + 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[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + 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; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/* 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); + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + 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 -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) { + netdev_err(netdev, "No memory left for URBs\n"); + 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)) + goto failed; + 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; + +failed: + 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); + +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; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, 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; + } + + /* 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); + + 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 = kzalloc(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_cmd_msg_buffer; + } + + 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_cmd_msg_buffer: + kfree(priv->cmd_msg_buffer); + +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); + free_candev(priv->netdev); + + unlink_all_urbs(priv); + } + +} + +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 index a30b8f480f6..4e94057ef5c 100644 --- a/drivers/net/can/vcan.c +++ b/drivers/net/can/vcan.c @@ -37,8 +37,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> @@ -48,10 +46,11 @@ #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> -static __initdata const char banner[] = +static __initconst const char banner[] = KERN_INFO "vcan: Virtual CAN interface driver\n"; MODULE_DESCRIPTION("virtual CAN interface"); @@ -65,20 +64,19 @@ MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>"); * See Documentation/networking/can.txt for details. */ -static int echo; /* echo testing. Default: 0 (Off) */ +static bool echo; /* echo testing. Default: 0 (Off) */ module_param(echo, bool, S_IRUGO); MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)"); static void vcan_rx(struct sk_buff *skb, struct net_device *dev) { - struct can_frame *cf = (struct can_frame *)skb->data; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct net_device_stats *stats = &dev->stats; stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; + stats->rx_bytes += cfd->len; - skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->dev = dev; skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -88,7 +86,7 @@ static void vcan_rx(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) { - struct can_frame *cf = (struct can_frame *)skb->data; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct net_device_stats *stats = &dev->stats; int loop; @@ -96,7 +94,7 @@ static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; stats->tx_packets++; - stats->tx_bytes += cf->can_dlc; + stats->tx_bytes += cfd->len; /* set flag whether this packet has to be looped back */ loop = skb->pkt_type == PACKET_LOOPBACK; @@ -110,39 +108,51 @@ static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) * CAN core already did the echo for us */ stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; + stats->rx_bytes += cfd->len; } - kfree_skb(skb); + consume_skb(skb); return NETDEV_TX_OK; } /* perform standard echo handling for CAN network interfaces */ if (loop) { - struct sock *srcsk = skb->sk; - skb = skb_share_check(skb, GFP_ATOMIC); + skb = can_create_echo_skb(skb); if (!skb) return NETDEV_TX_OK; /* receive with packet counting */ - skb->sk = srcsk; vcan_rx(skb, dev); } else { /* no looped packets => no counting */ - kfree_skb(skb); + 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 = sizeof(struct can_frame); + dev->mtu = CAN_MTU; dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 0; diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c new file mode 100644 index 00000000000..5e8b5609c06 --- /dev/null +++ b/drivers/net/can/xilinx_can.c @@ -0,0 +1,1208 @@ +/* Xilinx CAN device driver + * + * Copyright (C) 2012 - 2014 Xilinx, Inc. + * Copyright (C) 2009 PetaLogix. All rights reserved. + * + * 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/platform_device.h> +#include <linux/skbuff.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 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 */ + XCAN_TXFIFO_ID_OFFSET = 0x30,/* TX FIFO ID */ + XCAN_TXFIFO_DLC_OFFSET = 0x34, /* TX FIFO DLC */ + XCAN_TXFIFO_DW1_OFFSET = 0x38, /* TX FIFO Data Word 1 */ + XCAN_TXFIFO_DW2_OFFSET = 0x3C, /* TX FIFO Data Word 2 */ + XCAN_RXFIFO_ID_OFFSET = 0x50, /* RX FIFO ID */ + XCAN_RXFIFO_DLC_OFFSET = 0x54, /* RX FIFO DLC */ + XCAN_RXFIFO_DW1_OFFSET = 0x58, /* RX FIFO Data Word 1 */ + XCAN_RXFIFO_DW2_OFFSET = 0x5C, /* RX FIFO Data Word 2 */ +}; + +/* 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_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_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_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\ + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \ + XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \ + XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK) + +/* 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_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) + +/** + * struct xcan_priv - This definition define CAN driver instance + * @can: CAN private data structure. + * @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 + */ +struct xcan_priv { + struct can_priv can; + 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 net_device *dev; + void __iomem *reg_base; + unsigned long irq_flags; + struct clk *bus_clk; + struct clk *can_clk; +}; + +/* 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, +}; + +/** + * 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); +} + +/** + * 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); + } + + 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) << XCAN_BTR_TS2_SHIFT; + + /* Setting Synchronous jump width in BTR Register */ + btr1 |= (bt->sjw - 1) << XCAN_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 err, reg_msr, reg_sr_mask; + unsigned long timeout; + + /* 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 */ + priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL); + + /* 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; + } + + 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_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. This + * function uses the next available free txbuff and populates their fields to + * start the transmission. + * + * Return: 0 on success and failure value on error + */ +static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 id, dlc, data[2] = {0, 0}; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + /* Check if the TX buffer is full */ + if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) & + XCAN_SR_TXFLL_MASK)) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG!, TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + /* 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)); + + can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max); + priv->tx_head++; + + /* Write the Frame to Xilinx CAN TX FIFO */ + priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id); + /* If the CAN frame is RTR frame this write triggers tranmission */ + priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc); + if (!(cf->can_id & CAN_RTR_FLAG)) { + priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data[0]); + /* If the CAN frame is Standard/Extended frame this + * write triggers tranmission + */ + priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data[1]); + stats->tx_bytes += cf->can_dlc; + } + + /* Check if the TX buffer is full */ + if ((priv->tx_head - priv->tx_tail) == priv->tx_max) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +/** + * xcan_rx - Is called from CAN isr to complete the received + * frame processing + * @ndev: Pointer to net_device structure + * + * 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) +{ + 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_RXFIFO_ID_OFFSET); + dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) >> + 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; + } + + if (!(id_xcan & XCAN_IDR_SRR_MASK)) { + data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET); + data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET); + + /* 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_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, status, txerr = 0, rxerr = 0; + + 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); + txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK; + rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) & + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT); + status = priv->read_reg(priv, XCAN_SR_OFFSET); + + 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 if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) { + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (rxerr > 127) ? + CAN_ERR_CRTL_RX_PASSIVE : + CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } else if (status & XCAN_SR_ERRWRN_MASK) { + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + if (skb) { + 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; + } + } + + /* 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++; + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + /* Check for error interrupt */ + if (isr & XCAN_IXR_ERROR_MASK) { + if (skb) { + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + } + + /* 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 | + CAN_ERR_PROT_LOC_CRC_DEL; + } + } + 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_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 isr, ier; + int work_done = 0; + + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) { + if (isr & XCAN_IXR_RXOK_MASK) { + priv->write_reg(priv, XCAN_ICR_OFFSET, + XCAN_IXR_RXOK_MASK); + work_done += xcan_rx(ndev); + } else { + priv->write_reg(priv, XCAN_ICR_OFFSET, + XCAN_IXR_RXNEMP_MASK); + break; + } + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK); + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + } + + if (work_done) + can_led_event(ndev, CAN_LED_EVENT_RX); + + if (work_done < quota) { + napi_complete(napi); + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK); + 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; + + while ((priv->tx_head - priv->tx_tail > 0) && + (isr & XCAN_IXR_TXOK_MASK)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK); + can_get_echo_skb(ndev, priv->tx_tail % + priv->tx_max); + priv->tx_tail++; + stats->tx_packets++; + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + } + can_led_event(ndev, CAN_LED_EVENT_TX); + netif_wake_queue(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; + + /* 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 */ + if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK | + XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK | + XCAN_IXR_ARBLST_MASK)); + xcan_err_interrupt(ndev, isr); + } + + /* Check for the type of receive interrupt and Processing it */ + if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) { + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_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); + u32 ier; + + /* Disable interrupts and leave the can in configuration mode */ + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier &= ~XCAN_INTR_ALL; + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + 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 = 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; + } + + ret = clk_prepare_enable(priv->can_clk); + if (ret) { + netdev_err(ndev, "unable to enable device clock\n"); + goto err_irq; + } + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) { + netdev_err(ndev, "unable to enable bus clock\n"); + goto err_can_clk; + } + + /* Set chip into reset mode */ + ret = set_reset_mode(ndev); + if (ret < 0) { + netdev_err(ndev, "mode resetting failed!\n"); + goto err_bus_clk; + } + + /* Common open */ + ret = open_candev(ndev); + if (ret) + goto err_bus_clk; + + 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_bus_clk: + clk_disable_unprepare(priv->bus_clk); +err_can_clk: + clk_disable_unprepare(priv->can_clk); +err_irq: + free_irq(ndev->irq, ndev); +err: + 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); + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + free_irq(ndev->irq, ndev); + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + 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 = clk_prepare_enable(priv->can_clk); + if (ret) + goto err; + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + goto err_clk; + + 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); + + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + + return 0; + +err_clk: + clk_disable_unprepare(priv->can_clk); +err: + return ret; +} + + +static const struct net_device_ops xcan_netdev_ops = { + .ndo_open = xcan_open, + .ndo_stop = xcan_close, + .ndo_start_xmit = xcan_start_xmit, +}; + +/** + * xcan_suspend - Suspend method for the driver + * @dev: Address of the platform_device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused xcan_suspend(struct device *dev) +{ + struct platform_device *pdev = dev_get_drvdata(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct xcan_priv *priv = netdev_priv(ndev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + } + + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->bus_clk); + clk_disable(priv->can_clk); + + return 0; +} + +/** + * xcan_resume - Resume from suspend + * @dev: Address of the platformdevice structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused xcan_resume(struct device *dev) +{ + struct platform_device *pdev = dev_get_drvdata(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = clk_enable(priv->bus_clk); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + return ret; + } + ret = clk_enable(priv->can_clk); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + clk_disable_unprepare(priv->bus_clk); + return ret; + } + + priv->write_reg(priv, XCAN_MSR_OFFSET, 0); + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume); + +/** + * 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; + void __iomem *addr; + int ret, rx_max, tx_max; + + /* 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; + } + + ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", &tx_max); + if (ret < 0) + goto err; + + ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", &rx_max); + if (ret < 0) + goto err; + + /* Create a CAN device instance */ + ndev = alloc_candev(sizeof(struct xcan_priv), tx_max); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + priv->dev = ndev; + priv->can.bittiming_const = &xcan_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; + + /* Get IRQ for the device */ + ndev->irq = platform_get_irq(pdev, 0); + 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; + } + /* Check for type of CAN device */ + if (of_device_is_compatible(pdev->dev.of_node, + "xlnx,zynq-can-1.0")) { + priv->bus_clk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(priv->bus_clk)) { + dev_err(&pdev->dev, "bus clock not found\n"); + ret = PTR_ERR(priv->bus_clk); + goto err_free; + } + } else { + priv->bus_clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(priv->bus_clk)) { + dev_err(&pdev->dev, "bus clock not found\n"); + ret = PTR_ERR(priv->bus_clk); + goto err_free; + } + } + + ret = clk_prepare_enable(priv->can_clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable device clock\n"); + goto err_free; + } + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable bus clock\n"); + goto err_unprepare_disable_dev; + } + + priv->write_reg = xcan_write_reg_le; + priv->read_reg = xcan_read_reg_le; + + 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_unprepare_disable_busclk; + } + + devm_can_led_init(ndev); + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n", + priv->reg_base, ndev->irq, priv->can.clock.freq, + priv->tx_max); + + return 0; + +err_unprepare_disable_busclk: + clk_disable_unprepare(priv->bus_clk); +err_unprepare_disable_dev: + clk_disable_unprepare(priv->can_clk); +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); + + if (set_reset_mode(ndev) < 0) + netdev_err(ndev, "mode resetting failed!\n"); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); + + return 0; +} + +/* Match table for OF platform binding */ +static struct of_device_id xcan_of_match[] = { + { .compatible = "xlnx,zynq-can-1.0", }, + { .compatible = "xlnx,axi-can-1.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, xcan_of_match); + +static struct platform_driver xcan_driver = { + .probe = xcan_probe, + .remove = xcan_remove, + .driver = { + .owner = THIS_MODULE, + .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"); |
