Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights: - Gustavo A. R. Silva keeps working on the implicit switch fallthru changes. - Support 802.11ax High-Efficiency wireless in cfg80211 et al, From Luca Coelho. - Re-enable ASPM in r8169, from Kai-Heng Feng. - Add virtual XFRM interfaces, which avoids all of the limitations of existing IPSEC tunnels. From Steffen Klassert. - Convert GRO over to use a hash table, so that when we have many flows active we don't traverse a long list during accumluation. - Many new self tests for routing, TC, tunnels, etc. Too many contributors to mention them all, but I'm really happy to keep seeing this stuff. - Hardware timestamping support for dpaa_eth/fsl-fman from Yangbo Lu. - Lots of cleanups and fixes in L2TP code from Guillaume Nault. - Add IPSEC offload support to netdevsim, from Shannon Nelson. - Add support for slotting with non-uniform distribution to netem packet scheduler, from Yousuk Seung. - Add UDP GSO support to mlx5e, from Boris Pismenny. - Support offloading of Team LAG in NFP, from John Hurley. - Allow to configure TX queue selection based upon RX queue, from Amritha Nambiar. - Support ethtool ring size configuration in aquantia, from Anton Mikaev. - Support DSCP and flowlabel per-transport in SCTP, from Xin Long. - Support list based batching and stack traversal of SKBs, this is very exciting work. From Edward Cree. - Busyloop optimizations in vhost_net, from Toshiaki Makita. - Introduce the ETF qdisc, which allows time based transmissions. IGB can offload this in hardware. From Vinicius Costa Gomes. - Add parameter support to devlink, from Moshe Shemesh. - Several multiplication and division optimizations for BPF JIT in nfp driver, from Jiong Wang. - Lots of prepatory work to make more of the packet scheduler layer lockless, when possible, from Vlad Buslov. - Add ACK filter and NAT awareness to sch_cake packet scheduler, from Toke Høiland-Jørgensen. - Support regions and region snapshots in devlink, from Alex Vesker. - Allow to attach XDP programs to both HW and SW at the same time on a given device, with initial support in nfp. From Jakub Kicinski. - Add TLS RX offload and support in mlx5, from Ilya Lesokhin. - Use PHYLIB in r8169 driver, from Heiner Kallweit. - All sorts of changes to support Spectrum 2 in mlxsw driver, from Ido Schimmel. - PTP support in mv88e6xxx DSA driver, from Andrew Lunn. - Make TCP_USER_TIMEOUT socket option more accurate, from Jon Maxwell. - Support for templates in packet scheduler classifier, from Jiri Pirko. - IPV6 support in RDS, from Ka-Cheong Poon. - Native tproxy support in nf_tables, from Máté Eckl. - Maintain IP fragment queue in an rbtree, but optimize properly for in-order frags. From Peter Oskolkov. - Improvde handling of ACKs on hole repairs, from Yuchung Cheng" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1996 commits) bpf: test: fix spelling mistake "REUSEEPORT" -> "REUSEPORT" hv/netvsc: Fix NULL dereference at single queue mode fallback net: filter: mark expected switch fall-through xen-netfront: fix warn message as irq device name has '/' cxgb4: Add new T5 PCI device ids 0x50af and 0x50b0 net: dsa: mv88e6xxx: missing unlock on error path rds: fix building with IPV6=m inet/connection_sock: prefer _THIS_IP_ to current_text_addr net: dsa: mv88e6xxx: bitwise vs logical bug net: sock_diag: Fix spectre v1 gadget in __sock_diag_cmd() ieee802154: hwsim: using right kind of iteration net: hns3: Add vlan filter setting by ethtool command -K net: hns3: Set tx ring' tc info when netdev is up net: hns3: Remove tx ring BD len register in hns3_enet net: hns3: Fix desc num set to default when setting channel net: hns3: Fix for phy link issue when using marvell phy driver net: hns3: Fix for information of phydev lost problem when down/up net: hns3: Fix for command format parsing error in hclge_is_all_function_id_zero net: hns3: Add support for serdes loopback selftest bnxt_en: take coredump_record structure off stack ...
This commit is contained in:
@@ -73,7 +73,7 @@ MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 "
|
||||
|
||||
static int i82527_compat;
|
||||
module_param(i82527_compat, int, 0444);
|
||||
MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode "
|
||||
MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 compatibility mode "
|
||||
"without using additional functions");
|
||||
|
||||
/*
|
||||
|
@@ -649,8 +649,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
|
||||
can_skb_prv(skb)->ifindex = dev->ifindex;
|
||||
can_skb_prv(skb)->skbcnt = 0;
|
||||
|
||||
*cf = skb_put(skb, sizeof(struct can_frame));
|
||||
memset(*cf, 0, sizeof(struct can_frame));
|
||||
*cf = skb_put_zero(skb, sizeof(struct can_frame));
|
||||
|
||||
return skb;
|
||||
}
|
||||
@@ -678,8 +677,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
|
||||
can_skb_prv(skb)->ifindex = dev->ifindex;
|
||||
can_skb_prv(skb)->skbcnt = 0;
|
||||
|
||||
*cfd = skb_put(skb, sizeof(struct canfd_frame));
|
||||
memset(*cfd, 0, sizeof(struct canfd_frame));
|
||||
*cfd = skb_put_zero(skb, sizeof(struct canfd_frame));
|
||||
|
||||
return skb;
|
||||
}
|
||||
@@ -703,7 +701,8 @@ EXPORT_SYMBOL_GPL(alloc_can_err_skb);
|
||||
/*
|
||||
* Allocate and setup space for the CAN network device
|
||||
*/
|
||||
struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
|
||||
struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
|
||||
unsigned int txqs, unsigned int rxqs)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct can_priv *priv;
|
||||
@@ -715,7 +714,8 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
|
||||
else
|
||||
size = sizeof_priv;
|
||||
|
||||
dev = alloc_netdev(size, "can%d", NET_NAME_UNKNOWN, can_setup);
|
||||
dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup,
|
||||
txqs, rxqs);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
@@ -734,7 +734,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alloc_candev);
|
||||
EXPORT_SYMBOL_GPL(alloc_candev_mqs);
|
||||
|
||||
/*
|
||||
* Free space of the CAN network device
|
||||
|
@@ -1,24 +1,13 @@
|
||||
/*
|
||||
* flexcan.c - FLEXCAN CAN controller driver
|
||||
*
|
||||
* Copyright (c) 2005-2006 Varma Electronics Oy
|
||||
* Copyright (c) 2009 Sascha Hauer, Pengutronix
|
||||
* Copyright (c) 2010-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
|
||||
* Copyright (c) 2014 David Jander, Protonic Holland
|
||||
*
|
||||
* Based on code originally by Andrey Volkov <avolkov@varma-el.com>
|
||||
*
|
||||
* LICENCE:
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// flexcan.c - FLEXCAN CAN controller driver
|
||||
//
|
||||
// Copyright (c) 2005-2006 Varma Electronics Oy
|
||||
// Copyright (c) 2009 Sascha Hauer, Pengutronix
|
||||
// Copyright (c) 2010-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
|
||||
// Copyright (c) 2014 David Jander, Protonic Holland
|
||||
//
|
||||
// Based on code originally by Andrey Volkov <avolkov@varma-el.com>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/can.h>
|
||||
@@ -523,7 +512,7 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
|
@@ -1684,7 +1684,7 @@ static int ican3_stop(struct net_device *ndev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
static netdev_tx_t ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
struct ican3_dev *mod = netdev_priv(ndev);
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
|
@@ -486,7 +486,7 @@ int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv,
|
||||
if (msg_size <= 0)
|
||||
break;
|
||||
|
||||
msg_ptr += msg_size;
|
||||
msg_ptr += ALIGN(msg_size, 4);
|
||||
}
|
||||
|
||||
if (msg_size < 0)
|
||||
|
@@ -174,9 +174,6 @@ struct pciefd_page {
|
||||
u32 size;
|
||||
};
|
||||
|
||||
#define CANFD_IRQ_SET 0x00000001
|
||||
#define CANFD_TX_PATH_SET 0x00000002
|
||||
|
||||
/* CAN-FD channel object */
|
||||
struct pciefd_board;
|
||||
struct pciefd_can {
|
||||
@@ -418,7 +415,7 @@ static int pciefd_pre_cmd(struct peak_canfd_priv *ucan)
|
||||
break;
|
||||
|
||||
/* going into operational mode: setup IRQ handler */
|
||||
err = request_irq(priv->board->pci_dev->irq,
|
||||
err = request_irq(priv->ucan.ndev->irq,
|
||||
pciefd_irq_handler,
|
||||
IRQF_SHARED,
|
||||
PCIEFD_DRV_NAME,
|
||||
@@ -491,15 +488,18 @@ static int pciefd_post_cmd(struct peak_canfd_priv *ucan)
|
||||
|
||||
/* controller now in reset mode: */
|
||||
|
||||
/* stop and reset DMA addresses in Tx/Rx engines */
|
||||
pciefd_can_clear_tx_dma(priv);
|
||||
pciefd_can_clear_rx_dma(priv);
|
||||
|
||||
/* disable IRQ for this CAN */
|
||||
pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT,
|
||||
PCIEFD_REG_CAN_RX_CTL_CLR);
|
||||
|
||||
free_irq(priv->board->pci_dev->irq, priv);
|
||||
/* stop and reset DMA addresses in Tx/Rx engines */
|
||||
pciefd_can_clear_tx_dma(priv);
|
||||
pciefd_can_clear_rx_dma(priv);
|
||||
|
||||
/* wait for above commands to complete (read cycle) */
|
||||
(void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1);
|
||||
|
||||
free_irq(priv->ucan.ndev->irq, priv);
|
||||
|
||||
ucan->can.state = CAN_STATE_STOPPED;
|
||||
|
||||
@@ -638,7 +638,7 @@ static int pciefd_can_probe(struct pciefd_board *pciefd)
|
||||
GFP_KERNEL);
|
||||
if (!priv->tx_dma_vaddr) {
|
||||
dev_err(&pciefd->pci_dev->dev,
|
||||
"Tx dmaim_alloc_coherent(%u) failure\n",
|
||||
"Tx dmam_alloc_coherent(%u) failure\n",
|
||||
PCIEFD_TX_DMA_SIZE);
|
||||
goto err_free_candev;
|
||||
}
|
||||
@@ -691,7 +691,7 @@ static int pciefd_can_probe(struct pciefd_board *pciefd)
|
||||
pciefd->can[pciefd->can_count] = priv;
|
||||
|
||||
dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n",
|
||||
ndev->name, priv->reg_base, pciefd->pci_dev->irq);
|
||||
ndev->name, priv->reg_base, ndev->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@@ -608,7 +608,7 @@ static int peak_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
writeb(0x00, cfg_base + PITA_GPIOICR);
|
||||
/* Toggle reset */
|
||||
writeb(0x05, cfg_base + PITA_MISC + 3);
|
||||
mdelay(5);
|
||||
usleep_range(5000, 6000);
|
||||
/* Leave parport mux mode */
|
||||
writeb(0x04, cfg_base + PITA_MISC + 3);
|
||||
|
||||
|
@@ -530,7 +530,7 @@ static int pcan_add_channels(struct pcan_pccard *card)
|
||||
pcan_write_reg(card, PCC_CCR, ccr);
|
||||
|
||||
/* wait 2ms before unresetting channels */
|
||||
mdelay(2);
|
||||
usleep_range(2000, 3000);
|
||||
|
||||
ccr &= ~PCC_CCR_RST_ALL;
|
||||
pcan_write_reg(card, PCC_CCR, ccr);
|
||||
|
@@ -409,7 +409,7 @@ static int sun4ican_set_mode(struct net_device *dev, enum can_mode mode)
|
||||
* xx xx xx xx ff ll 00 11 22 33 44 55 66 77
|
||||
* [ can_id ] [flags] [len] [can data (up to 8 bytes]
|
||||
*/
|
||||
static int sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
static netdev_tx_t sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct sun4ican_priv *priv = netdev_priv(dev);
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
|
@@ -1,6 +1,12 @@
|
||||
menu "CAN USB interfaces"
|
||||
depends on USB
|
||||
|
||||
config CAN_8DEV_USB
|
||||
tristate "8 devices USB2CAN interface"
|
||||
---help---
|
||||
This driver supports the USB2CAN interface
|
||||
from 8 devices (http://www.8devices.com).
|
||||
|
||||
config CAN_EMS_USB
|
||||
tristate "EMS CPC-USB/ARM7 CAN/USB interface"
|
||||
---help---
|
||||
@@ -26,7 +32,7 @@ config CAN_KVASER_USB
|
||||
tristate "Kvaser CAN/USB interface"
|
||||
---help---
|
||||
This driver adds support for Kvaser CAN/USB devices like Kvaser
|
||||
Leaf Light and Kvaser USBcan II.
|
||||
Leaf Light, Kvaser USBcan II and Kvaser Memorator Pro 5xHS.
|
||||
|
||||
The driver provides support for the following devices:
|
||||
- Kvaser Leaf Light
|
||||
@@ -55,12 +61,30 @@ config CAN_KVASER_USB
|
||||
- Kvaser Memorator HS/HS
|
||||
- Kvaser Memorator HS/LS
|
||||
- Scania VCI2 (if you have the Kvaser logo on top)
|
||||
- Kvaser BlackBird v2
|
||||
- Kvaser Leaf Pro HS v2
|
||||
- Kvaser Hybrid 2xCAN/LIN
|
||||
- Kvaser Hybrid Pro 2xCAN/LIN
|
||||
- Kvaser Memorator 2xHS v2
|
||||
- Kvaser Memorator Pro 2xHS v2
|
||||
- Kvaser Memorator Pro 5xHS
|
||||
- Kvaser USBcan Light 4xHS
|
||||
- Kvaser USBcan Pro 2xHS v2
|
||||
- Kvaser USBcan Pro 5xHS
|
||||
- ATI Memorator Pro 2xHS v2
|
||||
- ATI USBcan Pro 2xHS v2
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called kvaser_usb.
|
||||
|
||||
config CAN_MCBA_USB
|
||||
tristate "Microchip CAN BUS Analyzer interface"
|
||||
---help---
|
||||
This driver supports the CAN BUS Analyzer interface
|
||||
from Microchip (http://www.microchip.com/development-tools/).
|
||||
|
||||
config CAN_PEAK_USB
|
||||
tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"
|
||||
---help---
|
||||
@@ -77,16 +101,26 @@ config CAN_PEAK_USB
|
||||
|
||||
(see also 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).
|
||||
|
||||
config CAN_MCBA_USB
|
||||
tristate "Microchip CAN BUS Analyzer interface"
|
||||
---help---
|
||||
This driver supports the CAN BUS Analyzer interface
|
||||
from Microchip (http://www.microchip.com/development-tools/).
|
||||
|
||||
config CAN_UCAN
|
||||
tristate "Theobroma Systems UCAN interface"
|
||||
---help---
|
||||
This driver supports the Theobroma Systems
|
||||
UCAN USB-CAN interface.
|
||||
|
||||
The UCAN driver supports the microcontroller-based USB/CAN
|
||||
adapters from Theobroma Systems. There are two form-factors
|
||||
that run essentially the same firmware:
|
||||
|
||||
* Seal: standalone USB stick
|
||||
https://www.theobroma-systems.com/seal)
|
||||
* Mule: integrated on the PCB of various System-on-Modules
|
||||
from Theobroma Systems like the A31-µQ7 and the RK3399-Q7
|
||||
(https://www.theobroma-systems.com/rk3399-q7)
|
||||
|
||||
endmenu
|
||||
|
@@ -3,10 +3,11 @@
|
||||
# Makefile for the Linux Controller Area Network USB drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
|
||||
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
|
||||
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
|
||||
obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
|
||||
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
|
||||
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
|
||||
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
|
||||
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/
|
||||
obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
|
||||
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
|
||||
obj-$(CONFIG_CAN_UCAN) += ucan.o
|
||||
|
File diff suppressed because it is too large
Load Diff
2
drivers/net/can/usb/kvaser_usb/Makefile
Normal file
2
drivers/net/can/usb/kvaser_usb/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
|
||||
kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o
|
188
drivers/net/can/usb/kvaser_usb/kvaser_usb.h
Normal file
188
drivers/net/can/usb/kvaser_usb/kvaser_usb.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Parts of this driver are based on the following:
|
||||
* - Kvaser linux leaf driver (version 4.78)
|
||||
* - CAN driver for esd CAN-USB/2
|
||||
* - Kvaser linux usbcanII driver (version 5.3)
|
||||
* - Kvaser linux mhydra driver (version 5.24)
|
||||
*
|
||||
* Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved.
|
||||
* Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
|
||||
* Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
|
||||
* Copyright (C) 2015 Valeo S.A.
|
||||
*/
|
||||
|
||||
#ifndef KVASER_USB_H
|
||||
#define KVASER_USB_H
|
||||
|
||||
/* Kvaser USB CAN dongles are divided into three major platforms:
|
||||
* - Hydra: Running firmware labeled as 'mhydra'
|
||||
* - Leaf: Based on Renesas M32C or Freescale i.MX28, running firmware labeled
|
||||
* as 'filo'
|
||||
* - UsbcanII: Based on Renesas M16C, running firmware labeled as 'helios'
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
|
||||
#define KVASER_USB_MAX_RX_URBS 4
|
||||
#define KVASER_USB_MAX_TX_URBS 128
|
||||
#define KVASER_USB_TIMEOUT 1000 /* msecs */
|
||||
#define KVASER_USB_RX_BUFFER_SIZE 3072
|
||||
#define KVASER_USB_MAX_NET_DEVICES 5
|
||||
|
||||
/* USB devices features */
|
||||
#define KVASER_USB_HAS_SILENT_MODE BIT(0)
|
||||
#define KVASER_USB_HAS_TXRX_ERRORS BIT(1)
|
||||
|
||||
/* Device capabilities */
|
||||
#define KVASER_USB_CAP_BERR_CAP 0x01
|
||||
#define KVASER_USB_CAP_EXT_CAP 0x02
|
||||
#define KVASER_USB_HYDRA_CAP_EXT_CMD 0x04
|
||||
|
||||
struct kvaser_usb_dev_cfg;
|
||||
|
||||
enum kvaser_usb_leaf_family {
|
||||
KVASER_LEAF,
|
||||
KVASER_USBCAN,
|
||||
};
|
||||
|
||||
#define KVASER_USB_HYDRA_MAX_CMD_LEN 128
|
||||
struct kvaser_usb_dev_card_data_hydra {
|
||||
u8 channel_to_he[KVASER_USB_MAX_NET_DEVICES];
|
||||
u8 sysdbg_he;
|
||||
spinlock_t transid_lock; /* lock for transid */
|
||||
u16 transid;
|
||||
/* lock for usb_rx_leftover and usb_rx_leftover_len */
|
||||
spinlock_t usb_rx_leftover_lock;
|
||||
u8 usb_rx_leftover[KVASER_USB_HYDRA_MAX_CMD_LEN];
|
||||
u8 usb_rx_leftover_len;
|
||||
};
|
||||
struct kvaser_usb_dev_card_data {
|
||||
u32 ctrlmode_supported;
|
||||
u32 capabilities;
|
||||
union {
|
||||
struct {
|
||||
enum kvaser_usb_leaf_family family;
|
||||
} leaf;
|
||||
struct kvaser_usb_dev_card_data_hydra hydra;
|
||||
};
|
||||
};
|
||||
|
||||
/* Context for an outstanding, not yet ACKed, transmission */
|
||||
struct kvaser_usb_tx_urb_context {
|
||||
struct kvaser_usb_net_priv *priv;
|
||||
u32 echo_index;
|
||||
int dlc;
|
||||
};
|
||||
|
||||
struct kvaser_usb {
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *intf;
|
||||
struct kvaser_usb_net_priv *nets[KVASER_USB_MAX_NET_DEVICES];
|
||||
const struct kvaser_usb_dev_ops *ops;
|
||||
const struct kvaser_usb_dev_cfg *cfg;
|
||||
|
||||
struct usb_endpoint_descriptor *bulk_in, *bulk_out;
|
||||
struct usb_anchor rx_submitted;
|
||||
|
||||
/* @max_tx_urbs: Firmware-reported maximum number of outstanding,
|
||||
* not yet ACKed, transmissions on this device. This value is
|
||||
* also used as a sentinel for marking free tx contexts.
|
||||
*/
|
||||
u32 fw_version;
|
||||
unsigned int nchannels;
|
||||
unsigned int max_tx_urbs;
|
||||
struct kvaser_usb_dev_card_data card_data;
|
||||
|
||||
bool rxinitdone;
|
||||
void *rxbuf[KVASER_USB_MAX_RX_URBS];
|
||||
dma_addr_t rxbuf_dma[KVASER_USB_MAX_RX_URBS];
|
||||
};
|
||||
|
||||
struct kvaser_usb_net_priv {
|
||||
struct can_priv can;
|
||||
struct can_berr_counter bec;
|
||||
|
||||
struct kvaser_usb *dev;
|
||||
struct net_device *netdev;
|
||||
int channel;
|
||||
|
||||
struct completion start_comp, stop_comp, flush_comp;
|
||||
struct usb_anchor tx_submitted;
|
||||
|
||||
spinlock_t tx_contexts_lock; /* lock for active_tx_contexts */
|
||||
int active_tx_contexts;
|
||||
struct kvaser_usb_tx_urb_context tx_contexts[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kvaser_usb_dev_ops - Device specific functions
|
||||
* @dev_set_mode: used for can.do_set_mode
|
||||
* @dev_set_bittiming: used for can.do_set_bittiming
|
||||
* @dev_set_data_bittiming: used for can.do_set_data_bittiming
|
||||
* @dev_get_berr_counter: used for can.do_get_berr_counter
|
||||
*
|
||||
* @dev_setup_endpoints: setup USB in and out endpoints
|
||||
* @dev_init_card: initialize card
|
||||
* @dev_get_software_info: get software info
|
||||
* @dev_get_software_details: get software details
|
||||
* @dev_get_card_info: get card info
|
||||
* @dev_get_capabilities: discover device capabilities
|
||||
*
|
||||
* @dev_set_opt_mode: set ctrlmod
|
||||
* @dev_start_chip: start the CAN controller
|
||||
* @dev_stop_chip: stop the CAN controller
|
||||
* @dev_reset_chip: reset the CAN controller
|
||||
* @dev_flush_queue: flush outstanding CAN messages
|
||||
* @dev_read_bulk_callback: handle incoming commands
|
||||
* @dev_frame_to_cmd: translate struct can_frame into device command
|
||||
*/
|
||||
struct kvaser_usb_dev_ops {
|
||||
int (*dev_set_mode)(struct net_device *netdev, enum can_mode mode);
|
||||
int (*dev_set_bittiming)(struct net_device *netdev);
|
||||
int (*dev_set_data_bittiming)(struct net_device *netdev);
|
||||
int (*dev_get_berr_counter)(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec);
|
||||
int (*dev_setup_endpoints)(struct kvaser_usb *dev);
|
||||
int (*dev_init_card)(struct kvaser_usb *dev);
|
||||
int (*dev_get_software_info)(struct kvaser_usb *dev);
|
||||
int (*dev_get_software_details)(struct kvaser_usb *dev);
|
||||
int (*dev_get_card_info)(struct kvaser_usb *dev);
|
||||
int (*dev_get_capabilities)(struct kvaser_usb *dev);
|
||||
int (*dev_set_opt_mode)(const struct kvaser_usb_net_priv *priv);
|
||||
int (*dev_start_chip)(struct kvaser_usb_net_priv *priv);
|
||||
int (*dev_stop_chip)(struct kvaser_usb_net_priv *priv);
|
||||
int (*dev_reset_chip)(struct kvaser_usb *dev, int channel);
|
||||
int (*dev_flush_queue)(struct kvaser_usb_net_priv *priv);
|
||||
void (*dev_read_bulk_callback)(struct kvaser_usb *dev, void *buf,
|
||||
int len);
|
||||
void *(*dev_frame_to_cmd)(const struct kvaser_usb_net_priv *priv,
|
||||
const struct sk_buff *skb, int *frame_len,
|
||||
int *cmd_len, u16 transid);
|
||||
};
|
||||
|
||||
struct kvaser_usb_dev_cfg {
|
||||
const struct can_clock clock;
|
||||
const unsigned int timestamp_freq;
|
||||
const struct can_bittiming_const * const bittiming_const;
|
||||
const struct can_bittiming_const * const data_bittiming_const;
|
||||
};
|
||||
|
||||
extern const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops;
|
||||
extern const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops;
|
||||
|
||||
int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len,
|
||||
int *actual_len);
|
||||
|
||||
int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len);
|
||||
|
||||
int kvaser_usb_send_cmd_async(struct kvaser_usb_net_priv *priv, void *cmd,
|
||||
int len);
|
||||
|
||||
int kvaser_usb_can_rx_over_error(struct net_device *netdev);
|
||||
#endif /* KVASER_USB_H */
|
835
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
Normal file
835
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
Normal file
@@ -0,0 +1,835 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Parts of this driver are based on the following:
|
||||
* - Kvaser linux leaf driver (version 4.78)
|
||||
* - CAN driver for esd CAN-USB/2
|
||||
* - Kvaser linux usbcanII driver (version 5.3)
|
||||
* - Kvaser linux mhydra driver (version 5.24)
|
||||
*
|
||||
* Copyright (C) 2002-2018 KVASER AB, Sweden. All rights reserved.
|
||||
* Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
|
||||
* Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
|
||||
* Copyright (C) 2015 Valeo S.A.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/error.h>
|
||||
#include <linux/can/netlink.h>
|
||||
|
||||
#include "kvaser_usb.h"
|
||||
|
||||
/* Kvaser USB vendor id. */
|
||||
#define KVASER_VENDOR_ID 0x0bfd
|
||||
|
||||
/* Kvaser Leaf USB devices product ids */
|
||||
#define USB_LEAF_DEVEL_PRODUCT_ID 10
|
||||
#define USB_LEAF_LITE_PRODUCT_ID 11
|
||||
#define USB_LEAF_PRO_PRODUCT_ID 12
|
||||
#define USB_LEAF_SPRO_PRODUCT_ID 14
|
||||
#define USB_LEAF_PRO_LS_PRODUCT_ID 15
|
||||
#define USB_LEAF_PRO_SWC_PRODUCT_ID 16
|
||||
#define USB_LEAF_PRO_LIN_PRODUCT_ID 17
|
||||
#define USB_LEAF_SPRO_LS_PRODUCT_ID 18
|
||||
#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19
|
||||
#define USB_MEMO2_DEVEL_PRODUCT_ID 22
|
||||
#define USB_MEMO2_HSHS_PRODUCT_ID 23
|
||||
#define USB_UPRO_HSHS_PRODUCT_ID 24
|
||||
#define USB_LEAF_LITE_GI_PRODUCT_ID 25
|
||||
#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26
|
||||
#define USB_MEMO2_HSLS_PRODUCT_ID 27
|
||||
#define USB_LEAF_LITE_CH_PRODUCT_ID 28
|
||||
#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29
|
||||
#define USB_OEM_MERCURY_PRODUCT_ID 34
|
||||
#define USB_OEM_LEAF_PRODUCT_ID 35
|
||||
#define USB_CAN_R_PRODUCT_ID 39
|
||||
#define USB_LEAF_LITE_V2_PRODUCT_ID 288
|
||||
#define USB_MINI_PCIE_HS_PRODUCT_ID 289
|
||||
#define USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID 290
|
||||
#define USB_USBCAN_LIGHT_2HS_PRODUCT_ID 291
|
||||
#define USB_MINI_PCIE_2HS_PRODUCT_ID 292
|
||||
|
||||
/* Kvaser USBCan-II devices product ids */
|
||||
#define USB_USBCAN_REVB_PRODUCT_ID 2
|
||||
#define USB_VCI2_PRODUCT_ID 3
|
||||
#define USB_USBCAN2_PRODUCT_ID 4
|
||||
#define USB_MEMORATOR_PRODUCT_ID 5
|
||||
|
||||
/* Kvaser Minihydra USB devices product ids */
|
||||
#define USB_BLACKBIRD_V2_PRODUCT_ID 258
|
||||
#define USB_MEMO_PRO_5HS_PRODUCT_ID 260
|
||||
#define USB_USBCAN_PRO_5HS_PRODUCT_ID 261
|
||||
#define USB_USBCAN_LIGHT_4HS_PRODUCT_ID 262
|
||||
#define USB_LEAF_PRO_HS_V2_PRODUCT_ID 263
|
||||
#define USB_USBCAN_PRO_2HS_V2_PRODUCT_ID 264
|
||||
#define USB_MEMO_2HS_PRODUCT_ID 265
|
||||
#define USB_MEMO_PRO_2HS_V2_PRODUCT_ID 266
|
||||
#define USB_HYBRID_CANLIN_PRODUCT_ID 267
|
||||
#define USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID 268
|
||||
#define USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID 269
|
||||
#define USB_HYBRID_PRO_CANLIN_PRODUCT_ID 270
|
||||
|
||||
static inline bool kvaser_is_leaf(const struct usb_device_id *id)
|
||||
{
|
||||
return (id->idProduct >= USB_LEAF_DEVEL_PRODUCT_ID &&
|
||||
id->idProduct <= USB_CAN_R_PRODUCT_ID) ||
|
||||
(id->idProduct >= USB_LEAF_LITE_V2_PRODUCT_ID &&
|
||||
id->idProduct <= USB_MINI_PCIE_2HS_PRODUCT_ID);
|
||||
}
|
||||
|
||||
static inline bool kvaser_is_usbcan(const struct usb_device_id *id)
|
||||
{
|
||||
return id->idProduct >= USB_USBCAN_REVB_PRODUCT_ID &&
|
||||
id->idProduct <= USB_MEMORATOR_PRODUCT_ID;
|
||||
}
|
||||
|
||||
static inline bool kvaser_is_hydra(const struct usb_device_id *id)
|
||||
{
|
||||
return id->idProduct >= USB_BLACKBIRD_V2_PRODUCT_ID &&
|
||||
id->idProduct <= USB_HYBRID_PRO_CANLIN_PRODUCT_ID;
|
||||
}
|
||||
|
||||
static const struct usb_device_id kvaser_usb_table[] = {
|
||||
/* Leaf USB product IDs */
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS |
|
||||
KVASER_USB_HAS_SILENT_MODE },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_2HS_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_2HS_PRODUCT_ID) },
|
||||
|
||||
/* USBCANII USB product IDs */
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_REVB_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMORATOR_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_VCI2_PRODUCT_ID),
|
||||
.driver_info = KVASER_USB_HAS_TXRX_ERRORS },
|
||||
|
||||
/* Minihydra USB product IDs */
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_PRO_5HS_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_PRO_5HS_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_4HS_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_HS_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_PRO_2HS_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_2HS_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO_PRO_2HS_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_HYBRID_CANLIN_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_HYBRID_PRO_CANLIN_PRODUCT_ID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
|
||||
|
||||
int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len)
|
||||
{
|
||||
int actual_len; /* Not used */
|
||||
|
||||
return usb_bulk_msg(dev->udev,
|
||||
usb_sndbulkpipe(dev->udev,
|
||||
dev->bulk_out->bEndpointAddress),
|
||||
cmd, len, &actual_len, KVASER_USB_TIMEOUT);
|
||||
}
|
||||
|
||||
int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len,
|
||||
int *actual_len)
|
||||
{
|
||||
return usb_bulk_msg(dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
cmd, len, actual_len, KVASER_USB_TIMEOUT);
|
||||
}
|
||||
|
||||
static void kvaser_usb_send_cmd_callback(struct urb *urb)
|
||||
{
|
||||
struct net_device *netdev = urb->context;
|
||||
|
||||
kfree(urb->transfer_buffer);
|
||||
|
||||
if (urb->status)
|
||||
netdev_warn(netdev, "urb status received: %d\n", urb->status);
|
||||
}
|
||||
|
||||
int kvaser_usb_send_cmd_async(struct kvaser_usb_net_priv *priv, void *cmd,
|
||||
int len)
|
||||
{
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
struct net_device *netdev = priv->netdev;
|
||||
struct urb *urb;
|
||||
int err;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
usb_fill_bulk_urb(urb, dev->udev,
|
||||
usb_sndbulkpipe(dev->udev,
|
||||
dev->bulk_out->bEndpointAddress),
|
||||
cmd, len, kvaser_usb_send_cmd_callback, netdev);
|
||||
usb_anchor_urb(urb, &priv->tx_submitted);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err) {
|
||||
netdev_err(netdev, "Error transmitting URB\n");
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
usb_free_urb(urb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvaser_usb_can_rx_over_error(struct net_device *netdev)
|
||||
{
|
||||
struct net_device_stats *stats = &netdev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
|
||||
stats->rx_over_errors++;
|
||||
stats->rx_errors++;
|
||||
|
||||
skb = alloc_can_err_skb(netdev, &cf);
|
||||
if (!skb) {
|
||||
stats->rx_dropped++;
|
||||
netdev_warn(netdev, "No memory left for err_skb\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
netif_rx(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvaser_usb_read_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct kvaser_usb *dev = urb->context;
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -EPIPE:
|
||||
case -EPROTO:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
default:
|
||||
dev_info(&dev->intf->dev, "Rx URB aborted (%d)\n", urb->status);
|
||||
goto resubmit_urb;
|
||||
}
|
||||
|
||||
dev->ops->dev_read_bulk_callback(dev, urb->transfer_buffer,
|
||||
urb->actual_length);
|
||||
|
||||
resubmit_urb:
|
||||
usb_fill_bulk_urb(urb, dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
urb->transfer_buffer, KVASER_USB_RX_BUFFER_SIZE,
|
||||
kvaser_usb_read_bulk_callback, dev);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err == -ENODEV) {
|
||||
for (i = 0; i < dev->nchannels; i++) {
|
||||
if (!dev->nets[i])
|
||||
continue;
|
||||
|
||||
netif_device_detach(dev->nets[i]->netdev);
|
||||
}
|
||||
} else if (err) {
|
||||
dev_err(&dev->intf->dev,
|
||||
"Failed resubmitting read bulk urb: %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
if (dev->rxinitdone)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < KVASER_USB_MAX_RX_URBS; i++) {
|
||||
struct urb *urb = NULL;
|
||||
u8 *buf = NULL;
|
||||
dma_addr_t buf_dma;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
buf = usb_alloc_coherent(dev->udev, KVASER_USB_RX_BUFFER_SIZE,
|
||||
GFP_KERNEL, &buf_dma);
|
||||
if (!buf) {
|
||||
dev_warn(&dev->intf->dev,
|
||||
"No memory left for USB buffer\n");
|
||||
usb_free_urb(urb);
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(urb, dev->udev,
|
||||
usb_rcvbulkpipe
|
||||
(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
buf, KVASER_USB_RX_BUFFER_SIZE,
|
||||
kvaser_usb_read_bulk_callback, dev);
|
||||
urb->transfer_dma = buf_dma;
|
||||
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
usb_anchor_urb(urb, &dev->rx_submitted);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (err) {
|
||||
usb_unanchor_urb(urb);
|
||||
usb_free_coherent(dev->udev,
|
||||
KVASER_USB_RX_BUFFER_SIZE, buf,
|
||||
buf_dma);
|
||||
usb_free_urb(urb);
|
||||
break;
|
||||
}
|
||||
|
||||
dev->rxbuf[i] = buf;
|
||||
dev->rxbuf_dma[i] = buf_dma;
|
||||
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
dev_warn(&dev->intf->dev, "Cannot setup read URBs, error %d\n",
|
||||
err);
|
||||
return err;
|
||||
} else if (i < KVASER_USB_MAX_RX_URBS) {
|
||||
dev_warn(&dev->intf->dev, "RX performances may be slow\n");
|
||||
}
|
||||
|
||||
dev->rxinitdone = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_open(struct net_device *netdev)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
int err;
|
||||
|
||||
err = open_candev(netdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kvaser_usb_setup_rx_urbs(dev);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = dev->ops->dev_set_opt_mode(priv);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = dev->ops->dev_start_chip(priv);
|
||||
if (err) {
|
||||
netdev_warn(netdev, "Cannot start device, error %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
close_candev(netdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
int i, max_tx_urbs;
|
||||
|
||||
max_tx_urbs = priv->dev->max_tx_urbs;
|
||||
|
||||
priv->active_tx_contexts = 0;
|
||||
for (i = 0; i < max_tx_urbs; i++)
|
||||
priv->tx_contexts[i].echo_index = max_tx_urbs;
|
||||
}
|
||||
|
||||
/* This method might sleep. Do not call it in the atomic context
|
||||
* of URB completions.
|
||||
*/
|
||||
static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
usb_kill_anchored_urbs(&priv->tx_submitted);
|
||||
kvaser_usb_reset_tx_urb_contexts(priv);
|
||||
}
|
||||
|
||||
static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
usb_kill_anchored_urbs(&dev->rx_submitted);
|
||||
|
||||
for (i = 0; i < KVASER_USB_MAX_RX_URBS; i++)
|
||||
usb_free_coherent(dev->udev, KVASER_USB_RX_BUFFER_SIZE,
|
||||
dev->rxbuf[i], dev->rxbuf_dma[i]);
|
||||
|
||||
for (i = 0; i < dev->nchannels; i++) {
|
||||
struct kvaser_usb_net_priv *priv = dev->nets[i];
|
||||
|
||||
if (priv)
|
||||
kvaser_usb_unlink_tx_urbs(priv);
|
||||
}
|
||||
}
|
||||
|
||||
static int kvaser_usb_close(struct net_device *netdev)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
int err;
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
err = dev->ops->dev_flush_queue(priv);
|
||||
if (err)
|
||||
netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
|
||||
|
||||
if (dev->ops->dev_reset_chip) {
|
||||
err = dev->ops->dev_reset_chip(dev, priv->channel);
|
||||
if (err)
|
||||
netdev_warn(netdev, "Cannot reset card, error %d\n",
|
||||
err);
|
||||
}
|
||||
|
||||
err = dev->ops->dev_stop_chip(priv);
|
||||
if (err)
|
||||
netdev_warn(netdev, "Cannot stop device, error %d\n", err);
|
||||
|
||||
/* reset tx contexts */
|
||||
kvaser_usb_unlink_tx_urbs(priv);
|
||||
|
||||
priv->can.state = CAN_STATE_STOPPED;
|
||||
close_candev(priv->netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvaser_usb_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct kvaser_usb_tx_urb_context *context = urb->context;
|
||||
struct kvaser_usb_net_priv *priv;
|
||||
struct net_device *netdev;
|
||||
|
||||
if (WARN_ON(!context))
|
||||
return;
|
||||
|
||||
priv = context->priv;
|
||||
netdev = priv->netdev;
|
||||
|
||||
kfree(urb->transfer_buffer);
|
||||
|
||||
if (!netif_device_present(netdev))
|
||||
return;
|
||||
|
||||
if (urb->status)
|
||||
netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
|
||||
}
|
||||
|
||||
static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
struct net_device_stats *stats = &netdev->stats;
|
||||
struct kvaser_usb_tx_urb_context *context = NULL;
|
||||
struct urb *urb;
|
||||
void *buf;
|
||||
int cmd_len = 0;
|
||||
int err, ret = NETDEV_TX_OK;
|
||||
unsigned int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (can_dropped_invalid_skb(netdev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (!urb) {
|
||||
stats->tx_dropped++;
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&priv->tx_contexts_lock, flags);
|
||||
for (i = 0; i < dev->max_tx_urbs; i++) {
|
||||
if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) {
|
||||
context = &priv->tx_contexts[i];
|
||||
|
||||
context->echo_index = i;
|
||||
can_put_echo_skb(skb, netdev, context->echo_index);
|
||||
++priv->active_tx_contexts;
|
||||
if (priv->active_tx_contexts >= (int)dev->max_tx_urbs)
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->tx_contexts_lock, flags);
|
||||
|
||||
/* This should never happen; it implies a flow control bug */
|
||||
if (!context) {
|
||||
netdev_warn(netdev, "cannot find free context\n");
|
||||
|
||||
ret = NETDEV_TX_BUSY;
|
||||
goto freeurb;
|
||||
}
|
||||
|
||||
buf = dev->ops->dev_frame_to_cmd(priv, skb, &context->dlc, &cmd_len,
|
||||
context->echo_index);
|
||||
if (!buf) {
|
||||
stats->tx_dropped++;
|
||||
dev_kfree_skb(skb);
|
||||
spin_lock_irqsave(&priv->tx_contexts_lock, flags);
|
||||
|
||||
can_free_echo_skb(netdev, context->echo_index);
|
||||
context->echo_index = dev->max_tx_urbs;
|
||||
--priv->active_tx_contexts;
|
||||
netif_wake_queue(netdev);
|
||||
|
||||
spin_unlock_irqrestore(&priv->tx_contexts_lock, flags);
|
||||
goto freeurb;
|
||||
}
|
||||
|
||||
context->priv = priv;
|
||||
|
||||
usb_fill_bulk_urb(urb, dev->udev,
|
||||
usb_sndbulkpipe(dev->udev,
|
||||
dev->bulk_out->bEndpointAddress),
|
||||
buf, cmd_len, kvaser_usb_write_bulk_callback,
|
||||
context);
|
||||
usb_anchor_urb(urb, &priv->tx_submitted);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (unlikely(err)) {
|
||||
spin_lock_irqsave(&priv->tx_contexts_lock, flags);
|
||||
|
||||
can_free_echo_skb(netdev, context->echo_index);
|
||||
context->echo_index = dev->max_tx_urbs;
|
||||
--priv->active_tx_contexts;
|
||||
netif_wake_queue(netdev);
|
||||
|
||||
spin_unlock_irqrestore(&priv->tx_contexts_lock, flags);
|
||||
|
||||
usb_unanchor_urb(urb);
|
||||
kfree(buf);
|
||||
|
||||
stats->tx_dropped++;
|
||||
|
||||
if (err == -ENODEV)
|
||||
netif_device_detach(netdev);
|
||||
else
|
||||
netdev_warn(netdev, "Failed tx_urb %d\n", err);
|
||||
|
||||
goto freeurb;
|
||||
}
|
||||
|
||||
ret = NETDEV_TX_OK;
|
||||
|
||||
freeurb:
|
||||
usb_free_urb(urb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct net_device_ops kvaser_usb_netdev_ops = {
|
||||
.ndo_open = kvaser_usb_open,
|
||||
.ndo_stop = kvaser_usb_close,
|
||||
.ndo_start_xmit = kvaser_usb_start_xmit,
|
||||
.ndo_change_mtu = can_change_mtu,
|
||||
};
|
||||
|
||||
static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->nchannels; i++) {
|
||||
if (!dev->nets[i])
|
||||
continue;
|
||||
|
||||
unregister_candev(dev->nets[i]->netdev);
|
||||
}
|
||||
|
||||
kvaser_usb_unlink_all_urbs(dev);
|
||||
|
||||
for (i = 0; i < dev->nchannels; i++) {
|
||||
if (!dev->nets[i])
|
||||
continue;
|
||||
|
||||
free_candev(dev->nets[i]->netdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int kvaser_usb_init_one(struct kvaser_usb *dev,
|
||||
const struct usb_device_id *id, int channel)
|
||||
{
|
||||
struct net_device *netdev;
|
||||
struct kvaser_usb_net_priv *priv;
|
||||
int err;
|
||||
|
||||
if (dev->ops->dev_reset_chip) {
|
||||
err = dev->ops->dev_reset_chip(dev, channel);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
netdev = alloc_candev(sizeof(*priv) +
|
||||
dev->max_tx_urbs * sizeof(*priv->tx_contexts),
|
||||
dev->max_tx_urbs);
|
||||
if (!netdev) {
|
||||
dev_err(&dev->intf->dev, "Cannot alloc candev\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = netdev_priv(netdev);
|
||||
|
||||
init_usb_anchor(&priv->tx_submitted);
|
||||
init_completion(&priv->start_comp);
|
||||
init_completion(&priv->stop_comp);
|
||||
priv->can.ctrlmode_supported = 0;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->netdev = netdev;
|
||||
priv->channel = channel;
|
||||
|
||||
spin_lock_init(&priv->tx_contexts_lock);
|
||||
kvaser_usb_reset_tx_urb_contexts(priv);
|
||||
|
||||
priv->can.state = CAN_STATE_STOPPED;
|
||||
priv->can.clock.freq = dev->cfg->clock.freq;
|
||||
priv->can.bittiming_const = dev->cfg->bittiming_const;
|
||||
priv->can.do_set_bittiming = dev->ops->dev_set_bittiming;
|
||||
priv->can.do_set_mode = dev->ops->dev_set_mode;
|
||||
if ((id->driver_info & KVASER_USB_HAS_TXRX_ERRORS) ||
|
||||
(priv->dev->card_data.capabilities & KVASER_USB_CAP_BERR_CAP))
|
||||
priv->can.do_get_berr_counter = dev->ops->dev_get_berr_counter;
|
||||
if (id->driver_info & KVASER_USB_HAS_SILENT_MODE)
|
||||
priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
|
||||
|
||||
priv->can.ctrlmode_supported |= dev->card_data.ctrlmode_supported;
|
||||
|
||||
if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
|
||||
priv->can.data_bittiming_const = dev->cfg->data_bittiming_const;
|
||||
priv->can.do_set_data_bittiming =
|
||||
dev->ops->dev_set_data_bittiming;
|
||||
}
|
||||
|
||||
netdev->flags |= IFF_ECHO;
|
||||
|
||||
netdev->netdev_ops = &kvaser_usb_netdev_ops;
|
||||
|
||||
SET_NETDEV_DEV(netdev, &dev->intf->dev);
|
||||
netdev->dev_id = channel;
|
||||
|
||||
dev->nets[channel] = priv;
|
||||
|
||||
err = register_candev(netdev);
|
||||
if (err) {
|
||||
dev_err(&dev->intf->dev, "Failed to register CAN device\n");
|
||||
free_candev(netdev);
|
||||
dev->nets[channel] = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
netdev_dbg(netdev, "device registered\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct kvaser_usb *dev;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (kvaser_is_leaf(id)) {
|
||||
dev->card_data.leaf.family = KVASER_LEAF;
|
||||
dev->ops = &kvaser_usb_leaf_dev_ops;
|
||||
} else if (kvaser_is_usbcan(id)) {
|
||||
dev->card_data.leaf.family = KVASER_USBCAN;
|
||||
dev->ops = &kvaser_usb_leaf_dev_ops;
|
||||
} else if (kvaser_is_hydra(id)) {
|
||||
dev->ops = &kvaser_usb_hydra_dev_ops;
|
||||
} else {
|
||||
dev_err(&intf->dev,
|
||||
"Product ID (%d) is not a supported Kvaser USB device\n",
|
||||
id->idProduct);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev->intf = intf;
|
||||
|
||||
err = dev->ops->dev_setup_endpoints(dev);
|
||||
if (err) {
|
||||
dev_err(&intf->dev, "Cannot get usb endpoint(s)");
|
||||
return err;
|
||||
}
|
||||
|
||||
dev->udev = interface_to_usbdev(intf);
|
||||
|
||||
init_usb_anchor(&dev->rx_submitted);
|
||||
|
||||
usb_set_intfdata(intf, dev);
|
||||
|
||||
dev->card_data.ctrlmode_supported = 0;
|
||||
dev->card_data.capabilities = 0;
|
||||
err = dev->ops->dev_init_card(dev);
|
||||
if (err) {
|
||||
dev_err(&intf->dev,
|
||||
"Failed to initialize card, error %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = dev->ops->dev_get_software_info(dev);
|
||||
if (err) {
|
||||
dev_err(&intf->dev,
|
||||
"Cannot get software info, error %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (dev->ops->dev_get_software_details) {
|
||||
err = dev->ops->dev_get_software_details(dev);
|
||||
if (err) {
|
||||
dev_err(&intf->dev,
|
||||
"Cannot get software details, error %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(!dev->cfg))
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
|
||||
((dev->fw_version >> 24) & 0xff),
|
||||
((dev->fw_version >> 16) & 0xff),
|
||||
(dev->fw_version & 0xffff));
|
||||
|
||||
dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs);
|
||||
|
||||
err = dev->ops->dev_get_card_info(dev);
|
||||
if (err) {
|
||||
dev_err(&intf->dev, "Cannot get card info, error %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (dev->ops->dev_get_capabilities) {
|
||||
err = dev->ops->dev_get_capabilities(dev);
|
||||
if (err) {
|
||||
dev_err(&intf->dev,
|
||||
"Cannot get capabilities, error %d\n", err);
|
||||
kvaser_usb_remove_interfaces(dev);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->nchannels; i++) {
|
||||
err = kvaser_usb_init_one(dev, id, i);
|
||||
if (err) {
|
||||
kvaser_usb_remove_interfaces(dev);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvaser_usb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct kvaser_usb *dev = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
kvaser_usb_remove_interfaces(dev);
|
||||
}
|
||||
|
||||
static struct usb_driver kvaser_usb_driver = {
|
||||
.name = "kvaser_usb",
|
||||
.probe = kvaser_usb_probe,
|
||||
.disconnect = kvaser_usb_disconnect,
|
||||
.id_table = kvaser_usb_table,
|
||||
};
|
||||
|
||||
module_usb_driver(kvaser_usb_driver);
|
||||
|
||||
MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
|
||||
MODULE_AUTHOR("Kvaser AB <support@kvaser.com>");
|
||||
MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
|
||||
MODULE_LICENSE("GPL v2");
|
2028
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
Normal file
2028
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
Normal file
File diff suppressed because it is too large
Load Diff
1358
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Normal file
1358
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -423,6 +423,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
|
||||
new_state = CAN_STATE_ERROR_WARNING;
|
||||
break;
|
||||
}
|
||||
/* else: fall through */
|
||||
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
if (n & PCAN_USB_ERROR_BUS_HEAVY) {
|
||||
|
@@ -353,6 +353,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
|
||||
default:
|
||||
netdev_warn(netdev, "tx urb submitting failed err=%d\n",
|
||||
err);
|
||||
/* fall through */
|
||||
case -ENOENT:
|
||||
/* cable unplugged */
|
||||
stats->tx_dropped++;
|
||||
|
@@ -141,8 +141,10 @@ static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...)
|
||||
switch (id) {
|
||||
case PCAN_USBPRO_TXMSG8:
|
||||
i += 4;
|
||||
/* fall through */
|
||||
case PCAN_USBPRO_TXMSG4:
|
||||
i += 4;
|
||||
/* fall through */
|
||||
case PCAN_USBPRO_TXMSG0:
|
||||
*pc++ = va_arg(ap, int);
|
||||
*pc++ = va_arg(ap, int);
|
||||
|
1613
drivers/net/can/usb/ucan.c
Normal file
1613
drivers/net/can/usb/ucan.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* Copyright (C) 2012 - 2014 Xilinx, Inc.
|
||||
* Copyright (C) 2009 PetaLogix. All rights reserved.
|
||||
* Copyright (C) 2017 Sandvik Mining and Construction Oy
|
||||
* Copyright (C) 2017 - 2018 Sandvik Mining and Construction Oy
|
||||
*
|
||||
* Description:
|
||||
* This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
|
||||
@@ -51,16 +51,34 @@ enum xcan_reg {
|
||||
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 */
|
||||
|
||||
/* not on CAN FD cores */
|
||||
XCAN_TXFIFO_OFFSET = 0x30, /* TX FIFO base */
|
||||
XCAN_RXFIFO_OFFSET = 0x50, /* RX FIFO base */
|
||||
XCAN_AFR_OFFSET = 0x60, /* Acceptance Filter */
|
||||
|
||||
/* only on CAN FD cores */
|
||||
XCAN_TRR_OFFSET = 0x0090, /* TX Buffer Ready Request */
|
||||
XCAN_AFR_EXT_OFFSET = 0x00E0, /* Acceptance Filter */
|
||||
XCAN_FSR_OFFSET = 0x00E8, /* RX FIFO Status */
|
||||
XCAN_TXMSG_BASE_OFFSET = 0x0100, /* TX Message Space */
|
||||
XCAN_RXMSG_BASE_OFFSET = 0x1100, /* RX Message Space */
|
||||
};
|
||||
|
||||
#define XCAN_FRAME_ID_OFFSET(frame_base) ((frame_base) + 0x00)
|
||||
#define XCAN_FRAME_DLC_OFFSET(frame_base) ((frame_base) + 0x04)
|
||||
#define XCAN_FRAME_DW1_OFFSET(frame_base) ((frame_base) + 0x08)
|
||||
#define XCAN_FRAME_DW2_OFFSET(frame_base) ((frame_base) + 0x0C)
|
||||
|
||||
#define XCAN_CANFD_FRAME_SIZE 0x48
|
||||
#define XCAN_TXMSG_FRAME_OFFSET(n) (XCAN_TXMSG_BASE_OFFSET + \
|
||||
XCAN_CANFD_FRAME_SIZE * (n))
|
||||
#define XCAN_RXMSG_FRAME_OFFSET(n) (XCAN_RXMSG_BASE_OFFSET + \
|
||||
XCAN_CANFD_FRAME_SIZE * (n))
|
||||
|
||||
/* the single TX mailbox used by this driver on CAN FD HW */
|
||||
#define XCAN_TX_MAILBOX_IDX 0
|
||||
|
||||
/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
|
||||
#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */
|
||||
#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */
|
||||
@@ -70,6 +88,9 @@ enum xcan_reg {
|
||||
#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */
|
||||
#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */
|
||||
#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */
|
||||
#define XCAN_BTR_SJW_MASK_CANFD 0x000F0000 /* Synchronous jump width */
|
||||
#define XCAN_BTR_TS2_MASK_CANFD 0x00000F00 /* Time segment 2 */
|
||||
#define XCAN_BTR_TS1_MASK_CANFD 0x0000003F /* Time segment 1 */
|
||||
#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */
|
||||
#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */
|
||||
#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */
|
||||
@@ -83,6 +104,7 @@ enum xcan_reg {
|
||||
#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */
|
||||
#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */
|
||||
#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */
|
||||
#define XCAN_IXR_RXMNF_MASK 0x00020000 /* RX match not finished */
|
||||
#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */
|
||||
#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */
|
||||
#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */
|
||||
@@ -100,15 +122,15 @@ enum xcan_reg {
|
||||
#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_RXOFLW_MASK | XCAN_IXR_ARBLST_MASK)
|
||||
#define XCAN_FSR_FL_MASK 0x00003F00 /* RX Fill Level */
|
||||
#define XCAN_FSR_IRI_MASK 0x00000080 /* RX Increment Read Index */
|
||||
#define XCAN_FSR_RI_MASK 0x0000001F /* RX Read Index */
|
||||
|
||||
/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
|
||||
#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
|
||||
#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */
|
||||
#define XCAN_BTR_SJW_SHIFT_CANFD 16 /* Synchronous jump width */
|
||||
#define XCAN_BTR_TS2_SHIFT_CANFD 8 /* Time segment 2 */
|
||||
#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */
|
||||
#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */
|
||||
#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */
|
||||
@@ -118,6 +140,27 @@ enum xcan_reg {
|
||||
#define XCAN_FRAME_MAX_DATA_LEN 8
|
||||
#define XCAN_TIMEOUT (1 * HZ)
|
||||
|
||||
/* TX-FIFO-empty interrupt available */
|
||||
#define XCAN_FLAG_TXFEMP 0x0001
|
||||
/* RX Match Not Finished interrupt available */
|
||||
#define XCAN_FLAG_RXMNF 0x0002
|
||||
/* Extended acceptance filters with control at 0xE0 */
|
||||
#define XCAN_FLAG_EXT_FILTERS 0x0004
|
||||
/* TX mailboxes instead of TX FIFO */
|
||||
#define XCAN_FLAG_TX_MAILBOXES 0x0008
|
||||
/* RX FIFO with each buffer in separate registers at 0x1100
|
||||
* instead of the regular FIFO at 0x50
|
||||
*/
|
||||
#define XCAN_FLAG_RX_FIFO_MULTI 0x0010
|
||||
|
||||
struct xcan_devtype_data {
|
||||
unsigned int flags;
|
||||
const struct can_bittiming_const *bittiming_const;
|
||||
const char *bus_clk_name;
|
||||
unsigned int btr_ts2_shift;
|
||||
unsigned int btr_sjw_shift;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct xcan_priv - This definition define CAN driver instance
|
||||
* @can: CAN private data structure.
|
||||
@@ -133,6 +176,7 @@ enum xcan_reg {
|
||||
* @irq_flags: For request_irq()
|
||||
* @bus_clk: Pointer to struct clk
|
||||
* @can_clk: Pointer to struct clk
|
||||
* @devtype: Device type specific constants
|
||||
*/
|
||||
struct xcan_priv {
|
||||
struct can_priv can;
|
||||
@@ -149,6 +193,7 @@ struct xcan_priv {
|
||||
unsigned long irq_flags;
|
||||
struct clk *bus_clk;
|
||||
struct clk *can_clk;
|
||||
struct xcan_devtype_data devtype;
|
||||
};
|
||||
|
||||
/* CAN Bittiming constants as per Xilinx CAN specs */
|
||||
@@ -164,9 +209,16 @@ static const struct can_bittiming_const xcan_bittiming_const = {
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
#define XCAN_CAP_WATERMARK 0x0001
|
||||
struct xcan_devtype_data {
|
||||
unsigned int caps;
|
||||
static const struct can_bittiming_const xcan_bittiming_const_canfd = {
|
||||
.name = DRIVER_NAME,
|
||||
.tseg1_min = 1,
|
||||
.tseg1_max = 64,
|
||||
.tseg2_min = 1,
|
||||
.tseg2_max = 16,
|
||||
.sjw_max = 16,
|
||||
.brp_min = 1,
|
||||
.brp_max = 256,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -223,6 +275,23 @@ static u32 xcan_read_reg_be(const struct xcan_priv *priv, enum xcan_reg reg)
|
||||
return ioread32be(priv->reg_base + reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_rx_int_mask - Get the mask for the receive interrupt
|
||||
* @priv: Driver private data structure
|
||||
*
|
||||
* Return: The receive interrupt mask used by the driver on this HW
|
||||
*/
|
||||
static u32 xcan_rx_int_mask(const struct xcan_priv *priv)
|
||||
{
|
||||
/* RXNEMP is better suited for our use case as it cannot be cleared
|
||||
* while the FIFO is non-empty, but CAN FD HW does not have it
|
||||
*/
|
||||
if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI)
|
||||
return XCAN_IXR_RXOK_MASK;
|
||||
else
|
||||
return XCAN_IXR_RXNEMP_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_reset_mode - Resets the CAN device mode
|
||||
* @ndev: Pointer to net_device structure
|
||||
@@ -287,10 +356,10 @@ static int xcan_set_bittiming(struct net_device *ndev)
|
||||
btr1 = (bt->prop_seg + bt->phase_seg1 - 1);
|
||||
|
||||
/* Setting Time Segment 2 in BTR Register */
|
||||
btr1 |= (bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT;
|
||||
btr1 |= (bt->phase_seg2 - 1) << priv->devtype.btr_ts2_shift;
|
||||
|
||||
/* Setting Synchronous jump width in BTR Register */
|
||||
btr1 |= (bt->sjw - 1) << XCAN_BTR_SJW_SHIFT;
|
||||
btr1 |= (bt->sjw - 1) << priv->devtype.btr_sjw_shift;
|
||||
|
||||
priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
|
||||
priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
|
||||
@@ -318,6 +387,7 @@ static int xcan_chip_start(struct net_device *ndev)
|
||||
u32 reg_msr, reg_sr_mask;
|
||||
int err;
|
||||
unsigned long timeout;
|
||||
u32 ier;
|
||||
|
||||
/* Check if it is in reset mode */
|
||||
err = set_reset_mode(ndev);
|
||||
@@ -329,7 +399,15 @@ static int xcan_chip_start(struct net_device *ndev)
|
||||
return err;
|
||||
|
||||
/* Enable interrupts */
|
||||
priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
|
||||
ier = XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |
|
||||
XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK |
|
||||
XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
|
||||
XCAN_IXR_ARBLST_MASK | xcan_rx_int_mask(priv);
|
||||
|
||||
if (priv->devtype.flags & XCAN_FLAG_RXMNF)
|
||||
ier |= XCAN_IXR_RXMNF_MASK;
|
||||
|
||||
priv->write_reg(priv, XCAN_IER_OFFSET, ier);
|
||||
|
||||
/* Check whether it is loopback mode or normal mode */
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
|
||||
@@ -340,6 +418,12 @@ static int xcan_chip_start(struct net_device *ndev)
|
||||
reg_sr_mask = XCAN_SR_NORMAL_MASK;
|
||||
}
|
||||
|
||||
/* enable the first extended filter, if any, as cores with extended
|
||||
* filtering default to non-receipt if all filters are disabled
|
||||
*/
|
||||
if (priv->devtype.flags & XCAN_FLAG_EXT_FILTERS)
|
||||
priv->write_reg(priv, XCAN_AFR_EXT_OFFSET, 0x00000001);
|
||||
|
||||
priv->write_reg(priv, XCAN_MSR_OFFSET, reg_msr);
|
||||
priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
|
||||
|
||||
@@ -390,34 +474,15 @@ static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* xcan_write_frame - Write a frame to HW
|
||||
* @skb: sk_buff pointer that contains data to be Txed
|
||||
* @frame_offset: Register offset to write the frame to
|
||||
*/
|
||||
static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb,
|
||||
int frame_offset)
|
||||
{
|
||||
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};
|
||||
unsigned long flags;
|
||||
|
||||
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;
|
||||
}
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
|
||||
/* Watch carefully on the bit sequence */
|
||||
if (cf->can_id & CAN_EFF_FLAG) {
|
||||
@@ -453,24 +518,44 @@ static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
if (cf->can_dlc > 4)
|
||||
data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
|
||||
|
||||
priv->write_reg(priv, XCAN_FRAME_ID_OFFSET(frame_offset), id);
|
||||
/* If the CAN frame is RTR frame this write triggers transmission
|
||||
* (not on CAN FD)
|
||||
*/
|
||||
priv->write_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_offset), dlc);
|
||||
if (!(cf->can_id & CAN_RTR_FLAG)) {
|
||||
priv->write_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_offset),
|
||||
data[0]);
|
||||
/* If the CAN frame is Standard/Extended frame this
|
||||
* write triggers transmission (not on CAN FD)
|
||||
*/
|
||||
priv->write_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_offset),
|
||||
data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_start_xmit_fifo - Starts the transmission (FIFO mode)
|
||||
*
|
||||
* Return: 0 on success, -ENOSPC if FIFO is full.
|
||||
*/
|
||||
static int xcan_start_xmit_fifo(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
unsigned long flags;
|
||||
|
||||
/* Check if the TX buffer is full */
|
||||
if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) &
|
||||
XCAN_SR_TXFLL_MASK))
|
||||
return -ENOSPC;
|
||||
|
||||
can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max);
|
||||
|
||||
spin_lock_irqsave(&priv->tx_lock, flags);
|
||||
|
||||
priv->tx_head++;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
xcan_write_frame(priv, skb, XCAN_TXFIFO_OFFSET);
|
||||
|
||||
/* Clear TX-FIFO-empty interrupt for xcan_tx_interrupt() */
|
||||
if (priv->tx_max > 1)
|
||||
@@ -482,6 +567,70 @@ static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
|
||||
spin_unlock_irqrestore(&priv->tx_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_start_xmit_mailbox - Starts the transmission (mailbox mode)
|
||||
*
|
||||
* Return: 0 on success, -ENOSPC if there is no space
|
||||
*/
|
||||
static int xcan_start_xmit_mailbox(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
unsigned long flags;
|
||||
|
||||
if (unlikely(priv->read_reg(priv, XCAN_TRR_OFFSET) &
|
||||
BIT(XCAN_TX_MAILBOX_IDX)))
|
||||
return -ENOSPC;
|
||||
|
||||
can_put_echo_skb(skb, ndev, 0);
|
||||
|
||||
spin_lock_irqsave(&priv->tx_lock, flags);
|
||||
|
||||
priv->tx_head++;
|
||||
|
||||
xcan_write_frame(priv, skb,
|
||||
XCAN_TXMSG_FRAME_OFFSET(XCAN_TX_MAILBOX_IDX));
|
||||
|
||||
/* Mark buffer as ready for transmit */
|
||||
priv->write_reg(priv, XCAN_TRR_OFFSET, BIT(XCAN_TX_MAILBOX_IDX));
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
|
||||
spin_unlock_irqrestore(&priv->tx_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_start_xmit - Starts the transmission
|
||||
* @skb: sk_buff pointer that contains data to be Txed
|
||||
* @ndev: Pointer to net_device structure
|
||||
*
|
||||
* This function is invoked from upper layers to initiate transmission.
|
||||
*
|
||||
* Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY when the tx queue is full
|
||||
*/
|
||||
static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
int ret;
|
||||
|
||||
if (can_dropped_invalid_skb(ndev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
if (priv->devtype.flags & XCAN_FLAG_TX_MAILBOXES)
|
||||
ret = xcan_start_xmit_mailbox(skb, ndev);
|
||||
else
|
||||
ret = xcan_start_xmit_fifo(skb, ndev);
|
||||
|
||||
if (ret < 0) {
|
||||
netdev_err(ndev, "BUG!, TX full when queue awake!\n");
|
||||
netif_stop_queue(ndev);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
@@ -489,13 +638,14 @@ static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
* xcan_rx - Is called from CAN isr to complete the received
|
||||
* frame processing
|
||||
* @ndev: Pointer to net_device structure
|
||||
* @frame_base: Register offset to the frame to be read
|
||||
*
|
||||
* This function is invoked from the CAN isr(poll) to process the Rx frames. It
|
||||
* does minimal processing and invokes "netif_receive_skb" to complete further
|
||||
* processing.
|
||||
* Return: 1 on success and 0 on failure.
|
||||
*/
|
||||
static int xcan_rx(struct net_device *ndev)
|
||||
static int xcan_rx(struct net_device *ndev, int frame_base)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
struct net_device_stats *stats = &ndev->stats;
|
||||
@@ -510,9 +660,9 @@ static int xcan_rx(struct net_device *ndev)
|
||||
}
|
||||
|
||||
/* 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;
|
||||
id_xcan = priv->read_reg(priv, XCAN_FRAME_ID_OFFSET(frame_base));
|
||||
dlc = priv->read_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_base)) >>
|
||||
XCAN_DLCR_DLC_SHIFT;
|
||||
|
||||
/* Change Xilinx CAN data length format to socketCAN data format */
|
||||
cf->can_dlc = get_can_dlc(dlc);
|
||||
@@ -535,8 +685,8 @@ static int xcan_rx(struct net_device *ndev)
|
||||
}
|
||||
|
||||
/* DW1/DW2 must always be read to remove message from RXFIFO */
|
||||
data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
|
||||
data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
|
||||
data[0] = priv->read_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_base));
|
||||
data[1] = priv->read_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_base));
|
||||
|
||||
if (!(cf->can_id & CAN_RTR_FLAG)) {
|
||||
/* Change Xilinx CAN data format to socketCAN data format */
|
||||
@@ -594,39 +744,19 @@ static void xcan_set_error_state(struct net_device *ndev,
|
||||
u32 ecr = priv->read_reg(priv, XCAN_ECR_OFFSET);
|
||||
u32 txerr = ecr & XCAN_ECR_TEC_MASK;
|
||||
u32 rxerr = (ecr & XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT;
|
||||
enum can_state tx_state = txerr >= rxerr ? new_state : 0;
|
||||
enum can_state rx_state = txerr <= rxerr ? new_state : 0;
|
||||
|
||||
priv->can.state = new_state;
|
||||
/* non-ERROR states are handled elsewhere */
|
||||
if (WARN_ON(new_state > CAN_STATE_ERROR_PASSIVE))
|
||||
return;
|
||||
|
||||
can_change_state(ndev, cf, tx_state, rx_state);
|
||||
|
||||
if (cf) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[6] = txerr;
|
||||
cf->data[7] = rxerr;
|
||||
}
|
||||
|
||||
switch (new_state) {
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
priv->can.can_stats.error_passive++;
|
||||
if (cf)
|
||||
cf->data[1] = (rxerr > 127) ?
|
||||
CAN_ERR_CRTL_RX_PASSIVE :
|
||||
CAN_ERR_CRTL_TX_PASSIVE;
|
||||
break;
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
priv->can.can_stats.error_warning++;
|
||||
if (cf)
|
||||
cf->data[1] |= (txerr > rxerr) ?
|
||||
CAN_ERR_CRTL_TX_WARNING :
|
||||
CAN_ERR_CRTL_RX_WARNING;
|
||||
break;
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
if (cf)
|
||||
cf->data[1] |= CAN_ERR_CRTL_ACTIVE;
|
||||
break;
|
||||
default:
|
||||
/* non-ERROR states are handled elsewhere */
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,7 +833,8 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
|
||||
} else {
|
||||
enum can_state new_state = xcan_current_error_state(ndev);
|
||||
|
||||
xcan_set_error_state(ndev, new_state, skb ? cf : NULL);
|
||||
if (new_state != priv->can.state)
|
||||
xcan_set_error_state(ndev, new_state, skb ? cf : NULL);
|
||||
}
|
||||
|
||||
/* Check for Arbitration lost interrupt */
|
||||
@@ -725,6 +856,17 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for RX Match Not Finished interrupt */
|
||||
if (isr & XCAN_IXR_RXMNF_MASK) {
|
||||
stats->rx_dropped++;
|
||||
stats->rx_errors++;
|
||||
netdev_err(ndev, "RX match not finished, frame discarded\n");
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] |= CAN_ERR_CRTL_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for error interrupt */
|
||||
if (isr & XCAN_IXR_ERROR_MASK) {
|
||||
if (skb)
|
||||
@@ -808,6 +950,44 @@ static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_rx_fifo_get_next_frame - Get register offset of next RX frame
|
||||
*
|
||||
* Return: Register offset of the next frame in RX FIFO.
|
||||
*/
|
||||
static int xcan_rx_fifo_get_next_frame(struct xcan_priv *priv)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) {
|
||||
u32 fsr;
|
||||
|
||||
/* clear RXOK before the is-empty check so that any newly
|
||||
* received frame will reassert it without a race
|
||||
*/
|
||||
priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXOK_MASK);
|
||||
|
||||
fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
|
||||
|
||||
/* check if RX FIFO is empty */
|
||||
if (!(fsr & XCAN_FSR_FL_MASK))
|
||||
return -ENOENT;
|
||||
|
||||
offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
|
||||
|
||||
} else {
|
||||
/* check if RX FIFO is empty */
|
||||
if (!(priv->read_reg(priv, XCAN_ISR_OFFSET) &
|
||||
XCAN_IXR_RXNEMP_MASK))
|
||||
return -ENOENT;
|
||||
|
||||
/* frames are read from a static offset */
|
||||
offset = XCAN_RXFIFO_OFFSET;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_rx_poll - Poll routine for rx packets (NAPI)
|
||||
* @napi: napi structure pointer
|
||||
@@ -822,14 +1002,24 @@ 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;
|
||||
u32 ier;
|
||||
int work_done = 0;
|
||||
int frame_offset;
|
||||
|
||||
isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
|
||||
while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
|
||||
work_done += xcan_rx(ndev);
|
||||
priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
|
||||
isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
|
||||
while ((frame_offset = xcan_rx_fifo_get_next_frame(priv)) >= 0 &&
|
||||
(work_done < quota)) {
|
||||
work_done += xcan_rx(ndev, frame_offset);
|
||||
|
||||
if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI)
|
||||
/* increment read index */
|
||||
priv->write_reg(priv, XCAN_FSR_OFFSET,
|
||||
XCAN_FSR_IRI_MASK);
|
||||
else
|
||||
/* clear rx-not-empty (will actually clear only if
|
||||
* empty)
|
||||
*/
|
||||
priv->write_reg(priv, XCAN_ICR_OFFSET,
|
||||
XCAN_IXR_RXNEMP_MASK);
|
||||
}
|
||||
|
||||
if (work_done) {
|
||||
@@ -840,7 +1030,7 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota)
|
||||
if (work_done < quota) {
|
||||
napi_complete_done(napi, work_done);
|
||||
ier = priv->read_reg(priv, XCAN_IER_OFFSET);
|
||||
ier |= XCAN_IXR_RXNEMP_MASK;
|
||||
ier |= xcan_rx_int_mask(priv);
|
||||
priv->write_reg(priv, XCAN_IER_OFFSET, ier);
|
||||
}
|
||||
return work_done;
|
||||
@@ -908,8 +1098,8 @@ static void xcan_tx_interrupt(struct net_device *ndev, u32 isr)
|
||||
}
|
||||
|
||||
while (frames_sent--) {
|
||||
can_get_echo_skb(ndev, priv->tx_tail %
|
||||
priv->tx_max);
|
||||
stats->tx_bytes += can_get_echo_skb(ndev, priv->tx_tail %
|
||||
priv->tx_max);
|
||||
priv->tx_tail++;
|
||||
stats->tx_packets++;
|
||||
}
|
||||
@@ -939,6 +1129,7 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id)
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
u32 isr, ier;
|
||||
u32 isr_errors;
|
||||
u32 rx_int_mask = xcan_rx_int_mask(priv);
|
||||
|
||||
/* Get the interrupt status from Xilinx CAN */
|
||||
isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
|
||||
@@ -958,16 +1149,17 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id)
|
||||
|
||||
/* Check for the type of error interrupt and Processing it */
|
||||
isr_errors = isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
|
||||
XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK);
|
||||
XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK |
|
||||
XCAN_IXR_RXMNF_MASK);
|
||||
if (isr_errors) {
|
||||
priv->write_reg(priv, XCAN_ICR_OFFSET, isr_errors);
|
||||
xcan_err_interrupt(ndev, isr);
|
||||
}
|
||||
|
||||
/* Check for the type of receive interrupt and Processing it */
|
||||
if (isr & XCAN_IXR_RXNEMP_MASK) {
|
||||
if (isr & rx_int_mask) {
|
||||
ier = priv->read_reg(priv, XCAN_IER_OFFSET);
|
||||
ier &= ~XCAN_IXR_RXNEMP_MASK;
|
||||
ier &= ~rx_int_mask;
|
||||
priv->write_reg(priv, XCAN_IER_OFFSET, ier);
|
||||
napi_schedule(&priv->napi);
|
||||
}
|
||||
@@ -1214,13 +1406,35 @@ static const struct dev_pm_ops xcan_dev_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_zynq_data = {
|
||||
.caps = XCAN_CAP_WATERMARK,
|
||||
.bittiming_const = &xcan_bittiming_const,
|
||||
.btr_ts2_shift = XCAN_BTR_TS2_SHIFT,
|
||||
.btr_sjw_shift = XCAN_BTR_SJW_SHIFT,
|
||||
.bus_clk_name = "pclk",
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_axi_data = {
|
||||
.bittiming_const = &xcan_bittiming_const,
|
||||
.btr_ts2_shift = XCAN_BTR_TS2_SHIFT,
|
||||
.btr_sjw_shift = XCAN_BTR_SJW_SHIFT,
|
||||
.bus_clk_name = "s_axi_aclk",
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_canfd_data = {
|
||||
.flags = XCAN_FLAG_EXT_FILTERS |
|
||||
XCAN_FLAG_RXMNF |
|
||||
XCAN_FLAG_TX_MAILBOXES |
|
||||
XCAN_FLAG_RX_FIFO_MULTI,
|
||||
.bittiming_const = &xcan_bittiming_const,
|
||||
.btr_ts2_shift = XCAN_BTR_TS2_SHIFT_CANFD,
|
||||
.btr_sjw_shift = XCAN_BTR_SJW_SHIFT_CANFD,
|
||||
.bus_clk_name = "s_axi_aclk",
|
||||
};
|
||||
|
||||
/* Match table for OF platform binding */
|
||||
static const struct of_device_id xcan_of_match[] = {
|
||||
{ .compatible = "xlnx,zynq-can-1.0", .data = &xcan_zynq_data },
|
||||
{ .compatible = "xlnx,axi-can-1.00.a", },
|
||||
{ .compatible = "xlnx,axi-can-1.00.a", .data = &xcan_axi_data },
|
||||
{ .compatible = "xlnx,canfd-1.0", .data = &xcan_canfd_data },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xcan_of_match);
|
||||
@@ -1240,9 +1454,12 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
struct net_device *ndev;
|
||||
struct xcan_priv *priv;
|
||||
const struct of_device_id *of_id;
|
||||
int caps = 0;
|
||||
const struct xcan_devtype_data *devtype = &xcan_axi_data;
|
||||
void __iomem *addr;
|
||||
int ret, rx_max, tx_max, tx_fifo_depth;
|
||||
int ret;
|
||||
int rx_max, tx_max;
|
||||
int hw_tx_max, hw_rx_max;
|
||||
const char *hw_tx_max_property;
|
||||
|
||||
/* Get the virtual base address for the device */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@@ -1252,25 +1469,33 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth",
|
||||
&tx_fifo_depth);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", &rx_max);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
of_id = of_match_device(xcan_of_match, &pdev->dev);
|
||||
if (of_id) {
|
||||
const struct xcan_devtype_data *devtype_data = of_id->data;
|
||||
if (of_id && of_id->data)
|
||||
devtype = of_id->data;
|
||||
|
||||
if (devtype_data)
|
||||
caps = devtype_data->caps;
|
||||
hw_tx_max_property = devtype->flags & XCAN_FLAG_TX_MAILBOXES ?
|
||||
"tx-mailbox-count" : "tx-fifo-depth";
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, hw_tx_max_property,
|
||||
&hw_tx_max);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "missing %s property\n",
|
||||
hw_tx_max_property);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* There is no way to directly figure out how many frames have been
|
||||
* sent when the TXOK interrupt is processed. If watermark programming
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth",
|
||||
&hw_rx_max);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"missing rx-fifo-depth property (mailbox mode is not supported)\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* With TX FIFO:
|
||||
*
|
||||
* There is no way to directly figure out how many frames have been
|
||||
* sent when the TXOK interrupt is processed. If TXFEMP
|
||||
* is supported, we can have 2 frames in the FIFO and use TXFEMP
|
||||
* to determine if 1 or 2 frames have been sent.
|
||||
* Theoretically we should be able to use TXFWMEMP to determine up
|
||||
@@ -1279,12 +1504,20 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
* than 2 frames in FIFO) is set anyway with no TXOK (a frame was
|
||||
* sent), which is not a sensible state - possibly TXFWMEMP is not
|
||||
* completely synchronized with the rest of the bits?
|
||||
*
|
||||
* With TX mailboxes:
|
||||
*
|
||||
* HW sends frames in CAN ID priority order. To preserve FIFO ordering
|
||||
* we submit frames one at a time.
|
||||
*/
|
||||
if (caps & XCAN_CAP_WATERMARK)
|
||||
tx_max = min(tx_fifo_depth, 2);
|
||||
if (!(devtype->flags & XCAN_FLAG_TX_MAILBOXES) &&
|
||||
(devtype->flags & XCAN_FLAG_TXFEMP))
|
||||
tx_max = min(hw_tx_max, 2);
|
||||
else
|
||||
tx_max = 1;
|
||||
|
||||
rx_max = hw_rx_max;
|
||||
|
||||
/* Create a CAN device instance */
|
||||
ndev = alloc_candev(sizeof(struct xcan_priv), tx_max);
|
||||
if (!ndev)
|
||||
@@ -1292,13 +1525,14 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
|
||||
priv = netdev_priv(ndev);
|
||||
priv->dev = &pdev->dev;
|
||||
priv->can.bittiming_const = &xcan_bittiming_const;
|
||||
priv->can.bittiming_const = devtype->bittiming_const;
|
||||
priv->can.do_set_mode = xcan_do_set_mode;
|
||||
priv->can.do_get_berr_counter = xcan_get_berr_counter;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
|
||||
CAN_CTRLMODE_BERR_REPORTING;
|
||||
priv->reg_base = addr;
|
||||
priv->tx_max = tx_max;
|
||||
priv->devtype = *devtype;
|
||||
spin_lock_init(&priv->tx_lock);
|
||||
|
||||
/* Get IRQ for the device */
|
||||
@@ -1316,22 +1550,12 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
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;
|
||||
}
|
||||
|
||||
priv->bus_clk = devm_clk_get(&pdev->dev, devtype->bus_clk_name);
|
||||
if (IS_ERR(priv->bus_clk)) {
|
||||
dev_err(&pdev->dev, "bus clock not found\n");
|
||||
ret = PTR_ERR(priv->bus_clk);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
priv->write_reg = xcan_write_reg_le;
|
||||
@@ -1364,9 +1588,9 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth: actual %d, using %d\n",
|
||||
priv->reg_base, ndev->irq, priv->can.clock.freq,
|
||||
tx_fifo_depth, priv->tx_max);
|
||||
netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx buffers: actual %d, using %d\n",
|
||||
priv->reg_base, ndev->irq, priv->can.clock.freq,
|
||||
hw_tx_max, priv->tx_max);
|
||||
|
||||
return 0;
|
||||
|
||||
|
Reference in New Issue
Block a user