Commit b2b509fb authored by David S. Miller's avatar David S. Miller

Merge tag 'linux-can-next-for-6.2-20221212' of...

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 21af0d55 47bf2b23
...@@ -17,6 +17,7 @@ properties: ...@@ -17,6 +17,7 @@ properties:
compatible: compatible:
oneOf: oneOf:
- enum: - enum:
- fsl,imx93-flexcan
- fsl,imx8qm-flexcan - fsl,imx8qm-flexcan
- fsl,imx8mp-flexcan - fsl,imx8mp-flexcan
- fsl,imx6q-flexcan - fsl,imx6q-flexcan
......
...@@ -9,9 +9,6 @@ title: Renesas R-Car CAN FD Controller ...@@ -9,9 +9,6 @@ title: Renesas R-Car CAN FD Controller
maintainers: maintainers:
- Fabrizio Castro <fabrizio.castro.jz@renesas.com> - Fabrizio Castro <fabrizio.castro.jz@renesas.com>
allOf:
- $ref: can-controller.yaml#
properties: properties:
compatible: compatible:
oneOf: oneOf:
...@@ -33,7 +30,7 @@ properties: ...@@ -33,7 +30,7 @@ properties:
- items: - items:
- enum: - enum:
- renesas,r9a07g043-canfd # RZ/G2UL - renesas,r9a07g043-canfd # RZ/G2UL and RZ/Five
- renesas,r9a07g044-canfd # RZ/G2{L,LC} - renesas,r9a07g044-canfd # RZ/G2{L,LC}
- renesas,r9a07g054-canfd # RZ/V2L - renesas,r9a07g054-canfd # RZ/V2L
- const: renesas,rzg2l-canfd # RZ/G2L family - const: renesas,rzg2l-canfd # RZ/G2L family
...@@ -77,12 +74,13 @@ properties: ...@@ -77,12 +74,13 @@ properties:
description: Maximum frequency of the CANFD clock. description: Maximum frequency of the CANFD clock.
patternProperties: patternProperties:
"^channel[01]$": "^channel[0-7]$":
type: object type: object
description: description:
The controller supports two channels and each is represented as a child The controller supports multiple channels and each is represented as a
node. Each child node supports the "status" property only, which child node. Each channel can be enabled/disabled individually.
is used to enable/disable the respective channel.
additionalProperties: false
required: required:
- compatible - compatible
...@@ -98,60 +96,73 @@ required: ...@@ -98,60 +96,73 @@ required:
- channel0 - channel0
- channel1 - channel1
if: allOf:
properties: - $ref: can-controller.yaml#
compatible:
contains: - if:
enum: properties:
- renesas,rzg2l-canfd compatible:
then: contains:
properties: enum:
interrupts: - renesas,rzg2l-canfd
items: then:
- description: CAN global error interrupt properties:
- description: CAN receive FIFO interrupt interrupts:
- description: CAN0 error interrupt items:
- description: CAN0 transmit interrupt - description: CAN global error interrupt
- description: CAN0 transmit/receive FIFO receive completion interrupt - description: CAN receive FIFO interrupt
- description: CAN1 error interrupt - description: CAN0 error interrupt
- description: CAN1 transmit interrupt - description: CAN0 transmit interrupt
- description: CAN1 transmit/receive FIFO receive completion interrupt - description: CAN0 transmit/receive FIFO receive completion interrupt
- description: CAN1 error interrupt
interrupt-names: - description: CAN1 transmit interrupt
items: - description: CAN1 transmit/receive FIFO receive completion interrupt
- const: g_err
- const: g_recc interrupt-names:
- const: ch0_err items:
- const: ch0_rec - const: g_err
- const: ch0_trx - const: g_recc
- const: ch1_err - const: ch0_err
- const: ch1_rec - const: ch0_rec
- const: ch1_trx - const: ch0_trx
- const: ch1_err
resets: - const: ch1_rec
maxItems: 2 - const: ch1_trx
reset-names: resets:
items: maxItems: 2
- const: rstp_n
- const: rstc_n reset-names:
items:
required: - const: rstp_n
- reset-names - const: rstc_n
else:
properties: required:
interrupts: - reset-names
items: else:
- description: Channel interrupt properties:
- description: Global interrupt interrupts:
items:
interrupt-names: - description: Channel interrupt
items: - description: Global interrupt
- const: ch_int
- const: g_int interrupt-names:
items:
resets: - const: ch_int
maxItems: 1 - const: g_int
resets:
maxItems: 1
- if:
not:
properties:
compatible:
contains:
const: renesas,r8a779a0-canfd
then:
patternProperties:
"^channel[2-7]$": false
unevaluatedProperties: false unevaluatedProperties: false
......
...@@ -198,6 +198,11 @@ fw.bundle_id ...@@ -198,6 +198,11 @@ fw.bundle_id
Unique identifier of the entire firmware bundle. Unique identifier of the entire firmware bundle.
fw.bootloader
-------------
Version of the bootloader.
Future work Future work
=========== ===========
......
.. 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 ...@@ -7682,6 +7682,7 @@ ETAS ES58X CAN/USB DRIVER
M: Vincent Mailhol <mailhol.vincent@wanadoo.fr> M: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
L: linux-can@vger.kernel.org L: linux-can@vger.kernel.org
S: Maintained S: Maintained
F: Documentation/networking/devlink/etas_es58x.rst
F: drivers/net/can/usb/etas_es58x/ F: drivers/net/can/usb/etas_es58x/
ETHERNET BRIDGE ETHERNET BRIDGE
......
...@@ -290,8 +290,7 @@ static int c_can_plat_probe(struct platform_device *pdev) ...@@ -290,8 +290,7 @@ static int c_can_plat_probe(struct platform_device *pdev)
goto exit; goto exit;
} }
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
addr = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(addr)) { if (IS_ERR(addr)) {
ret = PTR_ERR(addr); ret = PTR_ERR(addr);
goto exit; goto exit;
......
...@@ -23,7 +23,7 @@ config CAN_CTUCANFD_PCI ...@@ -23,7 +23,7 @@ config CAN_CTUCANFD_PCI
config CAN_CTUCANFD_PLATFORM config CAN_CTUCANFD_PLATFORM
tristate "CTU CAN-FD IP core platform (FPGA, SoC) driver" 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 select CAN_CTUCANFD
help help
The core has been tested together with OpenCores SJA1000 The core has been tested together with OpenCores SJA1000
......
...@@ -345,6 +345,15 @@ static struct flexcan_devtype_data fsl_imx8mp_devtype_data = { ...@@ -345,6 +345,15 @@ static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR, 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 = { static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX | FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX |
...@@ -532,9 +541,14 @@ static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) ...@@ -532,9 +541,14 @@ static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
ret = flexcan_stop_mode_enable_scfw(priv, true); ret = flexcan_stop_mode_enable_scfw(priv, true);
if (ret < 0) if (ret < 0)
return ret; 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, regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
1 << priv->stm.req_bit, 1 << priv->stm.req_bit); 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); return flexcan_low_power_enter_ack(priv);
...@@ -551,7 +565,7 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) ...@@ -551,7 +565,7 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
ret = flexcan_stop_mode_enable_scfw(priv, false); ret = flexcan_stop_mode_enable_scfw(priv, false);
if (ret < 0) if (ret < 0)
return ret; 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, regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
1 << priv->stm.req_bit, 0); 1 << priv->stm.req_bit, 0);
} }
...@@ -560,6 +574,12 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) ...@@ -560,6 +574,12 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
priv->write(reg_mcr, &regs->mcr); priv->write(reg_mcr, &regs->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); return flexcan_low_power_exit_ack(priv);
} }
...@@ -1974,6 +1994,8 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) ...@@ -1974,6 +1994,8 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
ret = flexcan_setup_stop_mode_scfw(pdev); ret = flexcan_setup_stop_mode_scfw(pdev);
else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR)
ret = flexcan_setup_stop_mode_gpr(pdev); ret = flexcan_setup_stop_mode_gpr(pdev);
else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE)
ret = 0;
else else
/* return 0 directly if doesn't support stop mode feature */ /* return 0 directly if doesn't support stop mode feature */
return 0; return 0;
...@@ -1992,6 +2014,7 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) ...@@ -1992,6 +2014,7 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
static const struct of_device_id flexcan_of_match[] = { static const struct of_device_id flexcan_of_match[] = {
{ .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, }, { .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
{ .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_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,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, }, { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
{ .compatible = "fsl,imx53-flexcan", .data = &fsl_imx25_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) ...@@ -2299,8 +2322,16 @@ static int __maybe_unused flexcan_noirq_suspend(struct device *device)
if (netif_running(dev)) { if (netif_running(dev)) {
int err; int err;
if (device_may_wakeup(device)) if (device_may_wakeup(device)) {
flexcan_enable_wakeup_irq(priv, true); 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); err = pm_runtime_force_suspend(device);
if (err) if (err)
......
...@@ -68,6 +68,8 @@ ...@@ -68,6 +68,8 @@
#define FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR BIT(15) #define FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR BIT(15)
/* Device supports RX via FIFO */ /* Device supports RX via FIFO */
#define FLEXCAN_QUIRK_SUPPORT_RX_FIFO BIT(16) #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 { struct flexcan_devtype_data {
u32 quirks; /* quirks needed for different IP cores */ u32 quirks; /* quirks needed for different IP cores */
......
...@@ -9,20 +9,20 @@ ...@@ -9,20 +9,20 @@
*/ */
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/can/dev.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.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" #include "m_can.h"
...@@ -369,9 +369,14 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val) ...@@ -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); 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) 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) 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, ...@@ -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 net_device_stats *stats = &dev->stats;
struct m_can_classdev *cdev = netdev_priv(dev); struct m_can_classdev *cdev = netdev_priv(dev);
struct canfd_frame *cf; struct canfd_frame *cf;
struct sk_buff *skb; struct sk_buff *skb;
struct id_and_dlc fifo_header; struct id_and_dlc fifo_header;
u32 fgi;
u32 timestamp = 0; u32 timestamp = 0;
int err; 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); err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2);
if (err) if (err)
goto out_fail; goto out_fail;
...@@ -528,9 +530,6 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs) ...@@ -528,9 +530,6 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
} }
stats->rx_packets++; 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; timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc) << 16;
m_can_receive_skb(cdev, skb, timestamp); m_can_receive_skb(cdev, skb, timestamp);
...@@ -549,7 +548,11 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) ...@@ -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); struct m_can_classdev *cdev = netdev_priv(dev);
u32 pkts = 0; u32 pkts = 0;
u32 rxfs; 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); rxfs = m_can_read(cdev, M_CAN_RXF0S);
if (!(rxfs & RXFS_FFL_MASK)) { if (!(rxfs & RXFS_FFL_MASK)) {
...@@ -557,16 +560,26 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) ...@@ -557,16 +560,26 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
return 0; return 0;
} }
while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) { rx_count = FIELD_GET(RXFS_FFL_MASK, rxfs);
err = m_can_read_fifo(dev, 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) if (err)
return err; break;
quota--; quota--;
pkts++; 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; return pkts;
} }
...@@ -900,14 +913,12 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, ...@@ -900,14 +913,12 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
return work_done; 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); struct m_can_classdev *cdev = netdev_priv(dev);
int rx_work_or_err; int rx_work_or_err;
int work_done = 0; int work_done = 0;
u32 irqstatus, psr;
irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR);
if (!irqstatus) if (!irqstatus)
goto end; goto end;
...@@ -932,13 +943,13 @@ static int m_can_rx_handler(struct net_device *dev, int quota) ...@@ -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) 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) 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) { if (irqstatus & IR_RF0N) {
rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done)); rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done));
...@@ -951,12 +962,12 @@ static int m_can_rx_handler(struct net_device *dev, int quota) ...@@ -951,12 +962,12 @@ static int m_can_rx_handler(struct net_device *dev, int quota)
return work_done; 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); struct m_can_classdev *cdev = netdev_priv(dev);
int work_done; 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 /* Don't re-enable interrupts if the driver had a fatal error
* (e.g., FIFO read failure). * (e.g., FIFO read failure).
...@@ -972,8 +983,11 @@ static int m_can_poll(struct napi_struct *napi, int quota) ...@@ -972,8 +983,11 @@ static int m_can_poll(struct napi_struct *napi, int quota)
struct net_device *dev = napi->dev; struct net_device *dev = napi->dev;
struct m_can_classdev *cdev = netdev_priv(dev); struct m_can_classdev *cdev = netdev_priv(dev);
int work_done; 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 /* Don't re-enable interrupts if the driver had a fatal error
* (e.g., FIFO read failure). * (e.g., FIFO read failure).
...@@ -1014,7 +1028,9 @@ static int m_can_echo_tx_event(struct net_device *dev) ...@@ -1014,7 +1028,9 @@ static int m_can_echo_tx_event(struct net_device *dev)
u32 txe_count = 0; u32 txe_count = 0;
u32 m_can_txefs; u32 m_can_txefs;
u32 fgi = 0; u32 fgi = 0;
int ack_fgi = -1;
int i = 0; int i = 0;
int err = 0;
unsigned int msg_mark; unsigned int msg_mark;
struct m_can_classdev *cdev = netdev_priv(dev); struct m_can_classdev *cdev = netdev_priv(dev);
...@@ -1024,34 +1040,34 @@ static int m_can_echo_tx_event(struct net_device *dev) ...@@ -1024,34 +1040,34 @@ static int m_can_echo_tx_event(struct net_device *dev)
/* Get Tx Event fifo element count */ /* Get Tx Event fifo element count */
txe_count = FIELD_GET(TXEFS_EFFL_MASK, m_can_txefs); 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 */ /* Get and process all sent elements */
for (i = 0; i < txe_count; i++) { for (i = 0; i < txe_count; i++) {
u32 txe, timestamp = 0; 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 */ /* get message marker, timestamp */
err = m_can_txe_fifo_read(cdev, fgi, 4, &txe); err = m_can_txe_fifo_read(cdev, fgi, 4, &txe);
if (err) { if (err) {
netdev_err(dev, "TXE FIFO read returned %d\n", err); netdev_err(dev, "TXE FIFO read returned %d\n", err);
return err; break;
} }
msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe); msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe);
timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe) << 16; timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe) << 16;
/* ack txe element */ ack_fgi = fgi;
m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, fgi = (++fgi >= cdev->mcfg[MRAM_TXE].num ? 0 : fgi);
fgi));
/* update stats */ /* update stats */
m_can_tx_update_stats(cdev, msg_mark, timestamp); 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) 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) ...@@ -1083,7 +1099,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
m_can_disable_all_interrupts(cdev); m_can_disable_all_interrupts(cdev);
if (!cdev->is_peripheral) if (!cdev->is_peripheral)
napi_schedule(&cdev->napi); napi_schedule(&cdev->napi);
else if (m_can_rx_peripheral(dev) < 0) else if (m_can_rx_peripheral(dev, ir) < 0)
goto out_fail; goto out_fail;
} }
...@@ -1243,10 +1259,17 @@ static int m_can_set_bittiming(struct net_device *dev) ...@@ -1243,10 +1259,17 @@ static int m_can_set_bittiming(struct net_device *dev)
* - setup bittiming * - setup bittiming
* - configure timestamp generation * - 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); struct m_can_classdev *cdev = netdev_priv(dev);
u32 cccr, test; 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); m_can_config_endisable(cdev, true);
...@@ -1370,18 +1393,25 @@ static void m_can_chip_config(struct net_device *dev) ...@@ -1370,18 +1393,25 @@ static void m_can_chip_config(struct net_device *dev)
if (cdev->ops->init) if (cdev->ops->init)
cdev->ops->init(cdev); 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); struct m_can_classdev *cdev = netdev_priv(dev);
int ret;
/* basic m_can configuration */ /* 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; cdev->can.state = CAN_STATE_ERROR_ACTIVE;
m_can_enable_all_interrupts(cdev); m_can_enable_all_interrupts(cdev);
return 0;
} }
static int m_can_set_mode(struct net_device *dev, enum can_mode mode) 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) ...@@ -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 sk_buff *skb = cdev->tx_skb;
struct id_and_dlc fifo_header; struct id_and_dlc fifo_header;
u32 cccr, fdflags; u32 cccr, fdflags;
u32 txfqs;
int err; int err;
int putidx; int putidx;
...@@ -1651,8 +1682,10 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) ...@@ -1651,8 +1682,10 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
} else { } else {
/* Transmit routine for version >= v3.1.x */ /* Transmit routine for version >= v3.1.x */
txfqs = m_can_read(cdev, M_CAN_TXFQS);
/* Check if FIFO full */ /* Check if FIFO full */
if (m_can_tx_fifo_full(cdev)) { if (_m_can_tx_fifo_full(txfqs)) {
/* This shouldn't happen */ /* This shouldn't happen */
netif_stop_queue(dev); netif_stop_queue(dev);
netdev_warn(dev, netdev_warn(dev,
...@@ -1668,8 +1701,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) ...@@ -1668,8 +1701,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
} }
/* get put index for frame */ /* get put index for frame */
putidx = FIELD_GET(TXFQS_TFQPI_MASK, putidx = FIELD_GET(TXFQS_TFQPI_MASK, txfqs);
m_can_read(cdev, M_CAN_TXFQS));
/* Construct DLC Field, with CAN-FD configuration. /* Construct DLC Field, with CAN-FD configuration.
* Use the put index of the fifo as the message marker, * Use the put index of the fifo as the message marker,
...@@ -1809,7 +1841,9 @@ static int m_can_open(struct net_device *dev) ...@@ -1809,7 +1841,9 @@ static int m_can_open(struct net_device *dev)
} }
/* start the m_can controller */ /* start the m_can controller */
m_can_start(dev); err = m_can_start(dev);
if (err)
goto exit_irq_fail;
if (!cdev->is_peripheral) if (!cdev->is_peripheral)
napi_enable(&cdev->napi); napi_enable(&cdev->napi);
...@@ -2068,9 +2102,13 @@ int m_can_class_resume(struct device *dev) ...@@ -2068,9 +2102,13 @@ int m_can_class_resume(struct device *dev)
ret = m_can_clk_start(cdev); ret = m_can_clk_start(cdev);
if (ret) if (ret)
return 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_device_attach(ndev);
netif_start_queue(ndev); netif_start_queue(ndev);
} }
......
...@@ -7,27 +7,27 @@ ...@@ -7,27 +7,27 @@
#define _CAN_M_CAN_H_ #define _CAN_M_CAN_H_
#include <linux/can/core.h> #include <linux/can/core.h>
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h> #include <linux/can/rx-offload.h>
#include <linux/clk.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/freezer.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/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.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/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 */ /* m_can lec values */
enum m_can_lec_type { enum m_can_lec_type {
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
// //
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
#include <linux/platform_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include "m_can.h" #include "m_can.h"
...@@ -140,10 +140,6 @@ static int m_can_plat_probe(struct platform_device *pdev) ...@@ -140,10 +140,6 @@ static int m_can_plat_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mcan_class); platform_set_drvdata(pdev, mcan_class);
ret = m_can_init_ram(mcan_class);
if (ret)
goto probe_fail;
pm_runtime_enable(mcan_class->dev); pm_runtime_enable(mcan_class->dev);
ret = m_can_class_register(mcan_class); ret = m_can_class_register(mcan_class);
if (ret) if (ret)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#define TCAN4X5X_DEV_ID1 0x04 #define TCAN4X5X_DEV_ID1 0x04
#define TCAN4X5X_REV 0x08 #define TCAN4X5X_REV 0x08
#define TCAN4X5X_STATUS 0x0C #define TCAN4X5X_STATUS 0x0C
#define TCAN4X5X_ERROR_STATUS 0x10 #define TCAN4X5X_ERROR_STATUS_MASK 0x10
#define TCAN4X5X_CONTROL 0x14 #define TCAN4X5X_CONTROL 0x14
#define TCAN4X5X_CONFIG 0x800 #define TCAN4X5X_CONFIG 0x800
...@@ -204,17 +204,7 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev) ...@@ -204,17 +204,7 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
if (ret) if (ret)
return ret; return ret;
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG, return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
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,
TCAN4X5X_CLEAR_ALL_INT); TCAN4X5X_CLEAR_ALL_INT);
} }
...@@ -234,8 +224,8 @@ static int tcan4x5x_init(struct m_can_classdev *cdev) ...@@ -234,8 +224,8 @@ static int tcan4x5x_init(struct m_can_classdev *cdev)
if (ret) if (ret)
return ret; return ret;
/* Zero out the MCAN buffers */ ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS_MASK,
ret = m_can_init_ram(cdev); TCAN4X5X_CLEAR_ALL_INT);
if (ret) if (ret)
return ret; return ret;
......
...@@ -90,16 +90,47 @@ static int tcan4x5x_regmap_read(void *context, ...@@ -90,16 +90,47 @@ static int tcan4x5x_regmap_read(void *context,
return 0; return 0;
} }
static const struct regmap_range tcan4x5x_reg_table_yes_range[] = { static const struct regmap_range tcan4x5x_reg_table_wr_range[] = {
regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */ /* Device ID and SPI Registers */
regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/ 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(0x1000, 0x10fc), /* M_CAN */
regmap_reg_range(0x8000, 0x87fc), /* MRAM */ regmap_reg_range(0x8000, 0x87fc), /* MRAM */
}; };
static const struct regmap_access_table tcan4x5x_reg_table = { static const struct regmap_access_table tcan4x5x_reg_table_wr = {
.yes_ranges = tcan4x5x_reg_table_yes_range, .yes_ranges = tcan4x5x_reg_table_wr_range,
.n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_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 = { static const struct regmap_config tcan4x5x_regmap = {
...@@ -107,8 +138,8 @@ static const struct regmap_config tcan4x5x_regmap = { ...@@ -107,8 +138,8 @@ static const struct regmap_config tcan4x5x_regmap = {
.reg_stride = 4, .reg_stride = 4,
.pad_bits = 8, .pad_bits = 8,
.val_bits = 32, .val_bits = 32,
.wr_table = &tcan4x5x_reg_table, .wr_table = &tcan4x5x_reg_table_wr,
.rd_table = &tcan4x5x_reg_table, .rd_table = &tcan4x5x_reg_table_rd,
.max_register = TCAN4X5X_MAX_REGISTER, .max_register = TCAN4X5X_MAX_REGISTER,
.cache_type = REGCACHE_NONE, .cache_type = REGCACHE_NONE,
.read_flag_mask = (__force unsigned long) .read_flag_mask = (__force unsigned long)
......
...@@ -41,12 +41,6 @@ ...@@ -41,12 +41,6 @@
#define RCANFD_DRV_NAME "rcar_canfd" #define RCANFD_DRV_NAME "rcar_canfd"
enum rcanfd_chip_id {
RENESAS_RCAR_GEN3 = 0,
RENESAS_RZG2L,
RENESAS_R8A779A0,
};
/* Global register bits */ /* Global register bits */
/* RSCFDnCFDGRMCFG */ /* RSCFDnCFDGRMCFG */
...@@ -522,6 +516,14 @@ enum rcar_canfd_fcanclk { ...@@ -522,6 +516,14 @@ enum rcar_canfd_fcanclk {
struct rcar_canfd_global; 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 */ /* Channel priv data */
struct rcar_canfd_channel { struct rcar_canfd_channel {
struct can_priv can; /* Must be the first member */ struct can_priv can; /* Must be the first member */
...@@ -547,8 +549,7 @@ struct rcar_canfd_global { ...@@ -547,8 +549,7 @@ struct rcar_canfd_global {
bool fdmode; /* CAN FD or Classical CAN only mode */ bool fdmode; /* CAN FD or Classical CAN only mode */
struct reset_control *rstc1; struct reset_control *rstc1;
struct reset_control *rstc2; struct reset_control *rstc2;
enum rcanfd_chip_id chip_id; const struct rcar_canfd_hw_info *info;
u32 max_channels;
}; };
/* CAN FD mode nominal rate constants */ /* CAN FD mode nominal rate constants */
...@@ -590,10 +591,28 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { ...@@ -590,10 +591,28 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = {
.brp_inc = 1, .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 */ /* Helper functions */
static inline bool is_v3u(struct rcar_canfd_global *gpriv) 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, 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) ...@@ -721,7 +740,7 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv)
rcar_canfd_set_mode(gpriv); rcar_canfd_set_mode(gpriv);
/* Transition all Channels to reset mode */ /* 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, rcar_canfd_clear_bit(gpriv->base,
RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR); RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR);
...@@ -762,7 +781,7 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv) ...@@ -762,7 +781,7 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv)
rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg); rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg);
/* Channel configuration settings */ /* 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), rcar_canfd_set_bit(gpriv->base, RCANFD_CCTR(ch),
RCANFD_CCTR_ERRD); RCANFD_CCTR_ERRD);
rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch), 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) ...@@ -1142,7 +1161,7 @@ static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id)
struct rcar_canfd_global *gpriv = dev_id; struct rcar_canfd_global *gpriv = dev_id;
u32 ch; 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); rcar_canfd_handle_global_err(gpriv, ch);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -1174,7 +1193,7 @@ static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_i ...@@ -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; struct rcar_canfd_global *gpriv = dev_id;
u32 ch; 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); rcar_canfd_handle_global_receive(gpriv, ch);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -1188,7 +1207,7 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) ...@@ -1188,7 +1207,7 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id)
/* Global error interrupts still indicate a condition specific /* Global error interrupts still indicate a condition specific
* to a channel. RxFIFO interrupt is a global interrupt. * 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_err(gpriv, ch);
rcar_canfd_handle_global_receive(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) ...@@ -1284,7 +1303,7 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id)
u32 ch; u32 ch;
/* Common FIFO is a per channel resource */ /* 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_err(gpriv, ch);
rcar_canfd_handle_channel_tx(gpriv, ch); rcar_canfd_handle_channel_tx(gpriv, ch);
} }
...@@ -1696,6 +1715,7 @@ static const struct ethtool_ops rcar_canfd_ethtool_ops = { ...@@ -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, static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch,
u32 fcan_freq) u32 fcan_freq)
{ {
const struct rcar_canfd_hw_info *info = gpriv->info;
struct platform_device *pdev = gpriv->pdev; struct platform_device *pdev = gpriv->pdev;
struct rcar_canfd_channel *priv; struct rcar_canfd_channel *priv;
struct net_device *ndev; struct net_device *ndev;
...@@ -1718,7 +1738,7 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, ...@@ -1718,7 +1738,7 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch,
priv->can.clock.freq = fcan_freq; priv->can.clock.freq = fcan_freq;
dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.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; char *irq_name;
int err_irq; int err_irq;
int tx_irq; int tx_irq;
...@@ -1818,6 +1838,7 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch) ...@@ -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) static int rcar_canfd_probe(struct platform_device *pdev)
{ {
const struct rcar_canfd_hw_info *info;
void __iomem *addr; void __iomem *addr;
u32 sts, ch, fcan_freq; u32 sts, ch, fcan_freq;
struct rcar_canfd_global *gpriv; struct rcar_canfd_global *gpriv;
...@@ -1826,18 +1847,15 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -1826,18 +1847,15 @@ static int rcar_canfd_probe(struct platform_device *pdev)
int err, ch_irq, g_irq; int err, ch_irq, g_irq;
int g_err_irq, g_recc_irq; int g_err_irq, g_recc_irq;
bool fdmode = true; /* CAN FD only mode - default */ bool fdmode = true; /* CAN FD only mode - default */
enum rcanfd_chip_id chip_id;
int max_channels;
char name[9] = "channelX"; char name[9] = "channelX";
int i; int i;
chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); info = of_device_get_match_data(&pdev->dev);
max_channels = chip_id == RENESAS_R8A779A0 ? 8 : 2;
if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd"))
fdmode = false; /* Classical CAN only mode */ 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; name[7] = '0' + i;
of_child = of_get_child_by_name(pdev->dev.of_node, name); of_child = of_get_child_by_name(pdev->dev.of_node, name);
if (of_child && of_device_is_available(of_child)) if (of_child && of_device_is_available(of_child))
...@@ -1845,7 +1863,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -1845,7 +1863,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
of_node_put(of_child); 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"); ch_irq = platform_get_irq_byname_optional(pdev, "ch_int");
if (ch_irq < 0) { if (ch_irq < 0) {
/* For backward compatibility get irq by index */ /* For backward compatibility get irq by index */
...@@ -1879,8 +1897,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -1879,8 +1897,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
gpriv->pdev = pdev; gpriv->pdev = pdev;
gpriv->channels_mask = channels_mask; gpriv->channels_mask = channels_mask;
gpriv->fdmode = fdmode; gpriv->fdmode = fdmode;
gpriv->chip_id = chip_id; gpriv->info = info;
gpriv->max_channels = max_channels;
gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev, gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev,
"rstp_n"); "rstp_n");
...@@ -1917,9 +1934,9 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -1917,9 +1934,9 @@ static int rcar_canfd_probe(struct platform_device *pdev)
} }
fcan_freq = clk_get_rate(gpriv->can_clk); 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 */ /* 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); addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(addr)) { if (IS_ERR(addr)) {
...@@ -1929,7 +1946,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -1929,7 +1946,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
gpriv->base = addr; gpriv->base = addr;
/* Request IRQ that's common for both channels */ /* 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, err = devm_request_irq(&pdev->dev, ch_irq,
rcar_canfd_channel_interrupt, 0, rcar_canfd_channel_interrupt, 0,
"canfd.ch_int", gpriv); "canfd.ch_int", gpriv);
...@@ -1995,7 +2012,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -1995,7 +2012,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
rcar_canfd_configure_controller(gpriv); rcar_canfd_configure_controller(gpriv);
/* Configure per channel attributes */ /* 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 */ /* Configure Channel's Rx fifo */
rcar_canfd_configure_rx(gpriv, ch); rcar_canfd_configure_rx(gpriv, ch);
...@@ -2021,7 +2038,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -2021,7 +2038,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
goto fail_mode; 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); err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq);
if (err) if (err)
goto fail_channel; goto fail_channel;
...@@ -2033,7 +2050,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) ...@@ -2033,7 +2050,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
return 0; return 0;
fail_channel: 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); rcar_canfd_channel_remove(gpriv, ch);
fail_mode: fail_mode:
rcar_canfd_disable_global_interrupts(gpriv); rcar_canfd_disable_global_interrupts(gpriv);
...@@ -2054,7 +2071,7 @@ static int rcar_canfd_remove(struct platform_device *pdev) ...@@ -2054,7 +2071,7 @@ static int rcar_canfd_remove(struct platform_device *pdev)
rcar_canfd_reset_controller(gpriv); rcar_canfd_reset_controller(gpriv);
rcar_canfd_disable_global_interrupts(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_disable_channel_interrupts(gpriv->ch[ch]);
rcar_canfd_channel_remove(gpriv, ch); rcar_canfd_channel_remove(gpriv, ch);
} }
...@@ -2082,9 +2099,9 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend, ...@@ -2082,9 +2099,9 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
rcar_canfd_resume); rcar_canfd_resume);
static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = { static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = {
{ .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 }, { .compatible = "renesas,rcar-gen3-canfd", .data = &rcar_gen3_hw_info },
{ .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L }, { .compatible = "renesas,rzg2l-canfd", .data = &rzg2l_hw_info },
{ .compatible = "renesas,r8a779a0-canfd", .data = (void *)RENESAS_R8A779A0 }, { .compatible = "renesas,r8a779a0-canfd", .data = &r8a779a0_hw_info },
{ } { }
}; };
......
...@@ -30,6 +30,7 @@ config CAN_ESD_USB ...@@ -30,6 +30,7 @@ config CAN_ESD_USB
config CAN_ETAS_ES58X config CAN_ETAS_ES58X
tristate "ETAS ES58X CAN/USB interfaces" tristate "ETAS ES58X CAN/USB interfaces"
select CRC16 select CRC16
select NET_DEVLINK
help help
This driver supports the ES581.4, ES582.1 and ES584.1 interfaces This driver supports the ES581.4, ES582.1 and ES584.1 interfaces
from ETAS GmbH (https://www.etas.com/en/products/es58x.php). from ETAS GmbH (https://www.etas.com/en/products/es58x.php).
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CAN_ETAS_ES58X) += etas_es58x.o 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 @@ ...@@ -6,12 +6,12 @@
* *
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
* Copyright (c) 2020 ETAS K.K.. 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/kernel.h>
#include <linux/units.h> #include <linux/units.h>
#include <asm/unaligned.h>
#include "es58x_core.h" #include "es58x_core.h"
#include "es581_4.h" #include "es581_4.h"
......
...@@ -7,15 +7,16 @@ ...@@ -7,15 +7,16 @@
* *
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
* Copyright (c) 2020 ETAS K.K.. 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/ethtool.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/crc16.h> #include <net/devlink.h>
#include <asm/unaligned.h>
#include "es58x_core.h" #include "es58x_core.h"
...@@ -2038,10 +2039,16 @@ static int es58x_set_mode(struct net_device *netdev, enum can_mode mode) ...@@ -2038,10 +2039,16 @@ static int es58x_set_mode(struct net_device *netdev, enum can_mode mode)
* @es58x_dev: ES58X device. * @es58x_dev: ES58X device.
* @priv: ES58X private parameters related to the network device. * @priv: ES58X private parameters related to the network device.
* @channel_idx: Index of 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, static int es58x_init_priv(struct es58x_device *es58x_dev,
struct es58x_priv *priv, int channel_idx) 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; const struct es58x_parameters *param = es58x_dev->param;
struct can_priv *can = &priv->can; struct can_priv *can = &priv->can;
...@@ -2060,6 +2067,10 @@ static void es58x_init_priv(struct es58x_device *es58x_dev, ...@@ -2060,6 +2067,10 @@ static void es58x_init_priv(struct es58x_device *es58x_dev,
can->state = CAN_STATE_STOPPED; can->state = CAN_STATE_STOPPED;
can->ctrlmode_supported = param->ctrlmode_supported; can->ctrlmode_supported = param->ctrlmode_supported;
can->do_set_mode = es58x_set_mode; 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) ...@@ -2083,7 +2094,10 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx)
} }
SET_NETDEV_DEV(netdev, dev); SET_NETDEV_DEV(netdev, dev);
es58x_dev->netdev[channel_idx] = netdev; 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->netdev_ops = &es58x_netdev_ops;
netdev->ethtool_ops = &es58x_ethtool_ops; netdev->ethtool_ops = &es58x_ethtool_ops;
...@@ -2091,16 +2105,20 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx) ...@@ -2091,16 +2105,20 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx)
netdev->dev_port = channel_idx; netdev->dev_port = channel_idx;
ret = register_candev(netdev); ret = register_candev(netdev);
if (ret) { if (ret)
es58x_dev->netdev[channel_idx] = NULL; goto devlink_port_unregister;
free_candev(netdev);
return ret;
}
netdev_queue_set_dql_min_limit(netdev_get_tx_queue(netdev, 0), netdev_queue_set_dql_min_limit(netdev_get_tx_queue(netdev, 0),
es58x_dev->param->dql_min_limit); es58x_dev->param->dql_min_limit);
return ret; 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) ...@@ -2117,53 +2135,12 @@ static void es58x_free_netdevs(struct es58x_device *es58x_dev)
if (!netdev) if (!netdev)
continue; continue;
unregister_candev(netdev); unregister_candev(netdev);
devlink_port_unregister(&es58x_priv(netdev)->devlink_port);
es58x_dev->netdev[i] = NULL; es58x_dev->netdev[i] = NULL;
free_candev(netdev); 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. * es58x_init_es58x_dev() - Initialize the ES58X device.
* @intf: USB interface. * @intf: USB interface.
...@@ -2177,6 +2154,7 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf, ...@@ -2177,6 +2154,7 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf,
{ {
struct device *dev = &intf->dev; struct device *dev = &intf->dev;
struct es58x_device *es58x_dev; struct es58x_device *es58x_dev;
struct devlink *devlink;
const struct es58x_parameters *param; const struct es58x_parameters *param;
const struct es58x_operators *ops; const struct es58x_operators *ops;
struct usb_device *udev = interface_to_usbdev(intf); struct usb_device *udev = interface_to_usbdev(intf);
...@@ -2199,11 +2177,12 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf, ...@@ -2199,11 +2177,12 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf,
ops = &es581_4_ops; ops = &es581_4_ops;
} }
es58x_dev = devm_kzalloc(dev, es58x_sizeof_es58x_device(param), devlink = devlink_alloc(&es58x_dl_ops, es58x_sizeof_es58x_device(param),
GFP_KERNEL); dev);
if (!es58x_dev) if (!devlink)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
es58x_dev = devlink_priv(devlink);
es58x_dev->param = param; es58x_dev->param = param;
es58x_dev->ops = ops; es58x_dev->ops = ops;
es58x_dev->dev = dev; es58x_dev->dev = dev;
...@@ -2240,25 +2219,24 @@ static int es58x_probe(struct usb_interface *intf, ...@@ -2240,25 +2219,24 @@ static int es58x_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct es58x_device *es58x_dev; struct es58x_device *es58x_dev;
int ch_idx, ret; int ch_idx;
es58x_dev = es58x_init_es58x_dev(intf, id->driver_info); es58x_dev = es58x_init_es58x_dev(intf, id->driver_info);
if (IS_ERR(es58x_dev)) if (IS_ERR(es58x_dev))
return PTR_ERR(es58x_dev); return PTR_ERR(es58x_dev);
ret = es58x_get_product_info(es58x_dev); es58x_parse_product_info(es58x_dev);
if (ret) devlink_register(priv_to_devlink(es58x_dev));
return ret;
for (ch_idx = 0; ch_idx < es58x_dev->num_can_ch; ch_idx++) { 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) { if (ret) {
es58x_free_netdevs(es58x_dev); es58x_free_netdevs(es58x_dev);
return ret; return ret;
} }
} }
return ret; return 0;
} }
/** /**
...@@ -2275,8 +2253,10 @@ static void es58x_disconnect(struct usb_interface *intf) ...@@ -2275,8 +2253,10 @@ static void es58x_disconnect(struct usb_interface *intf)
dev_info(&intf->dev, "Disconnecting %s %s\n", dev_info(&intf->dev, "Disconnecting %s %s\n",
es58x_dev->udev->manufacturer, es58x_dev->udev->product); es58x_dev->udev->manufacturer, es58x_dev->udev->product);
devlink_unregister(priv_to_devlink(es58x_dev));
es58x_free_netdevs(es58x_dev); es58x_free_netdevs(es58x_dev);
es58x_free_urbs(es58x_dev); es58x_free_urbs(es58x_dev);
devlink_free(priv_to_devlink(es58x_dev));
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
} }
......
...@@ -6,17 +6,18 @@ ...@@ -6,17 +6,18 @@
* *
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
* Copyright (c) 2020 ETAS K.K.. 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__ #ifndef __ES58X_COMMON_H__
#define __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.h>
#include <linux/can/dev.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 "es581_4.h"
#include "es58x_fd.h" #include "es58x_fd.h"
...@@ -230,6 +231,7 @@ union es58x_urb_cmd { ...@@ -230,6 +231,7 @@ union es58x_urb_cmd {
* @can: struct can_priv must be the first member (Socket CAN relies * @can: struct can_priv must be the first member (Socket CAN relies
* on the fact that function netdev_priv() returns a pointer to * on the fact that function netdev_priv() returns a pointer to
* a struct can_priv). * a struct can_priv).
* @devlink_port: devlink instance for the network interface.
* @es58x_dev: pointer to the corresponding ES58X device. * @es58x_dev: pointer to the corresponding ES58X device.
* @tx_urb: Used as a buffer to concatenate the TX messages and to do * @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 * a bulk send. Please refer to es58x_start_xmit() for more
...@@ -255,6 +257,7 @@ union es58x_urb_cmd { ...@@ -255,6 +257,7 @@ union es58x_urb_cmd {
*/ */
struct es58x_priv { struct es58x_priv {
struct can_priv can; struct can_priv can;
struct devlink_port devlink_port;
struct es58x_device *es58x_dev; struct es58x_device *es58x_dev;
struct urb *tx_urb; struct urb *tx_urb;
...@@ -356,6 +359,39 @@ struct es58x_operators { ...@@ -356,6 +359,39 @@ struct es58x_operators {
int (*get_timestamp)(struct es58x_device *es58x_dev); 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. * struct es58x_device - All information specific to an ES58X device.
* @dev: Device information. * @dev: Device information.
...@@ -373,6 +409,9 @@ struct es58x_operators { ...@@ -373,6 +409,9 @@ struct es58x_operators {
* queue wake/stop logic should prevent this URB from getting * queue wake/stop logic should prevent this URB from getting
* empty. Please refer to es58x_get_tx_urb() for more details. * empty. Please refer to es58x_get_tx_urb() for more details.
* @tx_urbs_idle_cnt: number of urbs in @tx_urbs_idle. * @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() * @ktime_req_ns: kernel timestamp when es58x_set_realtime_diff_ns()
* was called. * was called.
* @realtime_diff_ns: difference in nanoseconds between the clocks of * @realtime_diff_ns: difference in nanoseconds between the clocks of
...@@ -408,6 +447,10 @@ struct es58x_device { ...@@ -408,6 +447,10 @@ struct es58x_device {
struct usb_anchor tx_urbs_idle; struct usb_anchor tx_urbs_idle;
atomic_t tx_urbs_idle_cnt; 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; u64 ktime_req_ns;
s64 realtime_diff_ns; s64 realtime_diff_ns;
...@@ -674,6 +717,7 @@ static inline enum es58x_flag es58x_get_flags(const struct sk_buff *skb) ...@@ -674,6 +717,7 @@ static inline enum es58x_flag es58x_get_flags(const struct sk_buff *skb)
return es58x_flags; return es58x_flags;
} }
/* es58x_core.c. */
int es58x_can_get_echo_skb(struct net_device *netdev, u32 packet_idx, int es58x_can_get_echo_skb(struct net_device *netdev, u32 packet_idx,
u64 *tstamps, unsigned int pkts); u64 *tstamps, unsigned int pkts);
int es58x_tx_ack_msg(struct net_device *netdev, u16 tx_free_entries, 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, ...@@ -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, int es58x_send_msg(struct es58x_device *es58x_dev, u8 cmd_type, u8 cmd_id,
const void *msg, u16 cmd_len, int channel_idx); 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_parameters es581_4_param;
extern const struct es58x_operators es581_4_ops; extern const struct es58x_operators es581_4_ops;
/* es58x_fd.c. */
extern const struct es58x_parameters es58x_fd_param; extern const struct es58x_parameters es58x_fd_param;
extern const struct es58x_operators es58x_fd_ops; extern const struct es58x_operators es58x_fd_ops;
......
// 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 @@ ...@@ -8,12 +8,12 @@
* *
* Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
* Copyright (c) 2020 ETAS K.K.. 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/kernel.h>
#include <linux/units.h> #include <linux/units.h>
#include <asm/unaligned.h>
#include "es58x_core.h" #include "es58x_core.h"
#include "es58x_fd.h" #include "es58x_fd.h"
......
...@@ -299,7 +299,6 @@ struct gs_can { ...@@ -299,7 +299,6 @@ struct gs_can {
struct net_device *netdev; struct net_device *netdev;
struct usb_device *udev; struct usb_device *udev;
struct usb_interface *iface;
struct can_bittiming_const bt_const, data_bt_const; struct can_bittiming_const bt_const, data_bt_const;
unsigned int channel; /* channel number */ unsigned int channel; /* channel number */
...@@ -383,8 +382,7 @@ static int gs_cmd_reset(struct gs_can *dev) ...@@ -383,8 +382,7 @@ static int gs_cmd_reset(struct gs_can *dev)
.mode = GS_CAN_MODE_RESET, .mode = GS_CAN_MODE_RESET,
}; };
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE,
GS_USB_BREQ_MODE,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, &dm, sizeof(dm), 1000, dev->channel, 0, &dm, sizeof(dm), 1000,
GFP_KERNEL); GFP_KERNEL);
...@@ -396,8 +394,7 @@ static inline int gs_usb_get_timestamp(const struct gs_can *dev, ...@@ -396,8 +394,7 @@ static inline int gs_usb_get_timestamp(const struct gs_can *dev,
__le32 timestamp; __le32 timestamp;
int rc; int rc;
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_TIMESTAMP,
GS_USB_BREQ_TIMESTAMP,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, dev->channel, 0,
&timestamp, sizeof(timestamp), &timestamp, sizeof(timestamp),
...@@ -674,8 +671,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev) ...@@ -674,8 +671,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev)
}; };
/* request bit timings */ /* request bit timings */
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_BITTIMING,
GS_USB_BREQ_BITTIMING,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, &dbt, sizeof(dbt), 1000, dev->channel, 0, &dbt, sizeof(dbt), 1000,
GFP_KERNEL); GFP_KERNEL);
...@@ -698,8 +694,7 @@ static int gs_usb_set_data_bittiming(struct net_device *netdev) ...@@ -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 = GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING;
/* request data bit timings */ /* request data bit timings */
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, return usb_control_msg_send(dev->udev, 0, request,
request,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, &dbt, sizeof(dbt), 1000, dev->channel, 0, &dbt, sizeof(dbt), 1000,
GFP_KERNEL); GFP_KERNEL);
...@@ -941,8 +936,7 @@ static int gs_can_open(struct net_device *netdev) ...@@ -941,8 +936,7 @@ static int gs_can_open(struct net_device *netdev)
/* finally start device */ /* finally start device */
dev->can.state = CAN_STATE_ERROR_ACTIVE; dev->can.state = CAN_STATE_ERROR_ACTIVE;
dm.flags = cpu_to_le32(flags); dm.flags = cpu_to_le32(flags);
rc = usb_control_msg_send(interface_to_usbdev(dev->iface), 0, rc = usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE,
GS_USB_BREQ_MODE,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, &dm, sizeof(dm), 1000, dev->channel, 0, &dm, sizeof(dm), 1000,
GFP_KERNEL); GFP_KERNEL);
...@@ -969,8 +963,7 @@ static int gs_usb_get_state(const struct net_device *netdev, ...@@ -969,8 +963,7 @@ static int gs_usb_get_state(const struct net_device *netdev,
struct gs_device_state ds; struct gs_device_state ds;
int rc; int rc;
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_STATE,
GS_USB_BREQ_GET_STATE,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, dev->channel, 0,
&ds, sizeof(ds), &ds, sizeof(ds),
...@@ -1064,8 +1057,7 @@ static int gs_usb_set_identify(struct net_device *netdev, bool do_identify) ...@@ -1064,8 +1057,7 @@ static int gs_usb_set_identify(struct net_device *netdev, bool do_identify)
else else
imode.mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF); imode.mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF);
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_IDENTIFY,
GS_USB_BREQ_IDENTIFY,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, &imode, sizeof(imode), 100, dev->channel, 0, &imode, sizeof(imode), 100,
GFP_KERNEL); GFP_KERNEL);
...@@ -1118,8 +1110,7 @@ static int gs_usb_get_termination(struct net_device *netdev, u16 *term) ...@@ -1118,8 +1110,7 @@ static int gs_usb_get_termination(struct net_device *netdev, u16 *term)
struct gs_device_termination_state term_state; struct gs_device_termination_state term_state;
int rc; int rc;
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_TERMINATION,
GS_USB_BREQ_GET_TERMINATION,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, dev->channel, 0,
&term_state, sizeof(term_state), 1000, &term_state, sizeof(term_state), 1000,
...@@ -1145,8 +1136,7 @@ static int gs_usb_set_termination(struct net_device *netdev, u16 term) ...@@ -1145,8 +1136,7 @@ static int gs_usb_set_termination(struct net_device *netdev, u16 term)
else else
term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_OFF); term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_OFF);
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_SET_TERMINATION,
GS_USB_BREQ_SET_TERMINATION,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0, dev->channel, 0,
&term_state, sizeof(term_state), 1000, &term_state, sizeof(term_state), 1000,
...@@ -1210,7 +1200,6 @@ static struct gs_can *gs_make_candev(unsigned int channel, ...@@ -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->bt_const.brp_inc = le32_to_cpu(bt_const.brp_inc);
dev->udev = interface_to_usbdev(intf); dev->udev = interface_to_usbdev(intf);
dev->iface = intf;
dev->netdev = netdev; dev->netdev = netdev;
dev->channel = channel; dev->channel = channel;
......
...@@ -536,12 +536,11 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev) ...@@ -536,12 +536,11 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev)
struct kvaser_usb *dev = priv->dev; struct kvaser_usb *dev = priv->dev;
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
struct can_bittiming *bt = &priv->can.bittiming; struct can_bittiming *bt = &priv->can.bittiming;
struct kvaser_usb_busparams busparams; struct kvaser_usb_busparams busparams;
int tseg1 = bt->prop_seg + bt->phase_seg1; int tseg1 = bt->prop_seg + bt->phase_seg1;
int tseg2 = bt->phase_seg2; int tseg2 = bt->phase_seg2;
int sjw = bt->sjw; int sjw = bt->sjw;
int err = -EOPNOTSUPP; int err;
busparams.bitrate = cpu_to_le32(bt->bitrate); busparams.bitrate = cpu_to_le32(bt->bitrate);
busparams.sjw = (u8)sjw; busparams.sjw = (u8)sjw;
...@@ -581,7 +580,6 @@ static int kvaser_usb_set_data_bittiming(struct net_device *netdev) ...@@ -581,7 +580,6 @@ static int kvaser_usb_set_data_bittiming(struct net_device *netdev)
struct kvaser_usb *dev = priv->dev; struct kvaser_usb *dev = priv->dev;
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
struct can_bittiming *dbt = &priv->can.data_bittiming; struct can_bittiming *dbt = &priv->can.data_bittiming;
struct kvaser_usb_busparams busparams; struct kvaser_usb_busparams busparams;
int tseg1 = dbt->prop_seg + dbt->phase_seg1; int tseg1 = dbt->prop_seg + dbt->phase_seg1;
int tseg2 = dbt->phase_seg2; int tseg2 = dbt->phase_seg2;
......
...@@ -277,7 +277,6 @@ struct ucan_priv { ...@@ -277,7 +277,6 @@ struct ucan_priv {
/* linux USB device structures */ /* linux USB device structures */
struct usb_device *udev; struct usb_device *udev;
struct usb_interface *intf;
struct net_device *netdev; struct net_device *netdev;
/* lock for can->echo_skb (used around /* lock for can->echo_skb (used around
...@@ -1501,7 +1500,6 @@ static int ucan_probe(struct usb_interface *intf, ...@@ -1501,7 +1500,6 @@ static int ucan_probe(struct usb_interface *intf,
/* initialize data */ /* initialize data */
up->udev = udev; up->udev = udev;
up->intf = intf;
up->netdev = netdev; up->netdev = netdev;
up->intf_index = iface_desc->desc.bInterfaceNumber; up->intf_index = iface_desc->desc.bInterfaceNumber;
up->in_ep_addr = in_ep_addr; up->in_ep_addr = in_ep_addr;
...@@ -1534,9 +1532,8 @@ static int ucan_probe(struct usb_interface *intf, ...@@ -1534,9 +1532,8 @@ static int ucan_probe(struct usb_interface *intf,
sizeof(union ucan_ctl_payload)); sizeof(union ucan_ctl_payload));
if (ret > 0) { if (ret > 0) {
/* copy string while ensuring zero termination */ /* copy string while ensuring zero termination */
strncpy(firmware_str, up->ctl_msg_buffer->raw, strscpy(firmware_str, up->ctl_msg_buffer->raw,
sizeof(union ucan_ctl_payload)); sizeof(union ucan_ctl_payload) + 1);
firmware_str[sizeof(union ucan_ctl_payload)] = '\0';
} else { } else {
strcpy(firmware_str, "unknown"); strcpy(firmware_str, "unknown");
} }
......
...@@ -1037,6 +1037,7 @@ char *usb_cache_string(struct usb_device *udev, int index) ...@@ -1037,6 +1037,7 @@ char *usb_cache_string(struct usb_device *udev, int index)
} }
return smallbuf; return smallbuf;
} }
EXPORT_SYMBOL_GPL(usb_cache_string);
/* /*
* usb_get_device_descriptor - (re)reads the device descriptor (usbcore) * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
......
...@@ -47,7 +47,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev, ...@@ -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_set_isoch_delay(struct usb_device *dev);
extern int usb_get_bos_descriptor(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 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_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev); extern int usb_choose_configuration(struct usb_device *udev);
extern int usb_generic_driver_probe(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) ...@@ -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, extern int usb_string(struct usb_device *dev, int index,
char *buf, size_t size); char *buf, size_t size);
extern char *usb_cache_string(struct usb_device *udev, int index);
/* wrappers that also update important state inside usbcore */ /* wrappers that also update important state inside usbcore */
extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_clear_halt(struct usb_device *dev, int pipe);
......
...@@ -621,6 +621,8 @@ enum devlink_param_generic_id { ...@@ -621,6 +621,8 @@ enum devlink_param_generic_id {
#define DEVLINK_INFO_VERSION_GENERIC_FW_ROCE "fw.roce" #define DEVLINK_INFO_VERSION_GENERIC_FW_ROCE "fw.roce"
/* Firmware bundle identifier */ /* Firmware bundle identifier */
#define DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID "fw.bundle_id" #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 * 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, ...@@ -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 hlist_head *rcv_list;
struct can_dev_rcv_lists *dev_rcv_lists; struct can_dev_rcv_lists *dev_rcv_lists;
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
int err = 0;
/* insert new receiver (dev,canid,mask) -> (func,data) */ /* 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, ...@@ -481,7 +480,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
rcv_lists_stats->rcv_entries); rcv_lists_stats->rcv_entries);
spin_unlock_bh(&net->can.rcvlists_lock); spin_unlock_bh(&net->can.rcvlists_lock);
return err; return 0;
} }
EXPORT_SYMBOL(can_rx_register); EXPORT_SYMBOL(can_rx_register);
......
...@@ -857,6 +857,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -857,6 +857,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
skb->dev = dev; skb->dev = dev;
skb->priority = sk->sk_priority; skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
skb->tstamp = sockc.transmit_time; skb->tstamp = sockc.transmit_time;
skb_setup_tx_timestamp(skb, sockc.tsflags); skb_setup_tx_timestamp(skb, sockc.tsflags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment