1
0
mirror of https://github.com/torvalds/linux.git synced 2025-04-12 16:47:42 +00:00

linux-can-next-for-6.14-20250110

-----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEUEC6huC2BN0pvD5fKDiiPnotvG8FAmeA/ocTHG1rbEBwZW5n
 dXRyb25peC5kZQAKCRAoOKI+ei28b7FlB/9c3ThnLVI/eezSZGE2UpvjN3eZWemk
 UkTxypJ9NpJkd1UovpOGVTNjAmlN+6+yjATJm8GBpSg99cN5Y8gcu7lWnsnidopm
 mkUNE+ZPPmkxe/ay1d6BfqJKzZTqkdJ6w1QlIXFfDgbRr4YaX9bn3egdtoNhq4fR
 0qRt6++7V3hhhlr3x0hrhB+3nrTkL8eVBVWNEKFPCnCn9E3p7izXwZAG/y/IC4sP
 swTk1BbSZg9ajds/1DE2AVeUvqz63FnzjY7YChU5WdNY5bq/cFHGoCWn8aZEX/kC
 YFrQC3lonlNa/A5IFfb32HaFT0qFFbOKBHXru/wFuuwJCKVLUKsmLsd/
 =c3SH
 -----END PGP SIGNATURE-----

Merge tag 'linux-can-next-for-6.14-20250110' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next

Marc Kleine-Budde says:

====================
pull-request: can-next 2025-01-10

Pierre-Henry Moussay adds PIC64GX compatibility to the DT bindings for
Microchip's mpfs-can IP core.

The next 3 patches are by Sean Nyekjaer and target the tcan4x5x
driver. First the DT bindings is converted to DT schema, then nWKRQ
voltage selection is added to the driver.

Dario Binacchi's patch for the sun4i_can makes the driver more
consistent by adding a likely() to the driver.

Another patch by Sean Nyekjaer for the tcan4x5x driver gets rid of a
false error message.

Charan Pedumuru converts the atmel-can DT bindings to DT schema.

The next 2 patches are by Oliver Hartkopp. The first one maps Oliver's
former mail addresses to a dedicated CAN mail address. The second one
assigns net/sched/em_canid.c additionally to the CAN maintainers.

Ariel Otilibili's patch removes dead code from the CAN dev helper.

The next 3 patches are by Sean Nyekjaer and add HW standby support to
the tcan4x5x driver.

A patch by Dario Binacchi fixes the DT bindings for the st,stm32-bxcan
driver.

The last 4 patches are by Jimmy Assarsson and target the kvaser_usb
and the kvaser_pciefd driver: error statistics are improved and
CAN_CTRLMODE_BERR_REPORTING is added.

* tag 'linux-can-next-for-6.14-20250110' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next:
  can: kvaser_pciefd: Add support for CAN_CTRLMODE_BERR_REPORTING
  can: kvaser_pciefd: Update stats and state even if alloc_can_err_skb() fails
  can: kvaser_usb: Add support for CAN_CTRLMODE_BERR_REPORTING
  can: kvaser_usb: Update stats and state even if alloc_can_err_skb() fails
  dt-bindings: can: st,stm32-bxcan: fix st,gcan property type
  can: m_can: call deinit/init callback when going into suspend/resume
  can: tcan4x5x: add deinit callback to set standby mode
  can: m_can: add deinit callback
  can: dev: can_get_state_str(): Remove dead code
  MAINTAINERS: assign em_canid.c additionally to CAN maintainers
  mailmap: add an entry for Oliver Hartkopp
  dt-bindings: net: can: atmel: Convert to json schema
  can: tcan4x5x: get rid of false clock errors
  can: sun4i_can: continue to use likely() to check skb
  can: tcan4x5x: add option for selecting nWKRQ voltage
  dt-bindings: can: tcan4x5x: Document the ti,nwkrq-voltage-vio option
  dt-bindings: can: convert tcan4x5x.txt to DT schema
  dt-bindings: can: mpfs: add PIC64GX CAN compatibility
====================

Link: https://patch.msgid.link/20250110112712.3214173-1-mkl@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-01-10 22:46:08 -08:00
commit 7dc8f809b8
18 changed files with 432 additions and 213 deletions

@ -529,6 +529,8 @@ Oleksij Rempel <linux@rempel-privat.de> <external.Oleksij.Rempel@de.bosch.com>
Oleksij Rempel <linux@rempel-privat.de> <fixed-term.Oleksij.Rempel@de.bosch.com>
Oleksij Rempel <o.rempel@pengutronix.de>
Oleksij Rempel <o.rempel@pengutronix.de> <ore@pengutronix.de>
Oliver Hartkopp <socketcan@hartkopp.net> <oliver.hartkopp@volkswagen.de>
Oliver Hartkopp <socketcan@hartkopp.net> <oliver@hartkopp.net>
Oliver Upton <oliver.upton@linux.dev> <oupton@google.com>
Ondřej Jirman <megi@xff.cz> <megous@megous.com>
Oza Pawandeep <quic_poza@quicinc.com> <poza@codeaurora.org>

@ -0,0 +1,58 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/can/atmel,at91sam9263-can.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip AT91 CAN Controller
maintainers:
- Nicolas Ferre <nicolas.ferre@microchip.com>
allOf:
- $ref: can-controller.yaml#
properties:
compatible:
oneOf:
- enum:
- atmel,at91sam9263-can
- atmel,at91sam9x5-can
- items:
- enum:
- microchip,sam9x60-can
- const: atmel,at91sam9x5-can
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: can_clk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/at91.h>
can@f000c000 {
compatible = "atmel,at91sam9263-can";
reg = <0xf000c000 0x300>;
interrupts = <30 IRQ_TYPE_LEVEL_HIGH 3>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 12>;
clock-names = "can_clk";
};

@ -1,15 +0,0 @@
* AT91 CAN *
Required properties:
- compatible: Should be "atmel,at91sam9263-can", "atmel,at91sam9x5-can" or
"microchip,sam9x60-can"
- reg: Should contain CAN controller registers location and length
- interrupts: Should contain IRQ line for the CAN controller
Example:
can0: can@f000c000 {
compatible = "atmel,at91sam9x5-can";
reg = <0xf000c000 0x300>;
interrupts = <40 4 5>
};

@ -15,7 +15,11 @@ allOf:
properties:
compatible:
const: microchip,mpfs-can
oneOf:
- items:
- const: microchip,pic64gx-can
- const: microchip,mpfs-can
- const: microchip,mpfs-can
reg:
maxItems: 1

@ -63,7 +63,7 @@ properties:
maxItems: 1
st,gcan:
$ref: /schemas/types.yaml#/definitions/phandle-array
$ref: /schemas/types.yaml#/definitions/phandle
description:
The phandle to the gcan node which allows to access the 512-bytes
SRAM memory shared by the two bxCAN cells (CAN1 primary and CAN2

@ -1,48 +0,0 @@
Texas Instruments TCAN4x5x CAN Controller
================================================
This file provides device node information for the TCAN4x5x interface contains.
Required properties:
- compatible:
"ti,tcan4552", "ti,tcan4x5x"
"ti,tcan4553", "ti,tcan4x5x" or
"ti,tcan4x5x"
- reg: 0
- #address-cells: 1
- #size-cells: 0
- spi-max-frequency: Maximum frequency of the SPI bus the chip can
operate at should be less than or equal to 18 MHz.
- interrupt-parent: the phandle to the interrupt controller which provides
the interrupt.
- interrupts: interrupt specification for data-ready.
See Documentation/devicetree/bindings/net/can/bosch,m_can.yaml for additional
required property details.
Optional properties:
- reset-gpios: Hardwired output GPIO. If not defined then software
reset.
- device-state-gpios: Input GPIO that indicates if the device is in
a sleep state or if the device is active. Not
available with tcan4552/4553.
- device-wake-gpios: Wake up GPIO to wake up the TCAN device. Not
available with tcan4552/4553.
- wakeup-source: Leave the chip running when suspended, and configure
the RX interrupt to wake up the device.
Example:
tcan4x5x: tcan4x5x@0 {
compatible = "ti,tcan4x5x";
reg = <0>;
#address-cells = <1>;
#size-cells = <1>;
spi-max-frequency = <10000000>;
bosch,mram-cfg = <0x0 0 0 16 0 0 1 1>;
interrupt-parent = <&gpio1>;
interrupts = <14 IRQ_TYPE_LEVEL_LOW>;
device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>;
wakeup-source;
};

@ -0,0 +1,199 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/can/ti,tcan4x5x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TCAN4x5x CAN Controller
maintainers:
- Marc Kleine-Budde <mkl@pengutronix.de>
properties:
compatible:
oneOf:
- items:
- enum:
- ti,tcan4552
- ti,tcan4553
- const: ti,tcan4x5x
- const: ti,tcan4x5x
reg:
maxItems: 1
interrupts:
maxItems: 1
description: The GPIO parent interrupt.
clocks:
maxItems: 1
clock-names:
items:
- const: cclk
reset-gpios:
description: Hardwired output GPIO. If not defined then software reset.
maxItems: 1
device-state-gpios:
description:
Input GPIO that indicates if the device is in a sleep state or if the
device is active. Not available with tcan4552/4553.
maxItems: 1
device-wake-gpios:
description:
Wake up GPIO to wake up the TCAN device.
Not available with tcan4552/4553.
maxItems: 1
bosch,mram-cfg:
description: |
Message RAM configuration data.
Multiple M_CAN instances can share the same Message RAM
and each element(e.g Rx FIFO or Tx Buffer and etc) number
in Message RAM is also configurable, so this property is
telling driver how the shared or private Message RAM are
used by this M_CAN controller.
The format should be as follows:
<offset sidf_elems xidf_elems rxf0_elems rxf1_elems rxb_elems txe_elems txb_elems>
The 'offset' is an address offset of the Message RAM where
the following elements start from. This is usually set to
0x0 if you're using a private Message RAM. The remain cells
are used to specify how many elements are used for each FIFO/Buffer.
M_CAN includes the following elements according to user manual:
11-bit Filter 0-128 elements / 0-128 words
29-bit Filter 0-64 elements / 0-128 words
Rx FIFO 0 0-64 elements / 0-1152 words
Rx FIFO 1 0-64 elements / 0-1152 words
Rx Buffers 0-64 elements / 0-1152 words
Tx Event FIFO 0-32 elements / 0-64 words
Tx Buffers 0-32 elements / 0-576 words
Please refer to 2.4.1 Message RAM Configuration in Bosch
M_CAN user manual for details.
$ref: /schemas/types.yaml#/definitions/int32-array
items:
- description: The 'offset' is an address offset of the Message RAM where
the following elements start from. This is usually set to 0x0 if
you're using a private Message RAM.
default: 0
- description: 11-bit Filter 0-128 elements / 0-128 words
minimum: 0
maximum: 128
- description: 29-bit Filter 0-64 elements / 0-128 words
minimum: 0
maximum: 64
- description: Rx FIFO 0 0-64 elements / 0-1152 words
minimum: 0
maximum: 64
- description: Rx FIFO 1 0-64 elements / 0-1152 words
minimum: 0
maximum: 64
- description: Rx Buffers 0-64 elements / 0-1152 words
minimum: 0
maximum: 64
- description: Tx Event FIFO 0-32 elements / 0-64 words
minimum: 0
maximum: 32
- description: Tx Buffers 0-32 elements / 0-576 words
minimum: 0
maximum: 32
minItems: 1
spi-max-frequency:
description:
Must be half or less of "clocks" frequency.
maximum: 18000000
ti,nwkrq-voltage-vio:
type: boolean
description:
nWKRQ Pin GPO buffer voltage configuration.
Set nWKRQ to use VIO voltage rail.
When not set nWKRQ will use internal voltage rail.
wakeup-source:
$ref: /schemas/types.yaml#/definitions/flag
description:
Enable CAN remote wakeup.
allOf:
- $ref: can-controller.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
properties:
compatible:
contains:
enum:
- ti,tcan4552
- ti,tcan4553
then:
properties:
device-state-gpios: false
device-wake-gpios: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- bosch,mram-cfg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
can@0 {
compatible = "ti,tcan4x5x";
reg = <0>;
clocks = <&can0_osc>;
clock-names = "cclk";
pinctrl-names = "default";
pinctrl-0 = <&can0_pins>;
spi-max-frequency = <10000000>;
bosch,mram-cfg = <0x0 0 0 16 0 0 1 1>;
interrupt-parent = <&gpio1>;
interrupts = <14 IRQ_TYPE_LEVEL_LOW>;
device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>;
ti,nwkrq-voltage-vio;
wakeup-source;
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
can@0 {
compatible = "ti,tcan4552", "ti,tcan4x5x";
reg = <0>;
clocks = <&can0_osc>;
clock-names = "cclk";
pinctrl-names = "default";
pinctrl-0 = <&can0_pins>;
spi-max-frequency = <10000000>;
bosch,mram-cfg = <0x0 0 0 16 0 0 1 1>;
interrupt-parent = <&gpio1>;
interrupts = <14 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>;
wakeup-source;
};
};

@ -5115,6 +5115,7 @@ F: include/uapi/linux/can/gw.h
F: include/uapi/linux/can/isotp.h
F: include/uapi/linux/can/raw.h
F: net/can/
F: net/sched/em_canid.c
CAN-J1939 NETWORK LAYER
M: Robin van der Gracht <robin@protonic.nl>

@ -85,8 +85,6 @@ const char *can_get_state_str(const enum can_state state)
default:
return "<unknown>";
}
return "<unknown>";
}
EXPORT_SYMBOL_GPL(can_get_state_str);

@ -999,7 +999,8 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie)
can->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_FD |
CAN_CTRLMODE_FD_NON_ISO |
CAN_CTRLMODE_CC_LEN8_DLC;
CAN_CTRLMODE_CC_LEN8_DLC |
CAN_CTRLMODE_BERR_REPORTING;
status = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_STAT_REG);
if (!(status & KVASER_PCIEFD_KCAN_STAT_FD)) {
@ -1234,11 +1235,15 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie,
}
static void kvaser_pciefd_change_state(struct kvaser_pciefd_can *can,
const struct can_berr_counter *bec,
struct can_frame *cf,
enum can_state new_state,
enum can_state tx_state,
enum can_state rx_state)
{
enum can_state old_state;
old_state = can->can.state;
can_change_state(can->can.dev, cf, tx_state, rx_state);
if (new_state == CAN_STATE_BUS_OFF) {
@ -1254,6 +1259,18 @@ static void kvaser_pciefd_change_state(struct kvaser_pciefd_can *can,
can_bus_off(ndev);
}
}
if (old_state == CAN_STATE_BUS_OFF &&
new_state == CAN_STATE_ERROR_ACTIVE &&
can->can.restart_ms) {
can->can.can_stats.restarts++;
if (cf)
cf->can_id |= CAN_ERR_RESTARTED;
}
if (cf && new_state != CAN_STATE_BUS_OFF) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = bec->txerr;
cf->data[7] = bec->rxerr;
}
}
static void kvaser_pciefd_packet_to_state(struct kvaser_pciefd_rx_packet *p,
@ -1288,7 +1305,7 @@ static int kvaser_pciefd_rx_error_frame(struct kvaser_pciefd_can *can,
struct can_berr_counter bec;
enum can_state old_state, new_state, tx_state, rx_state;
struct net_device *ndev = can->can.dev;
struct sk_buff *skb;
struct sk_buff *skb = NULL;
struct can_frame *cf = NULL;
old_state = can->can.state;
@ -1297,16 +1314,10 @@ static int kvaser_pciefd_rx_error_frame(struct kvaser_pciefd_can *can,
bec.rxerr = FIELD_GET(KVASER_PCIEFD_SPACK_RXERR_MASK, p->header[0]);
kvaser_pciefd_packet_to_state(p, &bec, &new_state, &tx_state, &rx_state);
skb = alloc_can_err_skb(ndev, &cf);
if (can->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
skb = alloc_can_err_skb(ndev, &cf);
if (new_state != old_state) {
kvaser_pciefd_change_state(can, cf, new_state, tx_state, rx_state);
if (old_state == CAN_STATE_BUS_OFF &&
new_state == CAN_STATE_ERROR_ACTIVE &&
can->can.restart_ms) {
can->can.can_stats.restarts++;
if (skb)
cf->can_id |= CAN_ERR_RESTARTED;
}
kvaser_pciefd_change_state(can, &bec, cf, new_state, tx_state, rx_state);
}
can->err_rep_cnt++;
@ -1319,18 +1330,19 @@ static int kvaser_pciefd_rx_error_frame(struct kvaser_pciefd_can *can,
can->bec.txerr = bec.txerr;
can->bec.rxerr = bec.rxerr;
if (!skb) {
ndev->stats.rx_dropped++;
return -ENOMEM;
if (can->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
if (!skb) {
netdev_warn(ndev, "No memory left for err_skb\n");
ndev->stats.rx_dropped++;
return -ENOMEM;
}
kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp);
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_CNT;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
netif_rx(skb);
}
kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp);
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_CNT;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
netif_rx(skb);
return 0;
}
@ -1359,6 +1371,7 @@ static int kvaser_pciefd_handle_status_resp(struct kvaser_pciefd_can *can,
{
struct can_berr_counter bec;
enum can_state old_state, new_state, tx_state, rx_state;
int ret = 0;
old_state = can->can.state;
@ -1372,25 +1385,15 @@ static int kvaser_pciefd_handle_status_resp(struct kvaser_pciefd_can *can,
struct can_frame *cf;
skb = alloc_can_err_skb(ndev, &cf);
if (!skb) {
kvaser_pciefd_change_state(can, &bec, cf, new_state, tx_state, rx_state);
if (skb) {
kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp);
netif_rx(skb);
} else {
ndev->stats.rx_dropped++;
return -ENOMEM;
netdev_warn(ndev, "No memory left for err_skb\n");
ret = -ENOMEM;
}
kvaser_pciefd_change_state(can, cf, new_state, tx_state, rx_state);
if (old_state == CAN_STATE_BUS_OFF &&
new_state == CAN_STATE_ERROR_ACTIVE &&
can->can.restart_ms) {
can->can.can_stats.restarts++;
cf->can_id |= CAN_ERR_RESTARTED;
}
kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp);
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
netif_rx(skb);
}
can->bec.txerr = bec.txerr;
can->bec.rxerr = bec.rxerr;
@ -1398,7 +1401,7 @@ static int kvaser_pciefd_handle_status_resp(struct kvaser_pciefd_can *can,
if (bec.txerr || bec.rxerr)
mod_timer(&can->bec_poll_timer, KVASER_PCIEFD_BEC_POLL_FREQ);
return 0;
return ret;
}
static int kvaser_pciefd_handle_status_packet(struct kvaser_pciefd *pcie,

@ -1785,6 +1785,13 @@ static void m_can_stop(struct net_device *dev)
/* set the state as STOPPED */
cdev->can.state = CAN_STATE_STOPPED;
if (cdev->ops->deinit) {
ret = cdev->ops->deinit(cdev);
if (ret)
netdev_err(dev, "failed to deinitialize: %pe\n",
ERR_PTR(ret));
}
}
static int m_can_close(struct net_device *dev)
@ -2466,6 +2473,7 @@ int m_can_class_suspend(struct device *dev)
{
struct m_can_classdev *cdev = dev_get_drvdata(dev);
struct net_device *ndev = cdev->net;
int ret = 0;
if (netif_running(ndev)) {
netif_stop_queue(ndev);
@ -2478,6 +2486,9 @@ int m_can_class_suspend(struct device *dev)
if (cdev->pm_wake_source) {
hrtimer_cancel(&cdev->hrtimer);
m_can_write(cdev, M_CAN_IE, IR_RF0N);
if (cdev->ops->deinit)
ret = cdev->ops->deinit(cdev);
} else {
m_can_stop(ndev);
}
@ -2489,7 +2500,7 @@ int m_can_class_suspend(struct device *dev)
cdev->can.state = CAN_STATE_SLEEPING;
return 0;
return ret;
}
EXPORT_SYMBOL_GPL(m_can_class_suspend);
@ -2497,14 +2508,13 @@ int m_can_class_resume(struct device *dev)
{
struct m_can_classdev *cdev = dev_get_drvdata(dev);
struct net_device *ndev = cdev->net;
int ret = 0;
pinctrl_pm_select_default_state(dev);
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_running(ndev)) {
int ret;
ret = m_can_clk_start(cdev);
if (ret)
return ret;
@ -2517,6 +2527,10 @@ int m_can_class_resume(struct device *dev)
* again.
*/
cdev->active_interrupts |= IR_RF0N | IR_TEFN;
if (cdev->ops->init)
ret = cdev->ops->init(cdev);
m_can_write(cdev, M_CAN_IE, cdev->active_interrupts);
} else {
ret = m_can_start(ndev);
@ -2530,7 +2544,7 @@ int m_can_class_resume(struct device *dev)
netif_start_queue(ndev);
}
return 0;
return ret;
}
EXPORT_SYMBOL_GPL(m_can_class_resume);

@ -68,6 +68,7 @@ struct m_can_ops {
int (*write_fifo)(struct m_can_classdev *cdev, int addr_offset,
const void *val, size_t val_count);
int (*init)(struct m_can_classdev *cdev);
int (*deinit)(struct m_can_classdev *cdev);
};
struct m_can_tx_op {

@ -92,6 +92,8 @@
#define TCAN4X5X_MODE_STANDBY BIT(6)
#define TCAN4X5X_MODE_NORMAL BIT(7)
#define TCAN4X5X_NWKRQ_VOLTAGE_VIO BIT(19)
#define TCAN4X5X_DISABLE_WAKE_MSK (BIT(31) | BIT(30))
#define TCAN4X5X_DISABLE_INH_MSK BIT(9)
@ -267,9 +269,24 @@ static int tcan4x5x_init(struct m_can_classdev *cdev)
if (ret)
return ret;
if (tcan4x5x->nwkrq_voltage_vio) {
ret = regmap_set_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
TCAN4X5X_NWKRQ_VOLTAGE_VIO);
if (ret)
return ret;
}
return ret;
}
static int tcan4x5x_deinit(struct m_can_classdev *cdev)
{
struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_STANDBY);
};
static int tcan4x5x_disable_wake(struct m_can_classdev *cdev)
{
struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
@ -318,6 +335,14 @@ static const struct tcan4x5x_version_info
return &tcan4x5x_versions[TCAN4X5X];
}
static void tcan4x5x_get_dt_data(struct m_can_classdev *cdev)
{
struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
tcan4x5x->nwkrq_voltage_vio =
of_property_read_bool(cdev->dev->of_node, "ti,nwkrq-voltage-vio");
}
static int tcan4x5x_get_gpios(struct m_can_classdev *cdev,
const struct tcan4x5x_version_info *version_info)
{
@ -359,6 +384,7 @@ static int tcan4x5x_get_gpios(struct m_can_classdev *cdev,
static const struct m_can_ops tcan4x5x_ops = {
.init = tcan4x5x_init,
.deinit = tcan4x5x_deinit,
.read_reg = tcan4x5x_read_reg,
.write_reg = tcan4x5x_write_reg,
.write_fifo = tcan4x5x_write_fifo,
@ -392,7 +418,7 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
priv->power = NULL;
}
m_can_class_get_clocks(mcan_class);
mcan_class->cclk = devm_clk_get(mcan_class->dev, "cclk");
if (IS_ERR(mcan_class->cclk)) {
dev_err(&spi->dev, "no CAN clock source defined\n");
freq = TCAN4X5X_EXT_CLK_DEF;
@ -453,6 +479,8 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
goto out_power;
}
tcan4x5x_get_dt_data(mcan_class);
tcan4x5x_check_wake(priv);
ret = tcan4x5x_write_tcan_reg(mcan_class, TCAN4X5X_INT_EN, 0);

@ -42,6 +42,8 @@ struct tcan4x5x_priv {
struct tcan4x5x_map_buf map_buf_rx;
struct tcan4x5x_map_buf map_buf_tx;
bool nwkrq_voltage_vio;
};
static inline void

@ -570,7 +570,7 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
else
state = CAN_STATE_ERROR_ACTIVE;
}
if (skb && state != CAN_STATE_BUS_OFF) {
if (likely(skb) && state != CAN_STATE_BUS_OFF) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = txerr;
cf->data[7] = rxerr;

@ -818,7 +818,8 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel)
init_completion(&priv->stop_comp);
init_completion(&priv->flush_comp);
init_completion(&priv->get_busparams_comp);
priv->can.ctrlmode_supported = CAN_CTRLMODE_CC_LEN8_DLC;
priv->can.ctrlmode_supported = CAN_CTRLMODE_CC_LEN8_DLC |
CAN_CTRLMODE_BERR_REPORTING;
priv->dev = dev;
priv->netdev = netdev;

@ -926,6 +926,42 @@ kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv,
}
}
static void kvaser_usb_hydra_change_state(struct kvaser_usb_net_priv *priv,
const struct can_berr_counter *bec,
struct can_frame *cf,
enum can_state new_state)
{
struct net_device *netdev = priv->netdev;
enum can_state old_state = priv->can.state;
enum can_state tx_state, rx_state;
tx_state = (bec->txerr >= bec->rxerr) ?
new_state : CAN_STATE_ERROR_ACTIVE;
rx_state = (bec->txerr <= bec->rxerr) ?
new_state : CAN_STATE_ERROR_ACTIVE;
can_change_state(netdev, cf, tx_state, rx_state);
if (new_state == CAN_STATE_BUS_OFF && old_state < CAN_STATE_BUS_OFF) {
if (priv->can.restart_ms == 0)
kvaser_usb_hydra_send_simple_cmd_async(priv, CMD_STOP_CHIP_REQ);
can_bus_off(netdev);
}
if (priv->can.restart_ms &&
old_state >= CAN_STATE_BUS_OFF &&
new_state < CAN_STATE_BUS_OFF) {
priv->can.can_stats.restarts++;
if (cf)
cf->can_id |= CAN_ERR_RESTARTED;
}
if (cf && new_state != CAN_STATE_BUS_OFF) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = bec->txerr;
cf->data[7] = bec->rxerr;
}
}
static void kvaser_usb_hydra_update_state(struct kvaser_usb_net_priv *priv,
u8 bus_status,
const struct can_berr_counter *bec)
@ -951,41 +987,11 @@ static void kvaser_usb_hydra_update_state(struct kvaser_usb_net_priv *priv,
return;
skb = alloc_can_err_skb(netdev, &cf);
if (skb) {
enum can_state tx_state, rx_state;
tx_state = (bec->txerr >= bec->rxerr) ?
new_state : CAN_STATE_ERROR_ACTIVE;
rx_state = (bec->txerr <= bec->rxerr) ?
new_state : CAN_STATE_ERROR_ACTIVE;
can_change_state(netdev, cf, tx_state, rx_state);
}
if (new_state == CAN_STATE_BUS_OFF && old_state < CAN_STATE_BUS_OFF) {
if (!priv->can.restart_ms)
kvaser_usb_hydra_send_simple_cmd_async
(priv, CMD_STOP_CHIP_REQ);
can_bus_off(netdev);
}
if (!skb) {
kvaser_usb_hydra_change_state(priv, bec, cf, new_state);
if (skb)
netif_rx(skb);
else
netdev_warn(netdev, "No memory left for err_skb\n");
return;
}
if (priv->can.restart_ms &&
old_state >= CAN_STATE_BUS_OFF &&
new_state < CAN_STATE_BUS_OFF)
priv->can.can_stats.restarts++;
if (new_state != CAN_STATE_BUS_OFF) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = bec->txerr;
cf->data[7] = bec->rxerr;
}
netif_rx(skb);
}
static void kvaser_usb_hydra_state_event(const struct kvaser_usb *dev,
@ -1078,9 +1084,8 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv,
{
struct net_device *netdev = priv->netdev;
struct net_device_stats *stats = &netdev->stats;
struct can_frame *cf;
struct sk_buff *skb;
struct skb_shared_hwtstamps *shhwtstamps;
struct can_frame *cf = NULL;
struct sk_buff *skb = NULL;
struct can_berr_counter bec;
enum can_state new_state, old_state;
u8 bus_status;
@ -1096,52 +1101,26 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv,
kvaser_usb_hydra_bus_status_to_can_state(priv, bus_status, &bec,
&new_state);
skb = alloc_can_err_skb(netdev, &cf);
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
skb = alloc_can_err_skb(netdev, &cf);
if (new_state != old_state)
kvaser_usb_hydra_change_state(priv, &bec, cf, new_state);
if (new_state != old_state) {
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
if (skb) {
enum can_state tx_state, rx_state;
struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
tx_state = (bec.txerr >= bec.rxerr) ?
new_state : CAN_STATE_ERROR_ACTIVE;
rx_state = (bec.txerr <= bec.rxerr) ?
new_state : CAN_STATE_ERROR_ACTIVE;
can_change_state(netdev, cf, tx_state, rx_state);
if (priv->can.restart_ms &&
old_state >= CAN_STATE_BUS_OFF &&
new_state < CAN_STATE_BUS_OFF)
cf->can_id |= CAN_ERR_RESTARTED;
}
if (new_state == CAN_STATE_BUS_OFF) {
if (!priv->can.restart_ms)
kvaser_usb_hydra_send_simple_cmd_async
(priv, CMD_STOP_CHIP_REQ);
can_bus_off(netdev);
shhwtstamps->hwtstamp = hwtstamp;
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_CNT;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
netif_rx(skb);
} else {
stats->rx_dropped++;
netdev_warn(netdev, "No memory left for err_skb\n");
}
}
if (!skb) {
stats->rx_dropped++;
netdev_warn(netdev, "No memory left for err_skb\n");
return;
}
shhwtstamps = skb_hwtstamps(skb);
shhwtstamps->hwtstamp = hwtstamp;
cf->can_id |= CAN_ERR_BUSERROR;
if (new_state != CAN_STATE_BUS_OFF) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
}
netif_rx(skb);
priv->bec.txerr = bec.txerr;
priv->bec.rxerr = bec.rxerr;
}

@ -1120,10 +1120,8 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
const struct kvaser_usb_err_summary *es)
{
struct can_frame *cf;
struct can_frame tmp_cf = { .can_id = CAN_ERR_FLAG,
.len = CAN_ERR_DLC };
struct sk_buff *skb;
struct can_frame *cf = NULL;
struct sk_buff *skb = NULL;
struct net_device_stats *stats;
struct kvaser_usb_net_priv *priv;
struct kvaser_usb_net_leaf_priv *leaf;
@ -1143,18 +1141,10 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
if (!netif_running(priv->netdev))
return;
/* Update all of the CAN interface's state and error counters before
* trying any memory allocation that can actually fail with -ENOMEM.
*
* We send a temporary stack-allocated error CAN frame to
* can_change_state() for the very same reason.
*
* TODO: Split can_change_state() responsibility between updating the
* CAN interface's state and counters, and the setting up of CAN error
* frame ID and data to userspace. Remove stack allocation afterwards.
*/
old_state = priv->can.state;
kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf);
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
skb = alloc_can_err_skb(priv->netdev, &cf);
kvaser_usb_leaf_rx_error_update_can_state(priv, es, cf);
new_state = priv->can.state;
/* If there are errors, request status updates periodically as we do
@ -1168,13 +1158,6 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
schedule_delayed_work(&leaf->chip_state_req_work,
msecs_to_jiffies(500));
skb = alloc_can_err_skb(priv->netdev, &cf);
if (!skb) {
stats->rx_dropped++;
return;
}
memcpy(cf, &tmp_cf, sizeof(*cf));
if (new_state != old_state) {
if (es->status &
(M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) {
@ -1187,11 +1170,20 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
if (priv->can.restart_ms &&
old_state == CAN_STATE_BUS_OFF &&
new_state < CAN_STATE_BUS_OFF) {
cf->can_id |= CAN_ERR_RESTARTED;
if (cf)
cf->can_id |= CAN_ERR_RESTARTED;
netif_carrier_on(priv->netdev);
}
}
if (!skb) {
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
stats->rx_dropped++;
netdev_warn(priv->netdev, "No memory left for err_skb\n");
}
return;
}
switch (dev->driver_info->family) {
case KVASER_LEAF:
if (es->leaf.error_factor) {