Add driver for CAST CAN Controller. And add compatibility code which
based on StarFive JH7110 SoC.
Signed-off-by: William Qiu <[email protected]>
---
MAINTAINERS | 7 +
drivers/net/can/Kconfig | 7 +
drivers/net/can/Makefile | 1 +
drivers/net/can/cast_can.c | 1215 ++++++++++++++++++++++++++++++++++++
4 files changed, 1230 insertions(+)
create mode 100644 drivers/net/can/cast_can.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 8d1052fa6a69..daa3af8b4f10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4723,6 +4723,13 @@ S: Maintained
W: https://wireless.wiki.kernel.org/en/users/Drivers/carl9170
F: drivers/net/wireless/ath/carl9170/
+CAST CAN DRIVER
+M: William Qiu <[email protected]>
+L: [email protected]
+S: Supported
+F: Documentation/devicetree/bindings/net/can/cast,can.yaml
+F: drivers/net/can/cast_can.c
+
CAVIUM I2C DRIVER
M: Robert Richter <[email protected]>
S: Odd Fixes
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index eb410714afc2..d98ad06c8ff3 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -124,6 +124,13 @@ config CAN_CAN327
If this driver is built as a module, it will be called can327.
+config CAN_CASTCAN
+ tristate "CAST CAN"
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ depends on COMMON_CLK && HAS_IOMEM
+ help
+ CAST CAN driver. This driver supports both CAN and CANFD IP.
+
config CAN_FLEXCAN
tristate "Support for Freescale FLEXCAN based chips"
depends on OF || COLDFIRE || COMPILE_TEST
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index ff8f76295d13..d6fa47278859 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -16,6 +16,7 @@ obj-y += softing/
obj-$(CONFIG_CAN_AT91) += at91_can.o
obj-$(CONFIG_CAN_BXCAN) += bxcan.o
obj-$(CONFIG_CAN_CAN327) += can327.o
+obj-$(CONFIG_CAN_CASTCAN) += cast_can.o
obj-$(CONFIG_CAN_CC770) += cc770/
obj-$(CONFIG_CAN_C_CAN) += c_can/
obj-$(CONFIG_CAN_CTUCANFD) += ctucanfd/
diff --git a/drivers/net/can/cast_can.c b/drivers/net/can/cast_can.c
new file mode 100644
index 000000000000..404e050e41a0
--- /dev/null
+++ b/drivers/net/can/cast_can.c
@@ -0,0 +1,1215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CAST Controller Area Network Host Controller Driver
+ *
+ * Copyright (c) 2022-2023 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define DRIVER_NAME "cast_can"
+
+/* CAN registers set */
+enum ccan_device_reg {
+ CCAN_RUBF_OFFSET = 0x00, /* Receive Buffer Registers 0x00-0x4f */
+ CCAN_RUBF_ID_OFFSET = 0x00,
+ CCAN_RBUF_CTL_OFFSET = 0x04,
+ CCAN_RBUF_DATA_OFFSET = 0x08,
+ CCAN_TBUF_OFFSET = 0x50, /* Transmit Buffer Registers 0x50-0x97 */
+ CCAN_TBUF_ID_OFFSET = 0x50,
+ CCAN_TBUF_CTL_OFFSET = 0x54,
+ CCAN_TBUF_DATA_OFFSET = 0x58,
+ CCAN_TTS_OFFSET = 0x98, /* Transmission Time Stamp 0x98-0x9f */
+ CCAN_CFG_STAT_OFFSET = 0xa0,
+ CCAN_TCMD_OFFSET = 0xa1,
+ CCAN_TCTRL_OFFSET = 0xa2,
+ CCAN_RCTRL_OFFSET = 0xa3,
+ CCAN_RTIE_OFFSET = 0xa4,
+ CCAN_RTIF_OFFSET = 0xa5,
+ CCAN_ERRINT_OFFSET = 0xa6,
+ CCAN_LIMIT_OFFSET = 0xa7,
+ CCAN_S_SEG_1_OFFSET = 0xa8,
+ CCAN_S_SEG_2_OFFSET = 0xa9,
+ CCAN_S_SJW_OFFSET = 0xaa,
+ CCAN_S_PRESC_OFFSET = 0xab,
+ CCAN_F_SEG_1_OFFSET = 0xac,
+ CCAN_F_SEG_2_OFFSET = 0xad,
+ CCAN_F_SJW_OFFSET = 0xae,
+ CCAN_F_PRESC_OFFSET = 0xaf,
+ CCAN_EALCAP_OFFSET = 0xb0,
+ CCAN_RECNT_OFFSET = 0xb2,
+ CCAN_TECNT_OFFSET = 0xb3,
+};
+
+enum ccan_reg_bitchange {
+ CCAN_SET_RST_MASK = 0x80, /* Set Reset Bit */
+ CCAN_OFF_RST_MASK = 0x7f, /* Reset Off Bit */
+ CCAN_SET_FULLCAN_MASK = 0x10, /* set TTTBM as 1->full TTCAN mode */
+ CCAN_OFF_FULLCAN_MASK = 0xef, /* set TTTBM as 0->separate PTB and STB mode */
+ CCAN_SET_FIFO_MASK = 0x20, /* set TSMODE as 1->FIFO mode */
+ CCAN_OFF_FIFO_MASK = 0xdf, /* set TSMODE as 0->Priority mode */
+ CCAN_SET_TSONE_MASK = 0x04,
+ CCAN_OFF_TSONE_MASK = 0xfb,
+ CCAN_SET_TSALL_MASK = 0x02,
+ CCAN_OFF_TSALL_MASK = 0xfd,
+ CCAN_LBMEMOD_MASK = 0x40, /* set loop back mode, external */
+ CCAN_LBMIMOD_MASK = 0x20, /* set loopback internal mode */
+ CCAN_SET_BUSOFF_MASK = 0x01,
+ CCAN_OFF_BUSOFF_MASK = 0xfe,
+ CCAN_SET_TTSEN_MASK = 0x80, /* set ttsen, tts update enable */
+ CCAN_SET_BRS_MASK = 0x10, /* can fd Bit Rate Switch mask */
+ CCAN_OFF_BRS_MASK = 0xef,
+ CCAN_SET_EDL_MASK = 0x20, /* Extended Data Length */
+ CCAN_OFF_EDL_MASK = 0xdf,
+ CCAN_SET_DLC_MASK = 0x0f,
+ CCAN_SET_TENEXT_MASK = 0x40,
+ CCAN_SET_IDE_MASK = 0x80,
+ CCAN_OFF_IDE_MASK = 0x7f,
+ CCAN_SET_RTR_MASK = 0x40,
+ CCAN_OFF_RTR_MASK = 0xbf,
+ CCAN_INTR_ALL_MASK = 0xff, /* all interrupts enable mask */
+ CCAN_SET_RIE_MASK = 0x80,
+ CCAN_OFF_RIE_MASK = 0x7f,
+ CCAN_SET_RFIE_MASK = 0x20,
+ CCAN_OFF_RFIE_MASK = 0xdf,
+ CCAN_SET_RAFIE_MASK = 0x10,
+ CCAN_OFF_RAFIE_MASK = 0xef,
+ CCAN_SET_EIE_MASK = 0x02,
+ CCAN_OFF_EIE_MASK = 0xfd,
+ CCAN_TASCTIVE_MASK = 0x02,
+ CCAN_RASCTIVE_MASK = 0x04,
+ CCAN_SET_TBSEL_MASK = 0x80, /* message writen in STB */
+ CCAN_OFF_TBSEL_MASK = 0x7f, /* message writen in PTB */
+ CCAN_SET_STBY_MASK = 0x20,
+ CCAN_OFF_STBY_MASK = 0xdf,
+ CCAN_SET_TPE_MASK = 0x10, /* Transmit primary enable */
+ CCAN_SET_TPA_MASK = 0x08,
+ CCAN_SET_SACK_MASK = 0x80,
+ CCAN_SET_RREL_MASK = 0x10,
+ CCAN_RSTAT_NOT_EMPTY_MASK = 0x03,
+ CCAN_SET_RIF_MASK = 0x80,
+ CCAN_OFF_RIF_MASK = 0x7f,
+ CCAN_SET_RAFIF_MASK = 0x10,
+ CCAN_SET_RFIF_MASK = 0x20,
+ CCAN_SET_TPIF_MASK = 0x08, /* Transmission Primary Interrupt Flag */
+ CCAN_SET_TSIF_MASK = 0x04,
+ CCAN_SET_EIF_MASK = 0x02,
+ CCAN_SET_AIF_MASK = 0x01,
+ CCAN_SET_EWARN_MASK = 0x80,
+ CCAN_SET_EPASS_MASK = 0x40,
+ CCAN_SET_EPIE_MASK = 0x20,
+ CCAN_SET_EPIF_MASK = 0x10,
+ CCAN_SET_ALIE_MASK = 0x08,
+ CCAN_SET_ALIF_MASK = 0x04,
+ CCAN_SET_BEIE_MASK = 0x02,
+ CCAN_SET_BEIF_MASK = 0x01,
+ CCAN_OFF_EPIE_MASK = 0xdf,
+ CCAN_OFF_BEIE_MASK = 0xfd,
+ CCAN_SET_AFWL_MASK = 0x40,
+ CCAN_SET_EWL_MASK = 0x0b,
+ CCAN_SET_KOER_MASK = 0xe0,
+ CCAN_SET_BIT_ERROR_MASK = 0x20,
+ CCAN_SET_FORM_ERROR_MASK = 0x40,
+ CCAN_SET_STUFF_ERROR_MASK = 0x60,
+ CCAN_SET_ACK_ERROR_MASK = 0x80,
+ CCAN_SET_CRC_ERROR_MASK = 0xa0,
+ CCAN_SET_OTH_ERROR_MASK = 0xc0,
+};
+
+/* seg1,seg2,sjw,prescaler all have 8 bits */
+#define BITS_OF_BITTIMING_REG 8
+
+/* in can_bittiming strucure every field has 32 bits---->u32 */
+#define FBITS_IN_BITTIMING_STR 32
+#define SEG_1_SHIFT 0
+#define SEG_2_SHIFT 8
+#define SJW_SHIFT 16
+#define PRESC_SHIFT 24
+
+/* TTSEN bit used for 32 bit register read or write */
+#define TTSEN_8_32_SHIFT 24
+#define RTR_32_8_SHIFT 24
+
+/* transmit mode */
+#define XMIT_FULL 0
+#define XMIT_SEP_FIFO 1
+#define XMIT_SEP_PRIO 2
+#define XMIT_PTB_MODE 3
+
+enum cast_can_type {
+ CAST_CAN_TYPE_CAN = 0,
+ CAST_CAN_TYPE_CANFD,
+};
+
+struct ccan_priv {
+ struct can_priv can;
+ struct napi_struct napi;
+ struct device *dev;
+ struct regmap *reg_syscon;
+ void __iomem *reg_base;
+ u32 (*read_reg)(const struct ccan_priv *priv, enum ccan_device_reg reg);
+ void (*write_reg)(const struct ccan_priv *priv, enum ccan_device_reg reg, u32 val);
+ struct clk *can_clk;
+ struct clk *host_clk;
+ struct clk *timer_clk;
+ u32 tx_mode;
+ struct reset_control *resets;
+ u32 cantype;
+ bool is_starfive;
+};
+
+struct cast_can_data {
+ enum cast_can_type cantype;
+ const struct can_bittiming_const *bittime_const;
+ int (*starfive_parse_dt)(struct ccan_priv *priv);
+};
+
+static struct can_bittiming_const ccan_bittiming_const = {
+ .name = DRIVER_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 256,
+ .brp_inc = 1,
+};
+
+static struct can_bittiming_const ccan_bittiming_const_canfd = {
+ .name = DRIVER_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 64,
+ .tseg2_min = 2,
+ .tseg2_max = 16,
+ .sjw_max = 16,
+ .brp_min = 1,
+ .brp_max = 256,
+ .brp_inc = 1,
+};
+
+static struct can_bittiming_const ccan_data_bittiming_const_canfd = {
+ .name = DRIVER_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 8,
+ .brp_min = 1,
+ .brp_max = 256,
+ .brp_inc = 1,
+};
+
+static void ccan_write_reg_le(const struct ccan_priv *priv,
+ enum ccan_device_reg reg, u32 val)
+{
+ iowrite32(val, priv->reg_base + reg);
+}
+
+static u32 ccan_read_reg_le(const struct ccan_priv *priv,
+ enum ccan_device_reg reg)
+{
+ return ioread32(priv->reg_base + reg);
+}
+
+static inline unsigned char ccan_ioread8(const void *addr)
+{
+ void *addr_down;
+ union val {
+ u8 val_8[4];
+ u32 val_32;
+ } val;
+ u32 offset = 0;
+
+ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
+ offset = addr - addr_down;
+ val.val_32 = ioread32(addr_down);
+
+ return val.val_8[offset];
+}
+
+static inline void ccan_iowrite8(unsigned char value, void *addr)
+{
+ void *addr_down;
+ union val {
+ u8 val_8[4];
+ u32 val_32;
+ } val;
+ u8 offset = 0;
+
+ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
+ offset = addr - addr_down;
+ val.val_32 = ioread32(addr_down);
+ val.val_8[offset] = value;
+ iowrite32(val.val_32, addr_down);
+}
+
+static void ccan_reigister_set_bit(const struct ccan_priv *priv,
+ enum ccan_device_reg reg,
+ enum ccan_reg_bitchange mask)
+{
+ void *addr_down;
+ union val {
+ u8 val_8[4];
+ u32 val_32;
+ } val;
+ u8 offset = 0;
+
+ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
+ offset = (priv->reg_base + reg) - addr_down;
+ val.val_32 = ioread32(addr_down);
+ val.val_8[offset] |= mask;
+ iowrite32(val.val_32, addr_down);
+}
+
+static void ccan_reigister_off_bit(const struct ccan_priv *priv,
+ enum ccan_device_reg reg,
+ enum ccan_reg_bitchange mask)
+{
+ void *addr_down;
+ union val {
+ u8 val_8[4];
+ u32 val_32;
+ } val;
+ u8 offset = 0;
+
+ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
+ offset = (priv->reg_base + reg) - addr_down;
+ val.val_32 = ioread32(addr_down);
+ val.val_8[offset] &= mask;
+ iowrite32(val.val_32, addr_down);
+}
+
+static int ccan_device_driver_bittime_configuration(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct can_bittiming *dbt = &priv->can.data_bittiming;
+ u32 reset_test, bittiming_temp, data_bittiming;
+
+ reset_test = ccan_ioread8(priv->reg_base + CCAN_CFG_STAT_OFFSET);
+
+ if (!(reset_test & CCAN_SET_RST_MASK)) {
+ netdev_alert(ndev, "Not in reset mode, cannot set bit timing\n");
+ return -EPERM;
+ }
+
+ bittiming_temp = ((bt->phase_seg1 + bt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
+ ((bt->phase_seg2 - 1) << SEG_2_SHIFT) |
+ ((bt->sjw - 1) << SJW_SHIFT) |
+ ((bt->brp - 1) << PRESC_SHIFT);
+
+ /* Check the bittime parameter */
+ if ((((int)(bt->phase_seg1 + bt->prop_seg + 1) - 2) < 0) ||
+ (((int)(bt->phase_seg2) - 1) < 0) ||
+ (((int)(bt->sjw) - 1) < 0) ||
+ (((int)(bt->brp) - 1) < 0))
+ return -EINVAL;
+
+ priv->write_reg(priv, CCAN_S_SEG_1_OFFSET, bittiming_temp);
+
+ if (priv->cantype == CAST_CAN_TYPE_CANFD) {
+ data_bittiming = ((dbt->phase_seg1 + dbt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
+ ((dbt->phase_seg2 - 1) << SEG_2_SHIFT) |
+ ((dbt->sjw - 1) << SJW_SHIFT) |
+ ((dbt->brp - 1) << PRESC_SHIFT);
+
+ if ((((int)(dbt->phase_seg1 + dbt->prop_seg + 1) - 2) < 0) ||
+ (((int)(dbt->phase_seg2) - 1) < 0) ||
+ (((int)(dbt->sjw) - 1) < 0) ||
+ (((int)(dbt->brp) - 1) < 0))
+ return -EINVAL;
+
+ priv->write_reg(priv, CCAN_F_SEG_1_OFFSET, data_bittiming);
+ }
+
+ ccan_reigister_off_bit(priv, CCAN_CFG_STAT_OFFSET, CCAN_OFF_RST_MASK);
+
+ netdev_dbg(ndev, "Slow bit rate: %08x\n", priv->read_reg(priv, CCAN_S_SEG_1_OFFSET));
+ netdev_dbg(ndev, "Fast bit rate: %08x\n", priv->read_reg(priv, CCAN_F_SEG_1_OFFSET));
+
+ return 0;
+}
+
+int ccan_get_freebuffer(struct ccan_priv *priv)
+{
+ /* Get next transmit buffer */
+ ccan_reigister_set_bit(priv, CCAN_TCTRL_OFFSET, CCAN_SET_TENEXT_MASK);
+
+ if (ccan_ioread8(priv->reg_base + CCAN_TCTRL_OFFSET) & CCAN_SET_TENEXT_MASK)
+ return -EPERM;
+
+ return 0;
+}
+
+static void ccan_tx_interrupt(struct net_device *ndev, u8 isr)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+
+ /* wait till transmission of the PTB or STB finished */
+ while (isr & (CCAN_SET_TPIF_MASK | CCAN_SET_TSIF_MASK)) {
+ if (isr & CCAN_SET_TPIF_MASK)
+ ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET, CCAN_SET_TPIF_MASK);
+
+ if (isr & CCAN_SET_TSIF_MASK)
+ ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET, CCAN_SET_TSIF_MASK);
+
+ isr = ccan_ioread8(priv->reg_base + CCAN_RTIF_OFFSET);
+ }
+ netif_wake_queue(ndev);
+}
+
+static int ccan_rx(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u32 can_id;
+ u8 dlc, control, rx_status;
+
+ rx_status = ccan_ioread8(priv->reg_base + CCAN_RCTRL_OFFSET);
+
+ if (!(rx_status & CCAN_RSTAT_NOT_EMPTY_MASK))
+ return 0;
+
+ control = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET);
+ can_id = priv->read_reg(priv, CCAN_RUBF_ID_OFFSET);
+ dlc = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET) & CCAN_SET_DLC_MASK;
+
+ skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return 0;
+ }
+ cf->can_dlc = can_cc_dlc2len(dlc);
+
+ /* change the CANFD id into socketcan id format */
+ cf->can_id = can_id;
+ if (control & CCAN_SET_IDE_MASK)
+ cf->can_id |= CAN_EFF_FLAG;
+ else
+ cf->can_id &= ~CAN_EFF_FLAG;
+
+ if (control & CCAN_SET_RTR_MASK)
+ cf->can_id |= CAN_RTR_FLAG;
+
+ if (!(control & CCAN_SET_RTR_MASK)) {
+ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET);
+ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET + 4);
+ }
+
+ ccan_reigister_set_bit(priv, CCAN_RCTRL_OFFSET, CCAN_SET_RREL_MASK);
+ stats->rx_bytes += can_fd_dlc2len(cf->can_dlc);
+ stats->rx_packets++;
+ netif_receive_skb(skb);
+
+ return 1;
+}
+
+static int ccanfd_rx(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct canfd_frame *cf;
+ struct sk_buff *skb;
+ u32 can_id;
+ u8 dlc, control, rx_status;
+ int i;
+
+ rx_status = ccan_ioread8(priv->reg_base + CCAN_RCTRL_OFFSET);
+
+ if (!(rx_status & CCAN_RSTAT_NOT_EMPTY_MASK))
+ return 0;
+
+ control = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET);
+ can_id = priv->read_reg(priv, CCAN_RUBF_ID_OFFSET);
+ dlc = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET) & CCAN_SET_DLC_MASK;
+
+ if (control & CCAN_SET_EDL_MASK)
+ /* allocate sk_buffer for canfd frame */
+ skb = alloc_canfd_skb(ndev, &cf);
+ else
+ /* allocate sk_buffer for can frame */
+ skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
+
+ if (!skb) {
+ stats->rx_dropped++;
+ return 0;
+ }
+
+ /* change the CANFD or CAN2.0 data into socketcan data format */
+ if (control & CCAN_SET_EDL_MASK)
+ cf->len = can_fd_dlc2len(dlc);
+ else
+ cf->len = can_cc_dlc2len(dlc);
+
+ /* change the CANFD id into socketcan id format */
+ cf->can_id = can_id;
+ if (control & CCAN_SET_IDE_MASK)
+ cf->can_id |= CAN_EFF_FLAG;
+ else
+ cf->can_id &= ~CAN_EFF_FLAG;
+
+ if (!(control & CCAN_SET_EDL_MASK))
+ if (control & CCAN_SET_RTR_MASK)
+ cf->can_id |= CAN_RTR_FLAG;
+
+ /* CANFD frames handed over to SKB */
+ if (control & CCAN_SET_EDL_MASK) {
+ for (i = 0; i < cf->len; i += 4)
+ *((u32 *)(cf->data + i)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET + i);
+ } else {
+ /* skb reads the received datas, if the RTR bit not set */
+ if (!(control & CCAN_SET_RTR_MASK)) {
+ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET);
+ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET + 4);
+ }
+ }
+
+ ccan_reigister_set_bit(priv, CCAN_RCTRL_OFFSET, CCAN_SET_RREL_MASK);
+
+ stats->rx_bytes += cf->len;
+ stats->rx_packets++;
+ netif_receive_skb(skb);
+
+ return 1;
+}
+
+static int ccan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *ndev = napi->dev;
+ struct ccan_priv *priv = netdev_priv(ndev);
+ int work_done = 0;
+ u8 rx_status = 0, control = 0;
+
+ control = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET);
+ rx_status = ccan_ioread8(priv->reg_base + CCAN_RCTRL_OFFSET);
+
+ /* clear receive interrupt and deal with all the received frames */
+ while ((rx_status & CCAN_RSTAT_NOT_EMPTY_MASK) && (work_done < quota)) {
+ if (control & CCAN_SET_EDL_MASK)
+ work_done += ccanfd_rx(ndev);
+ else
+ work_done += ccan_rx(ndev);
+
+ control = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET);
+ rx_status = ccan_ioread8(priv->reg_base + CCAN_RCTRL_OFFSET);
+ }
+
+ napi_complete(napi);
+ ccan_reigister_set_bit(priv, CCAN_RTIE_OFFSET, CCAN_SET_RIE_MASK);
+
+ return work_done;
+}
+
+static void ccan_rxfull_interrupt(struct net_device *ndev, u8 isr)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+
+ if (isr & CCAN_SET_RAFIF_MASK)
+ ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET, CCAN_SET_RAFIF_MASK);
+
+ if (isr & (CCAN_SET_RAFIF_MASK | CCAN_SET_RFIF_MASK))
+ ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET,
+ (CCAN_SET_RAFIF_MASK | CCAN_SET_RFIF_MASK));
+}
+
+static int set_ccan_xmit_mode(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+
+ switch (priv->tx_mode) {
+ case XMIT_FULL:
+ ccan_reigister_set_bit(priv, CCAN_TCTRL_OFFSET, CCAN_SET_FULLCAN_MASK);
+ break;
+ case XMIT_SEP_FIFO:
+ ccan_reigister_off_bit(priv, CCAN_TCTRL_OFFSET, CCAN_OFF_FULLCAN_MASK);
+ ccan_reigister_set_bit(priv, CCAN_TCTRL_OFFSET, CCAN_SET_FIFO_MASK);
+ ccan_reigister_off_bit(priv, CCAN_TCMD_OFFSET, CCAN_SET_TBSEL_MASK);
+ break;
+ case XMIT_SEP_PRIO:
+ ccan_reigister_off_bit(priv, CCAN_TCTRL_OFFSET, CCAN_OFF_FULLCAN_MASK);
+ ccan_reigister_off_bit(priv, CCAN_TCTRL_OFFSET, CCAN_OFF_FIFO_MASK);
+ ccan_reigister_off_bit(priv, CCAN_TCMD_OFFSET, CCAN_SET_TBSEL_MASK);
+ break;
+ case XMIT_PTB_MODE:
+ ccan_reigister_off_bit(priv, CCAN_TCMD_OFFSET, CCAN_OFF_TBSEL_MASK);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static netdev_tx_t ccan_driver_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ struct net_device_stats *stats = &ndev->stats;
+ u32 ttsen, id, ctl, addr_off;
+ int i;
+
+ priv->tx_mode = XMIT_PTB_MODE;
+
+ if (can_dropped_invalid_skb(ndev, skb))
+ return NETDEV_TX_OK;
+
+ switch (priv->tx_mode) {
+ case XMIT_FULL:
+ return NETDEV_TX_BUSY;
+ case XMIT_PTB_MODE:
+ set_ccan_xmit_mode(ndev);
+ ccan_reigister_off_bit(priv, CCAN_TCMD_OFFSET, CCAN_OFF_STBY_MASK);
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ id = cf->can_id & CAN_EFF_MASK;
+ ttsen = 0 << TTSEN_8_32_SHIFT;
+ id |= ttsen;
+ } else {
+ id = cf->can_id & CAN_SFF_MASK;
+ ttsen = 0 << TTSEN_8_32_SHIFT;
+ id |= ttsen;
+ }
+
+ ctl = can_fd_len2dlc(cf->len);
+
+ /* transmit can fd frame */
+ if (priv->cantype == CAST_CAN_TYPE_CANFD) {
+ if (can_is_canfd_skb(skb)) {
+ if (cf->can_id & CAN_EFF_FLAG)
+ ctl |= CCAN_SET_IDE_MASK;
+ else
+ ctl &= CCAN_OFF_IDE_MASK;
+
+ if (cf->flags & CANFD_BRS)
+ ctl |= CCAN_SET_BRS_MASK;
+
+ ctl |= CCAN_SET_EDL_MASK;
+
+ addr_off = CCAN_TBUF_DATA_OFFSET;
+
+ for (i = 0; i < cf->len; i += 4) {
+ priv->write_reg(priv, addr_off,
+ *((u32 *)(cf->data + i)));
+ addr_off += 4;
+ }
+ } else {
+ ctl &= (CCAN_OFF_EDL_MASK | CCAN_OFF_BRS_MASK);
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ ctl |= CCAN_SET_IDE_MASK;
+ else
+ ctl &= CCAN_OFF_IDE_MASK;
+
+ if (cf->can_id & CAN_RTR_FLAG) {
+ ctl |= CCAN_SET_RTR_MASK;
+ priv->write_reg(priv,
+ CCAN_TBUF_ID_OFFSET, id);
+ priv->write_reg(priv,
+ CCAN_TBUF_CTL_OFFSET, ctl);
+ } else {
+ ctl &= CCAN_OFF_RTR_MASK;
+ addr_off = CCAN_TBUF_DATA_OFFSET;
+ priv->write_reg(priv, addr_off,
+ *((u32 *)(cf->data + 0)));
+ priv->write_reg(priv, addr_off + 4,
+ *((u32 *)(cf->data + 4)));
+ }
+ }
+ priv->write_reg(priv, CCAN_TBUF_ID_OFFSET, id);
+ priv->write_reg(priv, CCAN_TBUF_CTL_OFFSET, ctl);
+ addr_off = CCAN_TBUF_DATA_OFFSET;
+ } else {
+ ctl &= (CCAN_OFF_EDL_MASK | CCAN_OFF_BRS_MASK);
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ ctl |= CCAN_SET_IDE_MASK;
+ else
+ ctl &= CCAN_OFF_IDE_MASK;
+
+ if (cf->can_id & CAN_RTR_FLAG) {
+ ctl |= CCAN_SET_RTR_MASK;
+ priv->write_reg(priv, CCAN_TBUF_ID_OFFSET, id);
+ priv->write_reg(priv, CCAN_TBUF_CTL_OFFSET, ctl);
+ } else {
+ ctl &= CCAN_OFF_RTR_MASK;
+ priv->write_reg(priv, CCAN_TBUF_ID_OFFSET, id);
+ priv->write_reg(priv, CCAN_TBUF_CTL_OFFSET, ctl);
+ addr_off = CCAN_TBUF_DATA_OFFSET;
+ priv->write_reg(priv, addr_off,
+ *((u32 *)(cf->data + 0)));
+ priv->write_reg(priv, addr_off + 4,
+ *((u32 *)(cf->data + 4)));
+ }
+ }
+ ccan_reigister_set_bit(priv, CCAN_TCMD_OFFSET, CCAN_SET_TPE_MASK);
+ stats->tx_bytes += cf->len;
+ break;
+ default:
+ break;
+ }
+
+ if (!(ndev->flags & IFF_ECHO) || (skb->protocol != htons(ETH_P_CAN) &&
+ skb->protocol != htons(ETH_P_CANFD))) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ skb = can_create_echo_skb(skb);
+ if (!skb)
+ return -ENOMEM;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = ndev;
+
+ skb_tx_timestamp(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static int set_reset_mode(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ u8 ret;
+
+ ret = ccan_ioread8(priv->reg_base + CCAN_CFG_STAT_OFFSET);
+ ret |= CCAN_SET_RST_MASK;
+ ccan_iowrite8(ret, priv->reg_base + CCAN_CFG_STAT_OFFSET);
+
+ return 0;
+}
+
+static void ccan_driver_stop(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ret = set_reset_mode(ndev);
+ if (ret)
+ netdev_err(ndev, "Mode resetting failed!\n");
+
+ priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int ccan_driver_close(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ ccan_driver_stop(ndev);
+
+ close_candev(ndev);
+
+ return 0;
+}
+
+static enum can_state get_of_chip_status(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ u8 can_stat, eir;
+
+ can_stat = ccan_ioread8(priv->reg_base + CCAN_CFG_STAT_OFFSET);
+ eir = ccan_ioread8(priv->reg_base + CCAN_ERRINT_OFFSET);
+
+ if (can_stat & CCAN_SET_BUSOFF_MASK)
+ return CAN_STATE_BUS_OFF;
+
+ if ((eir & CCAN_SET_EPASS_MASK) && ~(can_stat & CCAN_SET_BUSOFF_MASK))
+ return CAN_STATE_ERROR_PASSIVE;
+
+ if (eir & CCAN_SET_EWARN_MASK && ~(eir & CCAN_SET_EPASS_MASK))
+ return CAN_STATE_ERROR_WARNING;
+
+ if (~(eir & CCAN_SET_EPASS_MASK))
+ return CAN_STATE_ERROR_ACTIVE;
+
+ return CAN_STATE_ERROR_ACTIVE;
+}
+
+static void ccan_error_interrupt(struct net_device *ndev, u8 isr, u8 eir)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u8 koer, recnt = 0, tecnt = 0, can_stat = 0;
+
+ skb = alloc_can_err_skb(ndev, &cf);
+
+ koer = ccan_ioread8(priv->reg_base + CCAN_EALCAP_OFFSET) & CCAN_SET_KOER_MASK;
+ recnt = ccan_ioread8(priv->reg_base + CCAN_RECNT_OFFSET);
+ tecnt = ccan_ioread8(priv->reg_base + CCAN_TECNT_OFFSET);
+
+ /*Read can status*/
+ can_stat = ccan_ioread8(priv->reg_base + CCAN_CFG_STAT_OFFSET);
+
+ /* Bus off --->active error mode */
+ if ((isr & CCAN_SET_EIF_MASK) && priv->can.state == CAN_STATE_BUS_OFF)
+ priv->can.state = get_of_chip_status(ndev);
+
+ /* State selection */
+ if (can_stat & CCAN_SET_BUSOFF_MASK) {
+ priv->can.state = get_of_chip_status(ndev);
+ priv->can.can_stats.bus_off++;
+ ccan_reigister_set_bit(priv, CCAN_CFG_STAT_OFFSET, CCAN_SET_BUSOFF_MASK);
+ can_bus_off(ndev);
+ if (skb)
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ } else if ((eir & CCAN_SET_EPASS_MASK) && ~(can_stat & CCAN_SET_BUSOFF_MASK)) {
+ priv->can.state = get_of_chip_status(ndev);
+ priv->can.can_stats.error_passive++;
+ if (skb) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= (recnt > 127) ? CAN_ERR_CRTL_RX_PASSIVE : 0;
+ cf->data[1] |= (tecnt > 127) ? CAN_ERR_CRTL_TX_PASSIVE : 0;
+ cf->data[6] = tecnt;
+ cf->data[7] = recnt;
+ }
+ } else if (eir & CCAN_SET_EWARN_MASK && ~(eir & CCAN_SET_EPASS_MASK)) {
+ priv->can.state = get_of_chip_status(ndev);
+ priv->can.can_stats.error_warning++;
+ if (skb) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= (recnt > 95) ? CAN_ERR_CRTL_RX_WARNING : 0;
+ cf->data[1] |= (tecnt > 95) ? CAN_ERR_CRTL_TX_WARNING : 0;
+ cf->data[6] = tecnt;
+ cf->data[7] = recnt;
+ }
+ }
+
+ /* Check for in protocol defined error interrupt */
+ if (eir & CCAN_SET_BEIF_MASK) {
+ if (skb)
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+
+ if (koer == CCAN_SET_BIT_ERROR_MASK) {
+ stats->tx_errors++;
+ if (skb)
+ cf->data[2] = CAN_ERR_PROT_BIT;
+ } else if (koer == CCAN_SET_FORM_ERROR_MASK) {
+ stats->rx_errors++;
+ if (skb)
+ cf->data[2] = CAN_ERR_PROT_FORM;
+ } else if (koer == CCAN_SET_STUFF_ERROR_MASK) {
+ stats->rx_errors++;
+ if (skb)
+ cf->data[3] = CAN_ERR_PROT_STUFF;
+ } else if (koer == CCAN_SET_ACK_ERROR_MASK) {
+ stats->tx_errors++;
+ if (skb)
+ cf->data[2] = CAN_ERR_PROT_LOC_ACK;
+ } else if (koer == CCAN_SET_CRC_ERROR_MASK) {
+ stats->rx_errors++;
+ if (skb)
+ cf->data[2] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ }
+ priv->can.can_stats.bus_error++;
+ }
+
+ if (skb) {
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ netif_rx(skb);
+ }
+
+ netdev_dbg(ndev, "Recnt is 0x%02x", ccan_ioread8(priv->reg_base + CCAN_RECNT_OFFSET));
+ netdev_dbg(ndev, "Tecnt is 0x%02x", ccan_ioread8(priv->reg_base + CCAN_TECNT_OFFSET));
+}
+
+static irqreturn_t ccan_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ccan_priv *priv = netdev_priv(ndev);
+ u8 isr, eir;
+ u8 isr_handled = 0, eir_handled = 0;
+
+ /* read the value of interrupt status register */
+ isr = ccan_ioread8(priv->reg_base + CCAN_RTIF_OFFSET);
+
+ /* read the value of error interrupt register */
+ eir = ccan_ioread8(priv->reg_base + CCAN_ERRINT_OFFSET);
+
+ /* Check for Tx interrupt and Processing it */
+ if (isr & (CCAN_SET_TPIF_MASK | CCAN_SET_TSIF_MASK)) {
+ ccan_tx_interrupt(ndev, isr);
+ isr_handled |= (CCAN_SET_TPIF_MASK | CCAN_SET_TSIF_MASK);
+ }
+
+ if (isr & (CCAN_SET_RAFIF_MASK | CCAN_SET_RFIF_MASK)) {
+ ccan_rxfull_interrupt(ndev, isr);
+ isr_handled |= (CCAN_SET_RAFIF_MASK | CCAN_SET_RFIF_MASK);
+ }
+
+ /* Check Rx interrupt and Processing the receive interrupt routine */
+ if (isr & CCAN_SET_RIF_MASK) {
+ ccan_reigister_off_bit(priv, CCAN_RTIE_OFFSET, CCAN_OFF_RIE_MASK);
+ ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET, CCAN_SET_RIF_MASK);
+
+ napi_schedule(&priv->napi);
+ isr_handled |= CCAN_SET_RIF_MASK;
+ }
+
+ if ((isr & CCAN_SET_EIF_MASK) |
+ (eir & (CCAN_SET_EPIF_MASK | CCAN_SET_BEIF_MASK))) {
+ /* reset EPIF and BEIF. Reset EIF */
+ ccan_reigister_set_bit(priv, CCAN_ERRINT_OFFSET,
+ eir & (CCAN_SET_EPIF_MASK | CCAN_SET_BEIF_MASK));
+ ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET,
+ isr & CCAN_SET_EIF_MASK);
+
+ ccan_error_interrupt(ndev, isr, eir);
+
+ isr_handled |= CCAN_SET_EIF_MASK;
+ eir_handled |= (CCAN_SET_EPIF_MASK | CCAN_SET_BEIF_MASK);
+ }
+
+ if (isr_handled == 0 && eir_handled == 0) {
+ netdev_err(ndev, "Unhandled interrupt!\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ccan_chip_start(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ int err;
+ u8 ret;
+
+ err = set_reset_mode(ndev);
+ if (err) {
+ netdev_err(ndev, "Mode resetting failed!\n");
+ return err;
+ }
+
+ err = ccan_device_driver_bittime_configuration(ndev);
+ if (err) {
+ netdev_err(ndev, "Bittime setting failed!\n");
+ return err;
+ }
+
+ /* Set Almost Full Warning Limit */
+ ccan_reigister_set_bit(priv, CCAN_LIMIT_OFFSET, CCAN_SET_AFWL_MASK);
+
+ /* Programmable Error Warning Limit = (EWL+1)*8. Set EWL=11->Error Warning=96 */
+ ccan_reigister_set_bit(priv, CCAN_LIMIT_OFFSET, CCAN_SET_EWL_MASK);
+
+ /* Interrupts enable */
+ ccan_iowrite8(CCAN_INTR_ALL_MASK, priv->reg_base + CCAN_RTIE_OFFSET);
+
+ /* Error Interrupts enable(Error Passive and Bus Error) */
+ ccan_reigister_set_bit(priv, CCAN_ERRINT_OFFSET, CCAN_SET_EPIE_MASK);
+
+ ret = ccan_ioread8(priv->reg_base + CCAN_CFG_STAT_OFFSET);
+
+ /* Check whether it is loopback mode or normal mode */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ ret |= CCAN_LBMIMOD_MASK;
+ else
+ ret &= ~(CCAN_LBMEMOD_MASK | CCAN_LBMIMOD_MASK);
+
+ ccan_iowrite8(ret, priv->reg_base + CCAN_CFG_STAT_OFFSET);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+}
+
+static int ccan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ int ret;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ ret = ccan_chip_start(ndev);
+ if (ret) {
+ netdev_err(ndev, "Could not start CAN device !\n");
+ return ret;
+ }
+ netif_wake_queue(ndev);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int ccan_driver_open(struct net_device *ndev)
+{
+ struct ccan_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ /* Set chip into reset mode */
+ ret = set_reset_mode(ndev);
+ if (ret) {
+ netdev_err(ndev, "Mode resetting failed!\n");
+ return ret;
+ }
+
+ /* Common open */
+ ret = open_candev(ndev);
+ if (ret)
+ return ret;
+
+ /* Register interrupt handler */
+ ret = devm_request_irq(priv->dev, ndev->irq, ccan_interrupt, IRQF_SHARED,
+ ndev->name, ndev);
+ if (ret) {
+ netdev_err(ndev, "Request_irq err: %d\n", ret);
+ goto err;
+ }
+
+ ret = ccan_chip_start(ndev);
+ if (ret) {
+ netdev_err(ndev, "Could not start CAN device !\n");
+ goto err;
+ }
+
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+
+ return 0;
+
+err:
+ close_candev(ndev);
+ return ret;
+}
+
+static int ccan_starfive_parse_dt(struct ccan_priv *priv)
+{
+ struct of_phandle_args args;
+ u32 syscon_mask, syscon_shift;
+ u32 syscon_offset, regval;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(priv->dev->of_node,
+ "starfive,sys-syscon", 3, 0, &args);
+ if (ret) {
+ dev_err(priv->dev, "Failed to parse starfive,sys-syscon\n");
+ return -EINVAL;
+ }
+
+ priv->reg_syscon = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ if (IS_ERR(priv->reg_syscon))
+ return PTR_ERR(priv->reg_syscon);
+
+ syscon_offset = args.args[0];
+ syscon_shift = args.args[1];
+ syscon_mask = args.args[2];
+
+ /* enable can2.0/canfd function */
+ regval = priv->cantype << syscon_shift;
+ ret = regmap_update_bits(priv->reg_syscon, syscon_offset, syscon_mask, regval);
+ if (ret)
+ return ret;
+
+ priv->is_starfive = true;
+
+ return 0;
+}
+
+static const struct net_device_ops ccan_netdev_ops = {
+ .ndo_open = ccan_driver_open,
+ .ndo_stop = ccan_driver_close,
+ .ndo_start_xmit = ccan_driver_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+static const struct cast_can_data ccan_can_data = {
+ .cantype = CAST_CAN_TYPE_CAN,
+ .bittime_const = &ccan_bittiming_const,
+};
+
+static const struct cast_can_data ccan_canfd_data = {
+ .cantype = CAST_CAN_TYPE_CANFD,
+ .bittime_const = &ccan_bittiming_const_canfd,
+};
+
+static const struct cast_can_data sfcan_can_data = {
+ .cantype = CAST_CAN_TYPE_CAN,
+ .bittime_const = &ccan_bittiming_const,
+ .starfive_parse_dt = ccan_starfive_parse_dt,
+};
+
+static const struct of_device_id ccan_of_match[] = {
+ { .compatible = "cast,can", .data = &ccan_can_data },
+ { .compatible = "cast,canfd", .data = &ccan_canfd_data },
+ { .compatible = "starfive,can", .data = &sfcan_can_data },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, ccan_of_match);
+
+static int ccan_driver_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ struct ccan_priv *priv;
+ const struct of_device_id *id;
+ const struct cast_can_data *ddata;
+ void __iomem *addr;
+ int ret;
+
+ addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(addr)) {
+ ret = PTR_ERR(addr);
+ goto exit;
+ }
+
+ id = of_match_device(ccan_of_match, &pdev->dev);
+ if (id && id->data)
+ ddata = id->data;
+
+ ndev = alloc_candev(sizeof(struct ccan_priv), 1);
+ if (!ndev) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ priv = netdev_priv(ndev);
+ priv->dev = &pdev->dev;
+ priv->is_starfive = false;
+
+ if (ddata) {
+ if (ddata->starfive_parse_dt) {
+ ret = ccan_starfive_parse_dt(priv);
+ if (ret)
+ goto free_exit;
+ }
+ }
+
+ priv->can_clk = devm_clk_get_enabled(&pdev->dev, "can_clk");
+ if (IS_ERR(priv->can_clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(priv->can_clk),
+ "Device clock not found\n");
+ goto free_exit;
+ }
+
+ priv->host_clk = devm_clk_get_enabled(&pdev->dev, "apb_clk");
+ if (IS_ERR(priv->host_clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(priv->host_clk),
+ "Host clock not found\n");
+ goto free_exit;
+ }
+
+ priv->timer_clk = devm_clk_get_enabled(&pdev->dev, "timer_clk");
+ if (IS_ERR(priv->timer_clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(priv->timer_clk),
+ "Timer clock not found\n");
+ goto free_exit;
+ }
+
+ priv->resets = devm_reset_control_array_get_exclusive(&pdev->dev);
+ if (IS_ERR(priv->resets)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(priv->resets),
+ "Failed to get CAN resets");
+ goto clk_exit;
+ }
+
+ ret = reset_control_deassert(priv->resets);
+ if (ret)
+ goto clk_exit;
+
+ priv->can.do_set_mode = ccan_do_set_mode;
+ priv->can.bittiming_const = ddata->bittime_const;
+ priv->cantype = ddata->cantype;
+
+ if (priv->cantype == CAST_CAN_TYPE_CANFD) {
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_FD;
+ priv->can.data_bittiming_const = &ccan_data_bittiming_const_canfd;
+ } else {
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK;
+ }
+
+ priv->reg_base = addr;
+ priv->write_reg = ccan_write_reg_le;
+ priv->read_reg = ccan_read_reg_le;
+ priv->can.clock.freq = clk_get_rate(priv->can_clk);
+ ndev->irq = platform_get_irq(pdev, 0);
+
+ /* we support local echo */
+ ndev->flags |= IFF_ECHO;
+ ndev->netdev_ops = &ccan_netdev_ops;
+
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ netif_napi_add_tx_weight(ndev, &priv->napi, ccan_rx_poll, 16);
+ ret = register_candev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register (err=%d)\n", ret);
+ goto reset_exit;
+ }
+
+ dev_dbg(&pdev->dev, "Driver registered: regs=%p, irp=%d, clock=%d\n",
+ priv->reg_base, ndev->irq, priv->can.clock.freq);
+
+ return 0;
+
+reset_exit:
+ reset_control_assert(priv->resets);
+clk_exit:
+ clk_disable_unprepare(priv->can_clk);
+ clk_disable_unprepare(priv->host_clk);
+ clk_disable_unprepare(priv->timer_clk);
+free_exit:
+ free_candev(ndev);
+exit:
+ return ret;
+}
+
+static int ccan_driver_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ccan_priv *priv = netdev_priv(ndev);
+
+ reset_control_assert(priv->resets);
+ clk_disable_unprepare(priv->can_clk);
+ clk_disable_unprepare(priv->host_clk);
+ clk_disable_unprepare(priv->timer_clk);
+
+ unregister_candev(ndev);
+ netif_napi_del(&priv->napi);
+ free_candev(ndev);
+
+ return 0;
+}
+
+static struct platform_driver ccan_driver = {
+ .probe = ccan_driver_probe,
+ .remove = ccan_driver_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = ccan_of_match,
+ },
+};
+
+module_platform_driver(ccan_driver);
+
+MODULE_DESCRIPTION("CAST CAN Controller Driver");
+MODULE_AUTHOR("William Qiu<[email protected]");
+MODULE_LICENSE("GPL");
--
2.34.1
Hello William Qiu,
thank you for your contribution. I've some quick notes about your
driver.
On 29.01.2024 11:12:38, William Qiu wrote:
> Add driver for CAST CAN Controller. And add compatibility code which
> based on StarFive JH7110 SoC.
Please add yourself or someone else at starfivetech to the Maintainers
file.
Please use BIT() and/or GEN_MASK() to create the _MASK enums. Please use
FIELD_GET(), FIELD_PREP.
Please replace the ccan_ioread8() by a proper 32 bit read and use
FIELD_GET to access any non 32 bit value. Instead of ccan_iowrite8() use
FIELD_PREP and a proper 32 bit write.
The enum ccan_reg_bitchange looks very strange, why do you have OFF and
SET values?
The ccan_reigister_set_bit() and ccan_reigister_off_bit() functions
looks very strange, too. I suggest to use a 32 bit read, set, clear the
bits followed by a 32 bit write. Having set_bit() clear_bit() functions
may lead to more register accesses than needed, if not handled with care.
If you think the driver absolutely needs bit set/clear functions, please
follow the name and signature of the regmap_update_bits(),
regmap_set_bits() and regmap_clear_bits().
Please use can_put_echo_skb(), can_get_echo_skb().
Please implement proper TX-flow control. Stop the TX queue, if you HW
queue is full, start the TX queue once the HW queue has space again.
Consider using the rx_offload helper
You claim you IRQ handler works with shared interrupts, but you return
an error if there are no interrupts by your IP core.
Please enable the clocks during open() and disabled during close()
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 |
> -----Original Message-----
> From: Marc Kleine-Budde <[email protected]>
> Sent: 2024年1月29日 16:26
> To: William Qiu <[email protected]>
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; Emil Renner
> Berthing <[email protected]>; Rob Herring <[email protected]>; Wolfgang
> Grandegger <[email protected]>; Philipp Zabel <[email protected]>;
> Krzysztof Kozlowski <[email protected]>; Conor Dooley
> <[email protected]>; David S . Miller <[email protected]>; Eric
> Dumazet <[email protected]>; Jakub Kicinski <[email protected]>; Paolo
> Abeni <[email protected]>; Paul Walmsley <[email protected]>;
> Palmer Dabbelt <[email protected]>; Albert Ou <[email protected]>
> Subject: Re: [PATCH v1 3/4] can: cast: add driver for CAST CAN controller
>
> Hello William Qiu,
>
> thank you for your contribution. I've some quick notes about your driver.
>
> On 29.01.2024 11:12:38, William Qiu wrote:
> > Add driver for CAST CAN Controller. And add compatibility code which
> > based on StarFive JH7110 SoC.
>
> Please add yourself or someone else at starfivetech to the Maintainers file.
>
> Please use BIT() and/or GEN_MASK() to create the _MASK enums. Please use
> FIELD_GET(), FIELD_PREP.
>
> Please replace the ccan_ioread8() by a proper 32 bit read and use FIELD_GET to
> access any non 32 bit value. Instead of ccan_iowrite8() use FIELD_PREP and a
> proper 32 bit write.
>
> The enum ccan_reg_bitchange looks very strange, why do you have OFF and SET
> values?
>
> The ccan_reigister_set_bit() and ccan_reigister_off_bit() functions looks very
> strange, too. I suggest to use a 32 bit read, set, clear the bits followed by a 32 bit
> write. Having set_bit() clear_bit() functions may lead to more register accesses
> than needed, if not handled with care.
>
> If you think the driver absolutely needs bit set/clear functions, please follow the
> name and signature of the regmap_update_bits(),
> regmap_set_bits() and regmap_clear_bits().
>
> Please use can_put_echo_skb(), can_get_echo_skb().
>
> Please implement proper TX-flow control. Stop the TX queue, if you HW queue is
> full, start the TX queue once the HW queue has space again.
>
> Consider using the rx_offload helper
>
> You claim you IRQ handler works with shared interrupts, but you return an error
> if there are no interrupts by your IP core.
>
> Please enable the clocks during open() and disabled during close()
>
> Marc
>
Thank you for your notes. I will modify my driver one by one according to your notes。
Thanks,
William
> --
> 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 |
Hi William,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master mkl-can-next/testing v6.8-rc2 next-20240129]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/William-Qiu/dt-bindings-vendor-prefixes-Add-cast-vendor-prefix/20240129-114752
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20240129031239.17037-4-william.qiu%40starfivetech.com
patch subject: [PATCH v1 3/4] can: cast: add driver for CAST CAN controller
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20240130/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 4a39d08908942b2d415db405844cbe4af73e75d4)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240130/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/net/can/cast_can.c:352:5: warning: no previous prototype for function 'ccan_get_freebuffer' [-Wmissing-prototypes]
352 | int ccan_get_freebuffer(struct ccan_priv *priv)
| ^
drivers/net/can/cast_can.c:352:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
352 | int ccan_get_freebuffer(struct ccan_priv *priv)
| ^
| static
1 warning generated.
vim +/ccan_get_freebuffer +352 drivers/net/can/cast_can.c
351
> 352 int ccan_get_freebuffer(struct ccan_priv *priv)
353 {
354 /* Get next transmit buffer */
355 ccan_reigister_set_bit(priv, CCAN_TCTRL_OFFSET, CCAN_SET_TENEXT_MASK);
356
357 if (ccan_ioread8(priv->reg_base + CCAN_TCTRL_OFFSET) & CCAN_SET_TENEXT_MASK)
358 return -EPERM;
359
360 return 0;
361 }
362
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi William,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master mkl-can-next/testing v6.8-rc2 next-20240129]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/William-Qiu/dt-bindings-vendor-prefixes-Add-cast-vendor-prefix/20240129-114752
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20240129031239.17037-4-william.qiu%40starfivetech.com
patch subject: [PATCH v1 3/4] can: cast: add driver for CAST CAN controller
config: openrisc-allmodconfig (https://download.01.org/0day-ci/archive/20240130/[email protected]/config)
compiler: or1k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240130/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/net/can/cast_can.c:352:5: warning: no previous prototype for 'ccan_get_freebuffer' [-Wmissing-prototypes]
352 | int ccan_get_freebuffer(struct ccan_priv *priv)
| ^~~~~~~~~~~~~~~~~~~
vim +/ccan_get_freebuffer +352 drivers/net/can/cast_can.c
351
> 352 int ccan_get_freebuffer(struct ccan_priv *priv)
353 {
354 /* Get next transmit buffer */
355 ccan_reigister_set_bit(priv, CCAN_TCTRL_OFFSET, CCAN_SET_TENEXT_MASK);
356
357 if (ccan_ioread8(priv->reg_base + CCAN_TCTRL_OFFSET) & CCAN_SET_TENEXT_MASK)
358 return -EPERM;
359
360 return 0;
361 }
362
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi William,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master mkl-can-next/testing v6.8-rc2 next-20240130]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/William-Qiu/dt-bindings-vendor-prefixes-Add-cast-vendor-prefix/20240129-114752
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20240129031239.17037-4-william.qiu%40starfivetech.com
patch subject: [PATCH v1 3/4] can: cast: add driver for CAST CAN controller
config: arm-randconfig-r131-20240131 (https://download.01.org/0day-ci/archive/20240131/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project fdac7d0b6f74f919d319b31a0680c77f66732586)
reproduce: (https://download.01.org/0day-ci/archive/20240131/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
sparse warnings: (new ones prefixed by >>)
>> drivers/net/can/cast_can.c:277:41: sparse: sparse: subtraction of different types can't work (different address spaces)
drivers/net/can/cast_can.c:295:41: sparse: sparse: subtraction of different types can't work (different address spaces)
>> drivers/net/can/cast_can.c:308:50: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:357:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
>> drivers/net/can/cast_can.c:352:5: sparse: sparse: symbol 'ccan_get_freebuffer' was not declared. Should it be static?
drivers/net/can/cast_can.c:375:51: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:389:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:394:47: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:396:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:438:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:443:47: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:445:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:504:47: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:505:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:514:55: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:515:57: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:695:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
>> drivers/net/can/cast_can.c:697:43: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:732:48: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:733:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:760:44: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:761:45: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:762:45: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:765:48: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:837:9: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:838:9: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:849:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:852:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:921:58: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:926:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const *addr @@ got void [noderef] __iomem * @@
drivers/net/can/cast_can.c:934:43: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void *addr @@ got void [noderef] __iomem * @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
drivers/net/can/cast_can.c:260:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:262:31: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
drivers/net/can/cast_can.c:260:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:262:31: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:244:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
drivers/net/can/cast_can.c:260:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
>> drivers/net/can/cast_can.c:262:31: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void volatile [noderef] __iomem *addr @@ got void *[assigned] addr_down @@
vim +277 drivers/net/can/cast_can.c
232
233 static inline unsigned char ccan_ioread8(const void *addr)
234 {
235 void *addr_down;
236 union val {
237 u8 val_8[4];
238 u32 val_32;
239 } val;
240 u32 offset = 0;
241
242 addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
243 offset = addr - addr_down;
> 244 val.val_32 = ioread32(addr_down);
245
246 return val.val_8[offset];
247 }
248
249 static inline void ccan_iowrite8(unsigned char value, void *addr)
250 {
251 void *addr_down;
252 union val {
253 u8 val_8[4];
254 u32 val_32;
255 } val;
256 u8 offset = 0;
257
258 addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
259 offset = addr - addr_down;
260 val.val_32 = ioread32(addr_down);
261 val.val_8[offset] = value;
> 262 iowrite32(val.val_32, addr_down);
263 }
264
265 static void ccan_reigister_set_bit(const struct ccan_priv *priv,
266 enum ccan_device_reg reg,
267 enum ccan_reg_bitchange mask)
268 {
269 void *addr_down;
270 union val {
271 u8 val_8[4];
272 u32 val_32;
273 } val;
274 u8 offset = 0;
275
276 addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
> 277 offset = (priv->reg_base + reg) - addr_down;
278 val.val_32 = ioread32(addr_down);
279 val.val_8[offset] |= mask;
280 iowrite32(val.val_32, addr_down);
281 }
282
283 static void ccan_reigister_off_bit(const struct ccan_priv *priv,
284 enum ccan_device_reg reg,
285 enum ccan_reg_bitchange mask)
286 {
287 void *addr_down;
288 union val {
289 u8 val_8[4];
290 u32 val_32;
291 } val;
292 u8 offset = 0;
293
294 addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
295 offset = (priv->reg_base + reg) - addr_down;
296 val.val_32 = ioread32(addr_down);
297 val.val_8[offset] &= mask;
298 iowrite32(val.val_32, addr_down);
299 }
300
301 static int ccan_device_driver_bittime_configuration(struct net_device *ndev)
302 {
303 struct ccan_priv *priv = netdev_priv(ndev);
304 struct can_bittiming *bt = &priv->can.bittiming;
305 struct can_bittiming *dbt = &priv->can.data_bittiming;
306 u32 reset_test, bittiming_temp, data_bittiming;
307
> 308 reset_test = ccan_ioread8(priv->reg_base + CCAN_CFG_STAT_OFFSET);
309
310 if (!(reset_test & CCAN_SET_RST_MASK)) {
311 netdev_alert(ndev, "Not in reset mode, cannot set bit timing\n");
312 return -EPERM;
313 }
314
315 bittiming_temp = ((bt->phase_seg1 + bt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
316 ((bt->phase_seg2 - 1) << SEG_2_SHIFT) |
317 ((bt->sjw - 1) << SJW_SHIFT) |
318 ((bt->brp - 1) << PRESC_SHIFT);
319
320 /* Check the bittime parameter */
321 if ((((int)(bt->phase_seg1 + bt->prop_seg + 1) - 2) < 0) ||
322 (((int)(bt->phase_seg2) - 1) < 0) ||
323 (((int)(bt->sjw) - 1) < 0) ||
324 (((int)(bt->brp) - 1) < 0))
325 return -EINVAL;
326
327 priv->write_reg(priv, CCAN_S_SEG_1_OFFSET, bittiming_temp);
328
329 if (priv->cantype == CAST_CAN_TYPE_CANFD) {
330 data_bittiming = ((dbt->phase_seg1 + dbt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
331 ((dbt->phase_seg2 - 1) << SEG_2_SHIFT) |
332 ((dbt->sjw - 1) << SJW_SHIFT) |
333 ((dbt->brp - 1) << PRESC_SHIFT);
334
335 if ((((int)(dbt->phase_seg1 + dbt->prop_seg + 1) - 2) < 0) ||
336 (((int)(dbt->phase_seg2) - 1) < 0) ||
337 (((int)(dbt->sjw) - 1) < 0) ||
338 (((int)(dbt->brp) - 1) < 0))
339 return -EINVAL;
340
341 priv->write_reg(priv, CCAN_F_SEG_1_OFFSET, data_bittiming);
342 }
343
344 ccan_reigister_off_bit(priv, CCAN_CFG_STAT_OFFSET, CCAN_OFF_RST_MASK);
345
346 netdev_dbg(ndev, "Slow bit rate: %08x\n", priv->read_reg(priv, CCAN_S_SEG_1_OFFSET));
347 netdev_dbg(ndev, "Fast bit rate: %08x\n", priv->read_reg(priv, CCAN_F_SEG_1_OFFSET));
348
349 return 0;
350 }
351
> 352 int ccan_get_freebuffer(struct ccan_priv *priv)
353 {
354 /* Get next transmit buffer */
355 ccan_reigister_set_bit(priv, CCAN_TCTRL_OFFSET, CCAN_SET_TENEXT_MASK);
356
357 if (ccan_ioread8(priv->reg_base + CCAN_TCTRL_OFFSET) & CCAN_SET_TENEXT_MASK)
358 return -EPERM;
359
360 return 0;
361 }
362
363 static void ccan_tx_interrupt(struct net_device *ndev, u8 isr)
364 {
365 struct ccan_priv *priv = netdev_priv(ndev);
366
367 /* wait till transmission of the PTB or STB finished */
368 while (isr & (CCAN_SET_TPIF_MASK | CCAN_SET_TSIF_MASK)) {
369 if (isr & CCAN_SET_TPIF_MASK)
370 ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET, CCAN_SET_TPIF_MASK);
371
372 if (isr & CCAN_SET_TSIF_MASK)
373 ccan_reigister_set_bit(priv, CCAN_RTIF_OFFSET, CCAN_SET_TSIF_MASK);
374
375 isr = ccan_ioread8(priv->reg_base + CCAN_RTIF_OFFSET);
376 }
377 netif_wake_queue(ndev);
378 }
379
380 static int ccan_rx(struct net_device *ndev)
381 {
382 struct ccan_priv *priv = netdev_priv(ndev);
383 struct net_device_stats *stats = &ndev->stats;
384 struct can_frame *cf;
385 struct sk_buff *skb;
386 u32 can_id;
387 u8 dlc, control, rx_status;
388
389 rx_status = ccan_ioread8(priv->reg_base + CCAN_RCTRL_OFFSET);
390
391 if (!(rx_status & CCAN_RSTAT_NOT_EMPTY_MASK))
392 return 0;
393
394 control = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET);
395 can_id = priv->read_reg(priv, CCAN_RUBF_ID_OFFSET);
> 396 dlc = ccan_ioread8(priv->reg_base + CCAN_RBUF_CTL_OFFSET) & CCAN_SET_DLC_MASK;
397
398 skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
399 if (!skb) {
400 stats->rx_dropped++;
401 return 0;
402 }
403 cf->can_dlc = can_cc_dlc2len(dlc);
404
405 /* change the CANFD id into socketcan id format */
406 cf->can_id = can_id;
407 if (control & CCAN_SET_IDE_MASK)
408 cf->can_id |= CAN_EFF_FLAG;
409 else
410 cf->can_id &= ~CAN_EFF_FLAG;
411
412 if (control & CCAN_SET_RTR_MASK)
413 cf->can_id |= CAN_RTR_FLAG;
414
415 if (!(control & CCAN_SET_RTR_MASK)) {
416 *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET);
417 *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CCAN_RBUF_DATA_OFFSET + 4);
418 }
419
420 ccan_reigister_set_bit(priv, CCAN_RCTRL_OFFSET, CCAN_SET_RREL_MASK);
421 stats->rx_bytes += can_fd_dlc2len(cf->can_dlc);
422 stats->rx_packets++;
423 netif_receive_skb(skb);
424
425 return 1;
426 }
427
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki