Commit ff60bfba authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

can: rockchip_canfd: add driver for Rockchip CAN-FD controller

Add driver for the Rockchip CAN-FD controller.

The IP core on the rk3568v2 SoC has 12 documented errata. Corrections
for these errata will be added in the upcoming patches.

Since several workarounds are required for the TX path, only add the
base driver that only implements the RX path.

Although the RX path implements CAN-FD support, it's not activated in
ctrlmode_supported, as the IP core in the rk3568v2 has problems with
receiving or sending certain CAN-FD frames.
Tested-by: default avatarAlibek Omarov <a1ba.omarov@gmail.com>
Acked-by: default avatarHeiko Stuebner <heiko@sntech.de>
Link: https://patch.msgid.link/20240904-rockchip-canfd-v5-4-8ae22bcb27cc@pengutronix.deSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 8b2f4d01
......@@ -19736,6 +19736,7 @@ R: kernel@pengutronix.de
L: linux-can@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
F: drivers/net/can/rockchip/
ROCKCHIP CRYPTO DRIVERS
M: Corentin Labbe <clabbe@baylibre.com>
......
......@@ -225,6 +225,7 @@ source "drivers/net/can/m_can/Kconfig"
source "drivers/net/can/mscan/Kconfig"
source "drivers/net/can/peak_canfd/Kconfig"
source "drivers/net/can/rcar/Kconfig"
source "drivers/net/can/rockchip/Kconfig"
source "drivers/net/can/sja1000/Kconfig"
source "drivers/net/can/softing/Kconfig"
source "drivers/net/can/spi/Kconfig"
......
......@@ -10,6 +10,7 @@ obj-$(CONFIG_CAN_SLCAN) += slcan/
obj-y += dev/
obj-y += esd/
obj-y += rcar/
obj-y += rockchip/
obj-y += spi/
obj-y += usb/
obj-y += softing/
......
# SPDX-License-Identifier: GPL-2.0
config CAN_ROCKCHIP_CANFD
tristate "Rockchip CAN-FD controller"
depends on OF || COMPILE_TEST
select CAN_RX_OFFLOAD
help
Say Y here if you want to use CAN-FD controller found on
Rockchip SoCs.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CAN_ROCKCHIP_CANFD) += rockchip_canfd.o
rockchip_canfd-objs :=
rockchip_canfd-objs += rockchip_canfd-core.o
rockchip_canfd-objs += rockchip_canfd-rx.o
rockchip_canfd-objs += rockchip_canfd-timestamp.o
rockchip_canfd-objs += rockchip_canfd-tx.o
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2023, 2024 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
//
#include "rockchip_canfd.h"
static unsigned int
rkcanfd_fifo_header_to_cfd_header(const struct rkcanfd_priv *priv,
const struct rkcanfd_fifo_header *header,
struct canfd_frame *cfd)
{
unsigned int len = sizeof(*cfd) - sizeof(cfd->data);
u8 dlc;
if (header->frameinfo & RKCANFD_REG_FD_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(RKCANFD_REG_FD_FRAMEINFO_DATA_LENGTH,
header->frameinfo);
/* CAN-FD */
if (header->frameinfo & RKCANFD_REG_FD_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 & RKCANFD_REG_FD_FRAMEINFO_BRS)
cfd->flags |= CANFD_BRS;
} else {
cfd->len = can_cc_dlc2len(dlc);
if (header->frameinfo & RKCANFD_REG_FD_FRAMEINFO_RTR) {
cfd->can_id |= CAN_RTR_FLAG;
return len;
}
}
return len + cfd->len;
}
static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
{
struct net_device_stats *stats = &priv->ndev->stats;
struct canfd_frame cfd[1] = { }, *skb_cfd;
struct rkcanfd_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 = rkcanfd_fifo_header_to_cfd_header(priv, header, cfd);
/* Drop any received CAN-FD frames if CAN-FD mode is not
* requested.
*/
if (header->frameinfo & RKCANFD_REG_FD_FRAMEINFO_FDF &&
!(priv->can.ctrlmode & CAN_CTRLMODE_FD)) {
stats->rx_dropped++;
return 0;
}
if (header->frameinfo & RKCANFD_REG_FD_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_timestamp(&priv->offload, skb, header->ts);
if (err)
stats->rx_fifo_errors++;
return 0;
}
static inline unsigned int
rkcanfd_rx_fifo_get_len(const struct rkcanfd_priv *priv)
{
const u32 reg = rkcanfd_read(priv, RKCANFD_REG_RX_FIFO_CTRL);
return FIELD_GET(RKCANFD_REG_RX_FIFO_CTRL_RX_FIFO_CNT, reg);
}
int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv)
{
unsigned int len;
int err;
while ((len = rkcanfd_rx_fifo_get_len(priv))) {
err = rkcanfd_handle_rx_int_one(priv);
if (err)
return err;
}
return 0;
}
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2023, 2024 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
//
#include "rockchip_canfd.h"
void rkcanfd_timestamp_init(struct rkcanfd_priv *priv)
{
u32 reg;
reg = RKCANFD_REG_TIMESTAMP_CTRL_TIME_BASE_COUNTER_ENABLE;
rkcanfd_write(priv, RKCANFD_REG_TIMESTAMP_CTRL, reg);
}
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2023, 2024 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
//
#include "rockchip_canfd.h"
int rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
return NETDEV_TX_OK;
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment