linux-can-next-for-6.2-20221212
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEBsvAIBsPu6mG7thcrX5LkNig010FAmOXCo0THG1rbEBwZW5n dXRyb25peC5kZQAKCRCtfkuQ2KDTXUD/B/4m3F/FIpfQjrLR8P/87hkHtu1vLnPV uo7SGVmq8aDiMRqWSLBWkiP6ceGememckrplG+qprx9QVZhagbtFN1/kE9jXxYSU s+hh4ARmLfpdZmcNCFzFi2S68G4fsQ3rTI8g/itbpQYCGyHt90yA5+PqT+QQZOvo J4l4uim4nVyBNEW136Vf13K0iFCmeJr4zr1eSqqHhZVSbK2cQSSN93HlhmZ+ccjT c1vZdNL1nd9mCQkNFHC+JPehfcgaI0r6hx8RYECvOCy8EbK1ZXdseWlM/z7IoxZm DiT2KhTao9xa9qcnRjI6oqTwO0omlqND2YDBj+SRwfFAs3HogeOaQjRT =w/j3 -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-6.2-20221212' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== linux-can-next-for-6.2-20221212 this is a pull request of 39 patches for net-next/master. The first 2 patches are by me fix a warning and coding style in the kvaser_usb driver. Vivek Yadav's patch sorts the includes of the m_can driver. Biju Das contributes 5 patches for the rcar_canfd driver improve the support for different IP core variants. Jean Delvare's patch for the ctucanfd drops the dependency on COMPILE_TEST. Vincent Mailhol's patch sorts the includes of the etas_es58x driver. Haibo Chen's contributes 2 patches that add i.MX93 support to the flexcan driver. Lad Prabhakar's patch updates the dt-bindings documentation of the rcar_canfd driver. Minghao Chi's patch converts the c_can platform driver to devm_platform_get_and_ioremap_resource(). In the next 7 patches Vincent Mailhol adds devlink support to the etas_es58x driver to report firmware, bootloader and hardware version. Xu Panda's patch converts a strncpy() -> strscpy() in the ucan driver. Ye Bin's patch removes a useless parameter from the AF_CAN protocol. The next 2 patches by Vincent Mailhol and remove unneeded or unused pointers to struct usb_interface in device's priv struct in the ucan and gs_usb driver. Vivek Yadav's patch cleans up the usage of the RAM initialization in the m_can driver. A patch by me add support for SO_MARK to the AF_CAN protocol. Geert Uytterhoeven's patch fixes the number of CAN channels in the rcan_canfd bindings documentation. In the last 11 patches Markus Schneider-Pargmann optimizes the register access in the t_can driver and cleans up the tcan glue driver. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b2b509fb5a
@ -17,6 +17,7 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,imx93-flexcan
|
||||
- fsl,imx8qm-flexcan
|
||||
- fsl,imx8mp-flexcan
|
||||
- fsl,imx6q-flexcan
|
||||
|
@ -9,9 +9,6 @@ title: Renesas R-Car CAN FD Controller
|
||||
maintainers:
|
||||
- Fabrizio Castro <fabrizio.castro.jz@renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: can-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -33,7 +30,7 @@ properties:
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a07g043-canfd # RZ/G2UL
|
||||
- renesas,r9a07g043-canfd # RZ/G2UL and RZ/Five
|
||||
- renesas,r9a07g044-canfd # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-canfd # RZ/V2L
|
||||
- const: renesas,rzg2l-canfd # RZ/G2L family
|
||||
@ -77,12 +74,13 @@ properties:
|
||||
description: Maximum frequency of the CANFD clock.
|
||||
|
||||
patternProperties:
|
||||
"^channel[01]$":
|
||||
"^channel[0-7]$":
|
||||
type: object
|
||||
description:
|
||||
The controller supports two channels and each is represented as a child
|
||||
node. Each child node supports the "status" property only, which
|
||||
is used to enable/disable the respective channel.
|
||||
The controller supports multiple channels and each is represented as a
|
||||
child node. Each channel can be enabled/disabled individually.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
@ -98,60 +96,73 @@ required:
|
||||
- channel0
|
||||
- channel1
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rzg2l-canfd
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
items:
|
||||
- description: CAN global error interrupt
|
||||
- description: CAN receive FIFO interrupt
|
||||
- description: CAN0 error interrupt
|
||||
- description: CAN0 transmit interrupt
|
||||
- description: CAN0 transmit/receive FIFO receive completion interrupt
|
||||
- description: CAN1 error interrupt
|
||||
- description: CAN1 transmit interrupt
|
||||
- description: CAN1 transmit/receive FIFO receive completion interrupt
|
||||
allOf:
|
||||
- $ref: can-controller.yaml#
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: g_err
|
||||
- const: g_recc
|
||||
- const: ch0_err
|
||||
- const: ch0_rec
|
||||
- const: ch0_trx
|
||||
- const: ch1_err
|
||||
- const: ch1_rec
|
||||
- const: ch1_trx
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rzg2l-canfd
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
items:
|
||||
- description: CAN global error interrupt
|
||||
- description: CAN receive FIFO interrupt
|
||||
- description: CAN0 error interrupt
|
||||
- description: CAN0 transmit interrupt
|
||||
- description: CAN0 transmit/receive FIFO receive completion interrupt
|
||||
- description: CAN1 error interrupt
|
||||
- description: CAN1 transmit interrupt
|
||||
- description: CAN1 transmit/receive FIFO receive completion interrupt
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: g_err
|
||||
- const: g_recc
|
||||
- const: ch0_err
|
||||
- const: ch0_rec
|
||||
- const: ch0_trx
|
||||
- const: ch1_err
|
||||
- const: ch1_rec
|
||||
- const: ch1_trx
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: rstp_n
|
||||
- const: rstc_n
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- reset-names
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
items:
|
||||
- description: Channel interrupt
|
||||
- description: Global interrupt
|
||||
reset-names:
|
||||
items:
|
||||
- const: rstp_n
|
||||
- const: rstc_n
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: ch_int
|
||||
- const: g_int
|
||||
required:
|
||||
- reset-names
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
items:
|
||||
- description: Channel interrupt
|
||||
- description: Global interrupt
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: ch_int
|
||||
- const: g_int
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r8a779a0-canfd
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel[2-7]$": false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
@ -198,6 +198,11 @@ fw.bundle_id
|
||||
|
||||
Unique identifier of the entire firmware bundle.
|
||||
|
||||
fw.bootloader
|
||||
-------------
|
||||
|
||||
Version of the bootloader.
|
||||
|
||||
Future work
|
||||
===========
|
||||
|
||||
|
36
Documentation/networking/devlink/etas_es58x.rst
Normal file
36
Documentation/networking/devlink/etas_es58x.rst
Normal file
@ -0,0 +1,36 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==========================
|
||||
etas_es58x devlink support
|
||||
==========================
|
||||
|
||||
This document describes the devlink features implemented by the
|
||||
``etas_es58x`` device driver.
|
||||
|
||||
Info versions
|
||||
=============
|
||||
|
||||
The ``etas_es58x`` driver reports the following versions
|
||||
|
||||
.. list-table:: devlink info versions implemented
|
||||
:widths: 5 5 90
|
||||
|
||||
* - Name
|
||||
- Type
|
||||
- Description
|
||||
* - ``fw``
|
||||
- running
|
||||
- Version of the firmware running on the device. Also available
|
||||
through ``ethtool -i`` as the first member of the
|
||||
``firmware-version``.
|
||||
* - ``fw.bootloader``
|
||||
- running
|
||||
- Version of the bootloader running on the device. Also available
|
||||
through ``ethtool -i`` as the second member of the
|
||||
``firmware-version``.
|
||||
* - ``board.rev``
|
||||
- fixed
|
||||
- The hardware revision of the device.
|
||||
* - ``serial_number``
|
||||
- fixed
|
||||
- The USB serial number. Also available through ``lsusb -v``.
|
@ -7682,6 +7682,7 @@ ETAS ES58X CAN/USB DRIVER
|
||||
M: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
L: linux-can@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/networking/devlink/etas_es58x.rst
|
||||
F: drivers/net/can/usb/etas_es58x/
|
||||
|
||||
ETHERNET BRIDGE
|
||||
|
@ -290,8 +290,7 @@ static int c_can_plat_probe(struct platform_device *pdev)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
addr = devm_ioremap_resource(&pdev->dev, mem);
|
||||
addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
|
||||
if (IS_ERR(addr)) {
|
||||
ret = PTR_ERR(addr);
|
||||
goto exit;
|
||||
|
@ -23,7 +23,7 @@ config CAN_CTUCANFD_PCI
|
||||
|
||||
config CAN_CTUCANFD_PLATFORM
|
||||
tristate "CTU CAN-FD IP core platform (FPGA, SoC) driver"
|
||||
depends on HAS_IOMEM && (OF || COMPILE_TEST)
|
||||
depends on HAS_IOMEM && OF
|
||||
select CAN_CTUCANFD
|
||||
help
|
||||
The core has been tested together with OpenCores SJA1000
|
||||
|
@ -345,6 +345,15 @@ static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
|
||||
FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR,
|
||||
};
|
||||
|
||||
static struct flexcan_devtype_data fsl_imx93_devtype_data = {
|
||||
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
|
||||
FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX |
|
||||
FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_AUTO_STOP_MODE |
|
||||
FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SUPPORT_ECC |
|
||||
FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX |
|
||||
FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR,
|
||||
};
|
||||
|
||||
static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
|
||||
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
|
||||
FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX |
|
||||
@ -532,9 +541,14 @@ static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
||||
ret = flexcan_stop_mode_enable_scfw(priv, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
} else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) {
|
||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||||
} else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) {
|
||||
/* For the auto stop mode, software do nothing, hardware will cover
|
||||
* all the operation automatically after system go into low power mode.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
return flexcan_low_power_enter_ack(priv);
|
||||
@ -551,7 +565,7 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
|
||||
ret = flexcan_stop_mode_enable_scfw(priv, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
} else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) {
|
||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||
1 << priv->stm.req_bit, 0);
|
||||
}
|
||||
@ -560,6 +574,12 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
|
||||
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
|
||||
priv->write(reg_mcr, ®s->mcr);
|
||||
|
||||
/* For the auto stop mode, hardware will exist stop mode
|
||||
* automatically after system go out of low power mode.
|
||||
*/
|
||||
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE)
|
||||
return 0;
|
||||
|
||||
return flexcan_low_power_exit_ack(priv);
|
||||
}
|
||||
|
||||
@ -1974,6 +1994,8 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
ret = flexcan_setup_stop_mode_scfw(pdev);
|
||||
else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR)
|
||||
ret = flexcan_setup_stop_mode_gpr(pdev);
|
||||
else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE)
|
||||
ret = 0;
|
||||
else
|
||||
/* return 0 directly if doesn't support stop mode feature */
|
||||
return 0;
|
||||
@ -1992,6 +2014,7 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
static const struct of_device_id flexcan_of_match[] = {
|
||||
{ .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
|
||||
{ .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, },
|
||||
{ .compatible = "fsl,imx93-flexcan", .data = &fsl_imx93_devtype_data, },
|
||||
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
|
||||
{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
|
||||
{ .compatible = "fsl,imx53-flexcan", .data = &fsl_imx25_devtype_data, },
|
||||
@ -2299,8 +2322,16 @@ static int __maybe_unused flexcan_noirq_suspend(struct device *device)
|
||||
if (netif_running(dev)) {
|
||||
int err;
|
||||
|
||||
if (device_may_wakeup(device))
|
||||
if (device_may_wakeup(device)) {
|
||||
flexcan_enable_wakeup_irq(priv, true);
|
||||
/* For auto stop mode, need to keep the clock on before
|
||||
* system go into low power mode. After system go into
|
||||
* low power mode, hardware will config the flexcan into
|
||||
* stop mode, and gate off the clock automatically.
|
||||
*/
|
||||
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = pm_runtime_force_suspend(device);
|
||||
if (err)
|
||||
|
@ -68,6 +68,8 @@
|
||||
#define FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR BIT(15)
|
||||
/* Device supports RX via FIFO */
|
||||
#define FLEXCAN_QUIRK_SUPPORT_RX_FIFO BIT(16)
|
||||
/* auto enter stop mode to support wakeup */
|
||||
#define FLEXCAN_QUIRK_AUTO_STOP_MODE BIT(17)
|
||||
|
||||
struct flexcan_devtype_data {
|
||||
u32 quirks; /* quirks needed for different IP cores */
|
||||
|
@ -9,20 +9,20 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
#include "m_can.h"
|
||||
|
||||
@ -369,9 +369,14 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val)
|
||||
return cdev->ops->read_fifo(cdev, addr_offset, val, 1);
|
||||
}
|
||||
|
||||
static inline bool _m_can_tx_fifo_full(u32 txfqs)
|
||||
{
|
||||
return !!(txfqs & TXFQS_TFQF);
|
||||
}
|
||||
|
||||
static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev)
|
||||
{
|
||||
return !!(m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQF);
|
||||
return _m_can_tx_fifo_full(m_can_read(cdev, M_CAN_TXFQS));
|
||||
}
|
||||
|
||||
static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable)
|
||||
@ -472,19 +477,16 @@ static void m_can_receive_skb(struct m_can_classdev *cdev,
|
||||
}
|
||||
}
|
||||
|
||||
static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
|
||||
static int m_can_read_fifo(struct net_device *dev, u32 fgi)
|
||||
{
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
struct canfd_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
struct id_and_dlc fifo_header;
|
||||
u32 fgi;
|
||||
u32 timestamp = 0;
|
||||
int err;
|
||||
|
||||
/* calculate the fifo get index for where to read data */
|
||||
fgi = FIELD_GET(RXFS_FGI_MASK, rxfs);
|
||||
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2);
|
||||
if (err)
|
||||
goto out_fail;
|
||||
@ -528,9 +530,6 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
|
||||
}
|
||||
stats->rx_packets++;
|
||||
|
||||
/* acknowledge rx fifo 0 */
|
||||
m_can_write(cdev, M_CAN_RXF0A, fgi);
|
||||
|
||||
timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc) << 16;
|
||||
|
||||
m_can_receive_skb(cdev, skb, timestamp);
|
||||
@ -549,7 +548,11 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
u32 pkts = 0;
|
||||
u32 rxfs;
|
||||
int err;
|
||||
u32 rx_count;
|
||||
u32 fgi;
|
||||
int ack_fgi = -1;
|
||||
int i;
|
||||
int err = 0;
|
||||
|
||||
rxfs = m_can_read(cdev, M_CAN_RXF0S);
|
||||
if (!(rxfs & RXFS_FFL_MASK)) {
|
||||
@ -557,16 +560,26 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) {
|
||||
err = m_can_read_fifo(dev, rxfs);
|
||||
rx_count = FIELD_GET(RXFS_FFL_MASK, rxfs);
|
||||
fgi = FIELD_GET(RXFS_FGI_MASK, rxfs);
|
||||
|
||||
for (i = 0; i < rx_count && quota > 0; ++i) {
|
||||
err = m_can_read_fifo(dev, fgi);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
|
||||
quota--;
|
||||
pkts++;
|
||||
rxfs = m_can_read(cdev, M_CAN_RXF0S);
|
||||
ack_fgi = fgi;
|
||||
fgi = (++fgi >= cdev->mcfg[MRAM_RXF0].num ? 0 : fgi);
|
||||
}
|
||||
|
||||
if (ack_fgi != -1)
|
||||
m_can_write(cdev, M_CAN_RXF0A, ack_fgi);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return pkts;
|
||||
}
|
||||
|
||||
@ -900,14 +913,12 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
|
||||
return work_done;
|
||||
}
|
||||
|
||||
static int m_can_rx_handler(struct net_device *dev, int quota)
|
||||
static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus)
|
||||
{
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int rx_work_or_err;
|
||||
int work_done = 0;
|
||||
u32 irqstatus, psr;
|
||||
|
||||
irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR);
|
||||
if (!irqstatus)
|
||||
goto end;
|
||||
|
||||
@ -932,13 +943,13 @@ static int m_can_rx_handler(struct net_device *dev, int quota)
|
||||
}
|
||||
}
|
||||
|
||||
psr = m_can_read(cdev, M_CAN_PSR);
|
||||
|
||||
if (irqstatus & IR_ERR_STATE)
|
||||
work_done += m_can_handle_state_errors(dev, psr);
|
||||
work_done += m_can_handle_state_errors(dev,
|
||||
m_can_read(cdev, M_CAN_PSR));
|
||||
|
||||
if (irqstatus & IR_ERR_BUS_30X)
|
||||
work_done += m_can_handle_bus_errors(dev, irqstatus, psr);
|
||||
work_done += m_can_handle_bus_errors(dev, irqstatus,
|
||||
m_can_read(cdev, M_CAN_PSR));
|
||||
|
||||
if (irqstatus & IR_RF0N) {
|
||||
rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done));
|
||||
@ -951,12 +962,12 @@ end:
|
||||
return work_done;
|
||||
}
|
||||
|
||||
static int m_can_rx_peripheral(struct net_device *dev)
|
||||
static int m_can_rx_peripheral(struct net_device *dev, u32 irqstatus)
|
||||
{
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int work_done;
|
||||
|
||||
work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT);
|
||||
work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, irqstatus);
|
||||
|
||||
/* Don't re-enable interrupts if the driver had a fatal error
|
||||
* (e.g., FIFO read failure).
|
||||
@ -972,8 +983,11 @@ static int m_can_poll(struct napi_struct *napi, int quota)
|
||||
struct net_device *dev = napi->dev;
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int work_done;
|
||||
u32 irqstatus;
|
||||
|
||||
work_done = m_can_rx_handler(dev, quota);
|
||||
irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR);
|
||||
|
||||
work_done = m_can_rx_handler(dev, quota, irqstatus);
|
||||
|
||||
/* Don't re-enable interrupts if the driver had a fatal error
|
||||
* (e.g., FIFO read failure).
|
||||
@ -1014,7 +1028,9 @@ static int m_can_echo_tx_event(struct net_device *dev)
|
||||
u32 txe_count = 0;
|
||||
u32 m_can_txefs;
|
||||
u32 fgi = 0;
|
||||
int ack_fgi = -1;
|
||||
int i = 0;
|
||||
int err = 0;
|
||||
unsigned int msg_mark;
|
||||
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
@ -1024,34 +1040,34 @@ static int m_can_echo_tx_event(struct net_device *dev)
|
||||
|
||||
/* Get Tx Event fifo element count */
|
||||
txe_count = FIELD_GET(TXEFS_EFFL_MASK, m_can_txefs);
|
||||
fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_txefs);
|
||||
|
||||
/* Get and process all sent elements */
|
||||
for (i = 0; i < txe_count; i++) {
|
||||
u32 txe, timestamp = 0;
|
||||
int err;
|
||||
|
||||
/* retrieve get index */
|
||||
fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_read(cdev, M_CAN_TXEFS));
|
||||
|
||||
/* get message marker, timestamp */
|
||||
err = m_can_txe_fifo_read(cdev, fgi, 4, &txe);
|
||||
if (err) {
|
||||
netdev_err(dev, "TXE FIFO read returned %d\n", err);
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
|
||||
msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe);
|
||||
timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe) << 16;
|
||||
|
||||
/* ack txe element */
|
||||
m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK,
|
||||
fgi));
|
||||
ack_fgi = fgi;
|
||||
fgi = (++fgi >= cdev->mcfg[MRAM_TXE].num ? 0 : fgi);
|
||||
|
||||
/* update stats */
|
||||
m_can_tx_update_stats(cdev, msg_mark, timestamp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (ack_fgi != -1)
|
||||
m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK,
|
||||
ack_fgi));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
@ -1083,7 +1099,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
|
||||
m_can_disable_all_interrupts(cdev);
|
||||
if (!cdev->is_peripheral)
|
||||
napi_schedule(&cdev->napi);
|
||||
else if (m_can_rx_peripheral(dev) < 0)
|
||||
else if (m_can_rx_peripheral(dev, ir) < 0)
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
@ -1243,10 +1259,17 @@ static int m_can_set_bittiming(struct net_device *dev)
|
||||
* - setup bittiming
|
||||
* - configure timestamp generation
|
||||
*/
|
||||
static void m_can_chip_config(struct net_device *dev)
|
||||
static int m_can_chip_config(struct net_device *dev)
|
||||
{
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
u32 cccr, test;
|
||||
int err;
|
||||
|
||||
err = m_can_init_ram(cdev);
|
||||
if (err) {
|
||||
dev_err(cdev->dev, "Message RAM configuration failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
m_can_config_endisable(cdev, true);
|
||||
|
||||
@ -1370,18 +1393,25 @@ static void m_can_chip_config(struct net_device *dev)
|
||||
|
||||
if (cdev->ops->init)
|
||||
cdev->ops->init(cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void m_can_start(struct net_device *dev)
|
||||
static int m_can_start(struct net_device *dev)
|
||||
{
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* basic m_can configuration */
|
||||
m_can_chip_config(dev);
|
||||
ret = m_can_chip_config(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
m_can_enable_all_interrupts(cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
|
||||
@ -1595,6 +1625,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
|
||||
struct sk_buff *skb = cdev->tx_skb;
|
||||
struct id_and_dlc fifo_header;
|
||||
u32 cccr, fdflags;
|
||||
u32 txfqs;
|
||||
int err;
|
||||
int putidx;
|
||||
|
||||
@ -1651,8 +1682,10 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
|
||||
} else {
|
||||
/* Transmit routine for version >= v3.1.x */
|
||||
|
||||
txfqs = m_can_read(cdev, M_CAN_TXFQS);
|
||||
|
||||
/* Check if FIFO full */
|
||||
if (m_can_tx_fifo_full(cdev)) {
|
||||
if (_m_can_tx_fifo_full(txfqs)) {
|
||||
/* This shouldn't happen */
|
||||
netif_stop_queue(dev);
|
||||
netdev_warn(dev,
|
||||
@ -1668,8 +1701,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
|
||||
}
|
||||
|
||||
/* get put index for frame */
|
||||
putidx = FIELD_GET(TXFQS_TFQPI_MASK,
|
||||
m_can_read(cdev, M_CAN_TXFQS));
|
||||
putidx = FIELD_GET(TXFQS_TFQPI_MASK, txfqs);
|
||||
|
||||
/* Construct DLC Field, with CAN-FD configuration.
|
||||
* Use the put index of the fifo as the message marker,
|
||||
@ -1809,7 +1841,9 @@ static int m_can_open(struct net_device *dev)
|
||||
}
|
||||
|
||||
/* start the m_can controller */
|
||||
m_can_start(dev);
|
||||
err = m_can_start(dev);
|
||||
if (err)
|
||||
goto exit_irq_fail;
|
||||
|
||||
if (!cdev->is_peripheral)
|
||||
napi_enable(&cdev->napi);
|
||||
@ -2068,9 +2102,13 @@ int m_can_class_resume(struct device *dev)
|
||||
ret = m_can_clk_start(cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = m_can_start(ndev);
|
||||
if (ret) {
|
||||
m_can_clk_stop(cdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_can_init_ram(cdev);
|
||||
m_can_start(ndev);
|
||||
netif_device_attach(ndev);
|
||||
netif_start_queue(ndev);
|
||||
}
|
||||
|
@ -7,27 +7,27 @@
|
||||
#define _CAN_M_CAN_H_
|
||||
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/rx-offload.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/* m_can lec values */
|
||||
enum m_can_lec_type {
|
||||
|
@ -5,8 +5,8 @@
|
||||
//
|
||||
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "m_can.h"
|
||||
|
||||
@ -140,10 +140,6 @@ static int m_can_plat_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, mcan_class);
|
||||
|
||||
ret = m_can_init_ram(mcan_class);
|
||||
if (ret)
|
||||
goto probe_fail;
|
||||
|
||||
pm_runtime_enable(mcan_class->dev);
|
||||
ret = m_can_class_register(mcan_class);
|
||||
if (ret)
|
||||
|
@ -10,7 +10,7 @@
|
||||
#define TCAN4X5X_DEV_ID1 0x04
|
||||
#define TCAN4X5X_REV 0x08
|
||||
#define TCAN4X5X_STATUS 0x0C
|
||||
#define TCAN4X5X_ERROR_STATUS 0x10
|
||||
#define TCAN4X5X_ERROR_STATUS_MASK 0x10
|
||||
#define TCAN4X5X_CONTROL 0x14
|
||||
|
||||
#define TCAN4X5X_CONFIG 0x800
|
||||
@ -204,17 +204,7 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG,
|
||||
TCAN4X5X_ENABLE_MCAN_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
|
||||
TCAN4X5X_CLEAR_ALL_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS,
|
||||
return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
|
||||
TCAN4X5X_CLEAR_ALL_INT);
|
||||
}
|
||||
|
||||
@ -234,8 +224,8 @@ static int tcan4x5x_init(struct m_can_classdev *cdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Zero out the MCAN buffers */
|
||||
ret = m_can_init_ram(cdev);
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS_MASK,
|
||||
TCAN4X5X_CLEAR_ALL_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -90,16 +90,47 @@ static int tcan4x5x_regmap_read(void *context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_range tcan4x5x_reg_table_yes_range[] = {
|
||||
regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */
|
||||
regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/
|
||||
static const struct regmap_range tcan4x5x_reg_table_wr_range[] = {
|
||||
/* Device ID and SPI Registers */
|
||||
regmap_reg_range(0x000c, 0x0010),
|
||||
/* Device configuration registers and Interrupt Flags*/
|
||||
regmap_reg_range(0x0800, 0x080c),
|
||||
regmap_reg_range(0x0814, 0x0814),
|
||||
regmap_reg_range(0x0820, 0x0820),
|
||||
regmap_reg_range(0x0830, 0x0830),
|
||||
/* M_CAN */
|
||||
regmap_reg_range(0x100c, 0x102c),
|
||||
regmap_reg_range(0x1048, 0x1048),
|
||||
regmap_reg_range(0x1050, 0x105c),
|
||||
regmap_reg_range(0x1080, 0x1088),
|
||||
regmap_reg_range(0x1090, 0x1090),
|
||||
regmap_reg_range(0x1098, 0x10a0),
|
||||
regmap_reg_range(0x10a8, 0x10b0),
|
||||
regmap_reg_range(0x10b8, 0x10c0),
|
||||
regmap_reg_range(0x10c8, 0x10c8),
|
||||
regmap_reg_range(0x10d0, 0x10d4),
|
||||
regmap_reg_range(0x10e0, 0x10e4),
|
||||
regmap_reg_range(0x10f0, 0x10f0),
|
||||
regmap_reg_range(0x10f8, 0x10f8),
|
||||
/* MRAM */
|
||||
regmap_reg_range(0x8000, 0x87fc),
|
||||
};
|
||||
|
||||
static const struct regmap_range tcan4x5x_reg_table_rd_range[] = {
|
||||
regmap_reg_range(0x0000, 0x0010), /* Device ID and SPI Registers */
|
||||
regmap_reg_range(0x0800, 0x0830), /* Device configuration registers and Interrupt Flags*/
|
||||
regmap_reg_range(0x1000, 0x10fc), /* M_CAN */
|
||||
regmap_reg_range(0x8000, 0x87fc), /* MRAM */
|
||||
};
|
||||
|
||||
static const struct regmap_access_table tcan4x5x_reg_table = {
|
||||
.yes_ranges = tcan4x5x_reg_table_yes_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range),
|
||||
static const struct regmap_access_table tcan4x5x_reg_table_wr = {
|
||||
.yes_ranges = tcan4x5x_reg_table_wr_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_wr_range),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table tcan4x5x_reg_table_rd = {
|
||||
.yes_ranges = tcan4x5x_reg_table_rd_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_rd_range),
|
||||
};
|
||||
|
||||
static const struct regmap_config tcan4x5x_regmap = {
|
||||
@ -107,8 +138,8 @@ static const struct regmap_config tcan4x5x_regmap = {
|
||||
.reg_stride = 4,
|
||||
.pad_bits = 8,
|
||||
.val_bits = 32,
|
||||
.wr_table = &tcan4x5x_reg_table,
|
||||
.rd_table = &tcan4x5x_reg_table,
|
||||
.wr_table = &tcan4x5x_reg_table_wr,
|
||||
.rd_table = &tcan4x5x_reg_table_rd,
|
||||
.max_register = TCAN4X5X_MAX_REGISTER,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.read_flag_mask = (__force unsigned long)
|
||||
|
@ -41,12 +41,6 @@
|
||||
|
||||
#define RCANFD_DRV_NAME "rcar_canfd"
|
||||
|
||||
enum rcanfd_chip_id {
|
||||
RENESAS_RCAR_GEN3 = 0,
|
||||
RENESAS_RZG2L,
|
||||
RENESAS_R8A779A0,
|
||||
};
|
||||
|
||||
/* Global register bits */
|
||||
|
||||
/* RSCFDnCFDGRMCFG */
|
||||
@ -522,6 +516,14 @@ enum rcar_canfd_fcanclk {
|
||||
|
||||
struct rcar_canfd_global;
|
||||
|
||||
struct rcar_canfd_hw_info {
|
||||
u8 max_channels;
|
||||
u8 postdiv;
|
||||
/* hardware features */
|
||||
unsigned shared_global_irqs:1; /* Has shared global irqs */
|
||||
unsigned multi_channel_irqs:1; /* Has multiple channel irqs */
|
||||
};
|
||||
|
||||
/* Channel priv data */
|
||||
struct rcar_canfd_channel {
|
||||
struct can_priv can; /* Must be the first member */
|
||||
@ -547,8 +549,7 @@ struct rcar_canfd_global {
|
||||
bool fdmode; /* CAN FD or Classical CAN only mode */
|
||||
struct reset_control *rstc1;
|
||||
struct reset_control *rstc2;
|
||||
enum rcanfd_chip_id chip_id;
|
||||
u32 max_channels;
|
||||
const struct rcar_canfd_hw_info *info;
|
||||
};
|
||||
|
||||
/* CAN FD mode nominal rate constants */
|
||||
@ -590,10 +591,28 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = {
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
static const struct rcar_canfd_hw_info rcar_gen3_hw_info = {
|
||||
.max_channels = 2,
|
||||
.postdiv = 2,
|
||||
.shared_global_irqs = 1,
|
||||
};
|
||||
|
||||
static const struct rcar_canfd_hw_info rzg2l_hw_info = {
|
||||
.max_channels = 2,
|
||||
.postdiv = 1,
|
||||
.multi_channel_irqs = 1,
|
||||
};
|
||||
|
||||
static const struct rcar_canfd_hw_info r8a779a0_hw_info = {
|
||||
.max_channels = 8,
|
||||
.postdiv = 2,
|
||||
.shared_global_irqs = 1,
|
||||
};
|
||||
|
||||
/* Helper functions */
|
||||
static inline bool is_v3u(struct rcar_canfd_global *gpriv)
|
||||
{
|
||||
return gpriv->chip_id == RENESAS_R8A779A0;
|
||||
return gpriv->info == &r8a779a0_hw_info;
|
||||
}
|
||||
|
||||
static inline u32 reg_v3u(struct rcar_canfd_global *gpriv,
|
||||
@ -721,7 +740,7 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv)
|
||||
rcar_canfd_set_mode(gpriv);
|
||||
|
||||
/* Transition all Channels to reset mode */
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
rcar_canfd_clear_bit(gpriv->base,
|
||||
RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR);
|
||||
|
||||
@ -762,7 +781,7 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv)
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg);
|
||||
|
||||
/* Channel configuration settings */
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_CCTR(ch),
|
||||
RCANFD_CCTR_ERRD);
|
||||
rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch),
|
||||
@ -1142,7 +1161,7 @@ static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id)
|
||||
struct rcar_canfd_global *gpriv = dev_id;
|
||||
u32 ch;
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels)
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels)
|
||||
rcar_canfd_handle_global_err(gpriv, ch);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -1174,7 +1193,7 @@ static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_i
|
||||
struct rcar_canfd_global *gpriv = dev_id;
|
||||
u32 ch;
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels)
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels)
|
||||
rcar_canfd_handle_global_receive(gpriv, ch);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -1188,7 +1207,7 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id)
|
||||
/* Global error interrupts still indicate a condition specific
|
||||
* to a channel. RxFIFO interrupt is a global interrupt.
|
||||
*/
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
rcar_canfd_handle_global_err(gpriv, ch);
|
||||
rcar_canfd_handle_global_receive(gpriv, ch);
|
||||
}
|
||||
@ -1284,7 +1303,7 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id)
|
||||
u32 ch;
|
||||
|
||||
/* Common FIFO is a per channel resource */
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
rcar_canfd_handle_channel_err(gpriv, ch);
|
||||
rcar_canfd_handle_channel_tx(gpriv, ch);
|
||||
}
|
||||
@ -1696,6 +1715,7 @@ static const struct ethtool_ops rcar_canfd_ethtool_ops = {
|
||||
static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch,
|
||||
u32 fcan_freq)
|
||||
{
|
||||
const struct rcar_canfd_hw_info *info = gpriv->info;
|
||||
struct platform_device *pdev = gpriv->pdev;
|
||||
struct rcar_canfd_channel *priv;
|
||||
struct net_device *ndev;
|
||||
@ -1718,7 +1738,7 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch,
|
||||
priv->can.clock.freq = fcan_freq;
|
||||
dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq);
|
||||
|
||||
if (gpriv->chip_id == RENESAS_RZG2L) {
|
||||
if (info->multi_channel_irqs) {
|
||||
char *irq_name;
|
||||
int err_irq;
|
||||
int tx_irq;
|
||||
@ -1818,6 +1838,7 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch)
|
||||
|
||||
static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct rcar_canfd_hw_info *info;
|
||||
void __iomem *addr;
|
||||
u32 sts, ch, fcan_freq;
|
||||
struct rcar_canfd_global *gpriv;
|
||||
@ -1826,18 +1847,15 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
int err, ch_irq, g_irq;
|
||||
int g_err_irq, g_recc_irq;
|
||||
bool fdmode = true; /* CAN FD only mode - default */
|
||||
enum rcanfd_chip_id chip_id;
|
||||
int max_channels;
|
||||
char name[9] = "channelX";
|
||||
int i;
|
||||
|
||||
chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
max_channels = chip_id == RENESAS_R8A779A0 ? 8 : 2;
|
||||
info = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd"))
|
||||
fdmode = false; /* Classical CAN only mode */
|
||||
|
||||
for (i = 0; i < max_channels; ++i) {
|
||||
for (i = 0; i < info->max_channels; ++i) {
|
||||
name[7] = '0' + i;
|
||||
of_child = of_get_child_by_name(pdev->dev.of_node, name);
|
||||
if (of_child && of_device_is_available(of_child))
|
||||
@ -1845,7 +1863,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
of_node_put(of_child);
|
||||
}
|
||||
|
||||
if (chip_id != RENESAS_RZG2L) {
|
||||
if (info->shared_global_irqs) {
|
||||
ch_irq = platform_get_irq_byname_optional(pdev, "ch_int");
|
||||
if (ch_irq < 0) {
|
||||
/* For backward compatibility get irq by index */
|
||||
@ -1879,8 +1897,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
gpriv->pdev = pdev;
|
||||
gpriv->channels_mask = channels_mask;
|
||||
gpriv->fdmode = fdmode;
|
||||
gpriv->chip_id = chip_id;
|
||||
gpriv->max_channels = max_channels;
|
||||
gpriv->info = info;
|
||||
|
||||
gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev,
|
||||
"rstp_n");
|
||||
@ -1917,9 +1934,9 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
}
|
||||
fcan_freq = clk_get_rate(gpriv->can_clk);
|
||||
|
||||
if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id != RENESAS_RZG2L)
|
||||
if (gpriv->fcan == RCANFD_CANFDCLK)
|
||||
/* CANFD clock is further divided by (1/2) within the IP */
|
||||
fcan_freq /= 2;
|
||||
fcan_freq /= info->postdiv;
|
||||
|
||||
addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(addr)) {
|
||||
@ -1929,7 +1946,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
gpriv->base = addr;
|
||||
|
||||
/* Request IRQ that's common for both channels */
|
||||
if (gpriv->chip_id != RENESAS_RZG2L) {
|
||||
if (info->shared_global_irqs) {
|
||||
err = devm_request_irq(&pdev->dev, ch_irq,
|
||||
rcar_canfd_channel_interrupt, 0,
|
||||
"canfd.ch_int", gpriv);
|
||||
@ -1995,7 +2012,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
rcar_canfd_configure_controller(gpriv);
|
||||
|
||||
/* Configure per channel attributes */
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
|
||||
/* Configure Channel's Rx fifo */
|
||||
rcar_canfd_configure_rx(gpriv, ch);
|
||||
|
||||
@ -2021,7 +2038,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
goto fail_mode;
|
||||
}
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
|
||||
err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq);
|
||||
if (err)
|
||||
goto fail_channel;
|
||||
@ -2033,7 +2050,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
fail_channel:
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, max_channels)
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels)
|
||||
rcar_canfd_channel_remove(gpriv, ch);
|
||||
fail_mode:
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
@ -2054,7 +2071,7 @@ static int rcar_canfd_remove(struct platform_device *pdev)
|
||||
rcar_canfd_reset_controller(gpriv);
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) {
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]);
|
||||
rcar_canfd_channel_remove(gpriv, ch);
|
||||
}
|
||||
@ -2082,9 +2099,9 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
|
||||
rcar_canfd_resume);
|
||||
|
||||
static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = {
|
||||
{ .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 },
|
||||
{ .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L },
|
||||
{ .compatible = "renesas,r8a779a0-canfd", .data = (void *)RENESAS_R8A779A0 },
|
||||
{ .compatible = "renesas,rcar-gen3-canfd", .data = &rcar_gen3_hw_info },
|
||||
{ .compatible = "renesas,rzg2l-canfd", .data = &rzg2l_hw_info },
|
||||
{ .compatible = "renesas,r8a779a0-canfd", .data = &r8a779a0_hw_info },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,7 @@ config CAN_ESD_USB
|
||||
config CAN_ETAS_ES58X
|
||||
tristate "ETAS ES58X CAN/USB interfaces"
|
||||
select CRC16
|
||||
select NET_DEVLINK
|
||||
help
|
||||
This driver supports the ES581.4, ES582.1 and ES584.1 interfaces
|
||||
from ETAS GmbH (https://www.etas.com/en/products/es58x.php).
|
||||
|
@ -1,3 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_CAN_ETAS_ES58X) += etas_es58x.o
|
||||
etas_es58x-y = es58x_core.o es581_4.o es58x_fd.o
|
||||
etas_es58x-y = es58x_core.o es58x_devlink.o es581_4.o es58x_fd.o
|
||||
|
@ -6,12 +6,12 @@
|
||||
*
|
||||
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
|
||||
* Copyright (c) 2020 ETAS K.K.. All rights reserved.
|
||||
* Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
* Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/units.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "es58x_core.h"
|
||||
#include "es581_4.h"
|
||||
|
@ -7,15 +7,16 @@
|
||||
*
|
||||
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
|
||||
* Copyright (c) 2020 ETAS K.K.. All rights reserved.
|
||||
* Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
* Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/crc16.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/crc16.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#include "es58x_core.h"
|
||||
|
||||
@ -2038,10 +2039,16 @@ static int es58x_set_mode(struct net_device *netdev, enum can_mode mode)
|
||||
* @es58x_dev: ES58X device.
|
||||
* @priv: ES58X private parameters related to the network device.
|
||||
* @channel_idx: Index of the network device.
|
||||
*
|
||||
* Return: zero on success, errno if devlink port could not be
|
||||
* properly registered.
|
||||
*/
|
||||
static void es58x_init_priv(struct es58x_device *es58x_dev,
|
||||
struct es58x_priv *priv, int channel_idx)
|
||||
static int es58x_init_priv(struct es58x_device *es58x_dev,
|
||||
struct es58x_priv *priv, int channel_idx)
|
||||
{
|
||||
struct devlink_port_attrs attrs = {
|
||||
.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL,
|
||||
};
|
||||
const struct es58x_parameters *param = es58x_dev->param;
|
||||
struct can_priv *can = &priv->can;
|
||||
|
||||
@ -2060,6 +2067,10 @@ static void es58x_init_priv(struct es58x_device *es58x_dev,
|
||||
can->state = CAN_STATE_STOPPED;
|
||||
can->ctrlmode_supported = param->ctrlmode_supported;
|
||||
can->do_set_mode = es58x_set_mode;
|
||||
|
||||
devlink_port_attrs_set(&priv->devlink_port, &attrs);
|
||||
return devlink_port_register(priv_to_devlink(es58x_dev),
|
||||
&priv->devlink_port, channel_idx);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2083,7 +2094,10 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx)
|
||||
}
|
||||
SET_NETDEV_DEV(netdev, dev);
|
||||
es58x_dev->netdev[channel_idx] = netdev;
|
||||
es58x_init_priv(es58x_dev, es58x_priv(netdev), channel_idx);
|
||||
ret = es58x_init_priv(es58x_dev, es58x_priv(netdev), channel_idx);
|
||||
if (ret)
|
||||
goto free_candev;
|
||||
SET_NETDEV_DEVLINK_PORT(netdev, &es58x_priv(netdev)->devlink_port);
|
||||
|
||||
netdev->netdev_ops = &es58x_netdev_ops;
|
||||
netdev->ethtool_ops = &es58x_ethtool_ops;
|
||||
@ -2091,16 +2105,20 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx)
|
||||
netdev->dev_port = channel_idx;
|
||||
|
||||
ret = register_candev(netdev);
|
||||
if (ret) {
|
||||
es58x_dev->netdev[channel_idx] = NULL;
|
||||
free_candev(netdev);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto devlink_port_unregister;
|
||||
|
||||
netdev_queue_set_dql_min_limit(netdev_get_tx_queue(netdev, 0),
|
||||
es58x_dev->param->dql_min_limit);
|
||||
|
||||
return ret;
|
||||
|
||||
devlink_port_unregister:
|
||||
devlink_port_unregister(&es58x_priv(netdev)->devlink_port);
|
||||
free_candev:
|
||||
es58x_dev->netdev[channel_idx] = NULL;
|
||||
free_candev(netdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2117,53 +2135,12 @@ static void es58x_free_netdevs(struct es58x_device *es58x_dev)
|
||||
if (!netdev)
|
||||
continue;
|
||||
unregister_candev(netdev);
|
||||
devlink_port_unregister(&es58x_priv(netdev)->devlink_port);
|
||||
es58x_dev->netdev[i] = NULL;
|
||||
free_candev(netdev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_get_product_info() - Get the product information and print them.
|
||||
* @es58x_dev: ES58X device.
|
||||
*
|
||||
* Do a synchronous call to get the product information.
|
||||
*
|
||||
* Return: zero on success, errno when any error occurs.
|
||||
*/
|
||||
static int es58x_get_product_info(struct es58x_device *es58x_dev)
|
||||
{
|
||||
struct usb_device *udev = es58x_dev->udev;
|
||||
const int es58x_prod_info_idx = 6;
|
||||
/* Empirical tests show a prod_info length of maximum 83,
|
||||
* below should be more than enough.
|
||||
*/
|
||||
const size_t prod_info_len = 127;
|
||||
char *prod_info;
|
||||
int ret;
|
||||
|
||||
prod_info = kmalloc(prod_info_len, GFP_KERNEL);
|
||||
if (!prod_info)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = usb_string(udev, es58x_prod_info_idx, prod_info, prod_info_len);
|
||||
if (ret < 0) {
|
||||
dev_err(es58x_dev->dev,
|
||||
"%s: Could not read the product info: %pe\n",
|
||||
__func__, ERR_PTR(ret));
|
||||
goto out_free;
|
||||
}
|
||||
if (ret >= prod_info_len - 1) {
|
||||
dev_warn(es58x_dev->dev,
|
||||
"%s: Buffer is too small, result might be truncated\n",
|
||||
__func__);
|
||||
}
|
||||
dev_info(es58x_dev->dev, "Product info: %s\n", prod_info);
|
||||
|
||||
out_free:
|
||||
kfree(prod_info);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_init_es58x_dev() - Initialize the ES58X device.
|
||||
* @intf: USB interface.
|
||||
@ -2177,6 +2154,7 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf,
|
||||
{
|
||||
struct device *dev = &intf->dev;
|
||||
struct es58x_device *es58x_dev;
|
||||
struct devlink *devlink;
|
||||
const struct es58x_parameters *param;
|
||||
const struct es58x_operators *ops;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
@ -2199,11 +2177,12 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf,
|
||||
ops = &es581_4_ops;
|
||||
}
|
||||
|
||||
es58x_dev = devm_kzalloc(dev, es58x_sizeof_es58x_device(param),
|
||||
GFP_KERNEL);
|
||||
if (!es58x_dev)
|
||||
devlink = devlink_alloc(&es58x_dl_ops, es58x_sizeof_es58x_device(param),
|
||||
dev);
|
||||
if (!devlink)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
es58x_dev = devlink_priv(devlink);
|
||||
es58x_dev->param = param;
|
||||
es58x_dev->ops = ops;
|
||||
es58x_dev->dev = dev;
|
||||
@ -2240,25 +2219,24 @@ static int es58x_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct es58x_device *es58x_dev;
|
||||
int ch_idx, ret;
|
||||
int ch_idx;
|
||||
|
||||
es58x_dev = es58x_init_es58x_dev(intf, id->driver_info);
|
||||
if (IS_ERR(es58x_dev))
|
||||
return PTR_ERR(es58x_dev);
|
||||
|
||||
ret = es58x_get_product_info(es58x_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
es58x_parse_product_info(es58x_dev);
|
||||
devlink_register(priv_to_devlink(es58x_dev));
|
||||
|
||||
for (ch_idx = 0; ch_idx < es58x_dev->num_can_ch; ch_idx++) {
|
||||
ret = es58x_init_netdev(es58x_dev, ch_idx);
|
||||
int ret = es58x_init_netdev(es58x_dev, ch_idx);
|
||||
if (ret) {
|
||||
es58x_free_netdevs(es58x_dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2275,8 +2253,10 @@ static void es58x_disconnect(struct usb_interface *intf)
|
||||
dev_info(&intf->dev, "Disconnecting %s %s\n",
|
||||
es58x_dev->udev->manufacturer, es58x_dev->udev->product);
|
||||
|
||||
devlink_unregister(priv_to_devlink(es58x_dev));
|
||||
es58x_free_netdevs(es58x_dev);
|
||||
es58x_free_urbs(es58x_dev);
|
||||
devlink_free(priv_to_devlink(es58x_dev));
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
|
@ -6,17 +6,18 @@
|
||||
*
|
||||
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
|
||||
* Copyright (c) 2020 ETAS K.K.. All rights reserved.
|
||||
* Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
* Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
*/
|
||||
|
||||
#ifndef __ES58X_COMMON_H__
|
||||
#define __ES58X_COMMON_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#include "es581_4.h"
|
||||
#include "es58x_fd.h"
|
||||
@ -230,6 +231,7 @@ union es58x_urb_cmd {
|
||||
* @can: struct can_priv must be the first member (Socket CAN relies
|
||||
* on the fact that function netdev_priv() returns a pointer to
|
||||
* a struct can_priv).
|
||||
* @devlink_port: devlink instance for the network interface.
|
||||
* @es58x_dev: pointer to the corresponding ES58X device.
|
||||
* @tx_urb: Used as a buffer to concatenate the TX messages and to do
|
||||
* a bulk send. Please refer to es58x_start_xmit() for more
|
||||
@ -255,6 +257,7 @@ union es58x_urb_cmd {
|
||||
*/
|
||||
struct es58x_priv {
|
||||
struct can_priv can;
|
||||
struct devlink_port devlink_port;
|
||||
struct es58x_device *es58x_dev;
|
||||
struct urb *tx_urb;
|
||||
|
||||
@ -356,6 +359,39 @@ struct es58x_operators {
|
||||
int (*get_timestamp)(struct es58x_device *es58x_dev);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct es58x_sw_version - Version number of the firmware or the
|
||||
* bootloader.
|
||||
* @major: Version major number, represented on two digits.
|
||||
* @minor: Version minor number, represented on two digits.
|
||||
* @revision: Version revision number, represented on two digits.
|
||||
*
|
||||
* The firmware and the bootloader share the same format: "xx.xx.xx"
|
||||
* where 'x' is a digit. Both can be retrieved from the product
|
||||
* information string.
|
||||
*/
|
||||
struct es58x_sw_version {
|
||||
u8 major;
|
||||
u8 minor;
|
||||
u8 revision;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct es58x_hw_revision - Hardware revision number.
|
||||
* @letter: Revision letter.
|
||||
* @major: Version major number, represented on three digits.
|
||||
* @minor: Version minor number, represented on three digits.
|
||||
*
|
||||
* The hardware revision uses its own format: "axxx/xxx" where 'a' is
|
||||
* a letter and 'x' a digit. It can be retrieved from the product
|
||||
* information string.
|
||||
*/
|
||||
struct es58x_hw_revision {
|
||||
char letter;
|
||||
u16 major;
|
||||
u16 minor;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct es58x_device - All information specific to an ES58X device.
|
||||
* @dev: Device information.
|
||||
@ -373,6 +409,9 @@ struct es58x_operators {
|
||||
* queue wake/stop logic should prevent this URB from getting
|
||||
* empty. Please refer to es58x_get_tx_urb() for more details.
|
||||
* @tx_urbs_idle_cnt: number of urbs in @tx_urbs_idle.
|
||||
* @firmware_version: The firmware version number.
|
||||
* @bootloader_version: The bootloader version number.
|
||||
* @hardware_revision: The hardware revision number.
|
||||
* @ktime_req_ns: kernel timestamp when es58x_set_realtime_diff_ns()
|
||||
* was called.
|
||||
* @realtime_diff_ns: difference in nanoseconds between the clocks of
|
||||
@ -408,6 +447,10 @@ struct es58x_device {
|
||||
struct usb_anchor tx_urbs_idle;
|
||||
atomic_t tx_urbs_idle_cnt;
|
||||
|
||||
struct es58x_sw_version firmware_version;
|
||||
struct es58x_sw_version bootloader_version;
|
||||
struct es58x_hw_revision hardware_revision;
|
||||
|
||||
u64 ktime_req_ns;
|
||||
s64 realtime_diff_ns;
|
||||
|
||||
@ -674,6 +717,7 @@ static inline enum es58x_flag es58x_get_flags(const struct sk_buff *skb)
|
||||
return es58x_flags;
|
||||
}
|
||||
|
||||
/* es58x_core.c. */
|
||||
int es58x_can_get_echo_skb(struct net_device *netdev, u32 packet_idx,
|
||||
u64 *tstamps, unsigned int pkts);
|
||||
int es58x_tx_ack_msg(struct net_device *netdev, u16 tx_free_entries,
|
||||
@ -691,9 +735,15 @@ int es58x_rx_cmd_ret_u32(struct net_device *netdev,
|
||||
int es58x_send_msg(struct es58x_device *es58x_dev, u8 cmd_type, u8 cmd_id,
|
||||
const void *msg, u16 cmd_len, int channel_idx);
|
||||
|
||||
/* es58x_devlink.c. */
|
||||
void es58x_parse_product_info(struct es58x_device *es58x_dev);
|
||||
extern const struct devlink_ops es58x_dl_ops;
|
||||
|
||||
/* es581_4.c. */
|
||||
extern const struct es58x_parameters es581_4_param;
|
||||
extern const struct es58x_operators es581_4_ops;
|
||||
|
||||
/* es58x_fd.c. */
|
||||
extern const struct es58x_parameters es58x_fd_param;
|
||||
extern const struct es58x_operators es58x_fd_ops;
|
||||
|
||||
|
235
drivers/net/can/usb/etas_es58x/es58x_devlink.c
Normal file
235
drivers/net/can/usb/etas_es58x/es58x_devlink.c
Normal file
@ -0,0 +1,235 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/* Driver for ETAS GmbH ES58X USB CAN(-FD) Bus Interfaces.
|
||||
*
|
||||
* File es58x_devlink.c: report the product information using devlink.
|
||||
*
|
||||
* Copyright (c) 2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#include "es58x_core.h"
|
||||
|
||||
/* USB descriptor index containing the product information string. */
|
||||
#define ES58X_PROD_INFO_IDX 6
|
||||
|
||||
/**
|
||||
* es58x_parse_sw_version() - Extract boot loader or firmware version.
|
||||
* @es58x_dev: ES58X device.
|
||||
* @prod_info: USB custom string returned by the device.
|
||||
* @prefix: Select which information should be parsed. Set it to "FW"
|
||||
* to parse the firmware version or to "BL" to parse the
|
||||
* bootloader version.
|
||||
*
|
||||
* The @prod_info string contains the firmware and the bootloader
|
||||
* version number all prefixed by a magic string and concatenated with
|
||||
* other numbers. Depending on the device, the firmware (bootloader)
|
||||
* format is either "FW_Vxx.xx.xx" ("BL_Vxx.xx.xx") or "FW:xx.xx.xx"
|
||||
* ("BL:xx.xx.xx") where 'x' represents a digit. @prod_info must
|
||||
* contains the common part of those prefixes: "FW" or "BL".
|
||||
*
|
||||
* Parse @prod_info and store the version number in
|
||||
* &es58x_dev.firmware_version or &es58x_dev.bootloader_version
|
||||
* according to @prefix value.
|
||||
*
|
||||
* Return: zero on success, -EINVAL if @prefix contains an invalid
|
||||
* value and -EBADMSG if @prod_info could not be parsed.
|
||||
*/
|
||||
static int es58x_parse_sw_version(struct es58x_device *es58x_dev,
|
||||
const char *prod_info, const char *prefix)
|
||||
{
|
||||
struct es58x_sw_version *version;
|
||||
int major, minor, revision;
|
||||
|
||||
if (!strcmp(prefix, "FW"))
|
||||
version = &es58x_dev->firmware_version;
|
||||
else if (!strcmp(prefix, "BL"))
|
||||
version = &es58x_dev->bootloader_version;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Go to prefix */
|
||||
prod_info = strstr(prod_info, prefix);
|
||||
if (!prod_info)
|
||||
return -EBADMSG;
|
||||
/* Go to beginning of the version number */
|
||||
while (!isdigit(*prod_info)) {
|
||||
prod_info++;
|
||||
if (!*prod_info)
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (sscanf(prod_info, "%2u.%2u.%2u", &major, &minor, &revision) != 3)
|
||||
return -EBADMSG;
|
||||
|
||||
version->major = major;
|
||||
version->minor = minor;
|
||||
version->revision = revision;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_parse_hw_rev() - Extract hardware revision number.
|
||||
* @es58x_dev: ES58X device.
|
||||
* @prod_info: USB custom string returned by the device.
|
||||
*
|
||||
* @prod_info contains the hardware revision prefixed by a magic
|
||||
* string and conquenated together with other numbers. Depending on
|
||||
* the device, the hardware revision format is either
|
||||
* "HW_VER:axxx/xxx" or "HR:axxx/xxx" where 'a' represents a letter
|
||||
* and 'x' a digit.
|
||||
*
|
||||
* Parse @prod_info and store the hardware revision number in
|
||||
* &es58x_dev.hardware_revision.
|
||||
*
|
||||
* Return: zero on success, -EBADMSG if @prod_info could not be
|
||||
* parsed.
|
||||
*/
|
||||
static int es58x_parse_hw_rev(struct es58x_device *es58x_dev,
|
||||
const char *prod_info)
|
||||
{
|
||||
char letter;
|
||||
int major, minor;
|
||||
|
||||
/* The only occurrence of 'H' is in the hardware revision prefix. */
|
||||
prod_info = strchr(prod_info, 'H');
|
||||
if (!prod_info)
|
||||
return -EBADMSG;
|
||||
/* Go to beginning of the hardware revision */
|
||||
prod_info = strchr(prod_info, ':');
|
||||
if (!prod_info)
|
||||
return -EBADMSG;
|
||||
prod_info++;
|
||||
|
||||
if (sscanf(prod_info, "%c%3u/%3u", &letter, &major, &minor) != 3)
|
||||
return -EBADMSG;
|
||||
|
||||
es58x_dev->hardware_revision.letter = letter;
|
||||
es58x_dev->hardware_revision.major = major;
|
||||
es58x_dev->hardware_revision.minor = minor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_parse_product_info() - Parse the ES58x product information
|
||||
* string.
|
||||
* @es58x_dev: ES58X device.
|
||||
*
|
||||
* Retrieve the product information string and parse it to extract the
|
||||
* firmware version, the bootloader version and the hardware
|
||||
* revision.
|
||||
*
|
||||
* If the function fails, simply emit a log message and continue
|
||||
* because product information is not critical for the driver to
|
||||
* operate.
|
||||
*/
|
||||
void es58x_parse_product_info(struct es58x_device *es58x_dev)
|
||||
{
|
||||
char *prod_info;
|
||||
|
||||
prod_info = usb_cache_string(es58x_dev->udev, ES58X_PROD_INFO_IDX);
|
||||
if (!prod_info) {
|
||||
dev_warn(es58x_dev->dev,
|
||||
"could not retrieve the product info string\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (es58x_parse_sw_version(es58x_dev, prod_info, "FW") ||
|
||||
es58x_parse_sw_version(es58x_dev, prod_info, "BL") ||
|
||||
es58x_parse_hw_rev(es58x_dev, prod_info))
|
||||
dev_info(es58x_dev->dev,
|
||||
"could not parse product info: '%s'\n", prod_info);
|
||||
|
||||
kfree(prod_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_sw_version_is_set() - Check if the version is a valid number.
|
||||
* @sw_ver: Version number of either the firmware or the bootloader.
|
||||
*
|
||||
* If &es58x_sw_version.major, &es58x_sw_version.minor and
|
||||
* &es58x_sw_version.revision are all zero, the product string could
|
||||
* not be parsed and the version number is invalid.
|
||||
*/
|
||||
static inline bool es58x_sw_version_is_set(struct es58x_sw_version *sw_ver)
|
||||
{
|
||||
return sw_ver->major || sw_ver->minor || sw_ver->revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_hw_revision_is_set() - Check if the revision is a valid number.
|
||||
* @hw_rev: Revision number of the hardware.
|
||||
*
|
||||
* If &es58x_hw_revision.letter is the null character, the product
|
||||
* string could not be parsed and the hardware revision number is
|
||||
* invalid.
|
||||
*/
|
||||
static inline bool es58x_hw_revision_is_set(struct es58x_hw_revision *hw_rev)
|
||||
{
|
||||
return hw_rev->letter != '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* es58x_devlink_info_get() - Report the product information.
|
||||
* @devlink: Devlink.
|
||||
* @req: skb wrapper where to put requested information.
|
||||
* @extack: Unused.
|
||||
*
|
||||
* Report the firmware version, the bootloader version, the hardware
|
||||
* revision and the serial number through netlink.
|
||||
*
|
||||
* Return: zero on success, errno when any error occurs.
|
||||
*/
|
||||
static int es58x_devlink_info_get(struct devlink *devlink,
|
||||
struct devlink_info_req *req,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct es58x_device *es58x_dev = devlink_priv(devlink);
|
||||
struct es58x_sw_version *fw_ver = &es58x_dev->firmware_version;
|
||||
struct es58x_sw_version *bl_ver = &es58x_dev->bootloader_version;
|
||||
struct es58x_hw_revision *hw_rev = &es58x_dev->hardware_revision;
|
||||
char buf[max(sizeof("xx.xx.xx"), sizeof("axxx/xxx"))];
|
||||
int ret = 0;
|
||||
|
||||
if (es58x_sw_version_is_set(fw_ver)) {
|
||||
snprintf(buf, sizeof(buf), "%02u.%02u.%02u",
|
||||
fw_ver->major, fw_ver->minor, fw_ver->revision);
|
||||
ret = devlink_info_version_running_put(req,
|
||||
DEVLINK_INFO_VERSION_GENERIC_FW,
|
||||
buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (es58x_sw_version_is_set(bl_ver)) {
|
||||
snprintf(buf, sizeof(buf), "%02u.%02u.%02u",
|
||||
bl_ver->major, bl_ver->minor, bl_ver->revision);
|
||||
ret = devlink_info_version_running_put(req,
|
||||
DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER,
|
||||
buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (es58x_hw_revision_is_set(hw_rev)) {
|
||||
snprintf(buf, sizeof(buf), "%c%03u/%03u",
|
||||
hw_rev->letter, hw_rev->major, hw_rev->minor);
|
||||
ret = devlink_info_version_fixed_put(req,
|
||||
DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,
|
||||
buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devlink_info_serial_number_put(req, es58x_dev->udev->serial);
|
||||
}
|
||||
|
||||
const struct devlink_ops es58x_dl_ops = {
|
||||
.info_get = es58x_devlink_info_get,
|
||||
};
|
@ -8,12 +8,12 @@
|
||||
*
|
||||
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
|
||||
* Copyright (c) 2020 ETAS K.K.. All rights reserved.
|
||||
* Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
* Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/units.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "es58x_core.h"
|
||||
#include "es58x_fd.h"
|
||||
|
@ -299,7 +299,6 @@ struct gs_can {
|
||||
|
||||
struct net_device *netdev;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *iface;
|
||||
|
||||
struct can_bittiming_const bt_const, data_bt_const;
|
||||
unsigned int channel; /* channel number */
|
||||
@ -383,8 +382,7 @@ static int gs_cmd_reset(struct gs_can *dev)
|
||||
.mode = GS_CAN_MODE_RESET,
|
||||
};
|
||||
|
||||
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_MODE,
|
||||
return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0, &dm, sizeof(dm), 1000,
|
||||
GFP_KERNEL);
|
||||
@ -396,8 +394,7 @@ static inline int gs_usb_get_timestamp(const struct gs_can *dev,
|
||||
__le32 timestamp;
|
||||
int rc;
|
||||
|
||||
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_TIMESTAMP,
|
||||
rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_TIMESTAMP,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0,
|
||||
×tamp, sizeof(timestamp),
|
||||
@ -674,8 +671,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev)
|
||||
};
|
||||
|
||||
/* request bit timings */
|
||||
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_BITTIMING,
|
||||
return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_BITTIMING,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0, &dbt, sizeof(dbt), 1000,
|
||||
GFP_KERNEL);
|
||||
@ -698,8 +694,7 @@ static int gs_usb_set_data_bittiming(struct net_device *netdev)
|
||||
request = GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING;
|
||||
|
||||
/* request data bit timings */
|
||||
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
|
||||
request,
|
||||
return usb_control_msg_send(dev->udev, 0, request,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0, &dbt, sizeof(dbt), 1000,
|
||||
GFP_KERNEL);
|
||||
@ -941,8 +936,7 @@ static int gs_can_open(struct net_device *netdev)
|
||||
/* finally start device */
|
||||
dev->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
dm.flags = cpu_to_le32(flags);
|
||||
rc = usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_MODE,
|
||||
rc = usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0, &dm, sizeof(dm), 1000,
|
||||
GFP_KERNEL);
|
||||
@ -969,8 +963,7 @@ static int gs_usb_get_state(const struct net_device *netdev,
|
||||
struct gs_device_state ds;
|
||||
int rc;
|
||||
|
||||
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_GET_STATE,
|
||||
rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_STATE,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0,
|
||||
&ds, sizeof(ds),
|
||||
@ -1064,8 +1057,7 @@ static int gs_usb_set_identify(struct net_device *netdev, bool do_identify)
|
||||
else
|
||||
imode.mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF);
|
||||
|
||||
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_IDENTIFY,
|
||||
return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_IDENTIFY,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0, &imode, sizeof(imode), 100,
|
||||
GFP_KERNEL);
|
||||
@ -1118,8 +1110,7 @@ static int gs_usb_get_termination(struct net_device *netdev, u16 *term)
|
||||
struct gs_device_termination_state term_state;
|
||||
int rc;
|
||||
|
||||
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_GET_TERMINATION,
|
||||
rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_TERMINATION,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0,
|
||||
&term_state, sizeof(term_state), 1000,
|
||||
@ -1145,8 +1136,7 @@ static int gs_usb_set_termination(struct net_device *netdev, u16 term)
|
||||
else
|
||||
term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_OFF);
|
||||
|
||||
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_SET_TERMINATION,
|
||||
return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_SET_TERMINATION,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0,
|
||||
&term_state, sizeof(term_state), 1000,
|
||||
@ -1210,7 +1200,6 @@ static struct gs_can *gs_make_candev(unsigned int channel,
|
||||
dev->bt_const.brp_inc = le32_to_cpu(bt_const.brp_inc);
|
||||
|
||||
dev->udev = interface_to_usbdev(intf);
|
||||
dev->iface = intf;
|
||||
dev->netdev = netdev;
|
||||
dev->channel = channel;
|
||||
|
||||
|
@ -536,12 +536,11 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev)
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
|
||||
struct kvaser_usb_busparams busparams;
|
||||
int tseg1 = bt->prop_seg + bt->phase_seg1;
|
||||
int tseg2 = bt->phase_seg2;
|
||||
int sjw = bt->sjw;
|
||||
int err = -EOPNOTSUPP;
|
||||
int err;
|
||||
|
||||
busparams.bitrate = cpu_to_le32(bt->bitrate);
|
||||
busparams.sjw = (u8)sjw;
|
||||
@ -581,7 +580,6 @@ static int kvaser_usb_set_data_bittiming(struct net_device *netdev)
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
|
||||
struct can_bittiming *dbt = &priv->can.data_bittiming;
|
||||
|
||||
struct kvaser_usb_busparams busparams;
|
||||
int tseg1 = dbt->prop_seg + dbt->phase_seg1;
|
||||
int tseg2 = dbt->phase_seg2;
|
||||
|
@ -277,7 +277,6 @@ struct ucan_priv {
|
||||
|
||||
/* linux USB device structures */
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *intf;
|
||||
struct net_device *netdev;
|
||||
|
||||
/* lock for can->echo_skb (used around
|
||||
@ -1501,7 +1500,6 @@ static int ucan_probe(struct usb_interface *intf,
|
||||
|
||||
/* initialize data */
|
||||
up->udev = udev;
|
||||
up->intf = intf;
|
||||
up->netdev = netdev;
|
||||
up->intf_index = iface_desc->desc.bInterfaceNumber;
|
||||
up->in_ep_addr = in_ep_addr;
|
||||
@ -1534,9 +1532,8 @@ static int ucan_probe(struct usb_interface *intf,
|
||||
sizeof(union ucan_ctl_payload));
|
||||
if (ret > 0) {
|
||||
/* copy string while ensuring zero termination */
|
||||
strncpy(firmware_str, up->ctl_msg_buffer->raw,
|
||||
sizeof(union ucan_ctl_payload));
|
||||
firmware_str[sizeof(union ucan_ctl_payload)] = '\0';
|
||||
strscpy(firmware_str, up->ctl_msg_buffer->raw,
|
||||
sizeof(union ucan_ctl_payload) + 1);
|
||||
} else {
|
||||
strcpy(firmware_str, "unknown");
|
||||
}
|
||||
|
@ -1037,6 +1037,7 @@ char *usb_cache_string(struct usb_device *udev, int index)
|
||||
}
|
||||
return smallbuf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_cache_string);
|
||||
|
||||
/*
|
||||
* usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
|
||||
|
@ -47,7 +47,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
extern int usb_set_isoch_delay(struct usb_device *dev);
|
||||
extern int usb_get_bos_descriptor(struct usb_device *dev);
|
||||
extern void usb_release_bos_descriptor(struct usb_device *dev);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||
extern int usb_choose_configuration(struct usb_device *udev);
|
||||
extern int usb_generic_driver_probe(struct usb_device *udev);
|
||||
|
@ -1829,6 +1829,7 @@ static inline int usb_get_ptm_status(struct usb_device *dev, void *data)
|
||||
|
||||
extern int usb_string(struct usb_device *dev, int index,
|
||||
char *buf, size_t size);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
|
||||
/* wrappers that also update important state inside usbcore */
|
||||
extern int usb_clear_halt(struct usb_device *dev, int pipe);
|
||||
|
@ -621,6 +621,8 @@ enum devlink_param_generic_id {
|
||||
#define DEVLINK_INFO_VERSION_GENERIC_FW_ROCE "fw.roce"
|
||||
/* Firmware bundle identifier */
|
||||
#define DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID "fw.bundle_id"
|
||||
/* Bootloader */
|
||||
#define DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER "fw.bootloader"
|
||||
|
||||
/**
|
||||
* struct devlink_flash_update_params - Flash Update parameters
|
||||
|
@ -446,7 +446,6 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
|
||||
struct hlist_head *rcv_list;
|
||||
struct can_dev_rcv_lists *dev_rcv_lists;
|
||||
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
|
||||
int err = 0;
|
||||
|
||||
/* insert new receiver (dev,canid,mask) -> (func,data) */
|
||||
|
||||
@ -481,7 +480,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
|
||||
rcv_lists_stats->rcv_entries);
|
||||
spin_unlock_bh(&net->can.rcvlists_lock);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(can_rx_register);
|
||||
|
||||
|
@ -857,6 +857,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
|
||||
skb->dev = dev;
|
||||
skb->priority = sk->sk_priority;
|
||||
skb->mark = sk->sk_mark;
|
||||
skb->tstamp = sockc.transmit_time;
|
||||
|
||||
skb_setup_tx_timestamp(skb, sockc.tsflags);
|
||||
|
Loading…
Reference in New Issue
Block a user