From 44afeed73e52a2f1cfb8b6ca2a0be8164a3b567a Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 6 Aug 2021 12:39:25 +0200 Subject: [PATCH 01/22] =?UTF-8?q?mailmap:=20update=20email=20address=20of?= =?UTF-8?q?=20Matthias=20Fuchs=20and=20Thomas=20K=C3=B6rper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matthias Fuchs's and Thomas Körper's email addresses aren't valid anymore. Use the newly created role account instead. Link: https://lore.kernel.org/r/20210809175843.207864-1-mkl@pengutronix.de Cc: socketcan@esd.eu Cc: Stefan Mätje Acked-by: Stefan Mätje Signed-off-by: Marc Kleine-Budde --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index a35ae244dfda..6e849110cb4e 100644 --- a/.mailmap +++ b/.mailmap @@ -229,6 +229,7 @@ Matthew Wilcox Matthew Wilcox Matthew Wilcox Matthew Wilcox +Matthias Fuchs Matthieu CASTET Matt Ranostay Matt Ranostay Matthew Ranostay @@ -341,6 +342,7 @@ Sumit Semwal Takashi YOSHII Tejun Heo Thomas Graf +Thomas Körper Thomas Pedersen Tiezhu Yang Todor Tomov From ef82641d68027b564ac6b12fca052065f9609f1a Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Aug 2021 09:12:30 +0200 Subject: [PATCH 02/22] dt-bindings: can-controller: add support for termination-gpios Some boards provide GPIO controllable termination resistor. Provide binding to make use of it. Link: https://lore.kernel.org/r/20210818071232.20585-2-o.rempel@pengutronix.de Signed-off-by: Oleksij Rempel Reviewed-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- .../devicetree/bindings/net/can/can-controller.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/net/can/can-controller.yaml b/Documentation/devicetree/bindings/net/can/can-controller.yaml index 9cf2ae097156..1f0e98051074 100644 --- a/Documentation/devicetree/bindings/net/can/can-controller.yaml +++ b/Documentation/devicetree/bindings/net/can/can-controller.yaml @@ -13,6 +13,15 @@ properties: $nodename: pattern: "^can(@.*)?$" + termination-gpios: + description: GPIO pin to enable CAN bus termination. + maxItems: 1 + + termination-ohms: + description: The resistance value of the CAN bus termination resistor. + minimum: 1 + maximum: 65535 + additionalProperties: true ... From fe7edf2482e1e3d677cd1b5acebf5dd06d6246e6 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Aug 2021 09:12:31 +0200 Subject: [PATCH 03/22] dt-bindings: can: fsl,flexcan: enable termination-* bindings Enable termination-* binding and provide validation example for it. Link: https://lore.kernel.org/r/20210818071232.20585-3-o.rempel@pengutronix.de Signed-off-by: Oleksij Rempel Reviewed-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- .../bindings/net/can/fsl,flexcan.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml b/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml index 55bff1586b6f..3f0ee17c1461 100644 --- a/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml +++ b/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml @@ -119,6 +119,9 @@ properties: minimum: 0 maximum: 2 + termination-gpios: true + termination-ohms: true + required: - compatible - reg @@ -148,3 +151,17 @@ examples: fsl,stop-mode = <&gpr 0x34 28>; fsl,scu-index = /bits/ 8 <1>; }; + - | + #include + #include + + can@2090000 { + compatible = "fsl,imx6q-flexcan"; + reg = <0x02090000 0x4000>; + interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 1>, <&clks 2>; + clock-names = "ipg", "per"; + fsl,stop-mode = <&gpr 0x34 28>; + termination-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + termination-ohms = <120>; + }; From 6e86a1543c378f2e8837ad88f361b7bf606c80f7 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Aug 2021 09:12:32 +0200 Subject: [PATCH 04/22] can: dev: provide optional GPIO based termination support For CAN buses to work, a termination resistor has to be present at both ends of the bus. This resistor is usually 120 Ohms, other values may be required for special bus topologies. This patch adds support for a generic GPIO based CAN termination. The resistor value has to be specified via device tree, and it can only be attached to or detached from the bus. By default the termination is not active. Link: https://lore.kernel.org/r/20210818071232.20585-4-o.rempel@pengutronix.de Signed-off-by: Oleksij Rempel Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev/dev.c | 66 +++++++++++++++++++++++++++++++++++++++ include/linux/can/dev.h | 8 +++++ 2 files changed, 74 insertions(+) diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c index 311d8564d611..e3d840b81357 100644 --- a/drivers/net/can/dev/dev.c +++ b/drivers/net/can/dev/dev.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define MOD_DESC "CAN device driver interface" @@ -400,10 +401,69 @@ void close_candev(struct net_device *dev) } EXPORT_SYMBOL_GPL(close_candev); +static int can_set_termination(struct net_device *ndev, u16 term) +{ + struct can_priv *priv = netdev_priv(ndev); + int set; + + if (term == priv->termination_gpio_ohms[CAN_TERMINATION_GPIO_ENABLED]) + set = 1; + else + set = 0; + + gpiod_set_value(priv->termination_gpio, set); + + return 0; +} + +static int can_get_termination(struct net_device *ndev) +{ + struct can_priv *priv = netdev_priv(ndev); + struct device *dev = ndev->dev.parent; + struct gpio_desc *gpio; + u32 term; + int ret; + + /* Disabling termination by default is the safe choice: Else if many + * bus participants enable it, no communication is possible at all. + */ + gpio = devm_gpiod_get_optional(dev, "termination", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return dev_err_probe(dev, PTR_ERR(gpio), + "Cannot get termination-gpios\n"); + + if (!gpio) + return 0; + + ret = device_property_read_u32(dev, "termination-ohms", &term); + if (ret) { + netdev_err(ndev, "Cannot get termination-ohms: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (term > U16_MAX) { + netdev_err(ndev, "Invalid termination-ohms value (%u > %u)\n", + term, U16_MAX); + return -EINVAL; + } + + priv->termination_const_cnt = ARRAY_SIZE(priv->termination_gpio_ohms); + priv->termination_const = priv->termination_gpio_ohms; + priv->termination_gpio = gpio; + priv->termination_gpio_ohms[CAN_TERMINATION_GPIO_DISABLED] = + CAN_TERMINATION_DISABLED; + priv->termination_gpio_ohms[CAN_TERMINATION_GPIO_ENABLED] = term; + priv->do_set_termination = can_set_termination; + + return 0; +} + /* Register the CAN network device */ int register_candev(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); + int err; /* Ensure termination_const, termination_const_cnt and * do_set_termination consistency. All must be either set or @@ -419,6 +479,12 @@ int register_candev(struct net_device *dev) if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt) return -EINVAL; + if (!priv->termination_const) { + err = can_get_termination(dev); + if (err) + return err; + } + dev->rtnl_link_ops = &can_link_ops; netif_carrier_off(dev); diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 27b275e463da..2413253e54c7 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -32,6 +32,12 @@ enum can_mode { CAN_MODE_SLEEP }; +enum can_termination_gpio { + CAN_TERMINATION_GPIO_DISABLED = 0, + CAN_TERMINATION_GPIO_ENABLED, + CAN_TERMINATION_GPIO_MAX, +}; + /* * CAN common private data */ @@ -55,6 +61,8 @@ struct can_priv { unsigned int termination_const_cnt; const u16 *termination_const; u16 termination; + struct gpio_desc *termination_gpio; + u16 termination_gpio_ohms[CAN_TERMINATION_GPIO_MAX]; enum can_state state; From e43aaa0fefcec5c02b23cff2589ce4ecb2bfcb9e Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Sun, 15 Aug 2021 12:32:42 +0900 Subject: [PATCH 05/22] can: netlink: allow user to turn off unsupported features The sanity checks on the control modes will reject any request related to an unsupported features, even turning it off. Example on an interface which does not support CAN-FD: $ ip link set can0 type can bitrate 500000 fd off RTNETLINK answers: Operation not supported This patch lets such command go through (but requests to turn on an unsupported feature are, of course, still denied). Link: https://lore.kernel.org/r/20210815033248.98111-2-mailhol.vincent@wanadoo.fr Signed-off-by: Vincent Mailhol Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c index 147c23d7dab7..80425636049d 100644 --- a/drivers/net/can/dev/netlink.c +++ b/drivers/net/can/dev/netlink.c @@ -116,7 +116,7 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[], maskedflags = cm->flags & cm->mask; /* check whether provided bits are allowed to be passed */ - if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic)) + if (maskedflags & ~(priv->ctrlmode_supported | ctrlstatic)) return -EOPNOTSUPP; /* do not check for static fd-non-iso if 'fd' is disabled */ From 7a4573cf3ae8e3ecff2f9865d2091ab4c00de085 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Sat, 14 Aug 2021 18:33:53 +0900 Subject: [PATCH 06/22] MAINTAINERS: add Vincent MAILHOL as maintainer for the ETAS ES58X CAN/USB driver Adding myself (Vincent Mailhol) as a maintainer for the ETAS ES58X CAN/USB driver. Link: https://lore.kernel.org/r/20210814093353.74391-1-mailhol.vincent@wanadoo.fr Signed-off-by: Vincent Mailhol Signed-off-by: Marc Kleine-Budde --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 43ec27b32ee5..652657b27e0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6905,6 +6905,12 @@ M: Mark Einon S: Odd Fixes F: drivers/net/ethernet/agere/ +ETAS ES58X CAN/USB DRIVER +M: Vincent Mailhol +L: linux-can@vger.kernel.org +S: Maintained +F: drivers/net/can/usb/etas_es58x/ + ETHERNET BRIDGE M: Roopa Prabhu M: Nikolay Aleksandrov From c734707820f8550ae492ddfb772234b752a9a798 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Sun, 15 Aug 2021 12:32:48 +0900 Subject: [PATCH 07/22] can: etas_es58x: clean-up documentation of struct es58x_fd_tx_conf_msg The documentation of struct es58x_fd_tx_conf_msg explains in details the different TDC parameters. However, those description are redundant with the documentation of struct can_tdc. Remove most of the description. Also, fixes a typo in the reference to the datasheet (E701 -> E70). Link: https://lore.kernel.org/r/20210815033248.98111-8-mailhol.vincent@wanadoo.fr Signed-off-by: Vincent Mailhol Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/etas_es58x/es58x_fd.h | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/net/can/usb/etas_es58x/es58x_fd.h b/drivers/net/can/usb/etas_es58x/es58x_fd.h index ee18a87e40c0..a191891b8777 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_fd.h +++ b/drivers/net/can/usb/etas_es58x/es58x_fd.h @@ -96,23 +96,14 @@ struct es58x_fd_bittiming { * @ctrlmode: type enum es58x_fd_ctrlmode. * @canfd_enabled: boolean (0: Classical CAN, 1: CAN and/or CANFD). * @data_bittiming: Bittiming for flexible data-rate transmission. - * @tdc_enabled: Transmitter Delay Compensation switch (0: disabled, - * 1: enabled). On very high bitrates, the delay between when the - * bit is sent and received on the CANTX and CANRX pins of the - * transceiver start to be significant enough for errors to occur - * and thus need to be compensated. - * @tdco: Transmitter Delay Compensation Offset. Offset value, in time - * quanta, defining the delay between the start of the bit - * reception on the CANRX pin of the transceiver and the SSP - * (Secondary Sample Point). Valid values: 0 to 127. - * @tdcf: Transmitter Delay Compensation Filter window. Defines the - * minimum value for the SSP position, in time quanta. The - * feature is enabled when TDCF is configured to a value greater - * than TDCO. Valid values: 0 to 127. + * @tdc_enabled: Transmitter Delay Compensation switch (0: TDC is + * disabled, 1: TDC is enabled). + * @tdco: Transmitter Delay Compensation Offset. + * @tdcf: Transmitter Delay Compensation Filter window. * - * Please refer to the microcontroller datasheet: "SAM - * E701/S70/V70/V71 Family" section 49 "Controller Area Network - * (MCAN)" for additional information. + * Please refer to the microcontroller datasheet: "SAM E70/S70/V70/V71 + * Family" section 49 "Controller Area Network (MCAN)" for additional + * information. */ struct es58x_fd_tx_conf_msg { struct es58x_fd_bittiming nominal_bittiming; From b2fcc70799368a9bc25509da4c9127583523ec37 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 9 Aug 2021 20:31:54 +0200 Subject: [PATCH 08/22] can: mcp251xfd: mark some instances of struct mcp251xfd_priv as const With the patch 07ff4aed015c ("time/timecounter: Mark 1st argument of timecounter_cyc2time() as const") some instances of the struct mcp251xfd_priv can be marked as const. This patch marks these as const. Link: https://lore.kernel.org/r/20210813091027.159379-1-mkl@pengutronix.de Cc: Manivannan Sadhasivam Cc: Thomas Kopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c | 2 +- drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c | 4 ++-- drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c index 6c369a399c45..673861ab665a 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -1456,7 +1456,7 @@ mcp251xfd_rx_ring_update(const struct mcp251xfd_priv *priv, } static void -mcp251xfd_hw_rx_obj_to_skb(struct mcp251xfd_priv *priv, +mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv, const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, struct sk_buff *skb) { diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c index ed3169274d24..712e09186987 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c @@ -13,7 +13,7 @@ static u64 mcp251xfd_timestamp_read(const struct cyclecounter *cc) { - struct mcp251xfd_priv *priv; + const struct mcp251xfd_priv *priv; u32 timestamp = 0; int err; @@ -39,7 +39,7 @@ static void mcp251xfd_timestamp_work(struct work_struct *work) MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); } -void mcp251xfd_skb_set_timestamp(struct mcp251xfd_priv *priv, +void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv, struct sk_buff *skb, u32 timestamp) { struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h index 1002f3902ad2..0f322dabaf65 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h @@ -853,7 +853,7 @@ int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv); u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size, const void *data, size_t data_size); u16 mcp251xfd_crc16_compute(const void *data, size_t data_size); -void mcp251xfd_skb_set_timestamp(struct mcp251xfd_priv *priv, +void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv, struct sk_buff *skb, u32 timestamp); void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv); void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv); From 1aa5a06c0a5dbebd0351ffe5db77c06e9089e4fc Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 27 Jul 2021 14:30:20 +0100 Subject: [PATCH 09/22] dt-bindings: net: can: renesas,rcar-canfd: Document RZ/G2L SoC Add CANFD binding documentation for Renesas RZ/G2L SoC. Link: https://lore.kernel.org/r/20210727133022.634-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Reviewed-by: Geert Uytterhoeven Reviewed-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- .../bindings/net/can/renesas,rcar-canfd.yaml | 69 +++++++++++++++++-- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml index 0b33ba9ccb47..546c6e6d2fb0 100644 --- a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml +++ b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml @@ -30,13 +30,15 @@ properties: - renesas,r8a77995-canfd # R-Car D3 - const: renesas,rcar-gen3-canfd # R-Car Gen3 and RZ/G2 + - items: + - enum: + - renesas,r9a07g044-canfd # RZ/G2{L,LC} + - const: renesas,rzg2l-canfd # RZ/G2L family + reg: maxItems: 1 - interrupts: - items: - - description: Channel interrupt - - description: Global interrupt + interrupts: true clocks: maxItems: 3 @@ -50,8 +52,7 @@ properties: power-domains: maxItems: 1 - resets: - maxItems: 1 + resets: true renesas,no-can-fd: $ref: /schemas/types.yaml#/definitions/flag @@ -91,6 +92,62 @@ required: - channel0 - channel1 +if: + properties: + compatible: + contains: + enum: + - renesas,rzg2l-canfd +then: + properties: + interrupts: + items: + - description: CAN global error interrupt + - description: CAN receive FIFO interrupt + - description: CAN0 error interrupt + - description: CAN0 transmit interrupt + - description: CAN0 transmit/receive FIFO receive completion interrupt + - description: CAN1 error interrupt + - description: CAN1 transmit interrupt + - description: CAN1 transmit/receive FIFO receive completion interrupt + + interrupt-names: + items: + - const: g_err + - const: g_recc + - const: ch0_err + - const: ch0_rec + - const: ch0_trx + - const: ch1_err + - const: ch1_rec + - const: ch1_trx + + resets: + maxItems: 2 + + reset-names: + items: + - const: rstp_n + - const: rstc_n + + required: + - interrupt-names + - reset-names +else: + properties: + interrupts: + items: + - description: Channel interrupt + - description: Global interrupt + + interrupt-names: + items: + - const: ch_int + - const: g_int + + resets: + maxItems: 1 + unevaluatedProperties: false examples: From 76e9353a80e9e9ff65b362a6dd8545d84427ec30 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 27 Jul 2021 14:30:21 +0100 Subject: [PATCH 10/22] can: rcar_canfd: Add support for RZ/G2L family CANFD block on RZ/G2L SoC is almost identical to one found on R-Car Gen3 SoC's. On RZ/G2L SoC interrupt sources for each channel are split into different sources and the IP doesn't divide (1/2) CANFD clock within the IP. This patch adds compatible string for RZ/G2L family and splits the irq handlers to accommodate both RZ/G2L and R-Car Gen3 SoC's. Link: https://lore.kernel.org/r/20210727133022.634-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das [mkl: fixed typo: recieve -> receive, thanks Geert] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 338 +++++++++++++++++++++++------- 1 file changed, 265 insertions(+), 73 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 311e6ca3bdc4..5d4d52afde15 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -37,9 +37,15 @@ #include #include #include +#include #define RCANFD_DRV_NAME "rcar_canfd" +enum rcanfd_chip_id { + RENESAS_RCAR_GEN3 = 0, + RENESAS_RZG2L, +}; + /* Global register bits */ /* RSCFDnCFDGRMCFG */ @@ -513,6 +519,9 @@ struct rcar_canfd_global { enum rcar_canfd_fcanclk fcan; /* CANFD or Ext clock */ unsigned long channels_mask; /* Enabled channels mask */ bool fdmode; /* CAN FD or Classical CAN only mode */ + struct reset_control *rstc1; + struct reset_control *rstc2; + enum rcanfd_chip_id chip_id; }; /* CAN FD mode nominal rate constants */ @@ -1070,38 +1079,70 @@ static void rcar_canfd_tx_done(struct net_device *ndev) can_led_event(ndev, CAN_LED_EVENT_TX); } +static void rcar_canfd_handle_global_err(struct rcar_canfd_global *gpriv, u32 ch) +{ + struct rcar_canfd_channel *priv = gpriv->ch[ch]; + struct net_device *ndev = priv->ndev; + u32 gerfl; + + /* Handle global error interrupts */ + gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); + if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl))) + rcar_canfd_global_error(ndev); +} + +static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id) +{ + struct rcar_canfd_global *gpriv = dev_id; + u32 ch; + + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + rcar_canfd_handle_global_err(gpriv, ch); + + return IRQ_HANDLED; +} + +static void rcar_canfd_handle_global_receive(struct rcar_canfd_global *gpriv, u32 ch) +{ + struct rcar_canfd_channel *priv = gpriv->ch[ch]; + u32 ridx = ch + RCANFD_RFFIFO_IDX; + u32 sts; + + /* Handle Rx interrupts */ + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + if (likely(sts & RCANFD_RFSTS_RFIF)) { + if (napi_schedule_prep(&priv->napi)) { + /* Disable Rx FIFO interrupts */ + rcar_canfd_clear_bit(priv->base, + RCANFD_RFCC(ridx), + RCANFD_RFCC_RFIE); + __napi_schedule(&priv->napi); + } + } +} + +static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_id) +{ + struct rcar_canfd_global *gpriv = dev_id; + u32 ch; + + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + rcar_canfd_handle_global_receive(gpriv, ch); + + return IRQ_HANDLED; +} + static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) { struct rcar_canfd_global *gpriv = dev_id; - struct net_device *ndev; - struct rcar_canfd_channel *priv; - u32 sts, gerfl; - u32 ch, ridx; + u32 ch; /* Global error interrupts still indicate a condition specific * to a channel. RxFIFO interrupt is a global interrupt. */ for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { - priv = gpriv->ch[ch]; - ndev = priv->ndev; - ridx = ch + RCANFD_RFFIFO_IDX; - - /* Global error interrupts */ - gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); - if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl))) - rcar_canfd_global_error(ndev); - - /* Handle Rx interrupts */ - sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); - if (likely(sts & RCANFD_RFSTS_RFIF)) { - if (napi_schedule_prep(&priv->napi)) { - /* Disable Rx FIFO interrupts */ - rcar_canfd_clear_bit(priv->base, - RCANFD_RFCC(ridx), - RCANFD_RFCC_RFIE); - __napi_schedule(&priv->napi); - } - } + rcar_canfd_handle_global_err(gpriv, ch); + rcar_canfd_handle_global_receive(gpriv, ch); } return IRQ_HANDLED; } @@ -1139,38 +1180,73 @@ static void rcar_canfd_state_change(struct net_device *ndev, } } +static void rcar_canfd_handle_channel_tx(struct rcar_canfd_global *gpriv, u32 ch) +{ + struct rcar_canfd_channel *priv = priv = gpriv->ch[ch]; + struct net_device *ndev = priv->ndev; + u32 sts; + + /* Handle Tx interrupts */ + sts = rcar_canfd_read(priv->base, + RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + if (likely(sts & RCANFD_CFSTS_CFTXIF)) + rcar_canfd_tx_done(ndev); +} + +static irqreturn_t rcar_canfd_channel_tx_interrupt(int irq, void *dev_id) +{ + struct rcar_canfd_global *gpriv = dev_id; + u32 ch; + + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + rcar_canfd_handle_channel_tx(gpriv, ch); + + return IRQ_HANDLED; +} + +static void rcar_canfd_handle_channel_err(struct rcar_canfd_global *gpriv, u32 ch) +{ + struct rcar_canfd_channel *priv = gpriv->ch[ch]; + struct net_device *ndev = priv->ndev; + u16 txerr, rxerr; + u32 sts, cerfl; + + /* Handle channel error interrupts */ + cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); + sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); + txerr = RCANFD_CSTS_TECCNT(sts); + rxerr = RCANFD_CSTS_RECCNT(sts); + if (unlikely(RCANFD_CERFL_ERR(cerfl))) + rcar_canfd_error(ndev, cerfl, txerr, rxerr); + + /* Handle state change to lower states */ + if (unlikely(priv->can.state != CAN_STATE_ERROR_ACTIVE && + priv->can.state != CAN_STATE_BUS_OFF)) + rcar_canfd_state_change(ndev, txerr, rxerr); +} + +static irqreturn_t rcar_canfd_channel_err_interrupt(int irq, void *dev_id) +{ + struct rcar_canfd_global *gpriv = dev_id; + u32 ch; + + for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + rcar_canfd_handle_channel_err(gpriv, ch); + + return IRQ_HANDLED; +} + static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) { struct rcar_canfd_global *gpriv = dev_id; - struct net_device *ndev; - struct rcar_canfd_channel *priv; - u32 sts, ch, cerfl; - u16 txerr, rxerr; + u32 ch; /* Common FIFO is a per channel resource */ for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { - priv = gpriv->ch[ch]; - ndev = priv->ndev; - - /* Channel error interrupts */ - cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); - sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); - txerr = RCANFD_CSTS_TECCNT(sts); - rxerr = RCANFD_CSTS_RECCNT(sts); - if (unlikely(RCANFD_CERFL_ERR(cerfl))) - rcar_canfd_error(ndev, cerfl, txerr, rxerr); - - /* Handle state change to lower states */ - if (unlikely((priv->can.state != CAN_STATE_ERROR_ACTIVE) && - (priv->can.state != CAN_STATE_BUS_OFF))) - rcar_canfd_state_change(ndev, txerr, rxerr); - - /* Handle Tx interrupts */ - sts = rcar_canfd_read(priv->base, - RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); - if (likely(sts & RCANFD_CFSTS_CFTXIF)) - rcar_canfd_tx_done(ndev); + rcar_canfd_handle_channel_err(gpriv, ch); + rcar_canfd_handle_channel_tx(gpriv, ch); } + return IRQ_HANDLED; } @@ -1577,6 +1653,53 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, priv->can.clock.freq = fcan_freq; dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq); + if (gpriv->chip_id == RENESAS_RZG2L) { + char *irq_name; + int err_irq; + int tx_irq; + + err_irq = platform_get_irq_byname(pdev, ch == 0 ? "ch0_err" : "ch1_err"); + if (err_irq < 0) { + err = err_irq; + goto fail; + } + + tx_irq = platform_get_irq_byname(pdev, ch == 0 ? "ch0_trx" : "ch1_trx"); + if (tx_irq < 0) { + err = tx_irq; + goto fail; + } + + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "canfd.ch%d_err", ch); + if (!irq_name) { + err = -ENOMEM; + goto fail; + } + err = devm_request_irq(&pdev->dev, err_irq, + rcar_canfd_channel_err_interrupt, 0, + irq_name, gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq CH Err(%d) failed, error %d\n", + err_irq, err); + goto fail; + } + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "canfd.ch%d_trx", ch); + if (!irq_name) { + err = -ENOMEM; + goto fail; + } + err = devm_request_irq(&pdev->dev, tx_irq, + rcar_canfd_channel_tx_interrupt, 0, + irq_name, gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq Tx (%d) failed, error %d\n", + tx_irq, err); + goto fail; + } + } + if (gpriv->fdmode) { priv->can.bittiming_const = &rcar_canfd_nom_bittiming_const; priv->can.data_bittiming_const = @@ -1636,7 +1759,11 @@ static int rcar_canfd_probe(struct platform_device *pdev) struct device_node *of_child; unsigned long channels_mask = 0; int err, ch_irq, g_irq; + int g_err_irq, g_recc_irq; bool fdmode = true; /* CAN FD only mode - default */ + enum rcanfd_chip_id chip_id; + + chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) fdmode = false; /* Classical CAN only mode */ @@ -1649,16 +1776,30 @@ static int rcar_canfd_probe(struct platform_device *pdev) if (of_child && of_device_is_available(of_child)) channels_mask |= BIT(1); /* Channel 1 */ - ch_irq = platform_get_irq(pdev, 0); - if (ch_irq < 0) { - err = ch_irq; - goto fail_dev; - } + if (chip_id == RENESAS_RCAR_GEN3) { + ch_irq = platform_get_irq_byname_optional(pdev, "ch_int"); + if (ch_irq < 0) { + /* For backward compatibility get irq by index */ + ch_irq = platform_get_irq(pdev, 0); + if (ch_irq < 0) + return ch_irq; + } - g_irq = platform_get_irq(pdev, 1); - if (g_irq < 0) { - err = g_irq; - goto fail_dev; + g_irq = platform_get_irq_byname_optional(pdev, "g_int"); + if (g_irq < 0) { + /* For backward compatibility get irq by index */ + g_irq = platform_get_irq(pdev, 1); + if (g_irq < 0) + return g_irq; + } + } else { + g_err_irq = platform_get_irq_byname(pdev, "g_err"); + if (g_err_irq < 0) + return g_err_irq; + + g_recc_irq = platform_get_irq_byname(pdev, "g_recc"); + if (g_recc_irq < 0) + return g_recc_irq; } /* Global controller context */ @@ -1670,6 +1811,19 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->pdev = pdev; gpriv->channels_mask = channels_mask; gpriv->fdmode = fdmode; + gpriv->chip_id = chip_id; + + if (gpriv->chip_id == RENESAS_RZG2L) { + gpriv->rstc1 = devm_reset_control_get_exclusive(&pdev->dev, "rstp_n"); + if (IS_ERR(gpriv->rstc1)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1), + "failed to get rstp_n\n"); + + gpriv->rstc2 = devm_reset_control_get_exclusive(&pdev->dev, "rstc_n"); + if (IS_ERR(gpriv->rstc2)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2), + "failed to get rstc_n\n"); + } /* Peripheral clock */ gpriv->clkp = devm_clk_get(&pdev->dev, "fck"); @@ -1699,7 +1853,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) } fcan_freq = clk_get_rate(gpriv->can_clk); - if (gpriv->fcan == RCANFD_CANFDCLK) + if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id == RENESAS_RCAR_GEN3) /* CANFD clock is further divided by (1/2) within the IP */ fcan_freq /= 2; @@ -1711,20 +1865,51 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->base = addr; /* Request IRQ that's common for both channels */ - err = devm_request_irq(&pdev->dev, ch_irq, - rcar_canfd_channel_interrupt, 0, - "canfd.chn", gpriv); - if (err) { - dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", - ch_irq, err); - goto fail_dev; + if (gpriv->chip_id == RENESAS_RCAR_GEN3) { + err = devm_request_irq(&pdev->dev, ch_irq, + rcar_canfd_channel_interrupt, 0, + "canfd.ch_int", gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", + ch_irq, err); + goto fail_dev; + } + + err = devm_request_irq(&pdev->dev, g_irq, + rcar_canfd_global_interrupt, 0, + "canfd.g_int", gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", + g_irq, err); + goto fail_dev; + } + } else { + err = devm_request_irq(&pdev->dev, g_recc_irq, + rcar_canfd_global_receive_fifo_interrupt, 0, + "canfd.g_recc", gpriv); + + if (err) { + dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", + g_recc_irq, err); + goto fail_dev; + } + + err = devm_request_irq(&pdev->dev, g_err_irq, + rcar_canfd_global_err_interrupt, 0, + "canfd.g_err", gpriv); + if (err) { + dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", + g_err_irq, err); + goto fail_dev; + } } - err = devm_request_irq(&pdev->dev, g_irq, - rcar_canfd_global_interrupt, 0, - "canfd.gbl", gpriv); + + err = reset_control_reset(gpriv->rstc1); + if (err) + goto fail_dev; + err = reset_control_reset(gpriv->rstc2); if (err) { - dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n", - g_irq, err); + reset_control_assert(gpriv->rstc1); goto fail_dev; } @@ -1733,7 +1918,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "failed to enable peripheral clock, error %d\n", err); - goto fail_dev; + goto fail_reset; } err = rcar_canfd_reset_controller(gpriv); @@ -1790,6 +1975,9 @@ fail_mode: rcar_canfd_disable_global_interrupts(gpriv); fail_clk: clk_disable_unprepare(gpriv->clkp); +fail_reset: + reset_control_assert(gpriv->rstc1); + reset_control_assert(gpriv->rstc2); fail_dev: return err; } @@ -1810,6 +1998,9 @@ static int rcar_canfd_remove(struct platform_device *pdev) /* Enter global sleep mode */ rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR); clk_disable_unprepare(gpriv->clkp); + reset_control_assert(gpriv->rstc1); + reset_control_assert(gpriv->rstc2); + return 0; } @@ -1827,7 +2018,8 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend, rcar_canfd_resume); static const struct of_device_id rcar_canfd_of_table[] = { - { .compatible = "renesas,rcar-gen3-canfd" }, + { .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 }, + { .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L }, { } }; From fede1ae2d35784a37827f6a437fa742fdd463af1 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 19 Aug 2021 11:48:45 +0200 Subject: [PATCH 11/22] can: tcan4x5x: cdev_to_priv(): remove stray empty line This patch removes a stray empty line in the cdev_to_priv() function. Fixes: ac33ffd3e2b0 ("can: m_can: let m_can_class_allocate_dev() allocate driver specific private data") Link: https://lore.kernel.org/r/20210819111703.599686-1-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index 4147cecfbbd6..a4cbfedb6621 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -105,7 +105,6 @@ static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev) { return container_of(cdev, struct tcan4x5x_priv, cdev); - } static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv) From 5020ced4455be2bc6b992714738f34c7b248018d Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 19 Aug 2021 12:20:03 +0200 Subject: [PATCH 12/22] can: m_can: fix block comment style This patch fixes the commenting style in the m_can driver. Fixes: 1be37d3b0414 ("can: m_can: fix periph RX path: use rx-offload to ensure skbs are sent from softirq context") Fixes: df06fd678260 ("can: m_can: m_can_chip_config(): enable and configure internal timestamps") Link: https://lore.kernel.org/r/20210819111703.599686-2-mkl@pengutronix.de Cc: Chandrasekar Ramakrishnan Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 91351eef6bf8..47ddee80c423 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -437,7 +437,7 @@ static void m_can_clean(struct net_device *net) * napi. For non-peripherals, RX is done in napi already, so push * directly. timestamp is used to ensure good skb ordering in * rx-offload and is ignored for non-peripherals. -*/ + */ static void m_can_receive_skb(struct m_can_classdev *cdev, struct sk_buff *skb, u32 timestamp) @@ -946,7 +946,7 @@ static int m_can_poll(struct napi_struct *napi, int quota) /* Echo tx skb and update net stats. Peripherals use rx-offload for * echo. timestamp is used for peripherals to ensure correct ordering * by rx-offload, and is ignored for non-peripherals. -*/ + */ static void m_can_tx_update_stats(struct m_can_classdev *cdev, unsigned int msg_mark, u32 timestamp) @@ -1306,7 +1306,8 @@ static void m_can_chip_config(struct net_device *dev) m_can_set_bittiming(dev); /* enable internal timestamp generation, with a prescalar of 16. The - * prescalar is applied to the nominal bit timing */ + * prescalar is applied to the nominal bit timing + */ m_can_write(cdev, M_CAN_TSCC, FIELD_PREP(TSCC_TCP_MASK, 0xf)); m_can_config_endisable(cdev, false); From e39381770ec9ca3c51d8b9bd9cc6e01d78ea974a Mon Sep 17 00:00:00 2001 From: Matt Kline Date: Mon, 16 Aug 2021 22:08:51 -0700 Subject: [PATCH 13/22] can: m_can: Disable IRQs on FIFO bus errors If FIFO reads or writes fail due to the underlying regmap (e.g., SPI) I/O, propagate that up to the m_can driver, log an error, and disable interrupts, similar to the mcp251xfd driver. While reworking the FIFO functions to add this error handling, add support for bulk reads and writes of multiple registers. Link: https://lore.kernel.org/r/20210817050853.14875-2-matt@bitbashing.io Signed-off-by: Matt Kline [mkl: re-wrap long lines, remove WARN_ON, convert to netdev block comments] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 177 +++++++++++++++++-------- drivers/net/can/m_can/m_can.h | 6 +- drivers/net/can/m_can/m_can_pci.c | 11 +- drivers/net/can/m_can/m_can_platform.c | 15 ++- drivers/net/can/m_can/tcan4x5x-core.c | 16 +-- 5 files changed, 152 insertions(+), 73 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 47ddee80c423..8922ca0f8e94 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -320,36 +320,39 @@ static inline void m_can_write(struct m_can_classdev *cdev, enum m_can_reg reg, cdev->ops->write_reg(cdev, reg, val); } -static u32 m_can_fifo_read(struct m_can_classdev *cdev, - u32 fgi, unsigned int offset) +static int +m_can_fifo_read(struct m_can_classdev *cdev, + u32 fgi, unsigned int offset, void *val, size_t val_count) { u32 addr_offset = cdev->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE + offset; - return cdev->ops->read_fifo(cdev, addr_offset); + return cdev->ops->read_fifo(cdev, addr_offset, val, val_count); } -static void m_can_fifo_write(struct m_can_classdev *cdev, - u32 fpi, unsigned int offset, u32 val) +static int +m_can_fifo_write(struct m_can_classdev *cdev, + u32 fpi, unsigned int offset, const void *val, size_t val_count) { u32 addr_offset = cdev->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE + offset; - cdev->ops->write_fifo(cdev, addr_offset, val); + return cdev->ops->write_fifo(cdev, addr_offset, val, val_count); } -static inline void m_can_fifo_write_no_off(struct m_can_classdev *cdev, - u32 fpi, u32 val) +static inline int m_can_fifo_write_no_off(struct m_can_classdev *cdev, + u32 fpi, u32 val) { - cdev->ops->write_fifo(cdev, fpi, val); + return cdev->ops->write_fifo(cdev, fpi, &val, 1); } -static u32 m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset) +static int +m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val) { u32 addr_offset = cdev->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE + offset; - return cdev->ops->read_fifo(cdev, addr_offset); + return cdev->ops->read_fifo(cdev, addr_offset, val, 1); } static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev) @@ -455,7 +458,7 @@ static void m_can_receive_skb(struct m_can_classdev *cdev, } } -static void m_can_read_fifo(struct net_device *dev, u32 rxfs) +static int m_can_read_fifo(struct net_device *dev, u32 rxfs) { struct net_device_stats *stats = &dev->stats; struct m_can_classdev *cdev = netdev_priv(dev); @@ -463,18 +466,21 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) struct sk_buff *skb; u32 id, fgi, dlc; u32 timestamp = 0; - int i; + int i, err; /* calculate the fifo get index for where to read data */ fgi = FIELD_GET(RXFS_FGI_MASK, rxfs); - dlc = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DLC); + err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DLC, &dlc, 1); + if (err) + goto out_fail; + if (dlc & RX_BUF_FDF) skb = alloc_canfd_skb(dev, &cf); else skb = alloc_can_skb(dev, (struct can_frame **)&cf); if (!skb) { stats->rx_dropped++; - return; + return 0; } if (dlc & RX_BUF_FDF) @@ -482,7 +488,10 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) else cf->len = can_cc_dlc2len((dlc >> 16) & 0x0F); - id = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID); + err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &id, 1); + if (err) + goto out_fail; + if (id & RX_BUF_XTD) cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; else @@ -499,10 +508,11 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) if (dlc & RX_BUF_BRS) cf->flags |= CANFD_BRS; - for (i = 0; i < cf->len; i += 4) - *(u32 *)(cf->data + i) = - m_can_fifo_read(cdev, fgi, - M_CAN_FIFO_DATA(i / 4)); + for (i = 0; i < cf->len; i += 4) { + err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA(i / 4), cf->data + i, 1); + if (err) + goto out_fail; + } } /* acknowledge rx fifo 0 */ @@ -514,6 +524,12 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) timestamp = FIELD_GET(RX_BUF_RXTS_MASK, dlc); m_can_receive_skb(cdev, skb, timestamp); + + return 0; + +out_fail: + netdev_err(dev, "FIFO read returned %d\n", err); + return err; } static int m_can_do_rx_poll(struct net_device *dev, int quota) @@ -521,6 +537,7 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) struct m_can_classdev *cdev = netdev_priv(dev); u32 pkts = 0; u32 rxfs; + int err; rxfs = m_can_read(cdev, M_CAN_RXF0S); if (!(rxfs & RXFS_FFL_MASK)) { @@ -529,7 +546,9 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) } while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) { - m_can_read_fifo(dev, rxfs); + err = m_can_read_fifo(dev, rxfs); + if (err) + return err; quota--; pkts++; @@ -875,6 +894,7 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, static int m_can_rx_handler(struct net_device *dev, int quota) { struct m_can_classdev *cdev = netdev_priv(dev); + int rx_work_or_err; int work_done = 0; u32 irqstatus, psr; @@ -911,8 +931,13 @@ static int m_can_rx_handler(struct net_device *dev, int quota) if (irqstatus & IR_ERR_BUS_30X) work_done += m_can_handle_bus_errors(dev, irqstatus, psr); - if (irqstatus & IR_RF0N) - work_done += m_can_do_rx_poll(dev, (quota - work_done)); + if (irqstatus & IR_RF0N) { + rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done)); + if (rx_work_or_err < 0) + return rx_work_or_err; + + work_done += rx_work_or_err; + } end: return work_done; } @@ -920,12 +945,17 @@ end: static int m_can_rx_peripheral(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); + int work_done; - m_can_rx_handler(dev, M_CAN_NAPI_WEIGHT); + work_done = m_can_rx_handler(dev, M_CAN_NAPI_WEIGHT); - m_can_enable_all_interrupts(cdev); + /* Don't re-enable interrupts if the driver had a fatal error + * (e.g., FIFO read failure). + */ + if (work_done >= 0) + m_can_enable_all_interrupts(cdev); - return 0; + return work_done; } static int m_can_poll(struct napi_struct *napi, int quota) @@ -935,7 +965,11 @@ static int m_can_poll(struct napi_struct *napi, int quota) int work_done; work_done = m_can_rx_handler(dev, quota); - if (work_done < quota) { + + /* Don't re-enable interrupts if the driver had a fatal error + * (e.g., FIFO read failure). + */ + if (work_done >= 0 && work_done < quota) { napi_complete_done(napi, work_done); m_can_enable_all_interrupts(cdev); } @@ -966,7 +1000,7 @@ static void m_can_tx_update_stats(struct m_can_classdev *cdev, stats->tx_packets++; } -static void m_can_echo_tx_event(struct net_device *dev) +static int m_can_echo_tx_event(struct net_device *dev) { u32 txe_count = 0; u32 m_can_txefs; @@ -985,12 +1019,18 @@ static void m_can_echo_tx_event(struct net_device *dev) /* Get and process all sent elements */ for (i = 0; i < txe_count; i++) { u32 txe, timestamp = 0; + int err; /* retrieve get index */ fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_read(cdev, M_CAN_TXEFS)); /* get message marker, timestamp */ - txe = m_can_txe_fifo_read(cdev, fgi, 4); + err = m_can_txe_fifo_read(cdev, fgi, 4, &txe); + if (err) { + netdev_err(dev, "TXE FIFO read returned %d\n", err); + return err; + } + msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe); timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe); @@ -1001,6 +1041,8 @@ static void m_can_echo_tx_event(struct net_device *dev) /* update stats */ m_can_tx_update_stats(cdev, msg_mark, timestamp); } + + return 0; } static irqreturn_t m_can_isr(int irq, void *dev_id) @@ -1032,8 +1074,8 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) m_can_disable_all_interrupts(cdev); if (!cdev->is_peripheral) napi_schedule(&cdev->napi); - else - m_can_rx_peripheral(dev); + else if (m_can_rx_peripheral(dev) < 0) + goto out_fail; } if (cdev->version == 30) { @@ -1051,7 +1093,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) } else { if (ir & IR_TEFN) { /* New TX FIFO Element arrived */ - m_can_echo_tx_event(dev); + if (m_can_echo_tx_event(dev) != 0) + goto out_fail; + can_led_event(dev, CAN_LED_EVENT_TX); if (netif_queue_stopped(dev) && !m_can_tx_fifo_full(cdev)) @@ -1063,6 +1107,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) can_rx_offload_threaded_irq_finish(&cdev->offload); return IRQ_HANDLED; + +out_fail: + m_can_disable_all_interrupts(cdev); + return IRQ_HANDLED; } static const struct can_bittiming_const m_can_bittiming_const_30X = { @@ -1535,8 +1583,8 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data; struct net_device *dev = cdev->net; struct sk_buff *skb = cdev->tx_skb; - u32 id, cccr, fdflags; - int i; + u32 id, dlc, cccr, fdflags; + int i, err; int putidx; cdev->tx_skb = NULL; @@ -1557,14 +1605,20 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) netif_stop_queue(dev); /* message ram configuration */ - m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, id); - m_can_fifo_write(cdev, 0, M_CAN_FIFO_DLC, - can_fd_len2dlc(cf->len) << 16); + err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, &id, 1); + if (err) + goto out_fail; - for (i = 0; i < cf->len; i += 4) - m_can_fifo_write(cdev, 0, - M_CAN_FIFO_DATA(i / 4), - *(u32 *)(cf->data + i)); + dlc = can_fd_len2dlc(cf->len) << 16; + err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DLC, &dlc, 1); + if (err) + goto out_fail; + + for (i = 0; i < cf->len; i += 4) { + err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DATA(i / 4), cf->data + i, 1); + if (err) + goto out_fail; + } can_put_echo_skb(skb, dev, 0, 0); @@ -1609,7 +1663,9 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) putidx = FIELD_GET(TXFQS_TFQPI_MASK, m_can_read(cdev, M_CAN_TXFQS)); /* Write ID Field to FIFO Element */ - m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, id); + err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, &id, 1); + if (err) + goto out_fail; /* get CAN FD configuration of frame */ fdflags = 0; @@ -1624,15 +1680,19 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) * it is used in TX interrupt for * sending the correct echo frame */ - m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DLC, - FIELD_PREP(TX_BUF_MM_MASK, putidx) | - FIELD_PREP(TX_BUF_DLC_MASK, - can_fd_len2dlc(cf->len)) | - fdflags | TX_BUF_EFC); + dlc = FIELD_PREP(TX_BUF_MM_MASK, putidx) | + FIELD_PREP(TX_BUF_DLC_MASK, can_fd_len2dlc(cf->len)) | + fdflags | TX_BUF_EFC; + err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DLC, &dlc, 1); + if (err) + goto out_fail; - for (i = 0; i < cf->len; i += 4) - m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA(i / 4), - *(u32 *)(cf->data + i)); + for (i = 0; i < cf->len; i += 4) { + err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA(i / 4), + cf->data + i, 1); + if (err) + goto out_fail; + } /* Push loopback echo. * Will be looped back on TX interrupt based on message marker @@ -1649,6 +1709,11 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) } return NETDEV_TX_OK; + +out_fail: + netdev_err(dev, "FIFO write returned %d\n", err); + m_can_disable_all_interrupts(cdev); + return NETDEV_TX_BUSY; } static void m_can_tx_work_queue(struct work_struct *ws) @@ -1820,9 +1885,10 @@ static void m_can_of_parse_mram(struct m_can_classdev *cdev, cdev->mcfg[MRAM_TXB].off, cdev->mcfg[MRAM_TXB].num); } -void m_can_init_ram(struct m_can_classdev *cdev) +int m_can_init_ram(struct m_can_classdev *cdev) { int end, i, start; + int err = 0; /* initialize the entire Message RAM in use to avoid possible * ECC/parity checksum errors when reading an uninitialized buffer @@ -1831,8 +1897,13 @@ void m_can_init_ram(struct m_can_classdev *cdev) end = cdev->mcfg[MRAM_TXB].off + cdev->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE; - for (i = start; i < end; i += 4) - m_can_fifo_write_no_off(cdev, i, 0x0); + for (i = start; i < end; i += 4) { + err = m_can_fifo_write_no_off(cdev, i, 0x0); + if (err) + break; + } + + return err; } EXPORT_SYMBOL_GPL(m_can_init_ram); diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 56e994376a7b..d18b515e6ccc 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -65,9 +65,9 @@ struct m_can_ops { int (*clear_interrupts)(struct m_can_classdev *cdev); u32 (*read_reg)(struct m_can_classdev *cdev, int reg); int (*write_reg)(struct m_can_classdev *cdev, int reg, int val); - u32 (*read_fifo)(struct m_can_classdev *cdev, int addr_offset); + int (*read_fifo)(struct m_can_classdev *cdev, int addr_offset, void *val, size_t val_count); int (*write_fifo)(struct m_can_classdev *cdev, int addr_offset, - int val); + const void *val, size_t val_count); int (*init)(struct m_can_classdev *cdev); }; @@ -101,7 +101,7 @@ void m_can_class_free_dev(struct net_device *net); int m_can_class_register(struct m_can_classdev *cdev); void m_can_class_unregister(struct m_can_classdev *cdev); int m_can_class_get_clocks(struct m_can_classdev *cdev); -void m_can_init_ram(struct m_can_classdev *priv); +int m_can_init_ram(struct m_can_classdev *priv); int m_can_class_suspend(struct device *dev); int m_can_class_resume(struct device *dev); diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c index 128808605c3f..89cc3d41e952 100644 --- a/drivers/net/can/m_can/m_can_pci.c +++ b/drivers/net/can/m_can/m_can_pci.c @@ -39,11 +39,13 @@ static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg) return readl(priv->base + reg); } -static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset) +static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count) { struct m_can_pci_priv *priv = cdev_to_priv(cdev); - return readl(priv->base + offset); + ioread32_rep(priv->base + offset, val, val_count); + + return 0; } static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) @@ -55,11 +57,12 @@ static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) return 0; } -static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val) +static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, + const void *val, size_t val_count) { struct m_can_pci_priv *priv = cdev_to_priv(cdev); - writel(val, priv->base + offset); + iowrite32_rep(priv->base + offset, val, val_count); return 0; } diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index a28c84aa8fa8..308d4f2fff00 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -29,11 +29,13 @@ static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg) return readl(priv->base + reg); } -static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset) +static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count) { struct m_can_plat_priv *priv = cdev_to_priv(cdev); - return readl(priv->mram_base + offset); + ioread32_rep(priv->mram_base + offset, val, val_count); + + return 0; } static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) @@ -45,11 +47,12 @@ static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) return 0; } -static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val) +static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, + const void *val, size_t val_count) { struct m_can_plat_priv *priv = cdev_to_priv(cdev); - writel(val, priv->mram_base + offset); + iowrite32_rep(priv->base + offset, val, val_count); return 0; } @@ -127,7 +130,9 @@ static int m_can_plat_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mcan_class); - m_can_init_ram(mcan_class); + ret = m_can_init_ram(mcan_class); + if (ret) + goto probe_fail; pm_runtime_enable(mcan_class->dev); ret = m_can_class_register(mcan_class); diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index a4cbfedb6621..04687b15b250 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -153,14 +153,12 @@ static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg) return val; } -static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset) +static int tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset, + void *val, size_t val_count) { struct tcan4x5x_priv *priv = cdev_to_priv(cdev); - u32 val; - regmap_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, &val); - - return val; + return regmap_bulk_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count); } static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val) @@ -171,11 +169,11 @@ static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val) } static int tcan4x5x_write_fifo(struct m_can_classdev *cdev, - int addr_offset, int val) + int addr_offset, const void *val, size_t val_count) { struct tcan4x5x_priv *priv = cdev_to_priv(cdev); - return regmap_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val); + return regmap_bulk_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count); } static int tcan4x5x_power_enable(struct regulator *reg, int enable) @@ -237,7 +235,9 @@ static int tcan4x5x_init(struct m_can_classdev *cdev) return ret; /* Zero out the MCAN buffers */ - m_can_init_ram(cdev); + ret = m_can_init_ram(cdev); + if (ret) + return ret; ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG, TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL); From 1aa6772f64b4a6763f14ae68455d7e42333e208e Mon Sep 17 00:00:00 2001 From: Matt Kline Date: Mon, 16 Aug 2021 22:08:52 -0700 Subject: [PATCH 14/22] can: m_can: Batch FIFO reads during CAN receive On peripherals communicating over a relatively slow SPI line (e.g. tcan4x5x), individual transfers have high fixed costs. This causes the driver to spend most of its time waiting between transfers and severely limits throughput. Reduce these overheads by reading more than one word at a time. Writing could get a similar treatment in follow-on commits. Link: https://lore.kernel.org/r/20210817050853.14875-3-matt@bitbashing.io Signed-off-by: Matt Kline [mkl: remove __packed from struct id_and_dlc] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 51 +++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 8922ca0f8e94..fbd32b48d265 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -309,6 +309,15 @@ enum m_can_reg { #define TX_EVENT_MM_MASK GENMASK(31, 24) #define TX_EVENT_TXTS_MASK GENMASK(15, 0) +/* The ID and DLC registers are adjacent in M_CAN FIFO memory, + * and we can save a (potentially slow) bus round trip by combining + * reads and writes to them. + */ +struct id_and_dlc { + u32 id; + u32 dlc; +}; + static inline u32 m_can_read(struct m_can_classdev *cdev, enum m_can_reg reg) { return cdev->ops->read_reg(cdev, reg); @@ -464,17 +473,18 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs) struct m_can_classdev *cdev = netdev_priv(dev); struct canfd_frame *cf; struct sk_buff *skb; - u32 id, fgi, dlc; + struct id_and_dlc fifo_header; + u32 fgi; u32 timestamp = 0; - int i, 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_DLC, &dlc, 1); + err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2); if (err) goto out_fail; - if (dlc & RX_BUF_FDF) + if (fifo_header.dlc & RX_BUF_FDF) skb = alloc_canfd_skb(dev, &cf); else skb = alloc_can_skb(dev, (struct can_frame **)&cf); @@ -483,36 +493,31 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs) return 0; } - if (dlc & RX_BUF_FDF) - cf->len = can_fd_dlc2len((dlc >> 16) & 0x0F); + if (fifo_header.dlc & RX_BUF_FDF) + cf->len = can_fd_dlc2len((fifo_header.dlc >> 16) & 0x0F); else - cf->len = can_cc_dlc2len((dlc >> 16) & 0x0F); + cf->len = can_cc_dlc2len((fifo_header.dlc >> 16) & 0x0F); - err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &id, 1); - if (err) - goto out_fail; - - if (id & RX_BUF_XTD) - cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; + if (fifo_header.id & RX_BUF_XTD) + cf->can_id = (fifo_header.id & CAN_EFF_MASK) | CAN_EFF_FLAG; else - cf->can_id = (id >> 18) & CAN_SFF_MASK; + cf->can_id = (fifo_header.id >> 18) & CAN_SFF_MASK; - if (id & RX_BUF_ESI) { + if (fifo_header.id & RX_BUF_ESI) { cf->flags |= CANFD_ESI; netdev_dbg(dev, "ESI Error\n"); } - if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) { + if (!(fifo_header.dlc & RX_BUF_FDF) && (fifo_header.id & RX_BUF_RTR)) { cf->can_id |= CAN_RTR_FLAG; } else { - if (dlc & RX_BUF_BRS) + if (fifo_header.dlc & RX_BUF_BRS) cf->flags |= CANFD_BRS; - for (i = 0; i < cf->len; i += 4) { - err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA(i / 4), cf->data + i, 1); - if (err) - goto out_fail; - } + err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA(0), + cf->data, DIV_ROUND_UP(cf->len, 4)); + if (err) + goto out_fail; } /* acknowledge rx fifo 0 */ @@ -521,7 +526,7 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs) stats->rx_packets++; stats->rx_bytes += cf->len; - timestamp = FIELD_GET(RX_BUF_RXTS_MASK, dlc); + timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc); m_can_receive_skb(cdev, skb, timestamp); From 812270e5445bd13859b70f9510e84068c6778333 Mon Sep 17 00:00:00 2001 From: Matt Kline Date: Mon, 16 Aug 2021 22:08:53 -0700 Subject: [PATCH 15/22] can: m_can: Batch FIFO writes during CAN transmit Give FIFO writes the same treatment as reads to avoid fixed costs of individual transfers on a slow bus (e.g., tcan4x5x). Link: https://lore.kernel.org/r/20210817050853.14875-4-matt@bitbashing.io Signed-off-by: Matt Kline Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 61 +++++++++++++++-------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index fbd32b48d265..2470c47b2e31 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -279,7 +279,7 @@ enum m_can_reg { /* Message RAM Elements */ #define M_CAN_FIFO_ID 0x0 #define M_CAN_FIFO_DLC 0x4 -#define M_CAN_FIFO_DATA(n) (0x8 + ((n) << 2)) +#define M_CAN_FIFO_DATA 0x8 /* Rx Buffer Element */ /* R0 */ @@ -514,7 +514,7 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs) if (fifo_header.dlc & RX_BUF_BRS) cf->flags |= CANFD_BRS; - err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA(0), + err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA, cf->data, DIV_ROUND_UP(cf->len, 4)); if (err) goto out_fail; @@ -1588,8 +1588,9 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data; struct net_device *dev = cdev->net; struct sk_buff *skb = cdev->tx_skb; - u32 id, dlc, cccr, fdflags; - int i, err; + struct id_and_dlc fifo_header; + u32 cccr, fdflags; + int err; int putidx; cdev->tx_skb = NULL; @@ -1597,34 +1598,30 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) /* Generate ID field for TX buffer Element */ /* Common to all supported M_CAN versions */ if (cf->can_id & CAN_EFF_FLAG) { - id = cf->can_id & CAN_EFF_MASK; - id |= TX_BUF_XTD; + fifo_header.id = cf->can_id & CAN_EFF_MASK; + fifo_header.id |= TX_BUF_XTD; } else { - id = ((cf->can_id & CAN_SFF_MASK) << 18); + fifo_header.id = ((cf->can_id & CAN_SFF_MASK) << 18); } if (cf->can_id & CAN_RTR_FLAG) - id |= TX_BUF_RTR; + fifo_header.id |= TX_BUF_RTR; if (cdev->version == 30) { netif_stop_queue(dev); - /* message ram configuration */ - err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, &id, 1); + fifo_header.dlc = can_fd_len2dlc(cf->len) << 16; + + /* Write the frame ID, DLC, and payload to the FIFO element. */ + err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, &fifo_header, 2); if (err) goto out_fail; - dlc = can_fd_len2dlc(cf->len) << 16; - err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DLC, &dlc, 1); + err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DATA, + cf->data, DIV_ROUND_UP(cf->len, 4)); if (err) goto out_fail; - for (i = 0; i < cf->len; i += 4) { - err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DATA(i / 4), cf->data + i, 1); - if (err) - goto out_fail; - } - can_put_echo_skb(skb, dev, 0, 0); if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { @@ -1667,10 +1664,11 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) /* get put index for frame */ putidx = FIELD_GET(TXFQS_TFQPI_MASK, m_can_read(cdev, M_CAN_TXFQS)); - /* Write ID Field to FIFO Element */ - err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, &id, 1); - if (err) - goto out_fail; + + /* Construct DLC Field, with CAN-FD configuration. + * Use the put index of the fifo as the message marker, + * used in the TX interrupt for sending the correct echo frame. + */ /* get CAN FD configuration of frame */ fdflags = 0; @@ -1680,24 +1678,17 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) fdflags |= TX_BUF_BRS; } - /* Construct DLC Field. Also contains CAN-FD configuration - * use put index of fifo as message marker - * it is used in TX interrupt for - * sending the correct echo frame - */ - dlc = FIELD_PREP(TX_BUF_MM_MASK, putidx) | + fifo_header.dlc = FIELD_PREP(TX_BUF_MM_MASK, putidx) | FIELD_PREP(TX_BUF_DLC_MASK, can_fd_len2dlc(cf->len)) | fdflags | TX_BUF_EFC; - err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DLC, &dlc, 1); + err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, &fifo_header, 2); if (err) goto out_fail; - for (i = 0; i < cf->len; i += 4) { - err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA(i / 4), - cf->data + i, 1); - if (err) - goto out_fail; - } + err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA, + cf->data, DIV_ROUND_UP(cf->len, 4)); + if (err) + goto out_fail; /* Push loopback echo. * Will be looped back on TX interrupt based on message marker From 06fc143b2edecea0f9bb3b044b95065fe5aac49f Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Thu, 5 Aug 2021 21:27:50 +0200 Subject: [PATCH 16/22] dt-bindings: net: can: c_can: convert to json-schema Convert the Bosch C_CAN/D_CAN controller device tree binding documentation to json-schema. Document missing properties. Remove "ti,hwmods" as it is no longer used in TI dts. Make "clocks" required as it is used in all dts. Update the examples. Link: https://lore.kernel.org/r/20210805192750.9051-1-dariobin@libero.it Signed-off-by: Dario Binacchi Reviewed-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- .../bindings/net/can/bosch,c_can.yaml | 119 ++++++++++++++++++ .../devicetree/bindings/net/can/c_can.txt | 65 ---------- 2 files changed, 119 insertions(+), 65 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/can/bosch,c_can.yaml delete mode 100644 Documentation/devicetree/bindings/net/can/c_can.txt diff --git a/Documentation/devicetree/bindings/net/can/bosch,c_can.yaml b/Documentation/devicetree/bindings/net/can/bosch,c_can.yaml new file mode 100644 index 000000000000..2cd145a642f1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/bosch,c_can.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/bosch,c_can.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bosch C_CAN/D_CAN controller Device Tree Bindings + +description: Bosch C_CAN/D_CAN controller for CAN bus + +maintainers: + - Dario Binacchi + +allOf: + - $ref: can-controller.yaml# + +properties: + compatible: + oneOf: + - enum: + - bosch,c_can + - bosch,d_can + - ti,dra7-d_can + - ti,am3352-d_can + - items: + - enum: + - ti,am4372-d_can + - const: ti,am3352-d_can + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 4 + + power-domains: + description: | + Should contain a phandle to a PM domain provider node and an args + specifier containing the DCAN device id value. It's mandatory for + Keystone 2 66AK2G SoCs only. + maxItems: 1 + + clocks: + description: | + CAN functional clock phandle. + maxItems: 1 + + clock-names: + maxItems: 1 + + syscon-raminit: + description: | + Handle to system control region that contains the RAMINIT register, + register offset to the RAMINIT register and the CAN instance number (0 + offset). + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: The phandle to the system control region. + - description: The register offset. + - description: The CAN instance number. + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +if: + properties: + compatible: + contains: + enum: + - bosch,d_can + +then: + properties: + interrupts: + minItems: 4 + maxItems: 4 + items: + - description: Error and status IRQ + - description: Message object IRQ + - description: RAM ECC correctable error IRQ + - description: RAM ECC non-correctable error IRQ + +else: + properties: + interrupts: + maxItems: 1 + items: + - description: Error and status IRQ + +additionalProperties: false + +examples: + - | + #include + + can@ffc00000 { + compatible = "bosch,d_can"; + reg = <0xffc00000 0x1000>; + interrupts = <0 131 4>, <0 132 4>, <0 133 4>, <0 134 4>; + clocks = <&can0_clk>; + resets = <&rst CAN0_RESET>; + }; + - | + can@0 { + compatible = "ti,am3352-d_can"; + reg = <0x0 0x2000>; + clocks = <&dcan1_fck>; + clock-names = "fck"; + syscon-raminit = <&scm_conf 0x644 1>; + interrupts = <55>; + }; diff --git a/Documentation/devicetree/bindings/net/can/c_can.txt b/Documentation/devicetree/bindings/net/can/c_can.txt deleted file mode 100644 index 366479806acb..000000000000 --- a/Documentation/devicetree/bindings/net/can/c_can.txt +++ /dev/null @@ -1,65 +0,0 @@ -Bosch C_CAN/D_CAN controller Device Tree Bindings -------------------------------------------------- - -Required properties: -- compatible : Should be "bosch,c_can" for C_CAN controllers and - "bosch,d_can" for D_CAN controllers. - Can be "ti,dra7-d_can", "ti,am3352-d_can" or - "ti,am4372-d_can". -- reg : physical base address and size of the C_CAN/D_CAN - registers map -- interrupts : property with a value describing the interrupt - number - -The following are mandatory properties for DRA7x, AM33xx and AM43xx SoCs only: -- ti,hwmods : Must be "d_can" or "c_can", n being the - instance number - -The following are mandatory properties for Keystone 2 66AK2G SoCs only: -- power-domains : Should contain a phandle to a PM domain provider node - and an args specifier containing the DCAN device id - value. This property is as per the binding, - Documentation/devicetree/bindings/soc/ti/sci-pm-domain.yaml -- clocks : CAN functional clock phandle. This property is as per the - binding, - Documentation/devicetree/bindings/clock/ti,sci-clk.yaml - -Optional properties: -- syscon-raminit : Handle to system control region that contains the - RAMINIT register, register offset to the RAMINIT - register and the CAN instance number (0 offset). - -Note: "ti,hwmods" field is used to fetch the base address and irq -resources from TI, omap hwmod data base during device registration. -Future plan is to migrate hwmod data base contents into device tree -blob so that, all the required data will be used from device tree dts -file. - -Example: - -Step1: SoC common .dtsi file - - dcan1: d_can@481d0000 { - compatible = "bosch,d_can"; - reg = <0x481d0000 0x2000>; - interrupts = <55>; - interrupt-parent = <&intc>; - status = "disabled"; - }; - -(or) - - dcan1: d_can@481d0000 { - compatible = "bosch,d_can"; - ti,hwmods = "d_can1"; - reg = <0x481d0000 0x2000>; - interrupts = <55>; - interrupt-parent = <&intc>; - status = "disabled"; - }; - -Step 2: board specific .dts file - - &dcan1 { - status = "okay"; - }; From 236de85f6a11346136733ab03c8d97fdf3f1d67b Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 6 Aug 2021 10:49:52 +0200 Subject: [PATCH 17/22] can: c_can: c_can_do_tx(): fix typo in comment This patch fixes a typo in the comment in c_can_do_tx(). Fixes: eddf67115040 ("can: c_can: add a comment about IF_RX interface's use") Link: https://lore.kernel.org/r/20210806105127.103302-1-mkl@pengutronix.de Cc: Dario Binacchi Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index 7588f70ca0fe..e65bd7a9cf1d 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -712,7 +712,7 @@ static void c_can_do_tx(struct net_device *dev) /* We use IF_RX interface instead of IF_TX because we * are called from c_can_poll(), which runs inside - * NAPI. We are not trasmitting. + * NAPI. We are not transmitting. */ c_can_inval_tx_object(dev, IF_RX, obj); can_get_echo_skb(dev, idx, NULL); From 05cb2ba4b23154c1b7d66284764651b1dd3456a9 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 9 Aug 2021 09:26:35 +0200 Subject: [PATCH 18/22] can: c_can: rename IF_RX -> IF_NAPI The C_CAN/D_CAN cores implement 2 interfaces to manage the message objects. To avoid concurrency and the need for locking one interface is used in the TX path (IF_TX). While the other one, named IF_RX is used from NAPI context only. As this interface is not only used to manage RX, but also TX message objects, this patch renames IF_RX to IF_NAPI. Link: https://lore.kernel.org/r/20210809080608.171545-1-mkl@pengutronix.de Cc: Dario Binacchi Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_main.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index e65bd7a9cf1d..052ff35ed4dc 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -160,8 +160,8 @@ #define IF_MCONT_TX (IF_MCONT_TXIE | IF_MCONT_EOB) -/* Use IF1 for RX and IF2 for TX */ -#define IF_RX 0 +/* Use IF1 in NAPI path and IF2 in TX path */ +#define IF_NAPI 0 #define IF_TX 1 /* minimum timeout for checking BUSY status */ @@ -529,13 +529,13 @@ static void c_can_configure_msg_objects(struct net_device *dev) /* first invalidate all message objects */ for (i = priv->msg_obj_rx_first; i <= priv->msg_obj_num; i++) - c_can_inval_msg_object(dev, IF_RX, i); + c_can_inval_msg_object(dev, IF_NAPI, i); /* setup receive message objects */ for (i = priv->msg_obj_rx_first; i < priv->msg_obj_rx_last; i++) - c_can_setup_receive_object(dev, IF_RX, i, 0, 0, IF_MCONT_RCV); + c_can_setup_receive_object(dev, IF_NAPI, i, 0, 0, IF_MCONT_RCV); - c_can_setup_receive_object(dev, IF_RX, priv->msg_obj_rx_last, 0, 0, + c_can_setup_receive_object(dev, IF_NAPI, priv->msg_obj_rx_last, 0, 0, IF_MCONT_RCV_EOB); } @@ -710,11 +710,11 @@ static void c_can_do_tx(struct net_device *dev) pend &= ~BIT(idx); obj = idx + priv->msg_obj_tx_first; - /* We use IF_RX interface instead of IF_TX because we + /* We use IF_NAPI interface instead of IF_TX because we * are called from c_can_poll(), which runs inside * NAPI. We are not transmitting. */ - c_can_inval_tx_object(dev, IF_RX, obj); + c_can_inval_tx_object(dev, IF_NAPI, obj); can_get_echo_skb(dev, idx, NULL); bytes += priv->dlc[idx]; pkts++; @@ -766,14 +766,14 @@ static u32 c_can_adjust_pending(u32 pend, u32 rx_mask) static inline void c_can_rx_object_get(struct net_device *dev, struct c_can_priv *priv, u32 obj) { - c_can_object_get(dev, IF_RX, obj, priv->comm_rcv_high); + c_can_object_get(dev, IF_NAPI, obj, priv->comm_rcv_high); } static inline void c_can_rx_finalize(struct net_device *dev, struct c_can_priv *priv, u32 obj) { if (priv->type != BOSCH_D_CAN) - c_can_object_get(dev, IF_RX, obj, IF_COMM_CLR_NEWDAT); + c_can_object_get(dev, IF_NAPI, obj, IF_COMM_CLR_NEWDAT); } static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv, @@ -785,10 +785,12 @@ static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv, pend &= ~BIT(obj - 1); c_can_rx_object_get(dev, priv, obj); - ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_RX)); + ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_NAPI)); if (ctrl & IF_MCONT_MSGLST) { - int n = c_can_handle_lost_msg_obj(dev, IF_RX, obj, ctrl); + int n; + + n = c_can_handle_lost_msg_obj(dev, IF_NAPI, obj, ctrl); pkts += n; quota -= n; @@ -803,7 +805,7 @@ static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv, continue; /* read the data from the message object */ - c_can_read_msg_object(dev, IF_RX, ctrl); + c_can_read_msg_object(dev, IF_NAPI, ctrl); c_can_rx_finalize(dev, priv, obj); From 5064e40596f478fce241cffbc780d3b156d74066 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sat, 7 Aug 2021 15:07:57 +0200 Subject: [PATCH 19/22] can: c_can: remove struct c_can_priv::priv field It references the clock but it is never used. So let's remove it. Link: https://lore.kernel.org/r/20210807130800.5246-2-dariobin@libero.it Signed-off-by: Dario Binacchi Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can.h | 1 - drivers/net/can/c_can/c_can_platform.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index 4247ff80a29c..8f23e9c83c84 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -200,7 +200,6 @@ struct c_can_priv { void (*write_reg32)(const struct c_can_priv *priv, enum reg index, u32 val); void __iomem *base; const u16 *regs; - void *priv; /* for board-specific data */ enum c_can_dev_id type; struct c_can_raminit raminit_sys; /* RAMINIT via syscon regmap */ void (*raminit)(const struct c_can_priv *priv, bool enable); diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 36950363682f..86e95e9d6533 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -385,7 +385,6 @@ static int c_can_plat_probe(struct platform_device *pdev) priv->base = addr; priv->device = &pdev->dev; priv->can.clock.freq = clk_get_rate(clk); - priv->priv = clk; priv->type = drvdata->id; platform_set_drvdata(pdev, dev); From a54cdbba9deee77ca48b5eec0c26fcc72538538c Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sat, 7 Aug 2021 15:07:58 +0200 Subject: [PATCH 20/22] can: c_can: exit c_can_do_tx() early if no frames have been sent The c_can_poll() handles RX/TX events unconditionally. It may therefore happen that c_can_do_tx() is called unnecessarily because the interrupt was triggered by the reception of a frame. In these cases, we avoid to execute unnecessary statements and exit immediately. Link: https://lore.kernel.org/r/20210807130800.5246-3-dariobin@libero.it Signed-off-by: Dario Binacchi Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index 052ff35ed4dc..1ec0ce664732 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -720,17 +720,18 @@ static void c_can_do_tx(struct net_device *dev) pkts++; } + if (!pkts) + return; + /* Clear the bits in the tx_active mask */ atomic_sub(clr, &priv->tx_active); if (clr & BIT(priv->msg_obj_tx_num - 1)) netif_wake_queue(dev); - if (pkts) { - stats->tx_bytes += bytes; - stats->tx_packets += pkts; - can_led_event(dev, CAN_LED_EVENT_TX); - } + stats->tx_bytes += bytes; + stats->tx_packets += pkts; + can_led_event(dev, CAN_LED_EVENT_TX); } /* If we have a gap in the pending bits, that means we either From 28e86e9ab522e65b08545e5008d0f1ac5b19dad1 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sat, 7 Aug 2021 15:07:59 +0200 Subject: [PATCH 21/22] can: c_can: support tx ring algorithm The algorithm is already used successfully by other CAN drivers (e.g. mcp251xfd). Its implementation was kindly suggested to me by Marc Kleine-Budde following a patch I had previously submitted. You can find every detail at https://lore.kernel.org/patchwork/patch/1422929/. The idea is that after this patch, it will be easier to patch the driver to use the message object memory as a true FIFO. Link: https://lore.kernel.org/r/20210807130800.5246-4-dariobin@libero.it Suggested-by: Marc Kleine-Budde Signed-off-by: Dario Binacchi Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can.h | 33 ++++++++++++++- drivers/net/can/c_can/c_can_main.c | 67 ++++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index 8f23e9c83c84..9b4e54c950a6 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -176,6 +176,13 @@ struct c_can_raminit { bool needs_pulse; }; +/* c_can tx ring structure */ +struct c_can_tx_ring { + unsigned int head; + unsigned int tail; + unsigned int obj_num; +}; + /* c_can private data structure */ struct c_can_priv { struct can_priv can; /* must be the first member */ @@ -190,10 +197,10 @@ struct c_can_priv { unsigned int msg_obj_tx_first; unsigned int msg_obj_tx_last; u32 msg_obj_rx_mask; - atomic_t tx_active; atomic_t sie_pending; unsigned long tx_dir; int last_status; + struct c_can_tx_ring tx; u16 (*read_reg)(const struct c_can_priv *priv, enum reg index); void (*write_reg)(const struct c_can_priv *priv, enum reg index, u16 val); u32 (*read_reg32)(const struct c_can_priv *priv, enum reg index); @@ -219,4 +226,28 @@ int c_can_power_down(struct net_device *dev); void c_can_set_ethtool_ops(struct net_device *dev); +static inline u8 c_can_get_tx_head(const struct c_can_tx_ring *ring) +{ + return ring->head & (ring->obj_num - 1); +} + +static inline u8 c_can_get_tx_tail(const struct c_can_tx_ring *ring) +{ + return ring->tail & (ring->obj_num - 1); +} + +static inline u8 c_can_get_tx_free(const struct c_can_tx_ring *ring) +{ + u8 head = c_can_get_tx_head(ring); + u8 tail = c_can_get_tx_tail(ring); + + /* This is not a FIFO. C/D_CAN sends out the buffers + * prioritized. The lowest buffer number wins. + */ + if (head < tail) + return 0; + + return ring->obj_num - head; +} + #endif /* C_CAN_H */ diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index 1ec0ce664732..eb324fffab09 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -427,24 +427,50 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface, c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP); } +static bool c_can_tx_busy(const struct c_can_priv *priv, + const struct c_can_tx_ring *tx_ring) +{ + if (c_can_get_tx_free(tx_ring) > 0) + return false; + + netif_stop_queue(priv->dev); + + /* Memory barrier before checking tx_free (head and tail) */ + smp_mb(); + + if (c_can_get_tx_free(tx_ring) == 0) { + netdev_dbg(priv->dev, + "Stopping tx-queue (tx_head=0x%08x, tx_tail=0x%08x, len=%d).\n", + tx_ring->head, tx_ring->tail, + tx_ring->head - tx_ring->tail); + return true; + } + + netif_start_queue(priv->dev); + return false; +} + static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct can_frame *frame = (struct can_frame *)skb->data; struct c_can_priv *priv = netdev_priv(dev); + struct c_can_tx_ring *tx_ring = &priv->tx; u32 idx, obj; if (can_dropped_invalid_skb(dev, skb)) return NETDEV_TX_OK; - /* This is not a FIFO. C/D_CAN sends out the buffers - * prioritized. The lowest buffer number wins. - */ - idx = fls(atomic_read(&priv->tx_active)); + + if (c_can_tx_busy(priv, tx_ring)) + return NETDEV_TX_BUSY; + + idx = c_can_get_tx_head(tx_ring); + tx_ring->head++; + if (c_can_get_tx_free(tx_ring) == 0) + netif_stop_queue(dev); + obj = idx + priv->msg_obj_tx_first; - /* If this is the last buffer, stop the xmit queue */ - if (idx == priv->msg_obj_tx_num - 1) - netif_stop_queue(dev); /* Store the message in the interface so we can call * can_put_echo_skb(). We must do this before we enable * transmit as we might race against do_tx(). @@ -453,8 +479,6 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, priv->dlc[idx] = frame->len; can_put_echo_skb(skb, dev, idx, 0); - /* Update the active bits */ - atomic_add(BIT(idx), &priv->tx_active); /* Start transmission */ c_can_object_put(dev, IF_TX, obj, IF_COMM_TX); @@ -567,6 +591,7 @@ static int c_can_software_reset(struct net_device *dev) static int c_can_chip_config(struct net_device *dev) { struct c_can_priv *priv = netdev_priv(dev); + struct c_can_tx_ring *tx_ring = &priv->tx; int err; err = c_can_software_reset(dev); @@ -598,7 +623,8 @@ static int c_can_chip_config(struct net_device *dev) priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED); /* Clear all internal status */ - atomic_set(&priv->tx_active, 0); + tx_ring->head = 0; + tx_ring->tail = 0; priv->tx_dir = 0; /* set bittiming params */ @@ -696,14 +722,14 @@ static int c_can_get_berr_counter(const struct net_device *dev, static void c_can_do_tx(struct net_device *dev) { struct c_can_priv *priv = netdev_priv(dev); + struct c_can_tx_ring *tx_ring = &priv->tx; struct net_device_stats *stats = &dev->stats; - u32 idx, obj, pkts = 0, bytes = 0, pend, clr; + u32 idx, obj, pkts = 0, bytes = 0, pend; if (priv->msg_obj_tx_last > 32) pend = priv->read_reg32(priv, C_CAN_INTPND3_REG); else pend = priv->read_reg(priv, C_CAN_INTPND2_REG); - clr = pend; while ((idx = ffs(pend))) { idx--; @@ -723,11 +749,14 @@ static void c_can_do_tx(struct net_device *dev) if (!pkts) return; - /* Clear the bits in the tx_active mask */ - atomic_sub(clr, &priv->tx_active); - - if (clr & BIT(priv->msg_obj_tx_num - 1)) - netif_wake_queue(dev); + tx_ring->tail += pkts; + if (c_can_get_tx_free(tx_ring)) { + /* Make sure that anybody stopping the queue after + * this sees the new tx_ring->tail. + */ + smp_mb(); + netif_wake_queue(priv->dev); + } stats->tx_bytes += bytes; stats->tx_packets += pkts; @@ -1208,6 +1237,10 @@ struct net_device *alloc_c_can_dev(int msg_obj_num) priv->msg_obj_tx_last = priv->msg_obj_tx_first + priv->msg_obj_tx_num - 1; + priv->tx.head = 0; + priv->tx.tail = 0; + priv->tx.obj_num = msg_obj_tx_num; + netif_napi_add(dev, &priv->napi, c_can_poll, priv->msg_obj_rx_num); priv->dev = dev; From 387da6bc7a826cc6d532b1c0002b7c7513238d5f Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sat, 7 Aug 2021 15:08:00 +0200 Subject: [PATCH 22/22] can: c_can: cache frames to operate as a true FIFO As reported by a comment in the c_can_start_xmit() this was not a FIFO. C/D_CAN controller sends out the buffers prioritized so that the lowest buffer number wins. What did c_can_start_xmit() do if head was less tail in the tx ring ? It waited until all the frames queued in the FIFO was actually transmitted by the controller before accepting a new CAN frame to transmit, even if the FIFO was not full, to ensure that the messages were transmitted in the order in which they were loaded. By storing the frames in the FIFO without requiring its transmission, we will be able to use the full size of the FIFO even in cases such as the one described above. The transmission interrupt will trigger their transmission only when all the messages previously loaded but stored in less priority positions of the buffers have been transmitted. Link: https://lore.kernel.org/r/20210807130800.5246-5-dariobin@libero.it Suggested-by: Gianluca Falavigna Signed-off-by: Dario Binacchi Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can.h | 11 +---------- drivers/net/can/c_can/c_can_main.c | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index 9b4e54c950a6..08b6efa7a1a7 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -238,16 +238,7 @@ static inline u8 c_can_get_tx_tail(const struct c_can_tx_ring *ring) static inline u8 c_can_get_tx_free(const struct c_can_tx_ring *ring) { - u8 head = c_can_get_tx_head(ring); - u8 tail = c_can_get_tx_tail(ring); - - /* This is not a FIFO. C/D_CAN sends out the buffers - * prioritized. The lowest buffer number wins. - */ - if (head < tail) - return 0; - - return ring->obj_num - head; + return ring->obj_num - (ring->head - ring->tail); } #endif /* C_CAN_H */ diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index eb324fffab09..52671d1ea17d 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -456,7 +456,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, struct can_frame *frame = (struct can_frame *)skb->data; struct c_can_priv *priv = netdev_priv(dev); struct c_can_tx_ring *tx_ring = &priv->tx; - u32 idx, obj; + u32 idx, obj, cmd = IF_COMM_TX; if (can_dropped_invalid_skb(dev, skb)) return NETDEV_TX_OK; @@ -469,7 +469,8 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, if (c_can_get_tx_free(tx_ring) == 0) netif_stop_queue(dev); - obj = idx + priv->msg_obj_tx_first; + if (idx < c_can_get_tx_tail(tx_ring)) + cmd &= ~IF_COMM_TXRQST; /* Cache the message */ /* Store the message in the interface so we can call * can_put_echo_skb(). We must do this before we enable @@ -478,9 +479,8 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, c_can_setup_tx_object(dev, IF_TX, frame, idx); priv->dlc[idx] = frame->len; can_put_echo_skb(skb, dev, idx, 0); - - /* Start transmission */ - c_can_object_put(dev, IF_TX, obj, IF_COMM_TX); + obj = idx + priv->msg_obj_tx_first; + c_can_object_put(dev, IF_TX, obj, cmd); return NETDEV_TX_OK; } @@ -725,6 +725,7 @@ static void c_can_do_tx(struct net_device *dev) struct c_can_tx_ring *tx_ring = &priv->tx; struct net_device_stats *stats = &dev->stats; u32 idx, obj, pkts = 0, bytes = 0, pend; + u8 tail; if (priv->msg_obj_tx_last > 32) pend = priv->read_reg32(priv, C_CAN_INTPND3_REG); @@ -761,6 +762,18 @@ static void c_can_do_tx(struct net_device *dev) stats->tx_bytes += bytes; stats->tx_packets += pkts; can_led_event(dev, CAN_LED_EVENT_TX); + + tail = c_can_get_tx_tail(tx_ring); + + if (tail == 0) { + u8 head = c_can_get_tx_head(tx_ring); + + /* Start transmission for all cached messages */ + for (idx = tail; idx < head; idx++) { + obj = idx + priv->msg_obj_tx_first; + c_can_object_put(dev, IF_NAPI, obj, IF_COMM_TXRQST); + } + } } /* If we have a gap in the pending bits, that means we either