* [PATCH v6 0/4] rockchip: add can for RK3576 Soc
@ 2025-05-26 6:25 Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller Elaine Zhang
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Elaine Zhang @ 2025-05-26 6:25 UTC (permalink / raw)
To: zhangqing, mkl, kernel, mailhol.vincent, robh, krzk+dt, conor+dt,
heiko, cl, kever.yang
Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel,
devicetree
rk3576 can is a new controller,new register layout and Bit position
definition:
Support CAN and CANFD protocol.
Support Dma.
There are major differences from the previous rk3568.
All errata on the rk3568 have been fixed and redesigned.
Change in V6:
[PATCH v6 1/4]: Fix dma is support only for rk3576.
[PATCH v6 2/4]: Fix the compilation warning.
[PATCH v6 3/4]: Fix the compilation warning.
[PATCH v6 4/4]: Fix the compilation warning.
Change in V5:
[PATCH v5 1/4]: Add rk3576 canfd to rockchip,rk3568v2-canfd.yaml, remove
rockchip,rk3576-canfd.yaml
[PATCH v5 2/4]: Encapsulate some hardware operation functions into
rkcanfd_devtype_data to provide differentiated
implementations for different models
(such as RK3568v2/v3)..
[PATCH v5 3/4]: Add rk3576 canfd,fix the register naming rule,
Delete the variables used by rockchip itself.
[PATCH v5 4/4]: Fix .h sorting.
Change in V4:
[PATCH v4 1/3]: Correct the format and add explanations.
[PATCH v4 2/3]: No change.
[PATCH v4 3/3]: No change.
Change in V3:
[PATCH v3 1/3]: Add documentation for the rk3576 CAN-FD.
[PATCH v3 2/3]: Adjust the differentiated code section and
add dma function.
[PATCH v3 3/3]: Remove dma, no use dma by default.
Change in V2:
[PATCH v2 1/2]: remove rk3576_canfd.c, use the rockchip_canfd driver
[PATCH v2 2/2]: code style.
Elaine Zhang (4):
dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller
net: can: rockchip: Refactor the rkcanfd_devtype_data structure
net: can: rockchip: add can for RK3576 Soc
net: can: rockchip: support dma for rk3576 rx
.../net/can/rockchip,rk3568v2-canfd.yaml | 41 ++
.../net/can/rockchip/rockchip_canfd-core.c | 596 ++++++++++++++++--
drivers/net/can/rockchip/rockchip_canfd-rx.c | 198 ++++++
drivers/net/can/rockchip/rockchip_canfd-tx.c | 27 +
drivers/net/can/rockchip/rockchip_canfd.h | 289 +++++++++
5 files changed, 1108 insertions(+), 43 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller
2025-05-26 6:25 [PATCH v6 0/4] rockchip: add can for RK3576 Soc Elaine Zhang
@ 2025-05-26 6:25 ` Elaine Zhang
2025-05-26 15:12 ` Conor Dooley
2025-05-26 6:25 ` [PATCH v6 2/4] net: can: rockchip: Refactor the rkcanfd_devtype_data structure Elaine Zhang
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Elaine Zhang @ 2025-05-26 6:25 UTC (permalink / raw)
To: zhangqing, mkl, kernel, mailhol.vincent, robh, krzk+dt, conor+dt,
heiko, cl, kever.yang
Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel,
devicetree
Add documentation for the rockchip rk3576 CAN-FD controller.
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
.../net/can/rockchip,rk3568v2-canfd.yaml | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml b/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
index a077c0330013..c6595fef6cb5 100644
--- a/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
+++ b/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
@@ -12,11 +12,27 @@ maintainers:
allOf:
- $ref: can-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3576-canfd
+ required:
+ - compatible
+ then:
+ required:
+ - dmas
+ - dma-names
+ else:
+ properties:
+ dmas: false
+ dma-names: false
properties:
compatible:
oneOf:
- const: rockchip,rk3568v2-canfd
+ - const: rockchip,rk3576-canfd
- items:
- const: rockchip,rk3568v3-canfd
- const: rockchip,rk3568v2-canfd
@@ -43,6 +59,13 @@ properties:
- const: core
- const: apb
+ dmas:
+ maxItems: 1
+
+ dma-names:
+ items:
+ - const: rx
+
required:
- compatible
- reg
@@ -72,3 +95,21 @@ examples:
reset-names = "core", "apb";
};
};
+
+ - |
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ can@2ac00000 {
+ compatible = "rockchip,rk3576-canfd";
+ reg = <0x0 0x2ac00000 0x0 0x1000>;
+ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru CLK_CAN0>, <&cru PCLK_CAN0>;
+ clock-names = "baud", "pclk";
+ resets = <&cru SRST_CAN0>, <&cru SRST_P_CAN0>;
+ reset-names = "core", "apb";
+ dmas = <&dmac0 20>;
+ dma-names = "rx";
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 2/4] net: can: rockchip: Refactor the rkcanfd_devtype_data structure
2025-05-26 6:25 [PATCH v6 0/4] rockchip: add can for RK3576 Soc Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller Elaine Zhang
@ 2025-05-26 6:25 ` Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 3/4] net: can: rockchip: add can for RK3576 Soc Elaine Zhang
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Elaine Zhang @ 2025-05-26 6:25 UTC (permalink / raw)
To: zhangqing, mkl, kernel, mailhol.vincent, robh, krzk+dt, conor+dt,
heiko, cl, kever.yang
Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel,
devicetree
Add new function pointer:
Encapsulate some hardware operation functions into
rkcanfd_devtype_data to provide differentiated implementations for
different models (such as RK3568v2/v3).
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
.../net/can/rockchip/rockchip_canfd-core.c | 104 ++++++++++--------
drivers/net/can/rockchip/rockchip_canfd.h | 11 ++
2 files changed, 72 insertions(+), 43 deletions(-)
diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
index c3fb3176ce42..c21ca4c1fb9a 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-core.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
@@ -24,32 +24,6 @@
#include "rockchip_canfd.h"
-static const struct rkcanfd_devtype_data rkcanfd_devtype_data_rk3568v2 = {
- .model = RKCANFD_MODEL_RK3568V2,
- .quirks = RKCANFD_QUIRK_RK3568_ERRATUM_1 | RKCANFD_QUIRK_RK3568_ERRATUM_2 |
- RKCANFD_QUIRK_RK3568_ERRATUM_3 | RKCANFD_QUIRK_RK3568_ERRATUM_4 |
- RKCANFD_QUIRK_RK3568_ERRATUM_5 | RKCANFD_QUIRK_RK3568_ERRATUM_6 |
- RKCANFD_QUIRK_RK3568_ERRATUM_7 | RKCANFD_QUIRK_RK3568_ERRATUM_8 |
- RKCANFD_QUIRK_RK3568_ERRATUM_9 | RKCANFD_QUIRK_RK3568_ERRATUM_10 |
- RKCANFD_QUIRK_RK3568_ERRATUM_11 | RKCANFD_QUIRK_RK3568_ERRATUM_12 |
- RKCANFD_QUIRK_CANFD_BROKEN,
-};
-
-/* The rk3568 CAN-FD errata sheet as of Tue 07 Nov 2023 11:25:31 +08:00
- * states that only the rk3568v2 is affected by erratum 5, but tests
- * with the rk3568v2 and rk3568v3 show that the RX_FIFO_CNT is
- * sometimes too high. In contrast to the errata sheet mark rk3568v3
- * as effected by erratum 5, too.
- */
-static const struct rkcanfd_devtype_data rkcanfd_devtype_data_rk3568v3 = {
- .model = RKCANFD_MODEL_RK3568V3,
- .quirks = RKCANFD_QUIRK_RK3568_ERRATUM_1 | RKCANFD_QUIRK_RK3568_ERRATUM_2 |
- RKCANFD_QUIRK_RK3568_ERRATUM_5 | RKCANFD_QUIRK_RK3568_ERRATUM_7 |
- RKCANFD_QUIRK_RK3568_ERRATUM_8 | RKCANFD_QUIRK_RK3568_ERRATUM_10 |
- RKCANFD_QUIRK_RK3568_ERRATUM_11 | RKCANFD_QUIRK_RK3568_ERRATUM_12 |
- RKCANFD_QUIRK_CANFD_BROKEN,
-};
-
static const char *__rkcanfd_get_model_str(enum rkcanfd_model model)
{
switch (model) {
@@ -212,7 +186,7 @@ static int rkcanfd_get_berr_counter(const struct net_device *ndev,
if (err)
return err;
- rkcanfd_get_berr_counter_corrected(priv, bec);
+ priv->devtype_data.get_berr_counter(priv, bec);
pm_runtime_put(ndev->dev.parent);
@@ -296,13 +270,13 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv)
memset(&priv->bec, 0x0, sizeof(priv->bec));
- rkcanfd_chip_fifo_setup(priv);
+ priv->devtype_data.fifo_setup(priv);
rkcanfd_timestamp_init(priv);
rkcanfd_timestamp_start(priv);
rkcanfd_set_bittiming(priv);
- rkcanfd_chip_interrupts_disable(priv);
+ priv->devtype_data.interrupts_disable(priv);
rkcanfd_chip_set_work_mode(priv);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
@@ -316,7 +290,7 @@ static void __rkcanfd_chip_stop(struct rkcanfd_priv *priv, const enum can_state
priv->can.state = state;
rkcanfd_chip_set_reset_mode(priv);
- rkcanfd_chip_interrupts_disable(priv);
+ priv->devtype_data.interrupts_disable(priv);
}
static void rkcanfd_chip_stop(struct rkcanfd_priv *priv, const enum can_state state)
@@ -342,8 +316,8 @@ static int rkcanfd_set_mode(struct net_device *ndev,
switch (mode) {
case CAN_MODE_START:
- rkcanfd_chip_start(priv);
- rkcanfd_chip_interrupts_enable(priv);
+ priv->devtype_data.chip_start(priv);
+ priv->devtype_data.interrupts_enable(priv);
netif_wake_queue(ndev);
break;
@@ -537,7 +511,7 @@ static int rkcanfd_handle_error_int(struct rkcanfd_priv *priv)
if (cf) {
struct can_berr_counter bec;
- rkcanfd_get_berr_counter_corrected(priv, &bec);
+ priv->devtype_data.get_berr_counter(priv, &bec);
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT;
cf->data[6] = bec.txerr;
cf->data[7] = bec.rxerr;
@@ -567,7 +541,7 @@ static int rkcanfd_handle_state_error_int(struct rkcanfd_priv *priv)
u32 timestamp;
int err;
- rkcanfd_get_berr_counter_corrected(priv, &bec);
+ priv->devtype_data.get_berr_counter(priv, &bec);
can_state_get_by_berr_counter(ndev, &bec, &tx_state, &rx_state);
new_state = max(tx_state, rx_state);
@@ -581,7 +555,7 @@ static int rkcanfd_handle_state_error_int(struct rkcanfd_priv *priv)
can_change_state(ndev, cf, tx_state, rx_state);
if (new_state == CAN_STATE_BUS_OFF) {
- rkcanfd_chip_stop(priv, CAN_STATE_BUS_OFF);
+ priv->devtype_data.chip_stop(priv, CAN_STATE_BUS_OFF);
can_bus_off(ndev);
}
@@ -620,7 +594,7 @@ rkcanfd_handle_rx_fifo_overflow_int(struct rkcanfd_priv *priv)
if (!skb)
return 0;
- rkcanfd_get_berr_counter_corrected(priv, &bec);
+ priv->devtype_data.get_berr_counter(priv, &bec);
cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
@@ -719,21 +693,21 @@ static int rkcanfd_open(struct net_device *ndev)
if (err)
goto out_close_candev;
- rkcanfd_chip_start(priv);
+ priv->devtype_data.chip_start(priv);
can_rx_offload_enable(&priv->offload);
- err = request_irq(ndev->irq, rkcanfd_irq, IRQF_SHARED, ndev->name, priv);
+ err = request_irq(ndev->irq, priv->devtype_data.irq, IRQF_SHARED, ndev->name, priv);
if (err)
goto out_rkcanfd_chip_stop;
- rkcanfd_chip_interrupts_enable(priv);
+ priv->devtype_data.interrupts_enable(priv);
netif_start_queue(ndev);
return 0;
out_rkcanfd_chip_stop:
- rkcanfd_chip_stop_sync(priv, CAN_STATE_STOPPED);
+ priv->devtype_data.chip_stop_sync(priv, CAN_STATE_STOPPED);
pm_runtime_put(ndev->dev.parent);
out_close_candev:
close_candev(ndev);
@@ -746,10 +720,10 @@ static int rkcanfd_stop(struct net_device *ndev)
netif_stop_queue(ndev);
- rkcanfd_chip_interrupts_disable(priv);
+ priv->devtype_data.interrupts_disable(priv);
free_irq(ndev->irq, priv);
can_rx_offload_disable(&priv->offload);
- rkcanfd_chip_stop_sync(priv, CAN_STATE_STOPPED);
+ priv->devtype_data.chip_stop_sync(priv, CAN_STATE_STOPPED);
close_candev(ndev);
pm_runtime_put(ndev->dev.parent);
@@ -818,7 +792,7 @@ static int rkcanfd_register(struct rkcanfd_priv *priv)
if (err)
goto out_pm_runtime_put_sync;
- rkcanfd_register_done(priv);
+ priv->devtype_data.register_done(priv);
pm_runtime_put(ndev->dev.parent);
@@ -840,6 +814,50 @@ static inline void rkcanfd_unregister(struct rkcanfd_priv *priv)
pm_runtime_disable(ndev->dev.parent);
}
+static const struct rkcanfd_devtype_data rkcanfd_devtype_data_rk3568v2 = {
+ .model = RKCANFD_MODEL_RK3568V2,
+ .quirks = RKCANFD_QUIRK_RK3568_ERRATUM_1 | RKCANFD_QUIRK_RK3568_ERRATUM_2 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_3 | RKCANFD_QUIRK_RK3568_ERRATUM_4 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_5 | RKCANFD_QUIRK_RK3568_ERRATUM_6 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_7 | RKCANFD_QUIRK_RK3568_ERRATUM_8 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_9 | RKCANFD_QUIRK_RK3568_ERRATUM_10 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_11 | RKCANFD_QUIRK_RK3568_ERRATUM_12 |
+ RKCANFD_QUIRK_CANFD_BROKEN,
+ .get_berr_counter = rkcanfd_get_berr_counter_corrected,
+ .interrupts_enable = rkcanfd_chip_interrupts_enable,
+ .interrupts_disable = rkcanfd_chip_interrupts_disable,
+ .fifo_setup = rkcanfd_chip_fifo_setup,
+ .chip_start = rkcanfd_chip_start,
+ .chip_stop = rkcanfd_chip_stop,
+ .chip_stop_sync = rkcanfd_chip_stop_sync,
+ .irq = rkcanfd_irq,
+ .register_done = rkcanfd_register_done,
+};
+
+/* The rk3568 CAN-FD errata sheet as of Tue 07 Nov 2023 11:25:31 +08:00
+ * states that only the rk3568v2 is affected by erratum 5, but tests
+ * with the rk3568v2 and rk3568v3 show that the RX_FIFO_CNT is
+ * sometimes too high. In contrast to the errata sheet mark rk3568v3
+ * as effected by erratum 5, too.
+ */
+static const struct rkcanfd_devtype_data rkcanfd_devtype_data_rk3568v3 = {
+ .model = RKCANFD_MODEL_RK3568V3,
+ .quirks = RKCANFD_QUIRK_RK3568_ERRATUM_1 | RKCANFD_QUIRK_RK3568_ERRATUM_2 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_5 | RKCANFD_QUIRK_RK3568_ERRATUM_7 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_8 | RKCANFD_QUIRK_RK3568_ERRATUM_10 |
+ RKCANFD_QUIRK_RK3568_ERRATUM_11 | RKCANFD_QUIRK_RK3568_ERRATUM_12 |
+ RKCANFD_QUIRK_CANFD_BROKEN,
+ .get_berr_counter = rkcanfd_get_berr_counter_corrected,
+ .interrupts_enable = rkcanfd_chip_interrupts_enable,
+ .interrupts_disable = rkcanfd_chip_interrupts_disable,
+ .fifo_setup = rkcanfd_chip_fifo_setup,
+ .chip_start = rkcanfd_chip_start,
+ .chip_stop = rkcanfd_chip_stop,
+ .chip_stop_sync = rkcanfd_chip_stop_sync,
+ .irq = rkcanfd_irq,
+ .register_done = rkcanfd_register_done,
+};
+
static const struct of_device_id rkcanfd_of_match[] = {
{
.compatible = "rockchip,rk3568v2-canfd",
diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
index 93131c7d7f54..bcd26d23062b 100644
--- a/drivers/net/can/rockchip/rockchip_canfd.h
+++ b/drivers/net/can/rockchip/rockchip_canfd.h
@@ -436,9 +436,20 @@ enum rkcanfd_model {
RKCANFD_MODEL_RK3568V3 = 0x35683,
};
+struct rkcanfd_priv;
+
struct rkcanfd_devtype_data {
enum rkcanfd_model model;
u32 quirks;
+ void (*get_berr_counter)(struct rkcanfd_priv *priv, struct can_berr_counter *bec);
+ void (*interrupts_enable)(const struct rkcanfd_priv *priv);
+ void (*interrupts_disable)(const struct rkcanfd_priv *priv);
+ void (*fifo_setup)(struct rkcanfd_priv *priv);
+ void (*chip_start)(struct rkcanfd_priv *priv);
+ void (*chip_stop)(struct rkcanfd_priv *priv, const enum can_state state);
+ void (*chip_stop_sync)(struct rkcanfd_priv *priv, const enum can_state state);
+ irqreturn_t (*irq)(int irq, void *dev_id);
+ void (*register_done)(const struct rkcanfd_priv *priv);
};
struct rkcanfd_fifo_header {
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 3/4] net: can: rockchip: add can for RK3576 Soc
2025-05-26 6:25 [PATCH v6 0/4] rockchip: add can for RK3576 Soc Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 2/4] net: can: rockchip: Refactor the rkcanfd_devtype_data structure Elaine Zhang
@ 2025-05-26 6:25 ` Elaine Zhang
2025-05-27 11:37 ` Marc Kleine-Budde
2025-05-26 6:25 ` [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx Elaine Zhang
2025-05-27 15:58 ` [PATCH v6 0/4] rockchip: add can for RK3576 Soc Marc Kleine-Budde
4 siblings, 1 reply; 10+ messages in thread
From: Elaine Zhang @ 2025-05-26 6:25 UTC (permalink / raw)
To: zhangqing, mkl, kernel, mailhol.vincent, robh, krzk+dt, conor+dt,
heiko, cl, kever.yang
Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel,
devicetree
Is new controller, new register layout and Bit position definition:
Support CAN and CANFD protocol, ISO 11898-1
Support transmit or receive error count
Support acceptance filter, more functional
Support interrupt and all interrupt can be masked
Support error code check
Support self test\silent\loop-back mode
Support auto retransmission mode
Support auto bus on after bus-off state
Support 2 transmit buffers
Support Internal Storage Mode
Support DMA
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
.../net/can/rockchip/rockchip_canfd-core.c | 453 ++++++++++++++++++
drivers/net/can/rockchip/rockchip_canfd-rx.c | 111 +++++
drivers/net/can/rockchip/rockchip_canfd-tx.c | 27 ++
drivers/net/can/rockchip/rockchip_canfd.h | 267 +++++++++++
4 files changed, 858 insertions(+)
diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
index c21ca4c1fb9a..92e260cb2527 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-core.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
@@ -31,6 +31,8 @@ static const char *__rkcanfd_get_model_str(enum rkcanfd_model model)
return "rk3568v2";
case RKCANFD_MODEL_RK3568V3:
return "rk3568v3";
+ case RKCANFD_MODEL_RK3576:
+ return "rk3576";
}
return "<unknown>";
@@ -176,6 +178,30 @@ static void rkcanfd_get_berr_counter_corrected(struct rkcanfd_priv *priv,
!!(reg_state & RKCANFD_REG_STATE_ERROR_WARNING_STATE));
}
+static void rk3576canfd_get_berr_counter_corrected(struct rkcanfd_priv *priv,
+ struct can_berr_counter *bec)
+{
+ struct can_berr_counter bec_raw;
+ u32 reg_state;
+
+ bec->rxerr = rkcanfd_read(priv, RK3576CANFD_REG_RXERRORCNT);
+ bec->txerr = rkcanfd_read(priv, RK3576CANFD_REG_TXERRORCNT);
+ bec_raw = *bec;
+
+ if (!bec->rxerr && !bec->txerr)
+ *bec = priv->bec;
+ else
+ priv->bec = *bec;
+
+ reg_state = rkcanfd_read(priv, RKCANFD_REG_STATE);
+ netdev_vdbg(priv->ndev,
+ "%s: Raw/Cor: txerr=%3u/%3u rxerr=%3u/%3u Bus Off=%u Warning=%u\n",
+ __func__,
+ bec_raw.txerr, bec->txerr, bec_raw.rxerr, bec->rxerr,
+ !!(reg_state & RK3576CANFD_REG_STATE_BUS_OFF_STATE),
+ !!(reg_state & RK3576CANFD_REG_STATE_ERROR_WARNING_STATE));
+}
+
static int rkcanfd_get_berr_counter(const struct net_device *ndev,
struct can_berr_counter *bec)
{
@@ -206,6 +232,11 @@ static void rkcanfd_chip_interrupts_disable(const struct rkcanfd_priv *priv)
rkcanfd_write(priv, RKCANFD_REG_INT_MASK, RKCANFD_REG_INT_ALL);
}
+static void rk3576canfd_chip_interrupts_disable(const struct rkcanfd_priv *priv)
+{
+ rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, RK3576CANFD_REG_INT_ALL);
+}
+
static void rkcanfd_chip_fifo_setup(struct rkcanfd_priv *priv)
{
u32 reg;
@@ -220,6 +251,72 @@ static void rkcanfd_chip_fifo_setup(struct rkcanfd_priv *priv)
netdev_reset_queue(priv->ndev);
}
+static void rk3576canfd_chip_fifo_setup(struct rkcanfd_priv *priv)
+{
+ u32 ism = 0, water = 0;
+
+ ism = RK3576CANFD_REG_STR_CTL_ISM_SEL_CANFD_FIXED;
+ water = RK3576CANFD_ISM_WATERMASK_CANFD;
+
+ /* internal sram mode */
+ rkcanfd_write(priv, RK3576CANFD_REG_STR_CTL,
+ (FIELD_PREP(RK3576CANFD_REG_STR_CTL_ISM_SEL, ism) |
+ RK3576CANFD_REG_STR_CTL_STORAGE_TIMEOUT_MODE));
+ rkcanfd_write(priv, RK3576CANFD_REG_STR_WTM, water);
+ WRITE_ONCE(priv->tx_head, 0);
+ WRITE_ONCE(priv->tx_tail, 0);
+ netdev_reset_queue(priv->ndev);
+}
+
+static int rk3576canfd_atf_config(struct rkcanfd_priv *priv, int mode)
+{
+ u32 id[10] = {0};
+ u32 dlc = 0, dlc_over = 0;
+
+ switch (mode) {
+ case RK3576CANFD_REG_ATFM_MASK_SEL_MASK_MODE:
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF0, id[0]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF1, id[1]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF2, id[2]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF3, id[3]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF4, id[4]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM0, RK3576CANFD_REG_ATFM_ID);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM1, RK3576CANFD_REG_ATFM_ID);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM2, RK3576CANFD_REG_ATFM_ID);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM3, RK3576CANFD_REG_ATFM_ID);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM4, RK3576CANFD_REG_ATFM_ID);
+ break;
+ case RK3576CANFD_REG_ATFM_MASK_SEL_LIST_MODE:
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF0, id[0]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF1, id[1]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF2, id[2]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF3, id[3]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF4, id[4]);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM0, id[5] | RK3576CANFD_REG_ATFM_MASK_SEL);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM1, id[6] | RK3576CANFD_REG_ATFM_MASK_SEL);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM2, id[7] | RK3576CANFD_REG_ATFM_MASK_SEL);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM3, id[8] | RK3576CANFD_REG_ATFM_MASK_SEL);
+ rkcanfd_write(priv, RK3576CANFD_REG_ATFM4, id[9] | RK3576CANFD_REG_ATFM_MASK_SEL);
+ break;
+ default:
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF_CTL, RK3576CANFD_REG_ATF_CTL_ATF_DIS_ALL);
+ return -EINVAL;
+ }
+
+ if (dlc) {
+ if (dlc_over)
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF_DLC,
+ dlc | RK3576CANFD_REG_ATF_DLC_ATF_DLC_EN);
+ else
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF_DLC,
+ dlc | RK3576CANFD_REG_ATF_DLC_ATF_DLC_EN |
+ RK3576CANFD_REG_ATF_DLC_ATF_DLC_MODE);
+ }
+ rkcanfd_write(priv, RK3576CANFD_REG_ATF_CTL, 0);
+
+ return 0;
+}
+
static void rkcanfd_chip_start(struct rkcanfd_priv *priv)
{
u32 reg;
@@ -285,6 +382,68 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv)
rkcanfd_read(priv, RKCANFD_REG_MODE));
}
+static void rk3576canfd_chip_start(struct rkcanfd_priv *priv)
+
+{
+ u32 reg;
+
+ rkcanfd_chip_set_reset_mode(priv);
+
+ /* Receiving Filter: accept all */
+ rk3576canfd_atf_config(priv, RK3576CANFD_REG_ATFM_MASK_SEL_MASK_MODE);
+
+ /* enable:
+ * - CAN_FD: enable CAN-FD
+ * - AUTO_RETX_MODE: auto retransmission on TX error
+ * - COVER_MODE: RX-FIFO overwrite mode, do not send OVERLOAD frames
+ * - RXSTX_MODE: Receive Self Transmit data mode
+ * - WORK_MODE: transition from reset to working mode
+ */
+ reg = rkcanfd_read(priv, RKCANFD_REG_MODE);
+ priv->reg_mode_default = reg | RKCANFD_REG_MODE_WORK_MODE;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ priv->reg_mode_default |= RKCANFD_REG_MODE_LBACK_MODE;
+ rkcanfd_write(priv, RK3576CANFD_REG_ERROR_MASK,
+ RK3576CANFD_REG_ERROR_MASK_ACK_ERROR);
+ }
+
+ /* mask, i.e. ignore:
+ * - TIMESTAMP_COUNTER_OVERFLOW_INT - timestamp counter overflow interrupt
+ * - TX_ARBIT_FAIL_INT - TX arbitration fail interrupt
+ * - OVERLOAD_INT - CAN bus overload interrupt
+ * - TX_FINISH_INT - Transmit finish interrupt
+ */
+ priv->reg_int_mask_default = RK3576CANFD_REG_INT_RX_FINISH_INT;
+
+ /* Do not mask the bus error interrupt if the bus error
+ * reporting is requested.
+ */
+ if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
+ priv->reg_int_mask_default |= RKCANFD_REG_INT_ERROR_INT;
+
+ memset(&priv->bec, 0x0, sizeof(priv->bec));
+
+ priv->devtype_data.fifo_setup(priv);
+
+ rkcanfd_write(priv, RK3576CANFD_REG_AUTO_RETX_CFG,
+ RK3576CANFD_REG_AUTO_RETX_CFG_AUTO_RETX_EN);
+
+ rkcanfd_write(priv, RK3576CANFD_REG_BRS_CFG,
+ RK3576CANFD_REG_BRS_CFG_BRS_NEGSYNC_EN |
+ RK3576CANFD_REG_BRS_CFG_BRS_POSSYNC_EN);
+
+ rkcanfd_set_bittiming(priv);
+
+ priv->devtype_data.interrupts_disable(priv);
+ rkcanfd_chip_set_work_mode(priv);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ netdev_dbg(priv->ndev, "%s: reg_mode=0x%08x\n", __func__,
+ rkcanfd_read(priv, RKCANFD_REG_MODE));
+}
+
static void __rkcanfd_chip_stop(struct rkcanfd_priv *priv, const enum can_state state)
{
priv->can.state = state;
@@ -301,6 +460,13 @@ static void rkcanfd_chip_stop(struct rkcanfd_priv *priv, const enum can_state st
__rkcanfd_chip_stop(priv, state);
}
+static void rk3576canfd_chip_stop(struct rkcanfd_priv *priv, const enum can_state state)
+{
+ priv->can.state = state;
+
+ __rkcanfd_chip_stop(priv, state);
+}
+
static void rkcanfd_chip_stop_sync(struct rkcanfd_priv *priv, const enum can_state state)
{
priv->can.state = state;
@@ -309,6 +475,13 @@ static void rkcanfd_chip_stop_sync(struct rkcanfd_priv *priv, const enum can_sta
__rkcanfd_chip_stop(priv, state);
}
+static void rk3576canfd_chip_stop_sync(struct rkcanfd_priv *priv, const enum can_state state)
+{
+ priv->can.state = state;
+
+ __rkcanfd_chip_stop(priv, state);
+}
+
static int rkcanfd_set_mode(struct net_device *ndev,
enum can_mode mode)
{
@@ -364,6 +537,9 @@ static const char *rkcanfd_get_error_type_str(unsigned int type)
#define RKCAN_ERROR_CODE(reg_ec, code) \
((reg_ec) & RKCANFD_REG_ERROR_CODE_##code ? __stringify(code) " " : "")
+#define RK3576CAN_ERROR_CODE(reg_ec, code) \
+ ((reg_ec) & RK3576CANFD_REG_ERROR_CODE_##code ? __stringify(code) " " : "")
+
static void
rkcanfd_handle_error_int_reg_ec(struct rkcanfd_priv *priv, struct can_frame *cf,
const u32 reg_ec)
@@ -493,6 +669,128 @@ rkcanfd_handle_error_int_reg_ec(struct rkcanfd_priv *priv, struct can_frame *cf,
}
}
+static void
+rk3576canfd_handle_error_int_reg_ec(struct rkcanfd_priv *priv, struct can_frame *cf,
+ const u32 reg_ec)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ unsigned int type;
+ u32 reg_state, reg_cmd;
+
+ type = FIELD_GET(RK3576CANFD_REG_ERROR_CODE_TYPE, reg_ec);
+ reg_cmd = rkcanfd_read(priv, RK3576CANFD_REG_CMD);
+ reg_state = rkcanfd_read(priv, RK3576CANFD_REG_STATE);
+
+ netdev_dbg(priv->ndev, "%s Error in %s %s Phase: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s(0x%08x) CMD=%u RX=%u TX=%u Error-Warning=%u Bus-Off=%u\n",
+ rkcanfd_get_error_type_str(type),
+ reg_ec & RK3576CANFD_REG_ERROR_CODE_DIRECTION_RX ? "RX" : "TX",
+ reg_ec & RK3576CANFD_REG_ERROR_CODE_PHASE ? "Data" : "Arbitration",
+ RK3576CAN_ERROR_CODE(reg_ec, TX_ACK_EOF),
+ RK3576CAN_ERROR_CODE(reg_ec, TX_CRC),
+ RK3576CAN_ERROR_CODE(reg_ec, TX_STUFF_COUNT),
+ RK3576CAN_ERROR_CODE(reg_ec, TX_DATA),
+ RK3576CAN_ERROR_CODE(reg_ec, TX_SOF_DLC),
+ RK3576CAN_ERROR_CODE(reg_ec, TX_IDLE),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_ERROR),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_OVERLOAD),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_SPACE),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_EOF),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_ACK_LIM),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_ACK),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_CRC_LIM),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_CRC),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_STUFF_COUNT),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_DATA),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_DLC),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_BRS_ESI),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_RES),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_FDF),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_ID2_RTR),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_SOF_IDE),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_BUS_IDLE),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_BUS_INT),
+ RK3576CAN_ERROR_CODE(reg_ec, RX_STOP),
+ reg_ec, reg_cmd,
+ !!(reg_state & RK3576CANFD_REG_STATE_RX_PERIOD),
+ !!(reg_state & RK3576CANFD_REG_STATE_TX_PERIOD),
+ !!(reg_state & RK3576CANFD_REG_STATE_ERROR_WARNING_STATE),
+ !!(reg_state & RK3576CANFD_REG_STATE_BUS_OFF_STATE));
+
+ priv->can.can_stats.bus_error++;
+
+ if (reg_ec & RK3576CANFD_REG_ERROR_CODE_DIRECTION_RX)
+ stats->rx_errors++;
+ else
+ stats->tx_errors++;
+
+ if (!cf)
+ return;
+
+ if (reg_ec & RK3576CANFD_REG_ERROR_CODE_DIRECTION_RX) {
+ if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_SOF_IDE)
+ cf->data[3] = CAN_ERR_PROT_LOC_SOF;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_ID2_RTR)
+ cf->data[3] = CAN_ERR_PROT_LOC_RTR;
+ /* RKCANFD_REG_ERROR_CODE_RX_FDF */
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_RES)
+ cf->data[3] = CAN_ERR_PROT_LOC_RES0;
+ /* RKCANFD_REG_ERROR_CODE_RX_BRS_ESI */
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_DLC)
+ cf->data[3] = CAN_ERR_PROT_LOC_DLC;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_DATA)
+ cf->data[3] = CAN_ERR_PROT_LOC_DATA;
+ /* RKCANFD_REG_ERROR_CODE_RX_STUFF_COUNT */
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_CRC)
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_CRC_LIM)
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK_DEL;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_ACK)
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_ACK_LIM)
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK_DEL;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_EOF)
+ cf->data[3] = CAN_ERR_PROT_LOC_EOF;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_RX_SPACE)
+ cf->data[3] = CAN_ERR_PROT_LOC_EOF;
+ } else {
+ cf->data[2] |= CAN_ERR_PROT_TX;
+
+ if (reg_ec & RK3576CANFD_REG_ERROR_CODE_TX_SOF_DLC)
+ cf->data[3] = CAN_ERR_PROT_LOC_SOF;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_TX_DATA)
+ cf->data[3] = CAN_ERR_PROT_LOC_DATA;
+ /* RKCANFD_REG_ERROR_CODE_TX_STUFF_COUNT */
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_TX_CRC)
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ else if (reg_ec & RK3576CANFD_REG_ERROR_CODE_TX_ACK_EOF)
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK_DEL;
+ }
+
+ switch (reg_ec & RK3576CANFD_REG_ERROR_CODE_TYPE) {
+ case FIELD_PREP_CONST(RK3576CANFD_REG_ERROR_CODE_TYPE,
+ RK3576CANFD_REG_ERROR_CODE_TYPE_BIT):
+
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case FIELD_PREP_CONST(RK3576CANFD_REG_ERROR_CODE_TYPE,
+ RK3576CANFD_REG_ERROR_CODE_TYPE_STUFF):
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ case FIELD_PREP_CONST(RK3576CANFD_REG_ERROR_CODE_TYPE,
+ RK3576CANFD_REG_ERROR_CODE_TYPE_FORM):
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case FIELD_PREP_CONST(RK3576CANFD_REG_ERROR_CODE_TYPE,
+ RK3576CANFD_REG_ERROR_CODE_TYPE_ACK):
+ cf->can_id |= CAN_ERR_ACK;
+ break;
+ case FIELD_PREP_CONST(RK3576CANFD_REG_ERROR_CODE_TYPE,
+ RK3576CANFD_REG_ERROR_CODE_TYPE_CRC):
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ break;
+ }
+}
+
static int rkcanfd_handle_error_int(struct rkcanfd_priv *priv)
{
struct net_device_stats *stats = &priv->ndev->stats;
@@ -530,6 +828,41 @@ static int rkcanfd_handle_error_int(struct rkcanfd_priv *priv)
return 0;
}
+static int rkcanfd_handle_rk3576_error_int(struct rkcanfd_priv *priv)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ struct can_frame *cf = NULL;
+ u32 reg_ec;
+ struct sk_buff *skb;
+ int err;
+
+ reg_ec = rkcanfd_read(priv, RK3576CANFD_REG_ERROR_CODE);
+ if (!reg_ec)
+ return 0;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
+ skb = alloc_can_err_skb(priv->ndev, &cf);
+ if (cf) {
+ struct can_berr_counter bec;
+
+ priv->devtype_data.get_berr_counter(priv, &bec);
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT;
+ cf->data[6] = bec.txerr;
+ cf->data[7] = bec.rxerr;
+ }
+ }
+
+ rk3576canfd_handle_error_int_reg_ec(priv, cf, reg_ec);
+ if (!cf)
+ return 0;
+
+ err = can_rx_offload_queue_tail(&priv->offload, skb);
+ if (err)
+ stats->rx_fifo_errors++;
+
+ return 0;
+}
+
static int rkcanfd_handle_state_error_int(struct rkcanfd_priv *priv)
{
struct net_device_stats *stats = &priv->ndev->stats;
@@ -575,6 +908,50 @@ static int rkcanfd_handle_state_error_int(struct rkcanfd_priv *priv)
return 0;
}
+static int rkcanfd_handle_rk3576_state_error_int(struct rkcanfd_priv *priv)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ enum can_state new_state, rx_state, tx_state;
+ struct net_device *ndev = priv->ndev;
+ struct can_berr_counter bec;
+ struct can_frame *cf = NULL;
+ struct sk_buff *skb;
+ int err;
+
+ priv->devtype_data.get_berr_counter(priv, &bec);
+ can_state_get_by_berr_counter(ndev, &bec, &tx_state, &rx_state);
+
+ new_state = max(tx_state, rx_state);
+ if (new_state == priv->can.state)
+ return 0;
+
+ /* The skb allocation might fail, but can_change_state()
+ * handles cf == NULL.
+ */
+ skb = alloc_can_err_skb(priv->ndev, &cf);
+ can_change_state(ndev, cf, tx_state, rx_state);
+
+ if (new_state == CAN_STATE_BUS_OFF) {
+ priv->devtype_data.chip_stop(priv, CAN_STATE_BUS_OFF);
+ can_bus_off(ndev);
+ }
+
+ if (!skb)
+ return 0;
+
+ if (new_state != CAN_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_CNT;
+ cf->data[6] = bec.txerr;
+ cf->data[7] = bec.rxerr;
+ }
+
+ err = can_rx_offload_queue_tail(&priv->offload, skb);
+ if (err)
+ stats->rx_fifo_errors++;
+
+ return 0;
+}
+
static int
rkcanfd_handle_rx_fifo_overflow_int(struct rkcanfd_priv *priv)
{
@@ -621,6 +998,55 @@ rkcanfd_handle_rx_fifo_overflow_int(struct rkcanfd_priv *priv)
err; \
})
+static irqreturn_t rk3576canfd_irq(int irq, void *dev_id)
+{
+ struct rkcanfd_priv *priv = dev_id;
+ u32 reg_int_unmasked, reg_int;
+
+ reg_int_unmasked = rkcanfd_read(priv, RK3576CANFD_REG_INT);
+ reg_int = reg_int_unmasked & ~priv->reg_int_mask_default;
+
+ if (!reg_int)
+ return IRQ_NONE;
+
+ rkcanfd_write(priv, RK3576CANFD_REG_INT, reg_int);
+
+ if (reg_int & (RK3576CANFD_REG_INT_RXSTR_TIMEOUT_INT |
+ RK3576CANFD_REG_INT_ISM_WTM_INT |
+ RK3576CANFD_REG_INT_RX_FIFO_FULL_INT)) {
+ rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK,
+ priv->reg_int_mask_default | RK3576CANFD_REG_INT_ISM_WTM_INT |
+ RK3576CANFD_REG_INT_RXSTR_TIMEOUT_INT |
+ RK3576CANFD_REG_INT_RX_FINISH_INT);
+ rkcanfd_handle(priv, rk3576_rx_int);
+ }
+
+ if (reg_int & RK3576CANFD_REG_INT_TX_FINISH_INT)
+ rkcanfd_handle(priv, rk3576_tx_int);
+
+ if (reg_int & RK3576CANFD_REG_INT_ERROR_INT)
+ rkcanfd_handle(priv, rk3576_error_int);
+
+ if (reg_int & (RK3576CANFD_REG_INT_BUS_OFF_INT |
+ RK3576CANFD_REG_INT_PASSIVE_ERROR_INT |
+ RK3576CANFD_REG_INT_ERROR_WARNING_INT) ||
+ priv->can.state > CAN_STATE_ERROR_ACTIVE)
+ rkcanfd_handle(priv, rk3576_state_error_int);
+
+ if (reg_int & RK3576CANFD_REG_INT_WAKEUP_INT)
+ netdev_info(priv->ndev, "%s: WAKEUP_INT\n", __func__);
+
+ if (reg_int & RK3576CANFD_REG_INT_BUS_OFF_RECOVERY_INT)
+ netdev_info(priv->ndev, "%s: BUS_OFF_RECOVERY_INT\n", __func__);
+
+ if (reg_int & RK3576CANFD_REG_INT_OVERLOAD_INT)
+ netdev_info(priv->ndev, "%s: OVERLOAD_INT\n", __func__);
+
+ can_rx_offload_irq_finish(&priv->offload);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t rkcanfd_irq(int irq, void *dev_id)
{
struct rkcanfd_priv *priv = dev_id;
@@ -775,6 +1201,16 @@ static void rkcanfd_register_done(const struct rkcanfd_priv *priv)
RKCANFD_ERRATUM_5_SYSCLOCK_HZ_MIN / MEGA);
}
+static void rk3576canfd_register_done(const struct rkcanfd_priv *priv)
+{
+ u32 dev_id;
+
+ dev_id = rkcanfd_read(priv, RK3576CANFD_REG_RTL_VERSION);
+ netdev_info(priv->ndev,
+ "Rockchip-CANFD %s rev%u.\n",
+ rkcanfd_get_model_str(priv), dev_id);
+}
+
static int rkcanfd_register(struct rkcanfd_priv *priv)
{
struct net_device *ndev = priv->ndev;
@@ -858,6 +1294,20 @@ static const struct rkcanfd_devtype_data rkcanfd_devtype_data_rk3568v3 = {
.register_done = rkcanfd_register_done,
};
+/* The rk3576 CAN-FD */
+static const struct rkcanfd_devtype_data rkcanfd_devtype_data_rk3576 = {
+ .model = RKCANFD_MODEL_RK3576,
+ .get_berr_counter = rk3576canfd_get_berr_counter_corrected,
+ .interrupts_enable = rkcanfd_chip_interrupts_enable,
+ .interrupts_disable = rk3576canfd_chip_interrupts_disable,
+ .fifo_setup = rk3576canfd_chip_fifo_setup,
+ .chip_start = rk3576canfd_chip_start,
+ .chip_stop = rk3576canfd_chip_stop,
+ .chip_stop_sync = rk3576canfd_chip_stop_sync,
+ .irq = rk3576canfd_irq,
+ .register_done = rk3576canfd_register_done,
+};
+
static const struct of_device_id rkcanfd_of_match[] = {
{
.compatible = "rockchip,rk3568v2-canfd",
@@ -865,6 +1315,9 @@ static const struct of_device_id rkcanfd_of_match[] = {
}, {
.compatible = "rockchip,rk3568v3-canfd",
.data = &rkcanfd_devtype_data_rk3568v3,
+ }, {
+ .compatible = "rockchip,rk3576-canfd",
+ .data = &rkcanfd_devtype_data_rk3576,
}, {
/* sentinel */
},
diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c
index 475c0409e215..8a383cabd9d2 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-rx.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c
@@ -91,6 +91,47 @@ rkcanfd_fifo_header_to_cfd_header(const struct rkcanfd_priv *priv,
return len + cfd->len;
}
+static unsigned int
+rk3576canfd_fifo_header_to_cfd_header(const struct rkcanfd_priv *priv,
+ const struct rk3576canfd_fifo_header *header,
+ struct canfd_frame *cfd)
+{
+ unsigned int len = sizeof(*cfd) - sizeof(cfd->data);
+ u8 dlc;
+
+ if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_FRAME_FORMAT)
+ cfd->can_id = FIELD_GET(RKCANFD_REG_FD_ID_EFF, header->id) |
+ CAN_EFF_FLAG;
+ else
+ cfd->can_id = FIELD_GET(RKCANFD_REG_FD_ID_SFF, header->id);
+
+ dlc = FIELD_GET(RK3576CANFD_REG_RXFRD_FRAMEINFO_DATA_LENGTH,
+ header->frameinfo);
+
+ /* CAN-FD */
+ if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_FDF) {
+ cfd->len = can_fd_dlc2len(dlc);
+
+ /* The cfd is not allocated by alloc_canfd_skb(), so
+ * set CANFD_FDF here.
+ */
+ cfd->flags |= CANFD_FDF;
+
+ if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_BRS)
+ cfd->flags |= CANFD_BRS;
+ } else {
+ cfd->len = can_cc_dlc2len(dlc);
+
+ if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_RTR) {
+ cfd->can_id |= CAN_RTR_FLAG;
+
+ return len;
+ }
+ }
+
+ return len + cfd->len;
+}
+
static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv,
const struct canfd_frame *cfd_rx, const u32 ts,
bool *tx_done)
@@ -198,6 +239,52 @@ rkcanfd_fifo_header_empty(const struct rkcanfd_fifo_header *header)
header->frameinfo == header->ts;
}
+static int rk3576canfd_handle_rx_int_one(struct rkcanfd_priv *priv)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ struct canfd_frame cfd[1] = { }, *skb_cfd;
+ struct rk3576canfd_fifo_header header[1] = { };
+ struct sk_buff *skb;
+ unsigned int len;
+ int err;
+
+ /* read header into separate struct and convert it later */
+ rkcanfd_read_rep(priv, RKCANFD_REG_RX_FIFO_RDATA,
+ header, sizeof(*header));
+ /* read data directly into cfd */
+ rkcanfd_read_rep(priv, RKCANFD_REG_RX_FIFO_RDATA,
+ cfd->data, sizeof(cfd->data));
+
+ len = rk3576canfd_fifo_header_to_cfd_header(priv, header, cfd);
+
+ /* Manual handling of CAN Bus Error counters. See
+ * rkcanfd_get_corrected_berr_counter() for detailed
+ * explanation.
+ */
+ if (priv->bec.rxerr)
+ priv->bec.rxerr = min(CAN_ERROR_PASSIVE_THRESHOLD,
+ priv->bec.rxerr) - 1;
+
+ if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_FDF)
+ skb = alloc_canfd_skb(priv->ndev, &skb_cfd);
+ else
+ skb = alloc_can_skb(priv->ndev, (struct can_frame **)&skb_cfd);
+
+ if (!skb) {
+ stats->rx_dropped++;
+
+ return 0;
+ }
+
+ memcpy(skb_cfd, cfd, len);
+
+ err = can_rx_offload_queue_tail(&priv->offload, skb);
+ if (err)
+ stats->rx_fifo_errors++;
+
+ return 0;
+}
+
static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
{
struct net_device_stats *stats = &priv->ndev->stats;
@@ -284,6 +371,15 @@ rkcanfd_rx_fifo_get_len(const struct rkcanfd_priv *priv)
return FIELD_GET(RKCANFD_REG_RX_FIFO_CTRL_RX_FIFO_CNT, reg);
}
+static inline unsigned int
+rk3576canfd_rx_fifo_get_len(const struct rkcanfd_priv *priv)
+{
+ const u32 reg = rkcanfd_read(priv, RK3576CANFD_REG_STR_STATE);
+ int val = FIELD_GET(RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT, reg);
+
+ return DIV_ROUND_UP(val, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
+}
+
int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv)
{
unsigned int len;
@@ -297,3 +393,18 @@ int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv)
return 0;
}
+
+int rkcanfd_handle_rk3576_rx_int(struct rkcanfd_priv *priv)
+{
+ unsigned int len;
+ int err;
+
+ while ((len = rk3576canfd_rx_fifo_get_len(priv))) {
+ err = rk3576canfd_handle_rx_int_one(priv);
+ if (err)
+ return err;
+ }
+ rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
+ return 0;
+}
+
diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c
index 865a15e033a9..77bbff9f0918 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-tx.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c
@@ -165,3 +165,30 @@ void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts,
frame_len_p);
stats->tx_packets++;
}
+
+int rkcanfd_handle_rk3576_tx_int(struct rkcanfd_priv *priv)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ unsigned int tx_tail;
+ unsigned int frame_len = 0;
+
+ tx_tail = rkcanfd_get_tx_tail(priv);
+
+ /* Manual handling of CAN Bus Error counters. See
+ * rkcanfd_get_corrected_berr_counter() for detailed
+ * explanation.
+ */
+ if (priv->bec.txerr)
+ priv->bec.txerr--;
+
+ stats->tx_bytes +=
+ can_rx_offload_get_echo_skb_queue_tail(&priv->offload,
+ tx_tail, &frame_len);
+ stats->tx_packets++;
+ WRITE_ONCE(priv->tx_tail, priv->tx_tail + 1);
+ netif_subqueue_completed_wake(priv->ndev, 0, 1, frame_len,
+ rkcanfd_get_effective_tx_free(priv),
+ RKCANFD_TX_START_THRESHOLD);
+ return 0;
+}
+
diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
index bcd26d23062b..9b91d757d054 100644
--- a/drivers/net/can/rockchip/rockchip_canfd.h
+++ b/drivers/net/can/rockchip/rockchip_canfd.h
@@ -287,6 +287,265 @@
#define RKCANFD_REG_RX_FIFO_RDATA 0x400
#define RKCANFD_REG_TXE_FIFO_RDATA 0x500
+#define RK3576CANFD_REG_MODE 0x000
+#define RK3576CANFD_REG_CMD 0x004
+
+#define RK3576CANFD_REG_STATE 0x008
+#define RK3576CANFD_REG_STATE_SLEEP_STATE BIT(5)
+#define RK3576CANFD_REG_STATE_BUS_OFF_STATE BIT(4)
+#define RK3576CANFD_REG_STATE_ERROR_WARNING_STATE BIT(3)
+#define RK3576CANFD_REG_STATE_TX_PERIOD BIT(2)
+#define RK3576CANFD_REG_STATE_RX_PERIOD BIT(1)
+#define RK3576CANFD_REG_STATE_TX_BUFFER_FULL BIT(0)
+
+#define RK3576CANFD_REG_INT 0x00c
+#define RK3576CANFD_REG_INT_BUSOFF_RCY_INT BIT(19)
+#define RK3576CANFD_REG_INT_ESM_WTM_INT BIT(18)
+#define RK3576CANFD_REG_INT_ISM_WTM_INT BIT(17)
+#define RK3576CANFD_REG_INT_BUSINT_INT BIT(16)
+#define RK3576CANFD_REG_INT_RXSTR_TIMEOUT_INT BIT(15)
+#define RK3576CANFD_REG_INT_MFI_TIMEOUT_INT BIT(14)
+#define RK3576CANFD_REG_INT_MFI_INT BIT(13)
+#define RK3576CANFD_REG_INT_AUTO_RETX_FAIL_INT BIT(12)
+#define RK3576CANFD_REG_INT_WAKEUP_INT BIT(11)
+#define RK3576CANFD_REG_INT_BUS_OFF_RECOVERY_INT BIT(10)
+#define RK3576CANFD_REG_INT_BUS_OFF_INT BIT(9)
+#define RK3576CANFD_REG_INT_RX_FIFO_OVERFLOW_INT BIT(8)
+#define RK3576CANFD_REG_INT_RX_FIFO_FULL_INT BIT(7)
+#define RK3576CANFD_REG_INT_ERROR_INT BIT(6)
+#define RK3576CANFD_REG_INT_TX_ARBIT_FAIL_INT BIT(5)
+#define RK3576CANFD_REG_INT_PASSIVE_ERROR_INT BIT(4)
+#define RK3576CANFD_REG_INT_OVERLOAD_INT BIT(3)
+#define RK3576CANFD_REG_INT_ERROR_WARNING_INT BIT(2)
+#define RK3576CANFD_REG_INT_TX_FINISH_INT BIT(1)
+#define RK3576CANFD_REG_INT_RX_FINISH_INT BIT(0)
+
+#define RK3576CANFD_REG_INT_ALL \
+ (RK3576CANFD_REG_INT_BUSOFF_RCY_INT | \
+ RK3576CANFD_REG_INT_ESM_WTM_INT | \
+ RK3576CANFD_REG_INT_ISM_WTM_INT | \
+ RK3576CANFD_REG_INT_BUSINT_INT | \
+ RK3576CANFD_REG_INT_RXSTR_TIMEOUT_INT | \
+ RK3576CANFD_REG_INT_MFI_TIMEOUT_INT | \
+ RK3576CANFD_REG_INT_MFI_INT | \
+ RK3576CANFD_REG_INT_AUTO_RETX_FAIL_INT | \
+ RK3576CANFD_REG_INT_WAKEUP_INT | \
+ RK3576CANFD_REG_INT_BUS_OFF_RECOVERY_INT | \
+ RK3576CANFD_REG_INT_BUS_OFF_INT | \
+ RK3576CANFD_REG_INT_RX_FIFO_OVERFLOW_INT | \
+ RK3576CANFD_REG_INT_RX_FIFO_FULL_INT | \
+ RK3576CANFD_REG_INT_ERROR_INT | \
+ RK3576CANFD_REG_INT_TX_ARBIT_FAIL_INT | \
+ RK3576CANFD_REG_INT_PASSIVE_ERROR_INT | \
+ RK3576CANFD_REG_INT_OVERLOAD_INT | \
+ RK3576CANFD_REG_INT_ERROR_WARNING_INT | \
+ RK3576CANFD_REG_INT_TX_FINISH_INT | \
+ RK3576CANFD_REG_INT_RX_FINISH_INT)
+
+#define RK3576CANFD_REG_INT_ALL_ERROR \
+ (RK3576CANFD_REG_INT_BUS_OFF_INT | \
+ RK3576CANFD_REG_INT_ERROR_INT | \
+ RK3576CANFD_REG_INT_PASSIVE_ERROR_INT | \
+ RK3576CANFD_REG_INT_ERROR_WARNING_INT)
+
+#define RK3576CANFD_REG_INT_MASK 0x010
+
+#define RK3576CANFD_REG_FD_NOMINAL_BITTIMING 0x100
+#define RK3576CANFD_REG_FD_NOMINAL_BITTIMING_SAMPLE_MODE BIT(31)
+#define RK3576CANFD_REG_FD_NOMINAL_BITTIMING_SJW GENMASK(30, 24)
+#define RK3576CANFD_REG_FD_NOMINAL_BITTIMING_BRP GENMASK(23, 16)
+#define RK3576CANFD_REG_FD_NOMINAL_BITTIMING_TSEG2 GENMASK(14, 8)
+#define RK3576CANFD_REG_FD_NOMINAL_BITTIMING_TSEG1 GENMASK(7, 0)
+
+#define RK3576CANFD_REG_FD_DATA_BITTIMING 0x104
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_BRS_TSEG1 GENMASK(31, 24)
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_BRS_MODE BIT(23)
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_ACKSLOT_SYNC_DIS BIT(22)
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_SJW GENMASK(20, 17)
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_BRP GENMASK(16, 9)
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_TSEG2 GENMASK(8, 5)
+#define RK3576CANFD_REG_FD_DATA_BITTIMING_TSEG1 GENMASK(4, 0)
+
+#define RK3576CANFD_REG_TRANSMIT_DELAY_COMPENSATION 0x108
+#define RK3576CANFD_REG_TRANSMIT_DELAY_COMPENSATION_TDC_OFFSET GENMASK(6, 1)
+#define RK3576CANFD_REG_TRANSMIT_DELAY_COMPENSATION_TDC_ENABLE BIT(0)
+
+#define RK3576CANFD_REG_BRS_CFG 0x10c
+#define RK3576CANFD_REG_BRS_CFG_TRIPLE_SYNC_MODE BIT(31)
+#define RK3576CANFD_REG_BRS_CFG_SP2_DTSEG1 GENMASK(30, 26)
+#define RK3576CANFD_REG_BRS_CFG_SP2_NTSEG1 GENMASK(25, 18)
+#define RK3576CANFD_REG_BRS_CFG_SP1_DTSEG1 GENMASK(17, 13)
+#define RK3576CANFD_REG_BRS_CFG_SP1_NTSEG1 GENMASK(12, 5)
+#define RK3576CANFD_REG_BRS_CFG_RESYNC_MODE BIT(3)
+#define RK3576CANFD_REG_BRS_CFG_BRS_POSSYNC_EN BIT(1)
+#define RK3576CANFD_REG_BRS_CFG_BRS_NEGSYNC_EN BIT(0)
+
+#define RK3576CANFD_REG_LOOP_CNT 0x110
+
+#define RK3576CANFD_REG_DMA_CTRL 0x11c
+#define RK3576CANFD_REG_DMA_CTRL_DMA_RX_EN BIT(9)
+#define RK3576CANFD_REG_DMA_CTRL_DMA_THR GENMASK(8, 0)
+
+#define RK3576CANFD_REG_FD_TXFRAMEINFO 0x200
+
+#define RK3576CANFD_REG_FD_TXID 0x204
+#define RK3576CANFD_REG_FD_ID_EFF GENMASK(28, 0)
+#define RK3576CANFD_REG_FD_ID_SFF GENMASK(11, 0)
+
+#define RK3576CANFD_REG_FD_TXDATA0 0x208
+#define RK3576CANFD_REG_FD_TXDATA1 0x20c
+#define RK3576CANFD_REG_FD_TXDATA2 0x210
+#define RK3576CANFD_REG_FD_TXDATA3 0x214
+#define RK3576CANFD_REG_FD_TXDATA4 0x218
+#define RK3576CANFD_REG_FD_TXDATA5 0x21c
+#define RK3576CANFD_REG_FD_TXDATA6 0x220
+#define RK3576CANFD_REG_FD_TXDATA7 0x224
+#define RK3576CANFD_REG_FD_TXDATA8 0x228
+#define RK3576CANFD_REG_FD_TXDATA9 0x22c
+#define RK3576CANFD_REG_FD_TXDATA10 0x230
+#define RK3576CANFD_REG_FD_TXDATA11 0x234
+#define RK3576CANFD_REG_FD_TXDATA12 0x238
+#define RK3576CANFD_REG_FD_TXDATA13 0x23c
+#define RK3576CANFD_REG_FD_TXDATA14 0x240
+#define RK3576CANFD_REG_FD_TXDATA15 0x244
+
+#define RK3576CANFD_REG_RXFRD 0x400
+#define RK3576CANFD_REG_RXFRD_FRAMEINFO_FRAME_FORMAT BIT(23)
+#define RK3576CANFD_REG_RXFRD_FRAMEINFO_RTR BIT(22)
+#define RK3576CANFD_REG_RXFRD_FRAMEINFO_FDF BIT(21)
+#define RK3576CANFD_REG_RXFRD_FRAMEINFO_BRS BIT(20)
+#define RK3576CANFD_REG_RXFRD_FRAMEINFO_DATA_LENGTH GENMASK(27, 24)
+
+#define RK3576CANFD_REG_STR_CTL 0x600
+#define RK3576CANFD_REG_STR_CTL_STORAGE_TIMEOUT_MODE BIT(8)
+#define RK3576CANFD_REG_STR_CTL_ESM_SEL_MASK GENMASK(7, 6)
+#define RK3576CANFD_REG_STR_CTL_RX_STORAGE_RESET BIT(4)
+#define RK3576CANFD_REG_STR_CTL_ISM_SEL GENMASK(3, 2)
+#define RK3576CANFD_REG_STR_CTL_ISM_SEL_FLEXIBLE 0x0
+#define RK3576CANFD_REG_STR_CTL_ISM_SEL_CAN_FIXED 0x1
+#define RK3576CANFD_REG_STR_CTL_ISM_SEL_CANFD_FIXED 0x2
+#define RK3576CANFD_REG_STR_CTL_EXT_STORAGE_MODE BIT(1)
+#define RK3576CANFD_REG_STR_CTL_BUFFER_MODE_ENABLE BIT(0)
+
+#define RK3576CANFD_REG_STR_STATE 0x604
+#define RK3576CANFD_REG_STR_STATE_INTM_FRAME_CNT GENMASK(25, 17)
+#define RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT GENMASK(16, 8)
+#define RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT 18
+#define RK3576CANFD_REG_STR_STATE_EXTM_FULL BIT(3)
+#define RK3576CANFD_REG_STR_STATE_EXTM_EMPTY BIT(2)
+#define RK3576CANFD_REG_STR_STATE_INTM_FULL BIT(1)
+#define RK3576CANFD_REG_STR_STATE_INTM_EMPTY BIT(0)
+
+#define RK3576CANFD_REG_STR_TIMEOUT 0x608
+
+#define RK3576CANFD_REG_STR_WTM 0x60c
+#define RK3576CANFD_REG_ATF0 0x700
+#define RK3576CANFD_REG_ATF1 0x704
+#define RK3576CANFD_REG_ATF2 0x708
+#define RK3576CANFD_REG_ATF3 0x70c
+#define RK3576CANFD_REG_ATF4 0x710
+#define RK3576CANFD_REG_ATFM0 0x714
+#define RK3576CANFD_REG_ATFM1 0x718
+#define RK3576CANFD_REG_ATFM2 0x71c
+#define RK3576CANFD_REG_ATFM3 0x720
+
+#define RK3576CANFD_REG_ATFM4 0x724
+#define RK3576CANFD_REG_ATFM_MASK_SEL BIT(31)
+#define RK3576CANFD_REG_ATFM_RTR_EN BIT(30)
+#define RK3576CANFD_REG_ATFM_RTR BIT(29)
+#define RK3576CANFD_REG_ATFM_MASK_SEL_MASK_MODE 0x0
+#define RK3576CANFD_REG_ATFM_MASK_SEL_LIST_MODE 0x1
+#define RK3576CANFD_REG_ATFM_ID GENMASK(28, 0)
+
+#define RK3576CANFD_REG_ATF_DLC 0x728
+#define RK3576CANFD_REG_ATF_DLC_ATF_DLC_MODE BIT(5)
+#define RK3576CANFD_REG_ATF_DLC_ATF_DLC_EN BIT(4)
+#define RK3576CANFD_REG_ATF_DLC_ATF_DLC GENMASK(3, 0)
+
+#define RK3576CANFD_REG_ATF_CTL 0x72c
+#define RK3576CANFD_REG_ATF_CTL_ATF_DIS(n) BIT(n)
+#define RK3576CANFD_REG_ATF_CTL_ATF_DIS_ALL GENMASK(15, 0)
+
+#define RK3576CANFD_REG_SPACE_CTRL 0x800
+
+#define RK3576CANFD_REG_AUTO_RETX_CFG 0x808
+#define RK3576CANFD_REG_AUTO_RETX_CFG_RETX_TIME_LIMIT GENMASK(18, 3)
+#define RK3576CANFD_REG_AUTO_RETX_CFG_RETX_LIMIT_EN BIT(1)
+#define RK3576CANFD_REG_AUTO_RETX_CFG_AUTO_RETX_EN BIT(0)
+
+#define RK3576CANFD_REG_AUTO_RETX_STATE0 0x80c
+#define RK3576CANFD_REG_AUTO_RETX_STATE0_AUTO_RETX_CNT GENMASK(15, 0)
+
+#define RK3576CANFD_REG_AUTO_RETX_STATE1 0x810
+#define RK3576CANFD_REG_OLF_CFG 0x814
+#define RK3576CANFD_REG_RXINT_CTRL 0x818
+#define RK3576CANFD_REG_RXINT_TIMEOUT 0x81c
+#define RK3576CANFD_REG_OTHER_CFG 0x820
+#define RK3576CANFD_REG_WAVE_FILTER_CFG 0x824
+#define RK3576CANFD_REG_RBC_CFG 0x828
+#define RK3576CANFD_REG_TXCRC_CFG 0x82c
+
+#define RK3576CANFD_REG_BUSOFFRCY_CFG 0x830
+#define RK3576CANFD_REG_BUSOFF_RCY_THR 0x834
+
+#define RK3576CANFD_REG_ERROR_CODE 0x900
+#define RK3576CANFD_REG_ERROR_MASK 0x904
+#define RK3576CANFD_REG_ERROR_MASK_ACK_ERROR BIT(4)
+#define RK3576CANFD_REG_ERROR_MASK_FORM_ERROR BIT(3)
+#define RK3576CANFD_REG_ERROR_MASK_CRC_ERROR BIT(2)
+#define RK3576CANFD_REG_ERROR_MASK_STUFF_ERROR BIT(1)
+#define RK3576CANFD_REG_ERROR_MASK_BIT_ERROR BIT(0)
+
+#define RK3576CANFD_REG_RXERRORCNT 0x910
+#define RK3576CANFD_REG_TXERRORCNT 0x914
+#define RK3576CANFD_REG_RX_RXSRAM_RDATA 0xc00
+#define RK3576CANFD_REG_RTL_VERSION 0xf0c
+
+#define RK3576CANFD_REG_ERROR_CODE_PHASE BIT(29)
+#define RK3576CANFD_REG_ERROR_CODE_TYPE GENMASK(28, 26)
+#define RK3576CANFD_REG_ERROR_CODE_TYPE_BIT 0x0
+#define RK3576CANFD_REG_ERROR_CODE_TYPE_STUFF 0x1
+#define RK3576CANFD_REG_ERROR_CODE_TYPE_FORM 0x2
+#define RK3576CANFD_REG_ERROR_CODE_TYPE_ACK 0x3
+#define RK3576CANFD_REG_ERROR_CODE_TYPE_CRC 0x4
+#define RK3576CANFD_REG_ERROR_CODE_DIRECTION_RX BIT(25)
+#define RK3576CANFD_REG_ERROR_CODE_TX GENMASK(24, 19)
+#define RK3576CANFD_REG_ERROR_CODE_TX_ACK_EOF BIT(24)
+#define RK3576CANFD_REG_ERROR_CODE_TX_CRC BIT(23)
+#define RK3576CANFD_REG_ERROR_CODE_TX_STUFF_COUNT BIT(22)
+#define RK3576CANFD_REG_ERROR_CODE_TX_DATA BIT(21)
+#define RK3576CANFD_REG_ERROR_CODE_TX_SOF_DLC BIT(20)
+#define RK3576CANFD_REG_ERROR_CODE_TX_IDLE BIT(19)
+#define RK3576CANFD_REG_ERROR_CODE_RX GENMASK(18, 0)
+#define RK3576CANFD_REG_ERROR_CODE_RX_ERROR BIT(18)
+#define RK3576CANFD_REG_ERROR_CODE_RX_OVERLOAD BIT(17)
+#define RK3576CANFD_REG_ERROR_CODE_RX_SPACE BIT(16)
+#define RK3576CANFD_REG_ERROR_CODE_RX_EOF BIT(15)
+#define RK3576CANFD_REG_ERROR_CODE_RX_ACK_LIM BIT(14)
+#define RK3576CANFD_REG_ERROR_CODE_RX_ACK BIT(13)
+#define RK3576CANFD_REG_ERROR_CODE_RX_CRC_LIM BIT(12)
+#define RK3576CANFD_REG_ERROR_CODE_RX_CRC BIT(11)
+#define RK3576CANFD_REG_ERROR_CODE_RX_STUFF_COUNT BIT(10)
+#define RK3576CANFD_REG_ERROR_CODE_RX_DATA BIT(9)
+#define RK3576CANFD_REG_ERROR_CODE_RX_DLC BIT(8)
+#define RK3576CANFD_REG_ERROR_CODE_RX_BRS_ESI BIT(7)
+#define RK3576CANFD_REG_ERROR_CODE_RX_RES BIT(6)
+#define RK3576CANFD_REG_ERROR_CODE_RX_FDF BIT(5)
+#define RK3576CANFD_REG_ERROR_CODE_RX_ID2_RTR BIT(4)
+#define RK3576CANFD_REG_ERROR_CODE_RX_SOF_IDE BIT(3)
+#define RK3576CANFD_REG_ERROR_CODE_RX_BUS_IDLE BIT(2)
+#define RK3576CANFD_REG_ERROR_CODE_RX_BUS_INT BIT(1)
+#define RK3576CANFD_REG_ERROR_CODE_RX_STOP BIT(0)
+
+#define RK3576CANFD_ISM_WATERMASK_CAN 0x6c /* word */
+#define RK3576CANFD_ISM_WATERMASK_CANFD 0x6c /* word */
+
+#define RK3576CANFD_SRAM_MAX_DEPTH 256 /* word */
+
+#define RK3576CANFD_CANFD_FILTER GENMASK(28, 0)
+
+#define RK3576CANFD_CANFD_FIFO_CNT GENMASK(7, 0)
+
#define DEVICE_NAME "rockchip_canfd"
#define RKCANFD_NAPI_WEIGHT 32
#define RKCANFD_TXFIFO_DEPTH 2
@@ -434,6 +693,7 @@
enum rkcanfd_model {
RKCANFD_MODEL_RK3568V2 = 0x35682,
RKCANFD_MODEL_RK3568V3 = 0x35683,
+ RKCANFD_MODEL_RK3576 = 0x3576,
};
struct rkcanfd_priv;
@@ -458,6 +718,11 @@ struct rkcanfd_fifo_header {
u32 ts;
};
+struct rk3576canfd_fifo_header {
+ u32 frameinfo;
+ u32 id;
+};
+
struct rkcanfd_stats {
struct u64_stats_sync syncp;
@@ -547,6 +812,8 @@ rkcanfd_get_tx_free(const struct rkcanfd_priv *priv)
void rkcanfd_ethtool_init(struct rkcanfd_priv *priv);
int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv);
+int rkcanfd_handle_rk3576_tx_int(struct rkcanfd_priv *priv);
+int rkcanfd_handle_rk3576_rx_int(struct rkcanfd_priv *priv);
void rkcanfd_skb_set_timestamp(const struct rkcanfd_priv *priv,
struct sk_buff *skb, const u32 timestamp);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx
2025-05-26 6:25 [PATCH v6 0/4] rockchip: add can for RK3576 Soc Elaine Zhang
` (2 preceding siblings ...)
2025-05-26 6:25 ` [PATCH v6 3/4] net: can: rockchip: add can for RK3576 Soc Elaine Zhang
@ 2025-05-26 6:25 ` Elaine Zhang
2025-05-27 11:00 ` Marc Kleine-Budde
2025-05-27 15:58 ` [PATCH v6 0/4] rockchip: add can for RK3576 Soc Marc Kleine-Budde
4 siblings, 1 reply; 10+ messages in thread
From: Elaine Zhang @ 2025-05-26 6:25 UTC (permalink / raw)
To: zhangqing, mkl, kernel, mailhol.vincent, robh, krzk+dt, conor+dt,
heiko, cl, kever.yang
Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel,
devicetree
The new can controller of rk3576 supports rx dma.
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
.../net/can/rockchip/rockchip_canfd-core.c | 39 +++++++++
drivers/net/can/rockchip/rockchip_canfd-rx.c | 87 +++++++++++++++++++
drivers/net/can/rockchip/rockchip_canfd.h | 11 +++
3 files changed, 137 insertions(+)
diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
index 92e260cb2527..9ef4a9ae19d8 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-core.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
@@ -433,6 +433,9 @@ static void rk3576canfd_chip_start(struct rkcanfd_priv *priv)
RK3576CANFD_REG_BRS_CFG_BRS_NEGSYNC_EN |
RK3576CANFD_REG_BRS_CFG_BRS_POSSYNC_EN);
+ if (priv->use_dma)
+ rkcanfd_write(priv, RK3576CANFD_REG_DMA_CTRL,
+ RK3576CANFD_REG_DMA_CTRL_DMA_RX_EN | priv->dma_thr);
rkcanfd_set_bittiming(priv);
priv->devtype_data.interrupts_disable(priv);
@@ -1324,10 +1327,31 @@ static const struct of_device_id rkcanfd_of_match[] = {
};
MODULE_DEVICE_TABLE(of, rkcanfd_of_match);
+static void rk3576_canfd_dma_init(struct rkcanfd_priv *priv)
+{
+ struct dma_slave_config rxconf = {
+ .direction = DMA_DEV_TO_MEM,
+ .src_addr = priv->rx_dma_src_addr,
+ .src_addr_width = 4,
+ .dst_addr_width = 4,
+ .src_maxburst = 9,
+ };
+
+ priv->dma_thr = rxconf.src_maxburst - 1;
+ priv->rxbuf = dma_alloc_coherent(priv->dev, priv->dma_size * 14,
+ &priv->rx_dma_dst_addr, GFP_KERNEL);
+ if (!priv->rxbuf) {
+ priv->use_dma = 0;
+ return;
+ }
+ dmaengine_slave_config(priv->rxchan, &rxconf);
+}
+
static int rkcanfd_probe(struct platform_device *pdev)
{
struct rkcanfd_priv *priv;
struct net_device *ndev;
+ struct resource *res;
const void *match;
int err;
@@ -1349,6 +1373,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
goto out_free_candev;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs)) {
err = PTR_ERR(priv->regs);
@@ -1376,6 +1401,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
priv->can.do_set_mode = rkcanfd_set_mode;
priv->can.do_get_berr_counter = rkcanfd_get_berr_counter;
priv->ndev = ndev;
+ priv->dev = &pdev->dev;
match = device_get_match_data(&pdev->dev);
if (match) {
@@ -1384,6 +1410,19 @@ static int rkcanfd_probe(struct platform_device *pdev)
priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
}
+ priv->rxchan = dma_request_chan(&pdev->dev, "rx");
+ if (IS_ERR(priv->rxchan)) {
+ dev_warn(&pdev->dev, "Failed to request rxchan\n");
+ priv->rxchan = NULL;
+ priv->use_dma = 0;
+ } else {
+ priv->rx_dma_src_addr = res->start + RK3576CANFD_REG_RXFRD;
+ priv->dma_size = RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT * 4;
+ priv->use_dma = 1;
+ }
+ if (priv->use_dma)
+ rk3576_canfd_dma_init(priv);
+
err = can_rx_offload_add_manual(ndev, &priv->offload,
RKCANFD_NAPI_WEIGHT);
if (err)
diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c
index 8a383cabd9d2..ac06e876552e 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-rx.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c
@@ -285,6 +285,52 @@ static int rk3576canfd_handle_rx_int_one(struct rkcanfd_priv *priv)
return 0;
}
+static int rk3576canfd_handle_rx_dma(struct rkcanfd_priv *priv, u32 addr)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ struct canfd_frame cfd[1] = { }, *skb_cfd;
+ struct rk3576canfd_fifo_header header[1] = { };
+ struct sk_buff *skb;
+ u32 __iomem *rxbuf = (u32 __iomem *)priv->rxbuf;
+ unsigned int len;
+ int i;
+
+ header->frameinfo = readl(rxbuf +
+ addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
+ header->id = readl(rxbuf + 1 +
+ addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
+ for (i = 0; i < (RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT - 2); i++)
+ cfd->data[i] = readl(rxbuf + 2 + i +
+ addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
+
+ len = rk3576canfd_fifo_header_to_cfd_header(priv, header, cfd);
+
+ /* Manual handling of CAN Bus Error counters. See
+ * rkcanfd_get_corrected_berr_counter() for detailed
+ * explanation.
+ */
+ if (priv->bec.rxerr)
+ priv->bec.rxerr = min(CAN_ERROR_PASSIVE_THRESHOLD,
+ priv->bec.rxerr) - 1;
+
+ if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_FDF)
+ skb = alloc_canfd_skb(priv->ndev, &skb_cfd);
+ else
+ skb = alloc_can_skb(priv->ndev, (struct can_frame **)&skb_cfd);
+
+ if (!skb) {
+ stats->rx_dropped++;
+
+ return 0;
+ }
+
+ memcpy(skb_cfd, cfd, len);
+ stats->rx_packets++;
+ stats->rx_bytes += cfd->len;
+ netif_rx(skb);
+ return 0;
+}
+
static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
{
struct net_device_stats *stats = &priv->ndev->stats;
@@ -380,6 +426,43 @@ rk3576canfd_rx_fifo_get_len(const struct rkcanfd_priv *priv)
return DIV_ROUND_UP(val, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
}
+static void rk3576_canfd_rx_dma_callback(void *data)
+{
+ struct rkcanfd_priv *priv = data;
+ int i;
+
+ for (i = 0; i < priv->quota; i++)
+ rk3576canfd_handle_rx_dma(priv, i);
+
+ rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
+}
+
+static int rk3576_canfd_rx_dma(struct rkcanfd_priv *priv)
+{
+ struct dma_async_tx_descriptor *rxdesc = NULL;
+ const u32 reg = rkcanfd_read(priv, RK3576CANFD_REG_STR_STATE);
+ int quota = FIELD_GET(RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT, reg);
+
+ quota = DIV_ROUND_UP(quota, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
+ priv->quota = quota;
+ if (priv->quota == 0) {
+ rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
+ return 0;
+ }
+
+ rxdesc = dmaengine_prep_slave_single(priv->rxchan, priv->rx_dma_dst_addr,
+ priv->dma_size * priv->quota, DMA_DEV_TO_MEM, 0);
+ if (!rxdesc)
+ return -ENOMSG;
+
+ rxdesc->callback = rk3576_canfd_rx_dma_callback;
+ rxdesc->callback_param = priv;
+
+ dmaengine_submit(rxdesc);
+ dma_async_issue_pending(priv->rxchan);
+ return 0;
+}
+
int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv)
{
unsigned int len;
@@ -399,6 +482,10 @@ int rkcanfd_handle_rk3576_rx_int(struct rkcanfd_priv *priv)
unsigned int len;
int err;
+ if (priv->use_dma) {
+ rk3576_canfd_rx_dma(priv);
+ return 0;
+ }
while ((len = rk3576canfd_rx_fifo_get_len(priv))) {
err = rk3576canfd_handle_rx_int_one(priv);
if (err)
diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
index 9b91d757d054..c7d6845c6d95 100644
--- a/drivers/net/can/rockchip/rockchip_canfd.h
+++ b/drivers/net/can/rockchip/rockchip_canfd.h
@@ -11,6 +11,8 @@
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/netdevice.h>
#include <linux/reset.h>
@@ -737,6 +739,7 @@ struct rkcanfd_priv {
struct can_priv can;
struct can_rx_offload offload;
struct net_device *ndev;
+ struct device *dev;
void __iomem *regs;
unsigned int tx_head;
@@ -758,6 +761,14 @@ struct rkcanfd_priv {
struct reset_control *reset;
struct clk_bulk_data *clks;
int clks_num;
+ bool use_dma;
+ u32 dma_size;
+ u32 dma_thr;
+ int quota;
+ struct dma_chan *rxchan;
+ u32 *rxbuf;
+ dma_addr_t rx_dma_src_addr;
+ dma_addr_t rx_dma_dst_addr;
};
static inline u32
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller
2025-05-26 6:25 ` [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller Elaine Zhang
@ 2025-05-26 15:12 ` Conor Dooley
0 siblings, 0 replies; 10+ messages in thread
From: Conor Dooley @ 2025-05-26 15:12 UTC (permalink / raw)
To: Elaine Zhang
Cc: mkl, kernel, mailhol.vincent, robh, krzk+dt, conor+dt, heiko, cl,
kever.yang, linux-can, linux-arm-kernel, linux-rockchip,
linux-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 2470 bytes --]
On Mon, May 26, 2025 at 02:25:56PM +0800, Elaine Zhang wrote:
> Add documentation for the rockchip rk3576 CAN-FD controller.
>
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> ---
> .../net/can/rockchip,rk3568v2-canfd.yaml | 41 +++++++++++++++++++
> 1 file changed, 41 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml b/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
> index a077c0330013..c6595fef6cb5 100644
> --- a/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
> +++ b/Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
> @@ -12,11 +12,27 @@ maintainers:
>
> allOf:
> - $ref: can-controller.yaml#
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: rockchip,rk3576-canfd
> + required:
> + - compatible
> + then:
> + required:
> + - dmas
> + - dma-names
> + else:
> + properties:
> + dmas: false
> + dma-names: false
Once the allOf contains more than just an include it should be moved
down below the properties that it is constraining. Otherwise, I think
this looks fine.
>
> properties:
> compatible:
> oneOf:
> - const: rockchip,rk3568v2-canfd
> + - const: rockchip,rk3576-canfd
> - items:
> - const: rockchip,rk3568v3-canfd
> - const: rockchip,rk3568v2-canfd
> @@ -43,6 +59,13 @@ properties:
> - const: core
> - const: apb
>
> + dmas:
> + maxItems: 1
> +
> + dma-names:
> + items:
> + - const: rx
> +
> required:
> - compatible
> - reg
> @@ -72,3 +95,21 @@ examples:
> reset-names = "core", "apb";
> };
> };
> +
> + - |
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + can@2ac00000 {
> + compatible = "rockchip,rk3576-canfd";
> + reg = <0x0 0x2ac00000 0x0 0x1000>;
> + interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&cru CLK_CAN0>, <&cru PCLK_CAN0>;
> + clock-names = "baud", "pclk";
> + resets = <&cru SRST_CAN0>, <&cru SRST_P_CAN0>;
> + reset-names = "core", "apb";
> + dmas = <&dmac0 20>;
> + dma-names = "rx";
> + };
> + };
> --
> 2.34.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx
2025-05-26 6:25 ` [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx Elaine Zhang
@ 2025-05-27 11:00 ` Marc Kleine-Budde
2025-05-28 7:21 ` zhangqing
0 siblings, 1 reply; 10+ messages in thread
From: Marc Kleine-Budde @ 2025-05-27 11:00 UTC (permalink / raw)
To: Elaine Zhang
Cc: kernel, mailhol.vincent, robh, krzk+dt, conor+dt, heiko, cl,
kever.yang, linux-can, linux-arm-kernel, linux-rockchip,
linux-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 10738 bytes --]
On 26.05.2025 14:25:59, Elaine Zhang wrote:
> The new can controller of rk3576 supports rx dma.
I'm missing dma_sync_single_for_cpu() and dma_sync_single_for_device().
What does the overall picture look like?
The CAN controller receives a CAN frame, it triggers an interrupt, the
driver starts a RX-DMA, the RX-DMA finishes triggers the callback, the
driver allocates an skb and copies the data from the DMA dest memory to
the skb. Finally the skb is passed to the networking stack.
Have you done any measurements if using DMA brings any benefits here? I
doubt that.
If your hardware supports, a better setup would be to allocate a bunch
of skbs and setup the a DMA request per skb and push them all to the DMA
engine during open(). If the CAN controller receives a CAN frame, the
DMA automatically starts and raises an IRQ after finish of the DMA
transfer. Transform the header and then pass the skb to the networking
stack - no need for memcpy.
This _might_ lower the total CPU cost of RX, but a lot of additional
code is used for that.
Never the less, review inline.
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> ---
> .../net/can/rockchip/rockchip_canfd-core.c | 39 +++++++++
> drivers/net/can/rockchip/rockchip_canfd-rx.c | 87 +++++++++++++++++++
> drivers/net/can/rockchip/rockchip_canfd.h | 11 +++
> 3 files changed, 137 insertions(+)
>
> diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
> index 92e260cb2527..9ef4a9ae19d8 100644
> --- a/drivers/net/can/rockchip/rockchip_canfd-core.c
> +++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
> @@ -433,6 +433,9 @@ static void rk3576canfd_chip_start(struct rkcanfd_priv *priv)
> RK3576CANFD_REG_BRS_CFG_BRS_NEGSYNC_EN |
> RK3576CANFD_REG_BRS_CFG_BRS_POSSYNC_EN);
>
> + if (priv->use_dma)
> + rkcanfd_write(priv, RK3576CANFD_REG_DMA_CTRL,
> + RK3576CANFD_REG_DMA_CTRL_DMA_RX_EN | priv->dma_thr);
> rkcanfd_set_bittiming(priv);
>
> priv->devtype_data.interrupts_disable(priv);
> @@ -1324,10 +1327,31 @@ static const struct of_device_id rkcanfd_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, rkcanfd_of_match);
>
> +static void rk3576_canfd_dma_init(struct rkcanfd_priv *priv)
> +{
> + struct dma_slave_config rxconf = {
> + .direction = DMA_DEV_TO_MEM,
> + .src_addr = priv->rx_dma_src_addr,
> + .src_addr_width = 4,
> + .dst_addr_width = 4,
> + .src_maxburst = 9,
> + };
> +
> + priv->dma_thr = rxconf.src_maxburst - 1;
> + priv->rxbuf = dma_alloc_coherent(priv->dev, priv->dma_size * 14,
Where does the 14 come from?
> + &priv->rx_dma_dst_addr, GFP_KERNEL);
I'm missing the cleanup for priv->rxbuf.
> + if (!priv->rxbuf) {
> + priv->use_dma = 0;
> + return;
> + }
> + dmaengine_slave_config(priv->rxchan, &rxconf);
Please add error handling.
> +}
> +
> static int rkcanfd_probe(struct platform_device *pdev)
> {
> struct rkcanfd_priv *priv;
> struct net_device *ndev;
> + struct resource *res;
> const void *match;
> int err;
>
> @@ -1349,6 +1373,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
> goto out_free_candev;
> }
>
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> priv->regs = devm_platform_ioremap_resource(pdev, 0);
Please use devm_platform_get_and_ioremap_resource();
> if (IS_ERR(priv->regs)) {
> err = PTR_ERR(priv->regs);
> @@ -1376,6 +1401,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
> priv->can.do_set_mode = rkcanfd_set_mode;
> priv->can.do_get_berr_counter = rkcanfd_get_berr_counter;
> priv->ndev = ndev;
> + priv->dev = &pdev->dev;
Please remove, priv->dev is not used in the hot path, use
priv->ndev->dev.parent instead.
>
> match = device_get_match_data(&pdev->dev);
> if (match) {
> @@ -1384,6 +1410,19 @@ static int rkcanfd_probe(struct platform_device *pdev)
> priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
> }
>
> + priv->rxchan = dma_request_chan(&pdev->dev, "rx");
I'm missing the cleanup for priv->rxchan.
> + if (IS_ERR(priv->rxchan)) {
Please handle -EPROBE_DEFER properly.
> + dev_warn(&pdev->dev, "Failed to request rxchan\n");
Please print the error value and state that you are continuing w/o DMA,
e.g.:
netdev_warn("Failed to request RX-DMA channel: %pe, continuing without DMA", priv->rxchan);
> + priv->rxchan = NULL;
> + priv->use_dma = 0;
> + } else {
> + priv->rx_dma_src_addr = res->start + RK3576CANFD_REG_RXFRD;
> + priv->dma_size = RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT * 4;
Why do you need priv->dma_size, it doesn't change during runtime, does it?
> + priv->use_dma = 1;
> + }
> + if (priv->use_dma)
> + rk3576_canfd_dma_init(priv);
> +
Can you move all DMA related functionality into rk3576_canfd_dma_init(),
pass res as a parameter, remove rx_dma_src_addr.
I think you don't need use_dma, checking rxchan != NULL should be
sufficient.
> err = can_rx_offload_add_manual(ndev, &priv->offload,
> RKCANFD_NAPI_WEIGHT);
> if (err)
> diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c
> index 8a383cabd9d2..ac06e876552e 100644
> --- a/drivers/net/can/rockchip/rockchip_canfd-rx.c
> +++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c
> @@ -285,6 +285,52 @@ static int rk3576canfd_handle_rx_int_one(struct rkcanfd_priv *priv)
> return 0;
> }
>
> +static int rk3576canfd_handle_rx_dma(struct rkcanfd_priv *priv, u32 addr)
> +{
> + struct net_device_stats *stats = &priv->ndev->stats;
> + struct canfd_frame cfd[1] = { }, *skb_cfd;
> + struct rk3576canfd_fifo_header header[1] = { };
> + struct sk_buff *skb;
> + u32 __iomem *rxbuf = (u32 __iomem *)priv->rxbuf;
Why do you cast rx_buf to __iomem? It's normal memory, isn't it?
> + unsigned int len;
> + int i;
> +
> + header->frameinfo = readl(rxbuf +
> + addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
> + header->id = readl(rxbuf + 1 +
> + addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
> + for (i = 0; i < (RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT - 2); i++)
> + cfd->data[i] = readl(rxbuf + 2 + i +
> + addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
> +
Remove cfd and header and directly access the rxbuf.
> + len = rk3576canfd_fifo_header_to_cfd_header(priv, header, cfd);
> +
> + /* Manual handling of CAN Bus Error counters. See
> + * rkcanfd_get_corrected_berr_counter() for detailed
> + * explanation.
> + */
> + if (priv->bec.rxerr)
> + priv->bec.rxerr = min(CAN_ERROR_PASSIVE_THRESHOLD,
> + priv->bec.rxerr) - 1;
> +
> + if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_FDF)
> + skb = alloc_canfd_skb(priv->ndev, &skb_cfd);
> + else
> + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&skb_cfd);
copy the data to the allocated skb directly.
> +
> + if (!skb) {
> + stats->rx_dropped++;
> +
> + return 0;
> + }
> +
> + memcpy(skb_cfd, cfd, len);
> + stats->rx_packets++;
> + stats->rx_bytes += cfd->len;
> + netif_rx(skb);
Use the rx_offload_helper here too.
> + return 0;
> +}
> +
> static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
> {
> struct net_device_stats *stats = &priv->ndev->stats;
> @@ -380,6 +426,43 @@ rk3576canfd_rx_fifo_get_len(const struct rkcanfd_priv *priv)
> return DIV_ROUND_UP(val, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
> }
>
> +static void rk3576_canfd_rx_dma_callback(void *data)
> +{
> + struct rkcanfd_priv *priv = data;
> + int i;
> +
> + for (i = 0; i < priv->quota; i++)
> + rk3576canfd_handle_rx_dma(priv, i);
> +
> + rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
> +}
> +
> +static int rk3576_canfd_rx_dma(struct rkcanfd_priv *priv)
> +{
> + struct dma_async_tx_descriptor *rxdesc = NULL;
> + const u32 reg = rkcanfd_read(priv, RK3576CANFD_REG_STR_STATE);
> + int quota = FIELD_GET(RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT, reg);
> +
> + quota = DIV_ROUND_UP(quota, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
> + priv->quota = quota;
> + if (priv->quota == 0) {
> + rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
> + return 0;
> + }
> +
> + rxdesc = dmaengine_prep_slave_single(priv->rxchan, priv->rx_dma_dst_addr,
> + priv->dma_size * priv->quota, DMA_DEV_TO_MEM, 0);
> + if (!rxdesc)
> + return -ENOMSG;
> +
> + rxdesc->callback = rk3576_canfd_rx_dma_callback;
> + rxdesc->callback_param = priv;
> +
> + dmaengine_submit(rxdesc);
Please add error handling.
> + dma_async_issue_pending(priv->rxchan);
> + return 0;
> +}
> +
> int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv)
> {
> unsigned int len;
> @@ -399,6 +482,10 @@ int rkcanfd_handle_rk3576_rx_int(struct rkcanfd_priv *priv)
> unsigned int len;
> int err;
>
> + if (priv->use_dma) {
> + rk3576_canfd_rx_dma(priv);
> + return 0;
Please add error handling.
> + }
> while ((len = rk3576canfd_rx_fifo_get_len(priv))) {
> err = rk3576canfd_handle_rx_int_one(priv);
> if (err)
> diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
> index 9b91d757d054..c7d6845c6d95 100644
> --- a/drivers/net/can/rockchip/rockchip_canfd.h
> +++ b/drivers/net/can/rockchip/rockchip_canfd.h
> @@ -11,6 +11,8 @@
> #include <linux/can/dev.h>
> #include <linux/can/rx-offload.h>
> #include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
> #include <linux/io.h>
> #include <linux/netdevice.h>
> #include <linux/reset.h>
> @@ -737,6 +739,7 @@ struct rkcanfd_priv {
> struct can_priv can;
> struct can_rx_offload offload;
> struct net_device *ndev;
> + struct device *dev;
It's not used in the hot path, please remove.
>
> void __iomem *regs;
> unsigned int tx_head;
> @@ -758,6 +761,14 @@ struct rkcanfd_priv {
> struct reset_control *reset;
> struct clk_bulk_data *clks;
> int clks_num;
> + bool use_dma;
> + u32 dma_size;
> + u32 dma_thr;
> + int quota;
> + struct dma_chan *rxchan;
> + u32 *rxbuf;
> + dma_addr_t rx_dma_src_addr;
> + dma_addr_t rx_dma_dst_addr;
> };
>
> static inline u32
I'll look at patch 3/4 later this week or next week.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 3/4] net: can: rockchip: add can for RK3576 Soc
2025-05-26 6:25 ` [PATCH v6 3/4] net: can: rockchip: add can for RK3576 Soc Elaine Zhang
@ 2025-05-27 11:37 ` Marc Kleine-Budde
0 siblings, 0 replies; 10+ messages in thread
From: Marc Kleine-Budde @ 2025-05-27 11:37 UTC (permalink / raw)
To: Elaine Zhang
Cc: kernel, mailhol.vincent, robh, krzk+dt, conor+dt, heiko, cl,
kever.yang, linux-can, linux-arm-kernel, linux-rockchip,
linux-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 9548 bytes --]
Hey,
here's a partial review.
On 26.05.2025 14:25:58, Elaine Zhang wrote:
> Is new controller, new register layout and Bit position definition:
> Support CAN and CANFD protocol, ISO 11898-1
> Support transmit or receive error count
> Support acceptance filter, more functional
> Support interrupt and all interrupt can be masked
> Support error code check
> Support self test\silent\loop-back mode
> Support auto retransmission mode
> Support auto bus on after bus-off state
> Support 2 transmit buffers
> Support Internal Storage Mode
> Support DMA
>
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> ---
> .../net/can/rockchip/rockchip_canfd-core.c | 453 ++++++++++++++++++
> drivers/net/can/rockchip/rockchip_canfd-rx.c | 111 +++++
> drivers/net/can/rockchip/rockchip_canfd-tx.c | 27 ++
> drivers/net/can/rockchip/rockchip_canfd.h | 267 +++++++++++
> 4 files changed, 858 insertions(+)
>
> diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
> index c21ca4c1fb9a..92e260cb2527 100644
> --- a/drivers/net/can/rockchip/rockchip_canfd-core.c
> +++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
> @@ -31,6 +31,8 @@ static const char *__rkcanfd_get_model_str(enum rkcanfd_model model)
> return "rk3568v2";
> case RKCANFD_MODEL_RK3568V3:
> return "rk3568v3";
> + case RKCANFD_MODEL_RK3576:
> + return "rk3576";
> }
>
> return "<unknown>";
> @@ -176,6 +178,30 @@ static void rkcanfd_get_berr_counter_corrected(struct rkcanfd_priv *priv,
> !!(reg_state & RKCANFD_REG_STATE_ERROR_WARNING_STATE));
> }
>
> +static void rk3576canfd_get_berr_counter_corrected(struct rkcanfd_priv *priv,
> + struct can_berr_counter *bec)
> +{
Is the rk3576 affected by this problem?
> + struct can_berr_counter bec_raw;
> + u32 reg_state;
> +
> + bec->rxerr = rkcanfd_read(priv, RK3576CANFD_REG_RXERRORCNT);
> + bec->txerr = rkcanfd_read(priv, RK3576CANFD_REG_TXERRORCNT);
> + bec_raw = *bec;
> +
> + if (!bec->rxerr && !bec->txerr)
> + *bec = priv->bec;
> + else
> + priv->bec = *bec;
> +
> + reg_state = rkcanfd_read(priv, RKCANFD_REG_STATE);
> + netdev_vdbg(priv->ndev,
> + "%s: Raw/Cor: txerr=%3u/%3u rxerr=%3u/%3u Bus Off=%u Warning=%u\n",
> + __func__,
> + bec_raw.txerr, bec->txerr, bec_raw.rxerr, bec->rxerr,
> + !!(reg_state & RK3576CANFD_REG_STATE_BUS_OFF_STATE),
> + !!(reg_state & RK3576CANFD_REG_STATE_ERROR_WARNING_STATE));
> +}
> +
> static int rkcanfd_get_berr_counter(const struct net_device *ndev,
> struct can_berr_counter *bec)
> {
> @@ -206,6 +232,11 @@ static void rkcanfd_chip_interrupts_disable(const struct rkcanfd_priv *priv)
> rkcanfd_write(priv, RKCANFD_REG_INT_MASK, RKCANFD_REG_INT_ALL);
> }
>
> +static void rk3576canfd_chip_interrupts_disable(const struct rkcanfd_priv *priv)
> +{
> + rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, RK3576CANFD_REG_INT_ALL);
> +}
> +
> static void rkcanfd_chip_fifo_setup(struct rkcanfd_priv *priv)
> {
> u32 reg;
> @@ -220,6 +251,72 @@ static void rkcanfd_chip_fifo_setup(struct rkcanfd_priv *priv)
> netdev_reset_queue(priv->ndev);
> }
>
> +static void rk3576canfd_chip_fifo_setup(struct rkcanfd_priv *priv)
> +{
> + u32 ism = 0, water = 0;
no need to init as 0
> +
> + ism = RK3576CANFD_REG_STR_CTL_ISM_SEL_CANFD_FIXED;
> + water = RK3576CANFD_ISM_WATERMASK_CANFD;
> +
> + /* internal sram mode */
personally I would prefer:
reg_ism = FIELD_PREP(RK3576CANFD_REG_STR_CTL_ISM_SEL,
RK3576CANFD_REG_STR_CTL_ISM_SEL_CANFD_FIXED) |
RK3576CANFD_REG_STR_CTL_STORAGE_TIMEOUT_MODE;
> + rkcanfd_write(priv, RK3576CANFD_REG_STR_CTL,
> + (FIELD_PREP(RK3576CANFD_REG_STR_CTL_ISM_SEL, ism) |
> + RK3576CANFD_REG_STR_CTL_STORAGE_TIMEOUT_MODE));
reg_water = RK3576CANFD_ISM_WATERMASK_CANFD;
> + rkcanfd_write(priv, RK3576CANFD_REG_STR_WTM, water);
> + WRITE_ONCE(priv->tx_head, 0);
> + WRITE_ONCE(priv->tx_tail, 0);
> + netdev_reset_queue(priv->ndev);
> +}
> +
> +static int rk3576canfd_atf_config(struct rkcanfd_priv *priv, int mode)
With the proposed cleanup, this will become a void function.
> +{
> + u32 id[10] = {0};
What's the use of this array?
> + u32 dlc = 0, dlc_over = 0;
> +
> + switch (mode) {
It's called with RK3576CANFD_REG_ATFM_MASK_SEL_MASK_MODE only...
> + case RK3576CANFD_REG_ATFM_MASK_SEL_MASK_MODE:
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF0, id[0]);
why not call it with 0x0?
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF1, id[1]);
create:
#define RK3576CANFD_REG_ATF(n) (0x700 + (n) << 2)
and use a for loop
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF2, id[2]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF3, id[3]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF4, id[4]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM0, RK3576CANFD_REG_ATFM_ID);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM1, RK3576CANFD_REG_ATFM_ID);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM2, RK3576CANFD_REG_ATFM_ID);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM3, RK3576CANFD_REG_ATFM_ID);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM4, RK3576CANFD_REG_ATFM_ID);
> + break;
> + case RK3576CANFD_REG_ATFM_MASK_SEL_LIST_MODE:
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF0, id[0]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF1, id[1]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF2, id[2]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF3, id[3]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF4, id[4]);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM0, id[5] | RK3576CANFD_REG_ATFM_MASK_SEL);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM1, id[6] | RK3576CANFD_REG_ATFM_MASK_SEL);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM2, id[7] | RK3576CANFD_REG_ATFM_MASK_SEL);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM3, id[8] | RK3576CANFD_REG_ATFM_MASK_SEL);
> + rkcanfd_write(priv, RK3576CANFD_REG_ATFM4, id[9] | RK3576CANFD_REG_ATFM_MASK_SEL);
> + break;
> + default:
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF_CTL, RK3576CANFD_REG_ATF_CTL_ATF_DIS_ALL);
> + return -EINVAL;
> + }
> +
> + if (dlc) {
> + if (dlc_over)
both are 0, please remove the dead code
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF_DLC,
> + dlc | RK3576CANFD_REG_ATF_DLC_ATF_DLC_EN);
> + else
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF_DLC,
> + dlc | RK3576CANFD_REG_ATF_DLC_ATF_DLC_EN |
> + RK3576CANFD_REG_ATF_DLC_ATF_DLC_MODE);
> + }
> + rkcanfd_write(priv, RK3576CANFD_REG_ATF_CTL, 0);
> +
> + return 0;
> +}
> +
> static void rkcanfd_chip_start(struct rkcanfd_priv *priv)
> {
> u32 reg;
> @@ -285,6 +382,68 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv)
> rkcanfd_read(priv, RKCANFD_REG_MODE));
> }
>
> +static void rk3576canfd_chip_start(struct rkcanfd_priv *priv)
> +
> +{
> + u32 reg;
> +
> + rkcanfd_chip_set_reset_mode(priv);
> +
> + /* Receiving Filter: accept all */
> + rk3576canfd_atf_config(priv, RK3576CANFD_REG_ATFM_MASK_SEL_MASK_MODE);
> +
> + /* enable:
> + * - CAN_FD: enable CAN-FD
> + * - AUTO_RETX_MODE: auto retransmission on TX error
> + * - COVER_MODE: RX-FIFO overwrite mode, do not send OVERLOAD frames
> + * - RXSTX_MODE: Receive Self Transmit data mode
> + * - WORK_MODE: transition from reset to working mode
> + */
please adjust the comments
> + reg = rkcanfd_read(priv, RKCANFD_REG_MODE);
> + priv->reg_mode_default = reg | RKCANFD_REG_MODE_WORK_MODE;
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
> + priv->reg_mode_default |= RKCANFD_REG_MODE_LBACK_MODE;
> + rkcanfd_write(priv, RK3576CANFD_REG_ERROR_MASK,
> + RK3576CANFD_REG_ERROR_MASK_ACK_ERROR);
> + }
> +
> + /* mask, i.e. ignore:
> + * - TIMESTAMP_COUNTER_OVERFLOW_INT - timestamp counter overflow interrupt
> + * - TX_ARBIT_FAIL_INT - TX arbitration fail interrupt
> + * - OVERLOAD_INT - CAN bus overload interrupt
> + * - TX_FINISH_INT - Transmit finish interrupt
> + */
> + priv->reg_int_mask_default = RK3576CANFD_REG_INT_RX_FINISH_INT;
please adjust the comments
> +
> + /* Do not mask the bus error interrupt if the bus error
> + * reporting is requested.
> + */
> + if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
> + priv->reg_int_mask_default |= RKCANFD_REG_INT_ERROR_INT;
> +
> + memset(&priv->bec, 0x0, sizeof(priv->bec));
> +
> + priv->devtype_data.fifo_setup(priv);
Why do you need a callback here? You're already know you're a
rk3576canfd, here.
> +
> + rkcanfd_write(priv, RK3576CANFD_REG_AUTO_RETX_CFG,
> + RK3576CANFD_REG_AUTO_RETX_CFG_AUTO_RETX_EN);
> +
> + rkcanfd_write(priv, RK3576CANFD_REG_BRS_CFG,
> + RK3576CANFD_REG_BRS_CFG_BRS_NEGSYNC_EN |
> + RK3576CANFD_REG_BRS_CFG_BRS_POSSYNC_EN);
> +
> + rkcanfd_set_bittiming(priv);
> +
> + priv->devtype_data.interrupts_disable(priv);
> + rkcanfd_chip_set_work_mode(priv);
> +
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + netdev_dbg(priv->ndev, "%s: reg_mode=0x%08x\n", __func__,
> + rkcanfd_read(priv, RKCANFD_REG_MODE));
> +}
> +
more later...
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 0/4] rockchip: add can for RK3576 Soc
2025-05-26 6:25 [PATCH v6 0/4] rockchip: add can for RK3576 Soc Elaine Zhang
` (3 preceding siblings ...)
2025-05-26 6:25 ` [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx Elaine Zhang
@ 2025-05-27 15:58 ` Marc Kleine-Budde
4 siblings, 0 replies; 10+ messages in thread
From: Marc Kleine-Budde @ 2025-05-27 15:58 UTC (permalink / raw)
To: Elaine Zhang
Cc: kernel, mailhol.vincent, robh, krzk+dt, conor+dt, heiko, cl,
kever.yang, linux-can, linux-arm-kernel, linux-rockchip,
linux-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 966 bytes --]
On 26.05.2025 14:25:55, Elaine Zhang wrote:
> rk3576 can is a new controller,new register layout and Bit position
> definition:
> Support CAN and CANFD protocol.
> Support Dma.
>
> There are major differences from the previous rk3568.
> All errata on the rk3568 have been fixed and redesigned.
Can you configure the IP core for CAN-2.0 a.k.a. CAN-CC only mode, so
that it only receives CAN-CC frames only and not CAN-FD?
What happens if you configure register RKCAN_STR_CTL, ism_sel to 2'b01:
CAN Fixed and receive a CAN-FD frame?
Is there a more detailed description to "2'b00: Flexible; cover_mode
must disable" and "2'b11: Mixed. cover_mode must disable"?
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx
2025-05-27 11:00 ` Marc Kleine-Budde
@ 2025-05-28 7:21 ` zhangqing
0 siblings, 0 replies; 10+ messages in thread
From: zhangqing @ 2025-05-28 7:21 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: kernel, mailhol.vincent, robh, krzk+dt, conor+dt, heiko, cl,
kever.yang, linux-can, linux-arm-kernel, linux-rockchip,
linux-kernel, devicetree
在 2025/5/27 19:00, Marc Kleine-Budde 写道:
> On 26.05.2025 14:25:59, Elaine Zhang wrote:
>> The new can controller of rk3576 supports rx dma.
> I'm missing dma_sync_single_for_cpu() and dma_sync_single_for_device().
>
> What does the overall picture look like?
>
> The CAN controller receives a CAN frame, it triggers an interrupt, the
> driver starts a RX-DMA, the RX-DMA finishes triggers the callback, the
> driver allocates an skb and copies the data from the DMA dest memory to
> the skb. Finally the skb is passed to the networking stack.
After enabling dma, when the water level of the fifo reaches the
expected level, dma is initiated. First, the frame numbers in the fifo
are read, and dma moves the data of these frame numbers at one time.
Then the dma completion interrupt is returned, and the cpu begins to
process the data moved by dma.
>
> Have you done any measurements if using DMA brings any benefits here? I
> doubt that.
It has been tested that for the RK3576 cpu with relatively strong
capabilities, switching dma has little impact.
However, for the RK3506 with only three cpu cores, enabling dma can
reduce cpu interruption. The most obvious one is that after enabling
dma, there will be no frame loss problem when the CAN load is over 90%.
Subsequently, both rk3506 and rv1126b used the rk3576 canfd controller.
>
> If your hardware supports, a better setup would be to allocate a bunch
> of skbs and setup the a DMA request per skb and push them all to the DMA
> engine during open(). If the CAN controller receives a CAN frame, the
> DMA automatically starts and raises an IRQ after finish of the DMA
> transfer. Transform the header and then pass the skb to the networking
> stack - no need for memcpy.
>
> This _might_ lower the total CPU cost of RX, but a lot of additional
> code is used for that.
I can try to modify.
Including other modifications proposed below, they will be updated in
the next version
>
> Never the less, review inline.
>
>> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
>> ---
>> .../net/can/rockchip/rockchip_canfd-core.c | 39 +++++++++
>> drivers/net/can/rockchip/rockchip_canfd-rx.c | 87 +++++++++++++++++++
>> drivers/net/can/rockchip/rockchip_canfd.h | 11 +++
>> 3 files changed, 137 insertions(+)
>>
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
>> index 92e260cb2527..9ef4a9ae19d8 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd-core.c
>> +++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
>> @@ -433,6 +433,9 @@ static void rk3576canfd_chip_start(struct rkcanfd_priv *priv)
>> RK3576CANFD_REG_BRS_CFG_BRS_NEGSYNC_EN |
>> RK3576CANFD_REG_BRS_CFG_BRS_POSSYNC_EN);
>>
>> + if (priv->use_dma)
>> + rkcanfd_write(priv, RK3576CANFD_REG_DMA_CTRL,
>> + RK3576CANFD_REG_DMA_CTRL_DMA_RX_EN | priv->dma_thr);
>> rkcanfd_set_bittiming(priv);
>>
>> priv->devtype_data.interrupts_disable(priv);
>> @@ -1324,10 +1327,31 @@ static const struct of_device_id rkcanfd_of_match[] = {
>> };
>> MODULE_DEVICE_TABLE(of, rkcanfd_of_match);
>>
>> +static void rk3576_canfd_dma_init(struct rkcanfd_priv *priv)
>> +{
>> + struct dma_slave_config rxconf = {
>> + .direction = DMA_DEV_TO_MEM,
>> + .src_addr = priv->rx_dma_src_addr,
>> + .src_addr_width = 4,
>> + .dst_addr_width = 4,
>> + .src_maxburst = 9,
>> + };
>> +
>> + priv->dma_thr = rxconf.src_maxburst - 1;
>> + priv->rxbuf = dma_alloc_coherent(priv->dev, priv->dma_size * 14,
> Where does the 14 come from?
In the fixed mode of CANFD, there are 18 words per frame, and the
maximum number of words in the fifo is 256. The fifo can store up to
256/18=14 frames at most.
>
>> + &priv->rx_dma_dst_addr, GFP_KERNEL);
> I'm missing the cleanup for priv->rxbuf.
>
>> + if (!priv->rxbuf) {
>> + priv->use_dma = 0;
>> + return;
>> + }
>> + dmaengine_slave_config(priv->rxchan, &rxconf);
> Please add error handling.
>
>> +}
>> +
>> static int rkcanfd_probe(struct platform_device *pdev)
>> {
>> struct rkcanfd_priv *priv;
>> struct net_device *ndev;
>> + struct resource *res;
>> const void *match;
>> int err;
>>
>> @@ -1349,6 +1373,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
>> goto out_free_candev;
>> }
>>
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> priv->regs = devm_platform_ioremap_resource(pdev, 0);
> Please use devm_platform_get_and_ioremap_resource();
>
>> if (IS_ERR(priv->regs)) {
>> err = PTR_ERR(priv->regs);
>> @@ -1376,6 +1401,7 @@ static int rkcanfd_probe(struct platform_device *pdev)
>> priv->can.do_set_mode = rkcanfd_set_mode;
>> priv->can.do_get_berr_counter = rkcanfd_get_berr_counter;
>> priv->ndev = ndev;
>> + priv->dev = &pdev->dev;
> Please remove, priv->dev is not used in the hot path, use
> priv->ndev->dev.parent instead.
>
>>
>> match = device_get_match_data(&pdev->dev);
>> if (match) {
>> @@ -1384,6 +1410,19 @@ static int rkcanfd_probe(struct platform_device *pdev)
>> priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
>> }
>>
>> + priv->rxchan = dma_request_chan(&pdev->dev, "rx");
> I'm missing the cleanup for priv->rxchan.
>
>> + if (IS_ERR(priv->rxchan)) {
> Please handle -EPROBE_DEFER properly.
>
>> + dev_warn(&pdev->dev, "Failed to request rxchan\n");
> Please print the error value and state that you are continuing w/o DMA,
> e.g.:
>
> netdev_warn("Failed to request RX-DMA channel: %pe, continuing without DMA", priv->rxchan);
>
>> + priv->rxchan = NULL;
>> + priv->use_dma = 0;
>> + } else {
>> + priv->rx_dma_src_addr = res->start + RK3576CANFD_REG_RXFRD;
>> + priv->dma_size = RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT * 4;
> Why do you need priv->dma_size, it doesn't change during runtime, does it?
>
>> + priv->use_dma = 1;
>> + }
>> + if (priv->use_dma)
>> + rk3576_canfd_dma_init(priv);
>> +
> Can you move all DMA related functionality into rk3576_canfd_dma_init(),
> pass res as a parameter, remove rx_dma_src_addr.
>
> I think you don't need use_dma, checking rxchan != NULL should be
> sufficient.
>
>> err = can_rx_offload_add_manual(ndev, &priv->offload,
>> RKCANFD_NAPI_WEIGHT);
>> if (err)
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c
>> index 8a383cabd9d2..ac06e876552e 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd-rx.c
>> +++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c
>> @@ -285,6 +285,52 @@ static int rk3576canfd_handle_rx_int_one(struct rkcanfd_priv *priv)
>> return 0;
>> }
>>
>> +static int rk3576canfd_handle_rx_dma(struct rkcanfd_priv *priv, u32 addr)
>> +{
>> + struct net_device_stats *stats = &priv->ndev->stats;
>> + struct canfd_frame cfd[1] = { }, *skb_cfd;
>> + struct rk3576canfd_fifo_header header[1] = { };
>> + struct sk_buff *skb;
>> + u32 __iomem *rxbuf = (u32 __iomem *)priv->rxbuf;
> Why do you cast rx_buf to __iomem? It's normal memory, isn't it?
>
>> + unsigned int len;
>> + int i;
>> +
>> + header->frameinfo = readl(rxbuf +
>> + addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
>> + header->id = readl(rxbuf + 1 +
>> + addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
>> + for (i = 0; i < (RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT - 2); i++)
>> + cfd->data[i] = readl(rxbuf + 2 + i +
>> + addr * RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
>> +
> Remove cfd and header and directly access the rxbuf.
>
>> + len = rk3576canfd_fifo_header_to_cfd_header(priv, header, cfd);
>> +
>> + /* Manual handling of CAN Bus Error counters. See
>> + * rkcanfd_get_corrected_berr_counter() for detailed
>> + * explanation.
>> + */
>> + if (priv->bec.rxerr)
>> + priv->bec.rxerr = min(CAN_ERROR_PASSIVE_THRESHOLD,
>> + priv->bec.rxerr) - 1;
>> +
>> + if (header->frameinfo & RK3576CANFD_REG_RXFRD_FRAMEINFO_FDF)
>> + skb = alloc_canfd_skb(priv->ndev, &skb_cfd);
>> + else
>> + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&skb_cfd);
> copy the data to the allocated skb directly.
>
>> +
>> + if (!skb) {
>> + stats->rx_dropped++;
>> +
>> + return 0;
>> + }
>> +
>> + memcpy(skb_cfd, cfd, len);
>> + stats->rx_packets++;
>> + stats->rx_bytes += cfd->len;
>> + netif_rx(skb);
> Use the rx_offload_helper here too.
>
>> + return 0;
>> +}
>> +
>> static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
>> {
>> struct net_device_stats *stats = &priv->ndev->stats;
>> @@ -380,6 +426,43 @@ rk3576canfd_rx_fifo_get_len(const struct rkcanfd_priv *priv)
>> return DIV_ROUND_UP(val, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
>> }
>>
>> +static void rk3576_canfd_rx_dma_callback(void *data)
>> +{
>> + struct rkcanfd_priv *priv = data;
>> + int i;
>> +
>> + for (i = 0; i < priv->quota; i++)
>> + rk3576canfd_handle_rx_dma(priv, i);
>> +
>> + rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
>> +}
>> +
>> +static int rk3576_canfd_rx_dma(struct rkcanfd_priv *priv)
>> +{
>> + struct dma_async_tx_descriptor *rxdesc = NULL;
>> + const u32 reg = rkcanfd_read(priv, RK3576CANFD_REG_STR_STATE);
>> + int quota = FIELD_GET(RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT, reg);
>> +
>> + quota = DIV_ROUND_UP(quota, RK3576CANFD_REG_STR_STATE_INTM_LEFT_CNT_UNIT);
>> + priv->quota = quota;
>> + if (priv->quota == 0) {
>> + rkcanfd_write(priv, RK3576CANFD_REG_INT_MASK, priv->reg_int_mask_default);
>> + return 0;
>> + }
>> +
>> + rxdesc = dmaengine_prep_slave_single(priv->rxchan, priv->rx_dma_dst_addr,
>> + priv->dma_size * priv->quota, DMA_DEV_TO_MEM, 0);
>> + if (!rxdesc)
>> + return -ENOMSG;
>> +
>> + rxdesc->callback = rk3576_canfd_rx_dma_callback;
>> + rxdesc->callback_param = priv;
>> +
>> + dmaengine_submit(rxdesc);
> Please add error handling.
>
>> + dma_async_issue_pending(priv->rxchan);
>> + return 0;
>> +}
>> +
>> int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv)
>> {
>> unsigned int len;
>> @@ -399,6 +482,10 @@ int rkcanfd_handle_rk3576_rx_int(struct rkcanfd_priv *priv)
>> unsigned int len;
>> int err;
>>
>> + if (priv->use_dma) {
>> + rk3576_canfd_rx_dma(priv);
>> + return 0;
> Please add error handling.
>
>> + }
>> while ((len = rk3576canfd_rx_fifo_get_len(priv))) {
>> err = rk3576canfd_handle_rx_int_one(priv);
>> if (err)
>> diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
>> index 9b91d757d054..c7d6845c6d95 100644
>> --- a/drivers/net/can/rockchip/rockchip_canfd.h
>> +++ b/drivers/net/can/rockchip/rockchip_canfd.h
>> @@ -11,6 +11,8 @@
>> #include <linux/can/dev.h>
>> #include <linux/can/rx-offload.h>
>> #include <linux/clk.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/dmaengine.h>
>> #include <linux/io.h>
>> #include <linux/netdevice.h>
>> #include <linux/reset.h>
>> @@ -737,6 +739,7 @@ struct rkcanfd_priv {
>> struct can_priv can;
>> struct can_rx_offload offload;
>> struct net_device *ndev;
>> + struct device *dev;
> It's not used in the hot path, please remove.
>
>>
>> void __iomem *regs;
>> unsigned int tx_head;
>> @@ -758,6 +761,14 @@ struct rkcanfd_priv {
>> struct reset_control *reset;
>> struct clk_bulk_data *clks;
>> int clks_num;
>> + bool use_dma;
>> + u32 dma_size;
>> + u32 dma_thr;
>> + int quota;
>> + struct dma_chan *rxchan;
>> + u32 *rxbuf;
>> + dma_addr_t rx_dma_src_addr;
>> + dma_addr_t rx_dma_dst_addr;
>> };
>>
>> static inline u32
> I'll look at patch 3/4 later this week or next week.
>
> regards,
> Marc
>
--
张晴
瑞芯微电子股份有限公司
Rockchip Electronics Co.,Ltd
地址:福建省福州市铜盘路软件大道89号软件园A区21号楼
Add:No.21 Building, A District, No.89 Software Boulevard Fuzhou, Fujian 350003, P.R.China
Tel:+86-0591-83991906-8601
邮编:350003
E-mail:elaine.zhang@rock-chips.com
****************************************************************************
保密提示:本邮件及其附件含有机密信息,仅发送给本邮件所指特定收件人。若非该特定收件人,请勿复制、使用或披露本邮件的任何内容。若误收本邮件,请从系统中永久性删除本邮件及所有附件,并以回复邮件或其他方式即刻告知发件人。福州瑞芯微电子有限公司拥有本邮件信息的著作权及解释权,禁止任何未经授权许可的侵权行为。
IMPORTANT NOTICE: This email is from Fuzhou Rockchip Electronics Co., Ltd .The contents of this email and any attachments may contain information that is privileged, confidential and/or exempt from disclosure under applicable law and relevant NDA. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, or use of the information is STRICTLY PROHIBITED. Please immediately contact the sender as soon as possible and destroy the material in its entirety in any format. Thank you.
****************************************************************************
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-05-28 7:26 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-26 6:25 [PATCH v6 0/4] rockchip: add can for RK3576 Soc Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 1/4] dt-bindings: can: rockchip_canfd: add rk3576 CAN-FD controller Elaine Zhang
2025-05-26 15:12 ` Conor Dooley
2025-05-26 6:25 ` [PATCH v6 2/4] net: can: rockchip: Refactor the rkcanfd_devtype_data structure Elaine Zhang
2025-05-26 6:25 ` [PATCH v6 3/4] net: can: rockchip: add can for RK3576 Soc Elaine Zhang
2025-05-27 11:37 ` Marc Kleine-Budde
2025-05-26 6:25 ` [PATCH v6 4/4] net: can: rockchip: support dma for rk3576 rx Elaine Zhang
2025-05-27 11:00 ` Marc Kleine-Budde
2025-05-28 7:21 ` zhangqing
2025-05-27 15:58 ` [PATCH v6 0/4] rockchip: add can for RK3576 Soc Marc Kleine-Budde
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).