mirror of https://gitee.com/openkylin/linux.git
linux-can-next-for-4.19-20180727
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEENrCndlB/VnAEWuH5k9IU1zQoZfEFAlta2oUTHG1rbEBwZW5n dXRyb25peC5kZQAKCRCT0hTXNChl8SWrCADJyQjWSWZaHHpHJeD1kUStgDEG5lYJ e3rgrCEz4CVqGz5/wfsRCiuch14APjyn+xiHxjd+wCOES0wEg0viPIe40NlY28w7 SGvIogYRC27PoIXGQClasFG2Y/MEyaKChVGXkMfgkLs/zoHvd22zkxx/0l6lIAK5 CGCj8wTM/ZPh7Fn0tbn35E12j7yoHsTIv5YN463/XZJ7qVUDm7JW0siyXxhw4yP9 W18CcjInMds7YyUvs/PMMKnSgqOmGFdBpaaAkuqHcJgk0buI52XJRVihnea8y0V1 aQya6U8NWVUM1PEN6gefhOIye1w9HapnIyS79m5gmRPm8zXxwwOBRLy2 =YwFQ -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-4.19-20180727' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== pull-request: can-next 2018-01-16 this is a pull request for net-next/master consisting of 38 patches. Dan Murphy's patch fixes the path to a file in the comment of the CAN Error Message Frame Mask structure. A patch by Colin Ian King fixes a typo in the cc770 driver. The next patch is by me an sorts the Kconfigand Makefile entries of the CAN-USB driver subdir alphabetically. The patch by Jakob Unterwurzacher adds support for the UCAN USB-CAN adapter. YueHaibing's patch replaces a open coded skb_put()+memset() by skb_put_zero() in the CAN-dev infrastructure. Zhu Yi provides a patch to enable multi-queue CAN devices. Three patches by Luc Van Oostenryck fix the return value of several driver's xmit function, I contribute a patch for the a fourth driver. Fabio Estevam's patch switches the flexcan driver to SPDX identifier. Two patches by Jia-Ju Bai replace the mdelay() by a usleep_range() in the sja1000 drivers. The next 6 patches are by Anssi Hannula and refactor the xilinx CAN driver and add support for the xilinx CAN FD core. A patch by Gustavo A. R. Silva adds fallthrough annotation to the peak_usb driver. 5 patches by Stephane Grosjean for the peak CANFD driver do some cleanups and provide more improvements for further firmware releases. The remaining 13 patches are by Jimmy Assarsson and the first clean up the kvaser_usb driver, so that the later patches add support for the Kvaser USB hydra family. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
41627cdb02
|
@ -2,20 +2,26 @@ Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
|
|||
---------------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "xlnx,zynq-can-1.0" for Zynq CAN
|
||||
controllers and "xlnx,axi-can-1.00.a" for Axi CAN
|
||||
controllers.
|
||||
- reg : Physical base address and size of the Axi CAN/Zynq
|
||||
CANPS registers map.
|
||||
- compatible : Should be:
|
||||
- "xlnx,zynq-can-1.0" for Zynq CAN controllers
|
||||
- "xlnx,axi-can-1.00.a" for Axi CAN controllers
|
||||
- "xlnx,canfd-1.0" for CAN FD controllers
|
||||
- reg : Physical base address and size of the controller
|
||||
registers map.
|
||||
- interrupts : Property with a value describing the interrupt
|
||||
number.
|
||||
- interrupt-parent : Must be core interrupt controller
|
||||
- clock-names : List of input clock names - "can_clk", "pclk"
|
||||
(For CANPS), "can_clk" , "s_axi_aclk"(For AXI CAN)
|
||||
- clock-names : List of input clock names
|
||||
- "can_clk", "pclk" (For CANPS),
|
||||
- "can_clk", "s_axi_aclk" (For AXI CAN and CAN FD).
|
||||
(See clock bindings for details).
|
||||
- clocks : Clock phandles (see clock bindings for details).
|
||||
- tx-fifo-depth : Can Tx fifo depth.
|
||||
- rx-fifo-depth : Can Rx fifo depth.
|
||||
- tx-fifo-depth : Can Tx fifo depth (Zynq, Axi CAN).
|
||||
- rx-fifo-depth : Can Rx fifo depth (Zynq, Axi CAN, CAN FD in
|
||||
sequential Rx mode).
|
||||
- tx-mailbox-count : Can Tx mailbox buffer count (CAN FD).
|
||||
- rx-mailbox-count : Can Rx mailbox buffer count (CAN FD in mailbox Rx
|
||||
mode).
|
||||
|
||||
|
||||
Example:
|
||||
|
@ -42,3 +48,14 @@ For Axi CAN Dts file:
|
|||
tx-fifo-depth = <0x40>;
|
||||
rx-fifo-depth = <0x40>;
|
||||
};
|
||||
For CAN FD Dts file:
|
||||
canfd_0: canfd@40000000 {
|
||||
compatible = "xlnx,canfd-1.0";
|
||||
clocks = <&clkc 0>, <&clkc 1>;
|
||||
clock-names = "can_clk", "s_axi_aclk";
|
||||
reg = <0x40000000 0x2000>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 59 1>;
|
||||
tx-mailbox-count = <0x20>;
|
||||
rx-fifo-depth = <0x20>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
=================
|
||||
The UCAN Protocol
|
||||
=================
|
||||
|
||||
UCAN is the protocol used by the microcontroller-based USB-CAN
|
||||
adapter that is integrated on System-on-Modules from Theobroma Systems
|
||||
and that is also available as a standalone USB stick.
|
||||
|
||||
The UCAN protocol has been designed to be hardware-independent.
|
||||
It is modeled closely after how Linux represents CAN devices
|
||||
internally. All multi-byte integers are encoded as Little Endian.
|
||||
|
||||
All structures mentioned in this document are defined in
|
||||
``drivers/net/can/usb/ucan.c``.
|
||||
|
||||
USB Endpoints
|
||||
=============
|
||||
|
||||
UCAN devices use three USB endpoints:
|
||||
|
||||
CONTROL endpoint
|
||||
The driver sends device management commands on this endpoint
|
||||
|
||||
IN endpoint
|
||||
The device sends CAN data frames and CAN error frames
|
||||
|
||||
OUT endpoint
|
||||
The driver sends CAN data frames on the out endpoint
|
||||
|
||||
|
||||
CONTROL Messages
|
||||
================
|
||||
|
||||
UCAN devices are configured using vendor requests on the control pipe.
|
||||
|
||||
To support multiple CAN interfaces in a single USB device all
|
||||
configuration commands target the corresponding interface in the USB
|
||||
descriptor.
|
||||
|
||||
The driver uses ``ucan_ctrl_command_in/out`` and
|
||||
``ucan_device_request_in`` to deliver commands to the device.
|
||||
|
||||
Setup Packet
|
||||
------------
|
||||
|
||||
================= =====================================================
|
||||
``bmRequestType`` Direction | Vendor | (Interface or Device)
|
||||
``bRequest`` Command Number
|
||||
``wValue`` Subcommand Number (16 Bit) or 0 if not used
|
||||
``wIndex`` USB Interface Index (0 for device commands)
|
||||
``wLength`` * Host to Device - Number of bytes to transmit
|
||||
* Device to Host - Maximum Number of bytes to
|
||||
receive. If the device send less. Commom ZLP
|
||||
semantics are used.
|
||||
================= =====================================================
|
||||
|
||||
Error Handling
|
||||
--------------
|
||||
|
||||
The device indicates failed control commands by stalling the
|
||||
pipe.
|
||||
|
||||
Device Commands
|
||||
---------------
|
||||
|
||||
UCAN_DEVICE_GET_FW_STRING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Dev2Host; optional*
|
||||
|
||||
Request the device firmware string.
|
||||
|
||||
|
||||
Interface Commands
|
||||
------------------
|
||||
|
||||
UCAN_COMMAND_START
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Bring the CAN interface up.
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.cmd_start``
|
||||
|
||||
==== ============================
|
||||
mode or mask of ``UCAN_MODE_*``
|
||||
==== ============================
|
||||
|
||||
UCAN_COMMAND_STOP
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Stop the CAN interface
|
||||
|
||||
Payload Format
|
||||
*empty*
|
||||
|
||||
UCAN_COMMAND_RESET
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Reset the CAN controller (including error counters)
|
||||
|
||||
Payload Format
|
||||
*empty*
|
||||
|
||||
UCAN_COMMAND_GET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Get Information from the Device
|
||||
|
||||
Subcommands
|
||||
^^^^^^^^^^^
|
||||
|
||||
UCAN_COMMAND_GET_INFO
|
||||
Request the device information structure ``ucan_ctl_payload_t.device_info``.
|
||||
|
||||
See the ``device_info`` field for details, and
|
||||
``uapi/linux/can/netlink.h`` for an explanation of the
|
||||
``can_bittiming fields``.
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.device_info``
|
||||
|
||||
UCAN_COMMAND_GET_PROTOCOL_VERSION
|
||||
|
||||
Request the device protocol version
|
||||
``ucan_ctl_payload_t.protocol_version``. The current protocol version is 3.
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.protocol_version``
|
||||
|
||||
.. note:: Devices that do not implement this command use the old
|
||||
protocol version 1
|
||||
|
||||
UCAN_COMMAND_SET_BITTIMING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Setup bittiming by sending the the structure
|
||||
``ucan_ctl_payload_t.cmd_set_bittiming`` (see ``struct bittiming`` for
|
||||
details)
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.cmd_set_bittiming``.
|
||||
|
||||
UCAN_SLEEP/WAKE
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; optional*
|
||||
|
||||
Configure sleep and wake modes. Not yet supported by the driver.
|
||||
|
||||
UCAN_FILTER
|
||||
~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; optional*
|
||||
|
||||
Setup hardware CAN filters. Not yet supported by the driver.
|
||||
|
||||
Allowed interface commands
|
||||
--------------------------
|
||||
|
||||
================== =================== ==================
|
||||
Legal Device State Command New Device State
|
||||
================== =================== ==================
|
||||
stopped SET_BITTIMING stopped
|
||||
stopped START started
|
||||
started STOP or RESET stopped
|
||||
stopped STOP or RESET stopped
|
||||
started RESTART started
|
||||
any GET *no change*
|
||||
================== =================== ==================
|
||||
|
||||
IN Message Format
|
||||
=================
|
||||
|
||||
A data packet on the USB IN endpoint contains one or more
|
||||
``ucan_message_in`` values. If multiple messages are batched in a USB
|
||||
data packet, the ``len`` field can be used to jump to the next
|
||||
``ucan_message_in`` value (take care to sanity-check the ``len`` value
|
||||
against the actual data size).
|
||||
|
||||
.. _can_ucan_in_message_len:
|
||||
|
||||
``len`` field
|
||||
-------------
|
||||
|
||||
Each ``ucan_message_in`` must be aligned to a 4-byte boundary (relative
|
||||
to the start of the start of the data buffer). That means that there
|
||||
may be padding bytes between multiple ``ucan_message_in`` values:
|
||||
|
||||
.. code::
|
||||
|
||||
+----------------------------+ < 0
|
||||
| |
|
||||
| struct ucan_message_in |
|
||||
| |
|
||||
+----------------------------+ < len
|
||||
[padding]
|
||||
+----------------------------+ < round_up(len, 4)
|
||||
| |
|
||||
| struct ucan_message_in |
|
||||
| |
|
||||
+----------------------------+
|
||||
[...]
|
||||
|
||||
``type`` field
|
||||
--------------
|
||||
|
||||
The ``type`` field specifies the type of the message.
|
||||
|
||||
UCAN_IN_RX
|
||||
~~~~~~~~~~
|
||||
|
||||
``subtype``
|
||||
zero
|
||||
|
||||
Data received from the CAN bus (ID + payload).
|
||||
|
||||
UCAN_IN_TX_COMPLETE
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``subtype``
|
||||
zero
|
||||
|
||||
The CAN device has sent a message to the CAN bus. It answers with a
|
||||
list of of tuples <echo-ids, flags>.
|
||||
|
||||
The echo-id identifies the frame from (echos the id from a previous
|
||||
UCAN_OUT_TX message). The flag indicates the result of the
|
||||
transmission. Whereas a set Bit 0 indicates success. All other bits
|
||||
are reserved and set to zero.
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
When receiving CAN messages there is no flow control on the USB
|
||||
buffer. The driver has to handle inbound message quickly enough to
|
||||
avoid drops. I case the device buffer overflow the condition is
|
||||
reported by sending corresponding error frames (see
|
||||
:ref:`can_ucan_error_handling`)
|
||||
|
||||
|
||||
OUT Message Format
|
||||
==================
|
||||
|
||||
A data packet on the USB OUT endpoint contains one or more ``struct
|
||||
ucan_message_out`` values. If multiple messages are batched into one
|
||||
data packet, the device uses the ``len`` field to jump to the next
|
||||
ucan_message_out value. Each ucan_message_out must be aligned to 4
|
||||
bytes (relative to the start of the data buffer). The mechanism is
|
||||
same as described in :ref:`can_ucan_in_message_len`.
|
||||
|
||||
.. code::
|
||||
|
||||
+----------------------------+ < 0
|
||||
| |
|
||||
| struct ucan_message_out |
|
||||
| |
|
||||
+----------------------------+ < len
|
||||
[padding]
|
||||
+----------------------------+ < round_up(len, 4)
|
||||
| |
|
||||
| struct ucan_message_out |
|
||||
| |
|
||||
+----------------------------+
|
||||
[...]
|
||||
|
||||
``type`` field
|
||||
--------------
|
||||
|
||||
In protocol version 3 only ``UCAN_OUT_TX`` is defined, others are used
|
||||
only by legacy devices (protocol version 1).
|
||||
|
||||
UCAN_OUT_TX
|
||||
~~~~~~~~~~~
|
||||
``subtype``
|
||||
echo id to be replied within a CAN_IN_TX_COMPLETE message
|
||||
|
||||
Transmit a CAN frame. (parameters: ``id``, ``data``)
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
When the device outbound buffers are full it starts sending *NAKs* on
|
||||
the *OUT* pipe until more buffers are available. The driver stops the
|
||||
queue when a certain threshold of out packets are incomplete.
|
||||
|
||||
.. _can_ucan_error_handling:
|
||||
|
||||
CAN Error Handling
|
||||
==================
|
||||
|
||||
If error reporting is turned on the device encodes errors into CAN
|
||||
error frames (see ``uapi/linux/can/error.h``) and sends it using the
|
||||
IN endpoint. The driver updates its error statistics and forwards
|
||||
it.
|
||||
|
||||
Although UCAN devices can suppress error frames completely, in Linux
|
||||
the driver is always interested. Hence, the device is always started with
|
||||
the ``UCAN_MODE_BERR_REPORT`` set. Filtering those messages for the
|
||||
user space is done by the driver.
|
||||
|
||||
Bus OFF
|
||||
-------
|
||||
|
||||
- The device does not recover from bus of automatically.
|
||||
- Bus OFF is indicated by an error frame (see ``uapi/linux/can/error.h``)
|
||||
- Bus OFF recovery is started by ``UCAN_COMMAND_RESTART``
|
||||
- Once Bus OFF recover is completed the device sends an error frame
|
||||
indicating that it is on ERROR-ACTIVE state.
|
||||
- During Bus OFF no frames are sent by the device.
|
||||
- During Bus OFF transmission requests from the host are completed
|
||||
immediately with the success bit left unset.
|
||||
|
||||
Example Conversation
|
||||
====================
|
||||
|
||||
#) Device is connected to USB
|
||||
#) Host sends command ``UCAN_COMMAND_RESET``, subcmd 0
|
||||
#) Host sends command ``UCAN_COMMAND_GET``, subcmd ``UCAN_COMMAND_GET_INFO``
|
||||
#) Device sends ``UCAN_IN_DEVICE_INFO``
|
||||
#) Host sends command ``UCAN_OUT_SET_BITTIMING``
|
||||
#) Host sends command ``UCAN_COMMAND_START``, subcmd 0, mode ``UCAN_MODE_BERR_REPORT``
|
|
@ -10,6 +10,7 @@ Contents:
|
|||
af_xdp
|
||||
batman-adv
|
||||
can
|
||||
can_ucan_protocol
|
||||
dpaa2/index
|
||||
e100
|
||||
e1000
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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 */
|
|
@ -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");
|
File diff suppressed because it is too large
Load Diff
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);
|
||||
|
|
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;
|
||||
|
||||
|
|
|
@ -143,7 +143,12 @@ u8 can_dlc2len(u8 can_dlc);
|
|||
/* map the sanitized data length to an appropriate data length code */
|
||||
u8 can_len2dlc(u8 len);
|
||||
|
||||
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);
|
||||
#define alloc_candev(sizeof_priv, echo_skb_max) \
|
||||
alloc_candev_mqs(sizeof_priv, echo_skb_max, 1, 1)
|
||||
#define alloc_candev_mq(sizeof_priv, echo_skb_max, count) \
|
||||
alloc_candev_mqs(sizeof_priv, echo_skb_max, count, count)
|
||||
void free_candev(struct net_device *dev);
|
||||
|
||||
/* a candev safe wrapper around netdev_priv */
|
||||
|
|
|
@ -77,7 +77,7 @@ typedef __u32 canid_t;
|
|||
/*
|
||||
* Controller Area Network Error Message Frame Mask structure
|
||||
*
|
||||
* bit 0-28 : error class mask (see include/linux/can/error.h)
|
||||
* bit 0-28 : error class mask (see include/uapi/linux/can/error.h)
|
||||
* bit 29-31 : set to zero
|
||||
*/
|
||||
typedef __u32 can_err_mask_t;
|
||||
|
|
Loading…
Reference in New Issue