* [PATCH 1/5] net: can: Sort the Kconfig includes @ 2016-01-11 18:47 Marek Vasut 2016-01-11 18:48 ` [PATCH 2/5] net: can: Sort the Makefile Marek Vasut ` (3 more replies) 0 siblings, 4 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-11 18:47 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger Sort the Kconfig includes, no functional change. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> --- drivers/net/can/Kconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 6d04183..ca49d15 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -147,22 +147,22 @@ config CAN_XILINXCAN Xilinx CAN driver. This driver supports both soft AXI CAN IP and Zynq CANPS IP. -source "drivers/net/can/mscan/Kconfig" - -source "drivers/net/can/sja1000/Kconfig" - source "drivers/net/can/c_can/Kconfig" +source "drivers/net/can/cc770/Kconfig" + source "drivers/net/can/m_can/Kconfig" -source "drivers/net/can/cc770/Kconfig" +source "drivers/net/can/mscan/Kconfig" + +source "drivers/net/can/sja1000/Kconfig" + +source "drivers/net/can/softing/Kconfig" source "drivers/net/can/spi/Kconfig" source "drivers/net/can/usb/Kconfig" -source "drivers/net/can/softing/Kconfig" - endif config CAN_DEBUG_DEVICES -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/5] net: can: Sort the Makefile 2016-01-11 18:47 [PATCH 1/5] net: can: Sort the Kconfig includes Marek Vasut @ 2016-01-11 18:48 ` Marek Vasut 2016-01-11 18:48 ` [PATCH 3/5] of: Add vendor prefix for I/F/I Marek Vasut ` (2 subsequent siblings) 3 siblings, 0 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-11 18:48 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger Just sort the drivers in the Makefile, no functional change. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> --- drivers/net/can/Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 1f21cef..4f85c2b 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -14,21 +14,21 @@ obj-y += spi/ obj-y += usb/ obj-y += softing/ -obj-$(CONFIG_CAN_SJA1000) += sja1000/ -obj-$(CONFIG_CAN_MSCAN) += mscan/ -obj-$(CONFIG_CAN_C_CAN) += c_can/ -obj-$(CONFIG_CAN_M_CAN) += m_can/ -obj-$(CONFIG_CAN_CC770) += cc770/ obj-$(CONFIG_CAN_AT91) += at91_can.o -obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o -obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o +obj-$(CONFIG_CAN_CC770) += cc770/ +obj-$(CONFIG_CAN_C_CAN) += c_can/ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o -obj-$(CONFIG_PCH_CAN) += pch_can.o obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o +obj-$(CONFIG_CAN_MSCAN) += mscan/ +obj-$(CONFIG_CAN_M_CAN) += m_can/ obj-$(CONFIG_CAN_RCAR) += rcar_can.o +obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o +obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o +obj-$(CONFIG_PCH_CAN) += pch_can.o subdir-ccflags-y += -D__CHECK_ENDIAN__ subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 3/5] of: Add vendor prefix for I/F/I 2016-01-11 18:47 [PATCH 1/5] net: can: Sort the Kconfig includes Marek Vasut 2016-01-11 18:48 ` [PATCH 2/5] net: can: Sort the Makefile Marek Vasut @ 2016-01-11 18:48 ` Marek Vasut 2016-01-11 18:48 ` [PATCH 4/5] net: can: ifi: Add DT bindings for ifi,canfd Marek Vasut 2016-01-11 18:48 ` [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support Marek Vasut 3 siblings, 0 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-11 18:48 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger Add vendor prefix for I/F/I, Ingenieurbüro Für IC-Technologie http://www.ifi-pld.de/ Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 55df1d4..e683e61 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -110,6 +110,7 @@ hp Hewlett Packard i2se I2SE GmbH ibm International Business Machines (IBM) idt Integrated Device Technologies, Inc. +ifi Ingenieurburo Fur Ic-Technologie (I/F/I) iom Iomega Corporation img Imagination Technologies Ltd. ingenic Ingenic Semiconductor -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 4/5] net: can: ifi: Add DT bindings for ifi,canfd 2016-01-11 18:47 [PATCH 1/5] net: can: Sort the Kconfig includes Marek Vasut 2016-01-11 18:48 ` [PATCH 2/5] net: can: Sort the Makefile Marek Vasut 2016-01-11 18:48 ` [PATCH 3/5] of: Add vendor prefix for I/F/I Marek Vasut @ 2016-01-11 18:48 ` Marek Vasut 2016-01-11 18:48 ` [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support Marek Vasut 3 siblings, 0 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-11 18:48 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger Add device tree bindings for the I/F/I CANFD controller IP core. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> --- Documentation/devicetree/bindings/net/can/ifi_canfd.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/ifi_canfd.txt diff --git a/Documentation/devicetree/bindings/net/can/ifi_canfd.txt b/Documentation/devicetree/bindings/net/can/ifi_canfd.txt new file mode 100644 index 0000000..20ea5c7 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/ifi_canfd.txt @@ -0,0 +1,15 @@ +IFI CANFD controller +-------------------- + +Required properties: + - compatible: Should be "ifi,canfd-1.0" + - reg: Should contain CAN controller registers location and length + - interrupts: Should contain IRQ line for the CAN controller + +Example: + + canfd0: canfd@ff220000 { + compatible = "ifi,canfd-1.0"; + reg = <0xff220000 0x00001000>; + interrupts = <0 43 0>; + }; -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-11 18:47 [PATCH 1/5] net: can: Sort the Kconfig includes Marek Vasut ` (2 preceding siblings ...) 2016-01-11 18:48 ` [PATCH 4/5] net: can: ifi: Add DT bindings for ifi,canfd Marek Vasut @ 2016-01-11 18:48 ` Marek Vasut 2016-01-11 19:29 ` kbuild test robot ` (2 more replies) 3 siblings, 3 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-11 18:48 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger The patch adds support for IFI CAN/FD controller [1]. This driver currently supports sending and receiving both standard CAN and new CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. [1] http://www.ifi-pld.de/IP/CANFD/canfd.html Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> NOTE: The driver is surprisingly similar to m_can, but the register layout of the IFI core is completely different, so it's clear that those are two different IP cores. --- drivers/net/can/Kconfig | 2 + drivers/net/can/Makefile | 1 + drivers/net/can/ifi_canfd/Kconfig | 8 + drivers/net/can/ifi_canfd/Makefile | 5 + drivers/net/can/ifi_canfd/ifi_canfd.c | 914 ++++++++++++++++++++++++++++++++++ 5 files changed, 930 insertions(+) create mode 100644 drivers/net/can/ifi_canfd/Kconfig create mode 100644 drivers/net/can/ifi_canfd/Makefile create mode 100644 drivers/net/can/ifi_canfd/ifi_canfd.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index ca49d15..20be638 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -151,6 +151,8 @@ source "drivers/net/can/c_can/Kconfig" source "drivers/net/can/cc770/Kconfig" +source "drivers/net/can/ifi_canfd/Kconfig" + source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 4f85c2b..e3db0c8 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_CAN_CC770) += cc770/ obj-$(CONFIG_CAN_C_CAN) += c_can/ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ diff --git a/drivers/net/can/ifi_canfd/Kconfig b/drivers/net/can/ifi_canfd/Kconfig new file mode 100644 index 0000000..9e8934f --- /dev/null +++ b/drivers/net/can/ifi_canfd/Kconfig @@ -0,0 +1,8 @@ +config CAN_IFI_CANFD + depends on HAS_IOMEM + tristate "IFI CAN_FD IP" + ---help--- + This driver adds support for the I/F/I CAN_FD soft IP block + connected to the "platform bus" (Linux abstraction for directly + to the processor attached devices). The CAN_FD is most often + synthesised into an FPGA or CPLD. diff --git a/drivers/net/can/ifi_canfd/Makefile b/drivers/net/can/ifi_canfd/Makefile new file mode 100644 index 0000000..b229960 --- /dev/null +++ b/drivers/net/can/ifi_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the IFI CANFD controller driver. +# + +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd.o diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c new file mode 100644 index 0000000..b1167ff --- /dev/null +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -0,0 +1,914 @@ +/* + * CAN bus driver for IFI CANFD controller + * + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * Details about this controller can be found at + * http://www.ifi-pld.de/IP/CANFD/canfd.html + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <linux/can/dev.h> + +#define IFI_CANFD_STCMD 0x0 +#define IFI_CANFD_STCMD_HARDRESET 0xDEADCAFD +#define IFI_CANFD_STCMD_ENABLE BIT(0) +#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) +#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) +#define IFI_CANFD_STCMD_BUSOFF BIT(4) +#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) +#define IFI_CANFD_STCMD_LOOPBACK BIT(18) +#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) +#define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) +#define IFI_CANFD_STCMD_NORMAL_MODE BIT(31) + +#define IFI_CANFD_RXSTCMD 0x4 +#define IFI_CANFD_RXSTCMD_REMOVE_MSG BIT(0) +#define IFI_CANFD_RXSTCMD_RESET BIT(7) +#define IFI_CANFD_RXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_RXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_TXSTCMD 0x8 +#define IFI_CANFD_TXSTCMD_ADD_MSG BIT(0) +#define IFI_CANFD_TXSTCMD_HIGH_PRIO BIT(1) +#define IFI_CANFD_TXSTCMD_RESET BIT(7) +#define IFI_CANFD_TXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_TXSTCMD_FULL BIT(12) +#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_INTERRUPT 0xc +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) +#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER BIT(25) +#define IFI_CANFD_INTERRUPT_SET_IRQ BIT(31) + +#define IFI_CANFD_IRQMASK 0x10 +#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) +#define IFI_CANFD_IRQMASK_SET_TS BIT(15) +#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_IRQMASK_SET_TX BIT(23) +#define IFI_CANFD_IRQMASK_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_IRQMASK_SET_RX BIT(31) + +#define IFI_CANFD_TIME 0x14 +#define IFI_CANFD_FTIME 0x18 +#define IFI_CANFD_TIME_TIMEB_OFF 0 +#define IFI_CANFD_TIME_TIMEA_OFF 8 +#define IFI_CANFD_TIME_PRESCALE_OFF 16 +#define IFI_CANFD_TIME_SJW_OFF_ISO 25 +#define IFI_CANFD_TIME_SJW_OFF_BOSCH 28 +#define IFI_CANFD_TIME_SET_SJW_BOSCH BIT(6) +#define IFI_CANFD_TIME_SET_TIMEB_BOSCH BIT(7) +#define IFI_CANFD_TIME_SET_PRESC_BOSCH BIT(14) +#define IFI_CANFD_TIME_SET_TIMEA_BOSCH BIT(15) + +#define IFI_CANFD_TDELAY 0x1c + +#define IFI_CANFD_ERROR 0x20 +#define IFI_CANFD_ERROR_TX_OFFSET 0 +#define IFI_CANFD_ERROR_TX_MASK 0xff +#define IFI_CANFD_ERROR_RX_OFFSET 16 +#define IFI_CANFD_ERROR_RX_MASK 0xff + +#define IFI_CANFD_ERRCNT 0x24 + +#define IFI_CANFD_SUSPEND 0x28 + +#define IFI_CANFD_REPEAT 0x2c + +#define IFI_CANFD_TRAFFIC 0x30 + +#define IFI_CANFD_TSCONTROL 0x34 + +#define IFI_CANFD_TSC 0x38 + +#define IFI_CANFD_TST 0x3c + +#define IFI_CANFD_RES1 0x40 + +#define IFI_CANFD_RES2 0x44 + +#define IFI_CANFD_PAR 0x48 + +#define IFI_CANFD_CANCLOCK 0x4c + +#define IFI_CANFD_SYSCLOCK 0x50 + +#define IFI_CANFD_VER 0x54 + +#define IFI_CANFD_IP_ID 0x58 +#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD + +#define IFI_CANFD_TEST 0x5c + +#define IFI_CANFD_RXFIFO_TS_63_32 0x60 + +#define IFI_CANFD_RXFIFO_TS_31_0 0x64 + +#define IFI_CANFD_RXFIFO_DLC 0x68 +#define IFI_CANFD_RXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_RXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_RXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_RXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_RXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_RXFIFO_DLC_ESI BIT(7) +#define IFI_CANFD_RXFIFO_DLC_OBJ_OFFSET 8 +#define IFI_CANFD_RXFIFO_DLC_OBJ_MASK 0x1ff +#define IFI_CANFD_RXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_RXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_RXFIFO_ID 0x6c +#define IFI_CANFD_RXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_RXFIFO_ID_ID_STD_MASK 0x3ff +#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK 0x1fffffff +#define IFI_CANFD_RXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_RXFIFO_DATA 0x70 /* 0x70..0xac */ + +#define IFI_CANFD_TXFIFO_SUSPEND_US 0xb0 + +#define IFI_CANFD_TXFIFO_REPEATCOUNT 0xb4 + +#define IFI_CANFD_TXFIFO_DLC 0xb8 +#define IFI_CANFD_TXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_TXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_TXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_TXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_TXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_TXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_TXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_TXFIFO_ID 0xbc +#define IFI_CANFD_TXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_TXFIFO_ID_ID_STD_MASK 0x3ff +#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK 0x1fffffff +#define IFI_CANFD_TXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_TXFIFO_DATA 0xc0 /* 0xb0..0xfc */ + +#define IFI_CANFD_FILTER_MASK(n) (0x800 + ((n) * 8) + 0) +#define IFI_CANFD_FILTER_MASK_EXT BIT(29) +#define IFI_CANFD_FILTER_MASK_EDL BIT(30) +#define IFI_CANFD_FILTER_MASK_VALID BIT(31) + +#define IFI_CANFD_FILTER_IDENT(n) (0x800 + ((n) * 8) + 4) +#define IFI_CANFD_FILTER_IDENT_IDE BIT(29) +#define IFI_CANFD_FILTER_IDENT_CANFD BIT(30) +#define IFI_CANFD_FILTER_IDENT_VALID BIT(31) + +/* IFI CANFD private data structure */ +struct ifi_canfd_priv { + struct can_priv can; /* must be the first member */ + struct napi_struct napi; + struct net_device *ndev; + struct device *device; + void __iomem *base; +}; + +static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 enirq = 0; + + if (enable) { + enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | + IFI_CANFD_IRQMASK_RXFIFO_NEMPTY; + } + + writel(IFI_CANFD_IRQMASK_SET_ERR | + IFI_CANFD_IRQMASK_SET_TS | + IFI_CANFD_IRQMASK_SET_TX | + IFI_CANFD_IRQMASK_SET_RX | enirq, + priv->base + IFI_CANFD_IRQMASK); +} + +static void ifi_canfd_read_fifo(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf; + struct sk_buff *skb; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + u32 rxdlc, rxid; + u32 dlc, id; + int i; + + rxdlc = readl(priv->base + IFI_CANFD_RXFIFO_DLC); + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + + if (!skb) { + stats->rx_dropped++; + return; + } + + dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) & + IFI_CANFD_RXFIFO_DLC_DLC_MASK; + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + cf->len = can_dlc2len(dlc); + else + cf->len = get_can_dlc(dlc); + + rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID); + id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET); + if (id & IFI_CANFD_RXFIFO_ID_IDE) + id &= IFI_CANFD_RXFIFO_ID_ID_XTD_MASK; + else + id &= IFI_CANFD_RXFIFO_ID_ID_STD_MASK; + cf->can_id = id; + + if (rxdlc & IFI_CANFD_RXFIFO_DLC_ESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(ndev, "ESI Error\n"); + } + + if (!(rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) && + (rxdlc & IFI_CANFD_RXFIFO_DLC_RTR)) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if (rxdlc & IFI_CANFD_RXFIFO_DLC_BRS) + cf->flags |= CANFD_BRS; + + for (i = 0; i < cf->len; i += 4) { + *(u32 *)(cf->data + i) = + readl(priv->base + IFI_CANFD_RXFIFO_DATA + i); + } + } + + /* Remove the packet from FIFO */ + writel(IFI_CANFD_RXSTCMD_REMOVE_MSG, priv->base + IFI_CANFD_RXSTCMD); + writel(rx_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_receive_skb(skb); +} + +static int ifi_canfd_do_rx_poll(struct net_device *ndev, int quota) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 pkts = 0; + u32 rxst; + + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) { + netdev_dbg(ndev, "No messages in RX FIFO\n"); + return 0; + } + + for (;;) { + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) + break; + if (quota <= 0) + break; + + ifi_canfd_read_fifo(ndev); + quota--; + pkts++; + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + } + + if (pkts) + can_led_event(ndev, CAN_LED_EVENT_RX); + + return pkts; +} + +static int ifi_canfd_handle_lost_msg(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(ndev, "RX FIFO overflow, message(s) lost.\n"); + + stats->rx_errors++; + stats->rx_over_errors++; + + skb = alloc_can_err_skb(ndev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 err; + + err = readl(priv->base + IFI_CANFD_ERROR); + bec->rxerr = (err >> IFI_CANFD_ERROR_RX_OFFSET) & + IFI_CANFD_ERROR_RX_MASK; + bec->txerr = (err >> IFI_CANFD_ERROR_TX_OFFSET) & + IFI_CANFD_ERROR_TX_MASK; + + return 0; +} + +static int ifi_canfd_handle_state_change(struct net_device *ndev, + enum can_state new_state) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + ifi_canfd_irq_enable(ndev, 0); + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + break; + default: + break; + } + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (unlikely(!skb)) + return 0; + + ifi_canfd_get_berr_counter(ndev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if (bec.txerr > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + cf->can_id |= CAN_ERR_BUSOFF; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int work_done = 0; + u32 isr; + + /* + * The ErrWarn condition is a little special, since the bit is + * located in the INTERRUPT register instead of STCMD register. + */ + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + /* Clear the interrupt */ + writel(IFI_CANFD_INTERRUPT_ERROR_WARNING, + priv->base + IFI_CANFD_INTERRUPT); + netdev_dbg(ndev, "Error, entered warning state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_WARNING); + } + + if ((stcmd & IFI_CANFD_STCMD_ERROR_PASSIVE) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(ndev, "Error, entered passive state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((stcmd & IFI_CANFD_STCMD_BUSOFF) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(ndev, "Error, entered bus-off state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_BUS_OFF); + } + + return work_done; +} + +static int ifi_canfd_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE | + IFI_CANFD_STCMD_BUSOFF; + int work_done = 0; + + u32 stcmd = readl(priv->base + IFI_CANFD_STCMD); + u32 rxstcmd = readl(priv->base + IFI_CANFD_STCMD); + + /* Handle bus state changes */ + if ((stcmd & stcmd_state_mask) || + ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0)) + work_done += ifi_canfd_handle_state_errors(ndev, stcmd); + + /* Handle lost messages on RX */ + if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) + work_done += ifi_canfd_handle_lost_msg(ndev); + + /* Handle normal messages on RX */ + if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) + work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); + + if (work_done < quota) { + napi_complete(napi); + ifi_canfd_irq_enable(ndev, 1); + } + + return work_done; +} + +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; + const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | + IFI_CANFD_INTERRUPT_ERROR_WARNING); + u32 isr; + + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + + /* No interrupt */ + if (isr == 0) + return IRQ_NONE; + + /* Clear all pending interrupts but ErrWarn */ + writel(clr_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + /* RX IRQ, start NAPI */ + if (isr & rx_irq_mask) { + ifi_canfd_irq_enable(ndev, 0); + napi_schedule(&priv->napi); + } + + /* TX IRQ */ + if (isr & tx_irq_mask) { + stats->tx_bytes += can_get_echo_skb(ndev, 0); + stats->tx_packets++; + can_led_event(ndev, CAN_LED_EVENT_TX); + netif_wake_queue(ndev); + } + + return IRQ_HANDLED; +} + +static const struct can_bittiming_const ifi_canfd_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 64, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +static const struct can_bittiming_const ifi_canfd_data_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + +static void ifi_canfd_set_bittiming(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 noniso_arg = 0; + u32 time_off; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) { + noniso_arg = IFI_CANFD_TIME_SET_TIMEB_BOSCH | + IFI_CANFD_TIME_SET_TIMEA_BOSCH | + IFI_CANFD_TIME_SET_PRESC_BOSCH | + IFI_CANFD_TIME_SET_SJW_BOSCH; + time_off = IFI_CANFD_TIME_SJW_OFF_BOSCH; + } else { + time_off = IFI_CANFD_TIME_SJW_OFF_ISO; + } + + /* Configure bit timing */ + brp = bt->brp - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << time_off), + priv->base + IFI_CANFD_TIME); + + /* Configure data bit timing */ + brp = dbt->brp - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << time_off) | + noniso_arg, + priv->base + IFI_CANFD_FTIME); +} + +static void ifi_canfd_set_filter(struct net_device *ndev, const u32 id, + const u32 mask, const u32 ident) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + writel(mask, priv->base + IFI_CANFD_FILTER_MASK(id)); + writel(ident, priv->base + IFI_CANFD_FILTER_IDENT(id)); +} + +static void ifi_canfd_set_filters(struct net_device *ndev) +{ + /* Receive all CAN frames (standard ID) */ + ifi_canfd_set_filter(ndev, 0, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID); + + /* Receive all CAN frames (extended ID) */ + ifi_canfd_set_filter(ndev, 1, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_IDE); + + /* Receive all CANFD frames */ + ifi_canfd_set_filter(ndev, 2, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EDL | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_CANFD | + IFI_CANFD_FILTER_IDENT_IDE); +} + +static void ifi_canfd_start(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 stcmd; + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + writel(0, priv->base + IFI_CANFD_STCMD); + + ifi_canfd_set_bittiming(ndev); + ifi_canfd_set_filters(ndev); + + /* Reset FIFOs */ + writel(IFI_CANFD_RXSTCMD_RESET, priv->base + IFI_CANFD_RXSTCMD); + writel(0, priv->base + IFI_CANFD_RXSTCMD); + writel(IFI_CANFD_TXSTCMD_RESET, priv->base + IFI_CANFD_TXSTCMD); + writel(0, priv->base + IFI_CANFD_TXSTCMD); + + /* Repeat transmission until successful */ + writel(0, priv->base + IFI_CANFD_REPEAT); + writel(0, priv->base + IFI_CANFD_SUSPEND); + + /* Clear all pending interrupts */ + writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); + + stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + stcmd |= IFI_CANFD_STCMD_BUSMONITOR; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + stcmd |= IFI_CANFD_STCMD_LOOPBACK; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + stcmd |= IFI_CANFD_STCMD_ENABLE_ISO; + + if (!(priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO))) + stcmd |= IFI_CANFD_STCMD_DISABLE_CANFD; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + ifi_canfd_irq_enable(ndev, 1); + + /* Enable controller */ + writel(stcmd, priv->base + IFI_CANFD_STCMD); +} + +static void ifi_canfd_stop(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + + /* Disable all interrupts */ + ifi_canfd_irq_enable(ndev, 0); + + /* Clear all pending interrupts */ + writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); + + /* Set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int ifi_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + ifi_canfd_start(ndev); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ifi_canfd_open(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int err; + + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "Failed to open CAN device\n"); + return err; + } + + ifi_canfd_start(ndev); + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +} + +static int ifi_canfd_close(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + + ifi_canfd_stop(ndev); + + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + return 0; +} + +static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 txst, txid; + u32 txdlc = 0; + int i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + /* Check if the TX buffer is full */ + txst = readl(priv->base + IFI_CANFD_TXSTCMD); + if (txst & IFI_CANFD_TXSTCMD_FULL) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG! TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(ndev); + + if (cf->can_id & CAN_EFF_FLAG) { + txid = cf->can_id & CAN_EFF_MASK; + txid |= IFI_CANFD_TXFIFO_ID_IDE; + } else { + txid = cf->can_id & CAN_SFF_MASK; + } + + if (priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO)) { + if (can_is_canfd_skb(skb)) { + txdlc |= IFI_CANFD_TXFIFO_DLC_EDL; + if (cf->flags & CANFD_BRS) + txdlc |= IFI_CANFD_TXFIFO_DLC_BRS; + } + } + + if (cf->can_id & CAN_RTR_FLAG) + txdlc |= IFI_CANFD_TXFIFO_DLC_RTR; + + /* message ram configuration */ + writel(txid, priv->base + IFI_CANFD_TXFIFO_ID); + writel(txdlc, priv->base + IFI_CANFD_TXFIFO_DLC); + + for (i = 0; i < cf->len; i += 4) { + writel(*(u32 *)(cf->data + i), + priv->base + IFI_CANFD_TXFIFO_DATA + i); + } + + writel(0, priv->base + IFI_CANFD_TXFIFO_REPEATCOUNT); + writel(0, priv->base + IFI_CANFD_TXFIFO_SUSPEND_US); + + can_put_echo_skb(skb, ndev, 0); + + /* Start the transmission */ + writel(IFI_CANFD_TXSTCMD_ADD_MSG, priv->base + IFI_CANFD_TXSTCMD); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops ifi_canfd_netdev_ops = { + .ndo_open = ifi_canfd_open, + .ndo_stop = ifi_canfd_close, + .ndo_start_xmit = ifi_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int ifi_canfd_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct ifi_canfd_priv *priv; + struct resource *res; + void __iomem *addr; + int irq, ret; + u32 id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(dev, res); + irq = platform_get_irq(pdev, 0); + if (IS_ERR(addr) || irq < 0) + return -EINVAL; + + id = readl(addr + IFI_CANFD_IP_ID); + if (id != IFI_CANFD_IP_ID_VALUE) { + dev_err(dev, "This block is not IFI CANFD, id=%08x\n", id); + return -EINVAL; + } + + ndev = alloc_candev(sizeof(*priv), 1); + if (!ndev) + return -ENOMEM; + + ndev->irq = irq; + ndev->flags |= IFF_ECHO; /* we support local echo */ + ndev->netdev_ops = &ifi_canfd_netdev_ops; + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + priv->device = dev; + + netif_napi_add(ndev, &priv->napi, ifi_canfd_poll, 64); + + priv->can.state = CAN_STATE_STOPPED; + + priv->can.clock.freq = readl(addr + IFI_CANFD_SYSCLOCK); + + priv->can.bittiming_const = &ifi_canfd_bittiming_const; + priv->can.data_bittiming_const = &ifi_canfd_data_bittiming_const; + priv->can.do_set_mode = ifi_canfd_set_mode; + priv->can.do_get_berr_counter = ifi_canfd_get_berr_counter; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode = CAN_CTRLMODE_FD; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO; + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, dev); + + /* register interrupt handler */ + ret = request_irq(ndev->irq, ifi_canfd_isr, IRQF_SHARED, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "Failed to request interrupt\n"); + goto err_irq; + } + + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "Failed to register (ret=%d)\n", ret); + goto err_reg; + } + + devm_can_led_init(ndev); + + dev_info(dev, "Driver registered: regs=%p, irq=%d, clock=%d\n", + priv->base, ndev->irq, priv->can.clock.freq); + + return 0; + +err_reg: + free_irq(ndev->irq, ndev); +err_irq: + free_candev(ndev); + return ret; +} + +static int ifi_canfd_plat_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_candev(ndev); + platform_set_drvdata(pdev, NULL); + free_candev(ndev); + + return 0; +} + +static const struct of_device_id ifi_canfd_of_table[] = { + { .compatible = "ifi,canfd-1.0", .data = NULL }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ifi_canfd_of_table); + +static struct platform_driver ifi_canfd_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ifi_canfd_of_table, + }, + .probe = ifi_canfd_plat_probe, + .remove = ifi_canfd_plat_remove, +}; + +module_platform_driver(ifi_canfd_plat_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for IFI CANFD controller"); -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-11 18:48 ` [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support Marek Vasut @ 2016-01-11 19:29 ` kbuild test robot 2016-01-11 19:33 ` Marek Vasut 2016-01-12 11:06 ` kbuild test robot 2016-01-12 23:59 ` [PATCH V2 " Marek Vasut 2 siblings, 1 reply; 22+ messages in thread From: kbuild test robot @ 2016-01-11 19:29 UTC (permalink / raw) To: Marek Vasut Cc: kbuild-all, netdev, Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 2453 bytes --] Hi Marek, [auto build test WARNING on net/master] [also build test WARNING on v4.4 next-20160111] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/Marek-Vasut/net-can-Sort-the-Kconfig-includes/20160112-025020 config: x86_64-allmodconfig (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=x86_64 All warnings (new ones prefixed by >>): drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_isr': >> drivers/net/can/ifi_canfd/ifi_canfd.c:486:27: warning: large integer implicitly truncated to unsigned type [-Woverflow] const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | ^ drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_start': drivers/net/can/ifi_canfd/ifi_canfd.c:640:9: warning: large integer implicitly truncated to unsigned type [-Woverflow] writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); ^ drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_stop': drivers/net/can/ifi_canfd/ifi_canfd.c:675:9: warning: large integer implicitly truncated to unsigned type [-Woverflow] writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); ^ vim +486 drivers/net/can/ifi_canfd/ifi_canfd.c 470 napi_complete(napi); 471 ifi_canfd_irq_enable(ndev, 1); 472 } 473 474 return work_done; 475 } 476 477 static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) 478 { 479 struct net_device *ndev = (struct net_device *)dev_id; 480 struct ifi_canfd_priv *priv = netdev_priv(ndev); 481 struct net_device_stats *stats = &ndev->stats; 482 const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | 483 IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; 484 const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | 485 IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; > 486 const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | 487 IFI_CANFD_INTERRUPT_ERROR_WARNING); 488 u32 isr; 489 490 isr = readl(priv->base + IFI_CANFD_INTERRUPT); 491 492 /* No interrupt */ 493 if (isr == 0) 494 return IRQ_NONE; --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 51054 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-11 19:29 ` kbuild test robot @ 2016-01-11 19:33 ` Marek Vasut 0 siblings, 0 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-11 19:33 UTC (permalink / raw) To: kbuild test robot Cc: kbuild-all, netdev, Marc Kleine-Budde, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger On Monday, January 11, 2016 at 08:29:28 PM, kbuild test robot wrote: > Hi Marek, Hi Robot, > [auto build test WARNING on net/master] > [also build test WARNING on v4.4 next-20160111] > [if your patch is applied to the wrong git tree, please drop us a note to > help improving the system] > > url: > https://github.com/0day-ci/linux/commits/Marek-Vasut/net-can-Sort-the-Kcon > fig-includes/20160112-025020 config: x86_64-allmodconfig (attached as > .config) > reproduce: > # save the attached .config to linux build tree > make ARCH=x86_64 > > All warnings (new ones prefixed by >>): > > drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_isr': > >> drivers/net/can/ifi_canfd/ifi_canfd.c:486:27: warning: large integer > >> implicitly truncated to unsigned type [-Woverflow] > > const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | > ^ > drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_start': > drivers/net/can/ifi_canfd/ifi_canfd.c:640:9: warning: large integer > implicitly truncated to unsigned type [-Woverflow] > writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); ^ > drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_stop': > drivers/net/can/ifi_canfd/ifi_canfd.c:675:9: warning: large integer > implicitly truncated to unsigned type [-Woverflow] > writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); ^ How did this splat came to be ? I would expect that the expansion goes like this: const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | IFI_CANFD_INTERRUPT_ERROR_WARNING); const u32 clr_irq_mask = ~(BIT(31) | BIT(1)); const u32 clr_irq_mask = ~(0x80000002); const u32 clr_irq_mask = 0x7ffffffd; Did I miss something there ? Thanks! Best regards, Marek Vasut ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-11 18:48 ` [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support Marek Vasut 2016-01-11 19:29 ` kbuild test robot @ 2016-01-12 11:06 ` kbuild test robot 2016-01-12 23:59 ` [PATCH V2 " Marek Vasut 2 siblings, 0 replies; 22+ messages in thread From: kbuild test robot @ 2016-01-12 11:06 UTC (permalink / raw) To: Marek Vasut Cc: kbuild-all, netdev, Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 3361 bytes --] Hi Marek, [auto build test WARNING on net/master] [also build test WARNING on v4.4 next-20160111] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/Marek-Vasut/net-can-Sort-the-Kconfig-includes/20160112-025020 config: ia64-allmodconfig (attached as .config) reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=ia64 All warnings (new ones prefixed by >>): drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_isr': drivers/net/can/ifi_canfd/ifi_canfd.c:486:27: warning: large integer implicitly truncated to unsigned type [-Woverflow] const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | ^ In file included from arch/ia64/include/asm/smp.h:20:0, from include/linux/smp.h:59, from include/linux/sched.h:34, from arch/ia64/include/asm/delay.h:16, from include/linux/delay.h:14, from drivers/net/can/ifi_canfd/ifi_canfd.c:15: drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_start': arch/ia64/include/asm/io.h:394:30: warning: large integer implicitly truncated to unsigned type [-Woverflow] #define writel(v,a) __writel((v), (a)) ^ >> drivers/net/can/ifi_canfd/ifi_canfd.c:640:2: note: in expansion of macro 'writel' writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); ^ drivers/net/can/ifi_canfd/ifi_canfd.c: In function 'ifi_canfd_stop': arch/ia64/include/asm/io.h:394:30: warning: large integer implicitly truncated to unsigned type [-Woverflow] #define writel(v,a) __writel((v), (a)) ^ drivers/net/can/ifi_canfd/ifi_canfd.c:675:2: note: in expansion of macro 'writel' writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); ^ vim +/writel +640 drivers/net/can/ifi_canfd/ifi_canfd.c 624 writel(0, priv->base + IFI_CANFD_STCMD); 625 626 ifi_canfd_set_bittiming(ndev); 627 ifi_canfd_set_filters(ndev); 628 629 /* Reset FIFOs */ 630 writel(IFI_CANFD_RXSTCMD_RESET, priv->base + IFI_CANFD_RXSTCMD); 631 writel(0, priv->base + IFI_CANFD_RXSTCMD); 632 writel(IFI_CANFD_TXSTCMD_RESET, priv->base + IFI_CANFD_TXSTCMD); 633 writel(0, priv->base + IFI_CANFD_TXSTCMD); 634 635 /* Repeat transmission until successful */ 636 writel(0, priv->base + IFI_CANFD_REPEAT); 637 writel(0, priv->base + IFI_CANFD_SUSPEND); 638 639 /* Clear all pending interrupts */ > 640 writel(~IFI_CANFD_INTERRUPT_SET_IRQ, priv->base + IFI_CANFD_INTERRUPT); 641 642 stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE; 643 644 if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) 645 stcmd |= IFI_CANFD_STCMD_BUSMONITOR; 646 647 if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 648 stcmd |= IFI_CANFD_STCMD_LOOPBACK; --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 41724 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-11 18:48 ` [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support Marek Vasut 2016-01-11 19:29 ` kbuild test robot 2016-01-12 11:06 ` kbuild test robot @ 2016-01-12 23:59 ` Marek Vasut 2016-01-20 10:39 ` Marc Kleine-Budde 2 siblings, 1 reply; 22+ messages in thread From: Marek Vasut @ 2016-01-12 23:59 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger The patch adds support for IFI CAN/FD controller [1]. This driver currently supports sending and receiving both standard CAN and new CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. [1] http://www.ifi-pld.de/IP/CANFD/canfd.html Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> --- V2: - Move request_irq()/free_irq() into ifi_canfd_open()/ifi_canfd_close() just like other drivers do it to prevent crash when reloading module. - Fix Woverflow complains on x86_64 and itanium, exactly the same way as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . NOTE: The driver is surprisingly similar to m_can, but the register layout of the IFI core is completely different, so it's clear that those are two different IP cores. --- drivers/net/can/Kconfig | 2 + drivers/net/can/Makefile | 1 + drivers/net/can/ifi_canfd/Kconfig | 8 + drivers/net/can/ifi_canfd/Makefile | 5 + drivers/net/can/ifi_canfd/ifi_canfd.c | 919 ++++++++++++++++++++++++++++++++++ 5 files changed, 935 insertions(+) create mode 100644 drivers/net/can/ifi_canfd/Kconfig create mode 100644 drivers/net/can/ifi_canfd/Makefile create mode 100644 drivers/net/can/ifi_canfd/ifi_canfd.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index ca49d15..20be638 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -151,6 +151,8 @@ source "drivers/net/can/c_can/Kconfig" source "drivers/net/can/cc770/Kconfig" +source "drivers/net/can/ifi_canfd/Kconfig" + source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 4f85c2b..e3db0c8 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_CAN_CC770) += cc770/ obj-$(CONFIG_CAN_C_CAN) += c_can/ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ diff --git a/drivers/net/can/ifi_canfd/Kconfig b/drivers/net/can/ifi_canfd/Kconfig new file mode 100644 index 0000000..9e8934f --- /dev/null +++ b/drivers/net/can/ifi_canfd/Kconfig @@ -0,0 +1,8 @@ +config CAN_IFI_CANFD + depends on HAS_IOMEM + tristate "IFI CAN_FD IP" + ---help--- + This driver adds support for the I/F/I CAN_FD soft IP block + connected to the "platform bus" (Linux abstraction for directly + to the processor attached devices). The CAN_FD is most often + synthesised into an FPGA or CPLD. diff --git a/drivers/net/can/ifi_canfd/Makefile b/drivers/net/can/ifi_canfd/Makefile new file mode 100644 index 0000000..b229960 --- /dev/null +++ b/drivers/net/can/ifi_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the IFI CANFD controller driver. +# + +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd.o diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c new file mode 100644 index 0000000..094c36b --- /dev/null +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -0,0 +1,919 @@ +/* + * CAN bus driver for IFI CANFD controller + * + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * Details about this controller can be found at + * http://www.ifi-pld.de/IP/CANFD/canfd.html + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <linux/can/dev.h> + +#define IFI_CANFD_STCMD 0x0 +#define IFI_CANFD_STCMD_HARDRESET 0xDEADCAFD +#define IFI_CANFD_STCMD_ENABLE BIT(0) +#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) +#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) +#define IFI_CANFD_STCMD_BUSOFF BIT(4) +#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) +#define IFI_CANFD_STCMD_LOOPBACK BIT(18) +#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) +#define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) +#define IFI_CANFD_STCMD_NORMAL_MODE BIT(31) + +#define IFI_CANFD_RXSTCMD 0x4 +#define IFI_CANFD_RXSTCMD_REMOVE_MSG BIT(0) +#define IFI_CANFD_RXSTCMD_RESET BIT(7) +#define IFI_CANFD_RXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_RXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_TXSTCMD 0x8 +#define IFI_CANFD_TXSTCMD_ADD_MSG BIT(0) +#define IFI_CANFD_TXSTCMD_HIGH_PRIO BIT(1) +#define IFI_CANFD_TXSTCMD_RESET BIT(7) +#define IFI_CANFD_TXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_TXSTCMD_FULL BIT(12) +#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_INTERRUPT 0xc +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) +#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER BIT(25) +#define IFI_CANFD_INTERRUPT_SET_IRQ BIT(31) + +#define IFI_CANFD_IRQMASK 0x10 +#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) +#define IFI_CANFD_IRQMASK_SET_TS BIT(15) +#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_IRQMASK_SET_TX BIT(23) +#define IFI_CANFD_IRQMASK_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_IRQMASK_SET_RX BIT(31) + +#define IFI_CANFD_TIME 0x14 +#define IFI_CANFD_FTIME 0x18 +#define IFI_CANFD_TIME_TIMEB_OFF 0 +#define IFI_CANFD_TIME_TIMEA_OFF 8 +#define IFI_CANFD_TIME_PRESCALE_OFF 16 +#define IFI_CANFD_TIME_SJW_OFF_ISO 25 +#define IFI_CANFD_TIME_SJW_OFF_BOSCH 28 +#define IFI_CANFD_TIME_SET_SJW_BOSCH BIT(6) +#define IFI_CANFD_TIME_SET_TIMEB_BOSCH BIT(7) +#define IFI_CANFD_TIME_SET_PRESC_BOSCH BIT(14) +#define IFI_CANFD_TIME_SET_TIMEA_BOSCH BIT(15) + +#define IFI_CANFD_TDELAY 0x1c + +#define IFI_CANFD_ERROR 0x20 +#define IFI_CANFD_ERROR_TX_OFFSET 0 +#define IFI_CANFD_ERROR_TX_MASK 0xff +#define IFI_CANFD_ERROR_RX_OFFSET 16 +#define IFI_CANFD_ERROR_RX_MASK 0xff + +#define IFI_CANFD_ERRCNT 0x24 + +#define IFI_CANFD_SUSPEND 0x28 + +#define IFI_CANFD_REPEAT 0x2c + +#define IFI_CANFD_TRAFFIC 0x30 + +#define IFI_CANFD_TSCONTROL 0x34 + +#define IFI_CANFD_TSC 0x38 + +#define IFI_CANFD_TST 0x3c + +#define IFI_CANFD_RES1 0x40 + +#define IFI_CANFD_RES2 0x44 + +#define IFI_CANFD_PAR 0x48 + +#define IFI_CANFD_CANCLOCK 0x4c + +#define IFI_CANFD_SYSCLOCK 0x50 + +#define IFI_CANFD_VER 0x54 + +#define IFI_CANFD_IP_ID 0x58 +#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD + +#define IFI_CANFD_TEST 0x5c + +#define IFI_CANFD_RXFIFO_TS_63_32 0x60 + +#define IFI_CANFD_RXFIFO_TS_31_0 0x64 + +#define IFI_CANFD_RXFIFO_DLC 0x68 +#define IFI_CANFD_RXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_RXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_RXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_RXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_RXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_RXFIFO_DLC_ESI BIT(7) +#define IFI_CANFD_RXFIFO_DLC_OBJ_OFFSET 8 +#define IFI_CANFD_RXFIFO_DLC_OBJ_MASK 0x1ff +#define IFI_CANFD_RXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_RXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_RXFIFO_ID 0x6c +#define IFI_CANFD_RXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_RXFIFO_ID_ID_STD_MASK 0x3ff +#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK 0x1fffffff +#define IFI_CANFD_RXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_RXFIFO_DATA 0x70 /* 0x70..0xac */ + +#define IFI_CANFD_TXFIFO_SUSPEND_US 0xb0 + +#define IFI_CANFD_TXFIFO_REPEATCOUNT 0xb4 + +#define IFI_CANFD_TXFIFO_DLC 0xb8 +#define IFI_CANFD_TXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_TXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_TXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_TXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_TXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_TXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_TXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_TXFIFO_ID 0xbc +#define IFI_CANFD_TXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_TXFIFO_ID_ID_STD_MASK 0x3ff +#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK 0x1fffffff +#define IFI_CANFD_TXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_TXFIFO_DATA 0xc0 /* 0xb0..0xfc */ + +#define IFI_CANFD_FILTER_MASK(n) (0x800 + ((n) * 8) + 0) +#define IFI_CANFD_FILTER_MASK_EXT BIT(29) +#define IFI_CANFD_FILTER_MASK_EDL BIT(30) +#define IFI_CANFD_FILTER_MASK_VALID BIT(31) + +#define IFI_CANFD_FILTER_IDENT(n) (0x800 + ((n) * 8) + 4) +#define IFI_CANFD_FILTER_IDENT_IDE BIT(29) +#define IFI_CANFD_FILTER_IDENT_CANFD BIT(30) +#define IFI_CANFD_FILTER_IDENT_VALID BIT(31) + +/* IFI CANFD private data structure */ +struct ifi_canfd_priv { + struct can_priv can; /* must be the first member */ + struct napi_struct napi; + struct net_device *ndev; + struct device *device; + void __iomem *base; +}; + +static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 enirq = 0; + + if (enable) { + enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | + IFI_CANFD_IRQMASK_RXFIFO_NEMPTY; + } + + writel(IFI_CANFD_IRQMASK_SET_ERR | + IFI_CANFD_IRQMASK_SET_TS | + IFI_CANFD_IRQMASK_SET_TX | + IFI_CANFD_IRQMASK_SET_RX | enirq, + priv->base + IFI_CANFD_IRQMASK); +} + +static void ifi_canfd_read_fifo(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf; + struct sk_buff *skb; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + u32 rxdlc, rxid; + u32 dlc, id; + int i; + + rxdlc = readl(priv->base + IFI_CANFD_RXFIFO_DLC); + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + + if (!skb) { + stats->rx_dropped++; + return; + } + + dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) & + IFI_CANFD_RXFIFO_DLC_DLC_MASK; + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + cf->len = can_dlc2len(dlc); + else + cf->len = get_can_dlc(dlc); + + rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID); + id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET); + if (id & IFI_CANFD_RXFIFO_ID_IDE) + id &= IFI_CANFD_RXFIFO_ID_ID_XTD_MASK; + else + id &= IFI_CANFD_RXFIFO_ID_ID_STD_MASK; + cf->can_id = id; + + if (rxdlc & IFI_CANFD_RXFIFO_DLC_ESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(ndev, "ESI Error\n"); + } + + if (!(rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) && + (rxdlc & IFI_CANFD_RXFIFO_DLC_RTR)) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if (rxdlc & IFI_CANFD_RXFIFO_DLC_BRS) + cf->flags |= CANFD_BRS; + + for (i = 0; i < cf->len; i += 4) { + *(u32 *)(cf->data + i) = + readl(priv->base + IFI_CANFD_RXFIFO_DATA + i); + } + } + + /* Remove the packet from FIFO */ + writel(IFI_CANFD_RXSTCMD_REMOVE_MSG, priv->base + IFI_CANFD_RXSTCMD); + writel(rx_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_receive_skb(skb); +} + +static int ifi_canfd_do_rx_poll(struct net_device *ndev, int quota) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 pkts = 0; + u32 rxst; + + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) { + netdev_dbg(ndev, "No messages in RX FIFO\n"); + return 0; + } + + for (;;) { + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) + break; + if (quota <= 0) + break; + + ifi_canfd_read_fifo(ndev); + quota--; + pkts++; + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + } + + if (pkts) + can_led_event(ndev, CAN_LED_EVENT_RX); + + return pkts; +} + +static int ifi_canfd_handle_lost_msg(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(ndev, "RX FIFO overflow, message(s) lost.\n"); + + stats->rx_errors++; + stats->rx_over_errors++; + + skb = alloc_can_err_skb(ndev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 err; + + err = readl(priv->base + IFI_CANFD_ERROR); + bec->rxerr = (err >> IFI_CANFD_ERROR_RX_OFFSET) & + IFI_CANFD_ERROR_RX_MASK; + bec->txerr = (err >> IFI_CANFD_ERROR_TX_OFFSET) & + IFI_CANFD_ERROR_TX_MASK; + + return 0; +} + +static int ifi_canfd_handle_state_change(struct net_device *ndev, + enum can_state new_state) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + ifi_canfd_irq_enable(ndev, 0); + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + break; + default: + break; + } + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (unlikely(!skb)) + return 0; + + ifi_canfd_get_berr_counter(ndev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if (bec.txerr > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + cf->can_id |= CAN_ERR_BUSOFF; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int work_done = 0; + u32 isr; + + /* + * The ErrWarn condition is a little special, since the bit is + * located in the INTERRUPT register instead of STCMD register. + */ + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + /* Clear the interrupt */ + writel(IFI_CANFD_INTERRUPT_ERROR_WARNING, + priv->base + IFI_CANFD_INTERRUPT); + netdev_dbg(ndev, "Error, entered warning state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_WARNING); + } + + if ((stcmd & IFI_CANFD_STCMD_ERROR_PASSIVE) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(ndev, "Error, entered passive state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((stcmd & IFI_CANFD_STCMD_BUSOFF) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(ndev, "Error, entered bus-off state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_BUS_OFF); + } + + return work_done; +} + +static int ifi_canfd_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE | + IFI_CANFD_STCMD_BUSOFF; + int work_done = 0; + + u32 stcmd = readl(priv->base + IFI_CANFD_STCMD); + u32 rxstcmd = readl(priv->base + IFI_CANFD_STCMD); + + /* Handle bus state changes */ + if ((stcmd & stcmd_state_mask) || + ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0)) + work_done += ifi_canfd_handle_state_errors(ndev, stcmd); + + /* Handle lost messages on RX */ + if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) + work_done += ifi_canfd_handle_lost_msg(ndev); + + /* Handle normal messages on RX */ + if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) + work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); + + if (work_done < quota) { + napi_complete(napi); + ifi_canfd_irq_enable(ndev, 1); + } + + return work_done; +} + +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; + const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | + IFI_CANFD_INTERRUPT_ERROR_WARNING)); + u32 isr; + + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + + /* No interrupt */ + if (isr == 0) + return IRQ_NONE; + + /* Clear all pending interrupts but ErrWarn */ + writel(clr_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + /* RX IRQ, start NAPI */ + if (isr & rx_irq_mask) { + ifi_canfd_irq_enable(ndev, 0); + napi_schedule(&priv->napi); + } + + /* TX IRQ */ + if (isr & tx_irq_mask) { + stats->tx_bytes += can_get_echo_skb(ndev, 0); + stats->tx_packets++; + can_led_event(ndev, CAN_LED_EVENT_TX); + netif_wake_queue(ndev); + } + + return IRQ_HANDLED; +} + +static const struct can_bittiming_const ifi_canfd_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 64, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +static const struct can_bittiming_const ifi_canfd_data_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + +static void ifi_canfd_set_bittiming(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 noniso_arg = 0; + u32 time_off; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) { + noniso_arg = IFI_CANFD_TIME_SET_TIMEB_BOSCH | + IFI_CANFD_TIME_SET_TIMEA_BOSCH | + IFI_CANFD_TIME_SET_PRESC_BOSCH | + IFI_CANFD_TIME_SET_SJW_BOSCH; + time_off = IFI_CANFD_TIME_SJW_OFF_BOSCH; + } else { + time_off = IFI_CANFD_TIME_SJW_OFF_ISO; + } + + /* Configure bit timing */ + brp = bt->brp - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << time_off), + priv->base + IFI_CANFD_TIME); + + /* Configure data bit timing */ + brp = dbt->brp - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << time_off) | + noniso_arg, + priv->base + IFI_CANFD_FTIME); +} + +static void ifi_canfd_set_filter(struct net_device *ndev, const u32 id, + const u32 mask, const u32 ident) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + writel(mask, priv->base + IFI_CANFD_FILTER_MASK(id)); + writel(ident, priv->base + IFI_CANFD_FILTER_IDENT(id)); +} + +static void ifi_canfd_set_filters(struct net_device *ndev) +{ + /* Receive all CAN frames (standard ID) */ + ifi_canfd_set_filter(ndev, 0, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID); + + /* Receive all CAN frames (extended ID) */ + ifi_canfd_set_filter(ndev, 1, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_IDE); + + /* Receive all CANFD frames */ + ifi_canfd_set_filter(ndev, 2, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EDL | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_CANFD | + IFI_CANFD_FILTER_IDENT_IDE); +} + +static void ifi_canfd_start(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 stcmd; + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + writel(0, priv->base + IFI_CANFD_STCMD); + + ifi_canfd_set_bittiming(ndev); + ifi_canfd_set_filters(ndev); + + /* Reset FIFOs */ + writel(IFI_CANFD_RXSTCMD_RESET, priv->base + IFI_CANFD_RXSTCMD); + writel(0, priv->base + IFI_CANFD_RXSTCMD); + writel(IFI_CANFD_TXSTCMD_RESET, priv->base + IFI_CANFD_TXSTCMD); + writel(0, priv->base + IFI_CANFD_TXSTCMD); + + /* Repeat transmission until successful */ + writel(0, priv->base + IFI_CANFD_REPEAT); + writel(0, priv->base + IFI_CANFD_SUSPEND); + + /* Clear all pending interrupts */ + writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), + priv->base + IFI_CANFD_INTERRUPT); + + stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + stcmd |= IFI_CANFD_STCMD_BUSMONITOR; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + stcmd |= IFI_CANFD_STCMD_LOOPBACK; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + stcmd |= IFI_CANFD_STCMD_ENABLE_ISO; + + if (!(priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO))) + stcmd |= IFI_CANFD_STCMD_DISABLE_CANFD; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + ifi_canfd_irq_enable(ndev, 1); + + /* Enable controller */ + writel(stcmd, priv->base + IFI_CANFD_STCMD); +} + +static void ifi_canfd_stop(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + + /* Mask all interrupts */ + writel(~0, priv->base + IFI_CANFD_IRQMASK); + + /* Clear all pending interrupts */ + writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), + priv->base + IFI_CANFD_INTERRUPT); + + /* Set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int ifi_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + ifi_canfd_start(ndev); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ifi_canfd_open(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int ret; + + ret = open_candev(ndev); + if (ret) { + netdev_err(ndev, "Failed to open CAN device\n"); + return ret; + } + + /* Register interrupt handler */ + ret = request_irq(ndev->irq, ifi_canfd_isr, IRQF_SHARED, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "Failed to request interrupt\n"); + goto err_irq; + } + + ifi_canfd_start(ndev); + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +err_irq: + close_candev(ndev); + return ret; +} + +static int ifi_canfd_close(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + + ifi_canfd_stop(ndev); + + free_irq(ndev->irq, ndev); + + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + return 0; +} + +static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 txst, txid; + u32 txdlc = 0; + int i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + /* Check if the TX buffer is full */ + txst = readl(priv->base + IFI_CANFD_TXSTCMD); + if (txst & IFI_CANFD_TXSTCMD_FULL) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG! TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(ndev); + + if (cf->can_id & CAN_EFF_FLAG) { + txid = cf->can_id & CAN_EFF_MASK; + txid |= IFI_CANFD_TXFIFO_ID_IDE; + } else { + txid = cf->can_id & CAN_SFF_MASK; + } + + if (priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO)) { + if (can_is_canfd_skb(skb)) { + txdlc |= IFI_CANFD_TXFIFO_DLC_EDL; + if (cf->flags & CANFD_BRS) + txdlc |= IFI_CANFD_TXFIFO_DLC_BRS; + } + } + + if (cf->can_id & CAN_RTR_FLAG) + txdlc |= IFI_CANFD_TXFIFO_DLC_RTR; + + /* message ram configuration */ + writel(txid, priv->base + IFI_CANFD_TXFIFO_ID); + writel(txdlc, priv->base + IFI_CANFD_TXFIFO_DLC); + + for (i = 0; i < cf->len; i += 4) { + writel(*(u32 *)(cf->data + i), + priv->base + IFI_CANFD_TXFIFO_DATA + i); + } + + writel(0, priv->base + IFI_CANFD_TXFIFO_REPEATCOUNT); + writel(0, priv->base + IFI_CANFD_TXFIFO_SUSPEND_US); + + can_put_echo_skb(skb, ndev, 0); + + /* Start the transmission */ + writel(IFI_CANFD_TXSTCMD_ADD_MSG, priv->base + IFI_CANFD_TXSTCMD); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops ifi_canfd_netdev_ops = { + .ndo_open = ifi_canfd_open, + .ndo_stop = ifi_canfd_close, + .ndo_start_xmit = ifi_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int ifi_canfd_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct ifi_canfd_priv *priv; + struct resource *res; + void __iomem *addr; + int irq, ret; + u32 id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(dev, res); + irq = platform_get_irq(pdev, 0); + if (IS_ERR(addr) || irq < 0) + return -EINVAL; + + id = readl(addr + IFI_CANFD_IP_ID); + if (id != IFI_CANFD_IP_ID_VALUE) { + dev_err(dev, "This block is not IFI CANFD, id=%08x\n", id); + return -EINVAL; + } + + ndev = alloc_candev(sizeof(*priv), 1); + if (!ndev) + return -ENOMEM; + + ndev->irq = irq; + ndev->flags |= IFF_ECHO; /* we support local echo */ + ndev->netdev_ops = &ifi_canfd_netdev_ops; + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + priv->device = dev; + + netif_napi_add(ndev, &priv->napi, ifi_canfd_poll, 64); + + priv->can.state = CAN_STATE_STOPPED; + + priv->can.clock.freq = readl(addr + IFI_CANFD_SYSCLOCK); + + priv->can.bittiming_const = &ifi_canfd_bittiming_const; + priv->can.data_bittiming_const = &ifi_canfd_data_bittiming_const; + priv->can.do_set_mode = ifi_canfd_set_mode; + priv->can.do_get_berr_counter = ifi_canfd_get_berr_counter; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode = CAN_CTRLMODE_FD; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO; + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, dev); + + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "Failed to register (ret=%d)\n", ret); + goto err_reg; + } + + devm_can_led_init(ndev); + + dev_info(dev, "Driver registered: regs=%p, irq=%d, clock=%d\n", + priv->base, ndev->irq, priv->can.clock.freq); + + return 0; + +err_reg: + free_candev(ndev); + return ret; +} + +static int ifi_canfd_plat_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_candev(ndev); + platform_set_drvdata(pdev, NULL); + free_candev(ndev); + + return 0; +} + +static const struct of_device_id ifi_canfd_of_table[] = { + { .compatible = "ifi,canfd-1.0", .data = NULL }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ifi_canfd_of_table); + +static struct platform_driver ifi_canfd_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ifi_canfd_of_table, + }, + .probe = ifi_canfd_plat_probe, + .remove = ifi_canfd_plat_remove, +}; + +module_platform_driver(ifi_canfd_plat_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for IFI CANFD controller"); -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-12 23:59 ` [PATCH V2 " Marek Vasut @ 2016-01-20 10:39 ` Marc Kleine-Budde 2016-01-20 10:54 ` Marc Kleine-Budde 0 siblings, 1 reply; 22+ messages in thread From: Marc Kleine-Budde @ 2016-01-20 10:39 UTC (permalink / raw) To: Marek Vasut, netdev; +Cc: Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 10351 bytes --] On 01/13/2016 12:59 AM, Marek Vasut wrote: > The patch adds support for IFI CAN/FD controller [1]. This driver > currently supports sending and receiving both standard CAN and new > CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. > > [1] http://www.ifi-pld.de/IP/CANFD/canfd.html > > Signed-off-by: Marek Vasut <marex@denx.de> > Cc: Marc Kleine-Budde <mkl@pengutronix.de> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: Oliver Hartkopp <socketcan@hartkopp.net> > Cc: Wolfgang Grandegger <wg@grandegger.com> > --- > V2: - Move request_irq()/free_irq() into ifi_canfd_open()/ifi_canfd_close() > just like other drivers do it to prevent crash when reloading module. > - Fix Woverflow complains on x86_64 and itanium, exactly the same way > as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . > > NOTE: The driver is surprisingly similar to m_can, but the register > layout of the IFI core is completely different, so it's clear > that those are two different IP cores. > --- > drivers/net/can/Kconfig | 2 + > drivers/net/can/Makefile | 1 + > drivers/net/can/ifi_canfd/Kconfig | 8 + > drivers/net/can/ifi_canfd/Makefile | 5 + > drivers/net/can/ifi_canfd/ifi_canfd.c | 919 ++++++++++++++++++++++++++++++++++ > 5 files changed, 935 insertions(+) > create mode 100644 drivers/net/can/ifi_canfd/Kconfig > create mode 100644 drivers/net/can/ifi_canfd/Makefile > create mode 100644 drivers/net/can/ifi_canfd/ifi_canfd.c > > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig > index ca49d15..20be638 100644 > --- a/drivers/net/can/Kconfig > +++ b/drivers/net/can/Kconfig > @@ -151,6 +151,8 @@ source "drivers/net/can/c_can/Kconfig" > > source "drivers/net/can/cc770/Kconfig" > > +source "drivers/net/can/ifi_canfd/Kconfig" > + > source "drivers/net/can/m_can/Kconfig" > > source "drivers/net/can/mscan/Kconfig" > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile > index 4f85c2b..e3db0c8 100644 > --- a/drivers/net/can/Makefile > +++ b/drivers/net/can/Makefile > @@ -20,6 +20,7 @@ obj-$(CONFIG_CAN_CC770) += cc770/ > obj-$(CONFIG_CAN_C_CAN) += c_can/ > obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o > obj-$(CONFIG_CAN_GRCAN) += grcan.o > +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ > obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o > obj-$(CONFIG_CAN_MSCAN) += mscan/ > obj-$(CONFIG_CAN_M_CAN) += m_can/ > diff --git a/drivers/net/can/ifi_canfd/Kconfig b/drivers/net/can/ifi_canfd/Kconfig > new file mode 100644 > index 0000000..9e8934f > --- /dev/null > +++ b/drivers/net/can/ifi_canfd/Kconfig > @@ -0,0 +1,8 @@ > +config CAN_IFI_CANFD > + depends on HAS_IOMEM > + tristate "IFI CAN_FD IP" > + ---help--- > + This driver adds support for the I/F/I CAN_FD soft IP block > + connected to the "platform bus" (Linux abstraction for directly > + to the processor attached devices). The CAN_FD is most often > + synthesised into an FPGA or CPLD. > diff --git a/drivers/net/can/ifi_canfd/Makefile b/drivers/net/can/ifi_canfd/Makefile > new file mode 100644 > index 0000000..b229960 > --- /dev/null > +++ b/drivers/net/can/ifi_canfd/Makefile > @@ -0,0 +1,5 @@ > +# > +# Makefile for the IFI CANFD controller driver. > +# > + > +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd.o > diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c > new file mode 100644 > index 0000000..094c36b > --- /dev/null > +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c > @@ -0,0 +1,919 @@ > +/* > + * CAN bus driver for IFI CANFD controller > + * > + * Copyright (C) 2016 Marek Vasut <marex@denx.de> > + * > + * Details about this controller can be found at > + * http://www.ifi-pld.de/IP/CANFD/canfd.html > + * > + * This file is licensed under the terms of the GNU General Public > + * License version 2. This program is licensed "as is" without any > + * warranty of any kind, whether express or implied. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/netdevice.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/platform_device.h> > + > +#include <linux/can/dev.h> > + > +#define IFI_CANFD_STCMD 0x0 > +#define IFI_CANFD_STCMD_HARDRESET 0xDEADCAFD > +#define IFI_CANFD_STCMD_ENABLE BIT(0) > +#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) > +#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) > +#define IFI_CANFD_STCMD_BUSOFF BIT(4) > +#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) > +#define IFI_CANFD_STCMD_LOOPBACK BIT(18) > +#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) > +#define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) > +#define IFI_CANFD_STCMD_NORMAL_MODE BIT(31) > + > +#define IFI_CANFD_RXSTCMD 0x4 > +#define IFI_CANFD_RXSTCMD_REMOVE_MSG BIT(0) > +#define IFI_CANFD_RXSTCMD_RESET BIT(7) > +#define IFI_CANFD_RXSTCMD_EMPTY BIT(8) > +#define IFI_CANFD_RXSTCMD_OVERFLOW BIT(13) > + > +#define IFI_CANFD_TXSTCMD 0x8 > +#define IFI_CANFD_TXSTCMD_ADD_MSG BIT(0) > +#define IFI_CANFD_TXSTCMD_HIGH_PRIO BIT(1) > +#define IFI_CANFD_TXSTCMD_RESET BIT(7) > +#define IFI_CANFD_TXSTCMD_EMPTY BIT(8) > +#define IFI_CANFD_TXSTCMD_FULL BIT(12) > +#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) > + > +#define IFI_CANFD_INTERRUPT 0xc > +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) > +#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) > +#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) > +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) > +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER BIT(25) > +#define IFI_CANFD_INTERRUPT_SET_IRQ BIT(31) > + > +#define IFI_CANFD_IRQMASK 0x10 > +#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) > +#define IFI_CANFD_IRQMASK_SET_TS BIT(15) > +#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) > +#define IFI_CANFD_IRQMASK_SET_TX BIT(23) > +#define IFI_CANFD_IRQMASK_RXFIFO_NEMPTY BIT(24) > +#define IFI_CANFD_IRQMASK_SET_RX BIT(31) > + > +#define IFI_CANFD_TIME 0x14 > +#define IFI_CANFD_FTIME 0x18 > +#define IFI_CANFD_TIME_TIMEB_OFF 0 > +#define IFI_CANFD_TIME_TIMEA_OFF 8 > +#define IFI_CANFD_TIME_PRESCALE_OFF 16 > +#define IFI_CANFD_TIME_SJW_OFF_ISO 25 > +#define IFI_CANFD_TIME_SJW_OFF_BOSCH 28 > +#define IFI_CANFD_TIME_SET_SJW_BOSCH BIT(6) > +#define IFI_CANFD_TIME_SET_TIMEB_BOSCH BIT(7) > +#define IFI_CANFD_TIME_SET_PRESC_BOSCH BIT(14) > +#define IFI_CANFD_TIME_SET_TIMEA_BOSCH BIT(15) > + > +#define IFI_CANFD_TDELAY 0x1c > + > +#define IFI_CANFD_ERROR 0x20 > +#define IFI_CANFD_ERROR_TX_OFFSET 0 > +#define IFI_CANFD_ERROR_TX_MASK 0xff > +#define IFI_CANFD_ERROR_RX_OFFSET 16 > +#define IFI_CANFD_ERROR_RX_MASK 0xff > + > +#define IFI_CANFD_ERRCNT 0x24 > + > +#define IFI_CANFD_SUSPEND 0x28 > + > +#define IFI_CANFD_REPEAT 0x2c > + > +#define IFI_CANFD_TRAFFIC 0x30 > + > +#define IFI_CANFD_TSCONTROL 0x34 > + > +#define IFI_CANFD_TSC 0x38 > + > +#define IFI_CANFD_TST 0x3c > + > +#define IFI_CANFD_RES1 0x40 > + > +#define IFI_CANFD_RES2 0x44 > + > +#define IFI_CANFD_PAR 0x48 > + > +#define IFI_CANFD_CANCLOCK 0x4c > + > +#define IFI_CANFD_SYSCLOCK 0x50 > + > +#define IFI_CANFD_VER 0x54 > + > +#define IFI_CANFD_IP_ID 0x58 > +#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD > + > +#define IFI_CANFD_TEST 0x5c > + > +#define IFI_CANFD_RXFIFO_TS_63_32 0x60 > + > +#define IFI_CANFD_RXFIFO_TS_31_0 0x64 > + > +#define IFI_CANFD_RXFIFO_DLC 0x68 > +#define IFI_CANFD_RXFIFO_DLC_DLC_OFFSET 0 > +#define IFI_CANFD_RXFIFO_DLC_DLC_MASK 0xf > +#define IFI_CANFD_RXFIFO_DLC_RTR BIT(4) > +#define IFI_CANFD_RXFIFO_DLC_EDL BIT(5) > +#define IFI_CANFD_RXFIFO_DLC_BRS BIT(6) > +#define IFI_CANFD_RXFIFO_DLC_ESI BIT(7) > +#define IFI_CANFD_RXFIFO_DLC_OBJ_OFFSET 8 > +#define IFI_CANFD_RXFIFO_DLC_OBJ_MASK 0x1ff > +#define IFI_CANFD_RXFIFO_DLC_FNR_OFFSET 24 > +#define IFI_CANFD_RXFIFO_DLC_FNR_MASK 0xff > + > +#define IFI_CANFD_RXFIFO_ID 0x6c > +#define IFI_CANFD_RXFIFO_ID_ID_OFFSET 0 > +#define IFI_CANFD_RXFIFO_ID_ID_STD_MASK 0x3ff > +#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK 0x1fffffff > +#define IFI_CANFD_RXFIFO_ID_IDE BIT(29) > + > +#define IFI_CANFD_RXFIFO_DATA 0x70 /* 0x70..0xac */ > + > +#define IFI_CANFD_TXFIFO_SUSPEND_US 0xb0 > + > +#define IFI_CANFD_TXFIFO_REPEATCOUNT 0xb4 > + > +#define IFI_CANFD_TXFIFO_DLC 0xb8 > +#define IFI_CANFD_TXFIFO_DLC_DLC_OFFSET 0 > +#define IFI_CANFD_TXFIFO_DLC_DLC_MASK 0xf > +#define IFI_CANFD_TXFIFO_DLC_RTR BIT(4) > +#define IFI_CANFD_TXFIFO_DLC_EDL BIT(5) > +#define IFI_CANFD_TXFIFO_DLC_BRS BIT(6) > +#define IFI_CANFD_TXFIFO_DLC_FNR_OFFSET 24 > +#define IFI_CANFD_TXFIFO_DLC_FNR_MASK 0xff > + > +#define IFI_CANFD_TXFIFO_ID 0xbc > +#define IFI_CANFD_TXFIFO_ID_ID_OFFSET 0 > +#define IFI_CANFD_TXFIFO_ID_ID_STD_MASK 0x3ff > +#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK 0x1fffffff > +#define IFI_CANFD_TXFIFO_ID_IDE BIT(29) > + > +#define IFI_CANFD_TXFIFO_DATA 0xc0 /* 0xb0..0xfc */ > + > +#define IFI_CANFD_FILTER_MASK(n) (0x800 + ((n) * 8) + 0) > +#define IFI_CANFD_FILTER_MASK_EXT BIT(29) > +#define IFI_CANFD_FILTER_MASK_EDL BIT(30) > +#define IFI_CANFD_FILTER_MASK_VALID BIT(31) > + > +#define IFI_CANFD_FILTER_IDENT(n) (0x800 + ((n) * 8) + 4) > +#define IFI_CANFD_FILTER_IDENT_IDE BIT(29) > +#define IFI_CANFD_FILTER_IDENT_CANFD BIT(30) > +#define IFI_CANFD_FILTER_IDENT_VALID BIT(31) > + > +/* IFI CANFD private data structure */ > +struct ifi_canfd_priv { > + struct can_priv can; /* must be the first member */ > + struct napi_struct napi; > + struct net_device *ndev; > + struct device *device; Nitpick: This variable is write only, correct? I can fix this while applying the patch. > + void __iomem *base; > +}; Otherweise looks good. regards, Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 455 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 10:39 ` Marc Kleine-Budde @ 2016-01-20 10:54 ` Marc Kleine-Budde 2016-01-20 12:58 ` Marek Vasut 0 siblings, 1 reply; 22+ messages in thread From: Marc Kleine-Budde @ 2016-01-20 10:54 UTC (permalink / raw) To: Marek Vasut, netdev; +Cc: Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 1243 bytes --] On 01/20/2016 11:39 AM, Marc Kleine-Budde wrote: >> +/* IFI CANFD private data structure */ >> +struct ifi_canfd_priv { >> + struct can_priv can; /* must be the first member */ >> + struct napi_struct napi; >> + struct net_device *ndev; >> + struct device *device; > > Nitpick: This variable is write only, correct? I can fix this while > applying the patch. > >> + void __iomem *base; >> +}; > > Otherweise looks good. Until compiling: > drivers/net/can/ifi_canfd/ifi_canfd.c:486:40: warning: cast truncates bits from constant value (ffffffff7ffffffd becomes 7ffffffd) > drivers/net/can/ifi_canfd/ifi_canfd.c:640:9: warning: cast truncates bits from constant value (ffffffff7fffffff becomes 7fffffff) > drivers/net/can/ifi_canfd/ifi_canfd.c:676:9: warning: cast truncates bits from constant value (ffffffff7fffffff becomes 7fffffff) target is ARMv5 with gcc version 4.9.2, compiled with C=2 using sparse v0.5.0. regards, Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 455 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 10:54 ` Marc Kleine-Budde @ 2016-01-20 12:58 ` Marek Vasut 2016-01-20 14:06 ` Marc Kleine-Budde 0 siblings, 1 reply; 22+ messages in thread From: Marek Vasut @ 2016-01-20 12:58 UTC (permalink / raw) To: Marc Kleine-Budde Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger On Wednesday, January 20, 2016 at 11:54:21 AM, Marc Kleine-Budde wrote: > On 01/20/2016 11:39 AM, Marc Kleine-Budde wrote: > >> +/* IFI CANFD private data structure */ > >> +struct ifi_canfd_priv { > >> + struct can_priv can; /* must be the first member */ > >> + struct napi_struct napi; > >> + struct net_device *ndev; > >> + struct device *device; > > > > Nitpick: This variable is write only, correct? I can fix this while > > applying the patch. > > > >> + void __iomem *base; > >> +}; > > > > Otherweise looks good. It's write-only, yeah. What would you propose to change here ? > Until compiling: > > drivers/net/can/ifi_canfd/ifi_canfd.c:486:40: warning: cast truncates > > bits from constant value (ffffffff7ffffffd becomes 7ffffffd) > > drivers/net/can/ifi_canfd/ifi_canfd.c:640:9: warning: cast truncates > > bits from constant value (ffffffff7fffffff becomes 7fffffff) > > drivers/net/can/ifi_canfd/ifi_canfd.c:676:9: warning: cast truncates > > bits from constant value (ffffffff7fffffff becomes 7fffffff) > > target is ARMv5 with gcc version 4.9.2, compiled with C=2 using sparse > v0.5.0. It's again the BIT() macro :-/ Do you have any idea how to fix this please ? Doing something like u32 foo = ~BIT(31); triggers this splat and I am running out of ideas how to fix that. Best regards, Marek Vasut ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 12:58 ` Marek Vasut @ 2016-01-20 14:06 ` Marc Kleine-Budde 2016-01-20 14:25 ` Marek Vasut 2016-01-20 14:33 ` [PATCH V3 " Marek Vasut 0 siblings, 2 replies; 22+ messages in thread From: Marc Kleine-Budde @ 2016-01-20 14:06 UTC (permalink / raw) To: Marek Vasut; +Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 1862 bytes --] On 01/20/2016 01:58 PM, Marek Vasut wrote: > On Wednesday, January 20, 2016 at 11:54:21 AM, Marc Kleine-Budde wrote: >> On 01/20/2016 11:39 AM, Marc Kleine-Budde wrote: >>>> +/* IFI CANFD private data structure */ >>>> +struct ifi_canfd_priv { >>>> + struct can_priv can; /* must be the first member */ >>>> + struct napi_struct napi; >>>> + struct net_device *ndev; >>>> + struct device *device; >>> >>> Nitpick: This variable is write only, correct? I can fix this while >>> applying the patch. >>> >>>> + void __iomem *base; >>>> +}; >>> >>> Otherweise looks good. > > It's write-only, yeah. What would you propose to change here ? Remove it completely. :D >> Until compiling: >>> drivers/net/can/ifi_canfd/ifi_canfd.c:486:40: warning: cast truncates >>> bits from constant value (ffffffff7ffffffd becomes 7ffffffd) >>> drivers/net/can/ifi_canfd/ifi_canfd.c:640:9: warning: cast truncates >>> bits from constant value (ffffffff7fffffff becomes 7fffffff) >>> drivers/net/can/ifi_canfd/ifi_canfd.c:676:9: warning: cast truncates >>> bits from constant value (ffffffff7fffffff becomes 7fffffff) >> >> target is ARMv5 with gcc version 4.9.2, compiled with C=2 using sparse >> v0.5.0. > > It's again the BIT() macro :-/ Do you have any idea how to fix this please ? > Doing something like u32 foo = ~BIT(31); triggers this splat and I am running > out of ideas how to fix that. -#define IFI_CANFD_INTERRUPT_SET_IRQ BIT(31) +#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31)) ...or change the bit to "30" :D Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 455 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:06 ` Marc Kleine-Budde @ 2016-01-20 14:25 ` Marek Vasut 2016-01-23 0:11 ` Rustad, Mark D 2016-01-20 14:33 ` [PATCH V3 " Marek Vasut 1 sibling, 1 reply; 22+ messages in thread From: Marek Vasut @ 2016-01-20 14:25 UTC (permalink / raw) To: Marc Kleine-Budde Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger On Wednesday, January 20, 2016 at 03:06:02 PM, Marc Kleine-Budde wrote: > On 01/20/2016 01:58 PM, Marek Vasut wrote: > > On Wednesday, January 20, 2016 at 11:54:21 AM, Marc Kleine-Budde wrote: > >> On 01/20/2016 11:39 AM, Marc Kleine-Budde wrote: > >>>> +/* IFI CANFD private data structure */ > >>>> +struct ifi_canfd_priv { > >>>> + struct can_priv can; /* must be the first member */ > >>>> + struct napi_struct napi; > >>>> + struct net_device *ndev; > >>>> + struct device *device; > >>> > >>> Nitpick: This variable is write only, correct? I can fix this while > >>> applying the patch. > >>> > >>>> + void __iomem *base; > >>>> +}; > >>> > >>> Otherweise looks good. > > > > It's write-only, yeah. What would you propose to change here ? > > Remove it completely. :D Erm, right. I see it now, thanks. > >> Until compiling: > >>> drivers/net/can/ifi_canfd/ifi_canfd.c:486:40: warning: cast truncates > >>> bits from constant value (ffffffff7ffffffd becomes 7ffffffd) > >>> drivers/net/can/ifi_canfd/ifi_canfd.c:640:9: warning: cast truncates > >>> bits from constant value (ffffffff7fffffff becomes 7fffffff) > >>> drivers/net/can/ifi_canfd/ifi_canfd.c:676:9: warning: cast truncates > >>> bits from constant value (ffffffff7fffffff becomes 7fffffff) > >> > >> target is ARMv5 with gcc version 4.9.2, compiled with C=2 using sparse > >> v0.5.0. > > > > It's again the BIT() macro :-/ Do you have any idea how to fix this > > please ? Doing something like u32 foo = ~BIT(31); triggers this splat > > and I am running out of ideas how to fix that. > > -#define IFI_CANFD_INTERRUPT_SET_IRQ BIT(31) > +#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31)) I see, so adding u32 also here works. I'm starting to wonder if the BIT macro is really that nice and if I shouldn't just use (1 << n) as usual. > ...or change the bit to "30" :D :-) I will send V3 in a bit. Best regards, Marek Vasut ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V2 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:25 ` Marek Vasut @ 2016-01-23 0:11 ` Rustad, Mark D 0 siblings, 0 replies; 22+ messages in thread From: Rustad, Mark D @ 2016-01-23 0:11 UTC (permalink / raw) To: Marek Vasut Cc: Marc Kleine-Budde, netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 610 bytes --] Marek Vasut <marex@denx.de> wrote: > I see, so adding u32 also here works. I'm starting to wonder if the BIT > macro is really that nice and if I shouldn't just use (1 << n) as usual. Actually, (1 << n) is not so good either when n is 31 - it can trigger overflow warnings since it will be presumed to be a signed value. (1U << n) should avoid that problem. Unfortunately, BIT() uses 1UL which produces 64-bit values on 64-bit arches. The bit ops are kind of a mess in that way. It would be nicer if BIT was restricted to int-size values and a new BIT_UL or something would produce the long values. ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:06 ` Marc Kleine-Budde 2016-01-20 14:25 ` Marek Vasut @ 2016-01-20 14:33 ` Marek Vasut 2016-01-20 14:39 ` Marc Kleine-Budde 1 sibling, 1 reply; 22+ messages in thread From: Marek Vasut @ 2016-01-20 14:33 UTC (permalink / raw) To: netdev Cc: Marc Kleine-Budde, Oliver Hartkopp, Marek Vasut, Mark Rutland, Wolfgang Grandegger The patch adds support for IFI CAN/FD controller [1]. This driver currently supports sending and receiving both standard CAN and new CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. [1] http://www.ifi-pld.de/IP/CANFD/canfd.html Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Oliver Hartkopp <socketcan@hartkopp.net> Cc: Wolfgang Grandegger <wg@grandegger.com> --- V2: - Move request_irq()/free_irq() into ifi_canfd_open()/ifi_canfd_close() just like other drivers do it to prevent crash when reloading module. - Fix Woverflow complains on x86_64 and itanium, exactly the same way as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . V3: - Hopefully fix all problems with BIT(31) by adding more u32 casts. - Drop struct device from struct ifi_canfd_priv . NOTE: The driver is surprisingly similar to m_can, but the register layout of the IFI core is completely different, so it's clear that those are two different IP cores. --- drivers/net/can/Kconfig | 2 + drivers/net/can/Makefile | 1 + drivers/net/can/ifi_canfd/Kconfig | 8 + drivers/net/can/ifi_canfd/Makefile | 5 + drivers/net/can/ifi_canfd/ifi_canfd.c | 917 ++++++++++++++++++++++++++++++++++ 5 files changed, 933 insertions(+) create mode 100644 drivers/net/can/ifi_canfd/Kconfig create mode 100644 drivers/net/can/ifi_canfd/Makefile create mode 100644 drivers/net/can/ifi_canfd/ifi_canfd.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index ca49d15..20be638 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -151,6 +151,8 @@ source "drivers/net/can/c_can/Kconfig" source "drivers/net/can/cc770/Kconfig" +source "drivers/net/can/ifi_canfd/Kconfig" + source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 4f85c2b..e3db0c8 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_CAN_CC770) += cc770/ obj-$(CONFIG_CAN_C_CAN) += c_can/ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ diff --git a/drivers/net/can/ifi_canfd/Kconfig b/drivers/net/can/ifi_canfd/Kconfig new file mode 100644 index 0000000..9e8934f --- /dev/null +++ b/drivers/net/can/ifi_canfd/Kconfig @@ -0,0 +1,8 @@ +config CAN_IFI_CANFD + depends on HAS_IOMEM + tristate "IFI CAN_FD IP" + ---help--- + This driver adds support for the I/F/I CAN_FD soft IP block + connected to the "platform bus" (Linux abstraction for directly + to the processor attached devices). The CAN_FD is most often + synthesised into an FPGA or CPLD. diff --git a/drivers/net/can/ifi_canfd/Makefile b/drivers/net/can/ifi_canfd/Makefile new file mode 100644 index 0000000..b229960 --- /dev/null +++ b/drivers/net/can/ifi_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the IFI CANFD controller driver. +# + +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd.o diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c new file mode 100644 index 0000000..639868b --- /dev/null +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -0,0 +1,917 @@ +/* + * CAN bus driver for IFI CANFD controller + * + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * Details about this controller can be found at + * http://www.ifi-pld.de/IP/CANFD/canfd.html + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <linux/can/dev.h> + +#define IFI_CANFD_STCMD 0x0 +#define IFI_CANFD_STCMD_HARDRESET 0xDEADCAFD +#define IFI_CANFD_STCMD_ENABLE BIT(0) +#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) +#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) +#define IFI_CANFD_STCMD_BUSOFF BIT(4) +#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) +#define IFI_CANFD_STCMD_LOOPBACK BIT(18) +#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) +#define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) +#define IFI_CANFD_STCMD_NORMAL_MODE ((u32)BIT(31)) + +#define IFI_CANFD_RXSTCMD 0x4 +#define IFI_CANFD_RXSTCMD_REMOVE_MSG BIT(0) +#define IFI_CANFD_RXSTCMD_RESET BIT(7) +#define IFI_CANFD_RXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_RXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_TXSTCMD 0x8 +#define IFI_CANFD_TXSTCMD_ADD_MSG BIT(0) +#define IFI_CANFD_TXSTCMD_HIGH_PRIO BIT(1) +#define IFI_CANFD_TXSTCMD_RESET BIT(7) +#define IFI_CANFD_TXSTCMD_EMPTY BIT(8) +#define IFI_CANFD_TXSTCMD_FULL BIT(12) +#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) + +#define IFI_CANFD_INTERRUPT 0xc +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) +#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER BIT(25) +#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31)) + +#define IFI_CANFD_IRQMASK 0x10 +#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) +#define IFI_CANFD_IRQMASK_SET_TS BIT(15) +#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) +#define IFI_CANFD_IRQMASK_SET_TX BIT(23) +#define IFI_CANFD_IRQMASK_RXFIFO_NEMPTY BIT(24) +#define IFI_CANFD_IRQMASK_SET_RX ((u32)BIT(31)) + +#define IFI_CANFD_TIME 0x14 +#define IFI_CANFD_FTIME 0x18 +#define IFI_CANFD_TIME_TIMEB_OFF 0 +#define IFI_CANFD_TIME_TIMEA_OFF 8 +#define IFI_CANFD_TIME_PRESCALE_OFF 16 +#define IFI_CANFD_TIME_SJW_OFF_ISO 25 +#define IFI_CANFD_TIME_SJW_OFF_BOSCH 28 +#define IFI_CANFD_TIME_SET_SJW_BOSCH BIT(6) +#define IFI_CANFD_TIME_SET_TIMEB_BOSCH BIT(7) +#define IFI_CANFD_TIME_SET_PRESC_BOSCH BIT(14) +#define IFI_CANFD_TIME_SET_TIMEA_BOSCH BIT(15) + +#define IFI_CANFD_TDELAY 0x1c + +#define IFI_CANFD_ERROR 0x20 +#define IFI_CANFD_ERROR_TX_OFFSET 0 +#define IFI_CANFD_ERROR_TX_MASK 0xff +#define IFI_CANFD_ERROR_RX_OFFSET 16 +#define IFI_CANFD_ERROR_RX_MASK 0xff + +#define IFI_CANFD_ERRCNT 0x24 + +#define IFI_CANFD_SUSPEND 0x28 + +#define IFI_CANFD_REPEAT 0x2c + +#define IFI_CANFD_TRAFFIC 0x30 + +#define IFI_CANFD_TSCONTROL 0x34 + +#define IFI_CANFD_TSC 0x38 + +#define IFI_CANFD_TST 0x3c + +#define IFI_CANFD_RES1 0x40 + +#define IFI_CANFD_RES2 0x44 + +#define IFI_CANFD_PAR 0x48 + +#define IFI_CANFD_CANCLOCK 0x4c + +#define IFI_CANFD_SYSCLOCK 0x50 + +#define IFI_CANFD_VER 0x54 + +#define IFI_CANFD_IP_ID 0x58 +#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD + +#define IFI_CANFD_TEST 0x5c + +#define IFI_CANFD_RXFIFO_TS_63_32 0x60 + +#define IFI_CANFD_RXFIFO_TS_31_0 0x64 + +#define IFI_CANFD_RXFIFO_DLC 0x68 +#define IFI_CANFD_RXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_RXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_RXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_RXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_RXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_RXFIFO_DLC_ESI BIT(7) +#define IFI_CANFD_RXFIFO_DLC_OBJ_OFFSET 8 +#define IFI_CANFD_RXFIFO_DLC_OBJ_MASK 0x1ff +#define IFI_CANFD_RXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_RXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_RXFIFO_ID 0x6c +#define IFI_CANFD_RXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_RXFIFO_ID_ID_STD_MASK 0x3ff +#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK 0x1fffffff +#define IFI_CANFD_RXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_RXFIFO_DATA 0x70 /* 0x70..0xac */ + +#define IFI_CANFD_TXFIFO_SUSPEND_US 0xb0 + +#define IFI_CANFD_TXFIFO_REPEATCOUNT 0xb4 + +#define IFI_CANFD_TXFIFO_DLC 0xb8 +#define IFI_CANFD_TXFIFO_DLC_DLC_OFFSET 0 +#define IFI_CANFD_TXFIFO_DLC_DLC_MASK 0xf +#define IFI_CANFD_TXFIFO_DLC_RTR BIT(4) +#define IFI_CANFD_TXFIFO_DLC_EDL BIT(5) +#define IFI_CANFD_TXFIFO_DLC_BRS BIT(6) +#define IFI_CANFD_TXFIFO_DLC_FNR_OFFSET 24 +#define IFI_CANFD_TXFIFO_DLC_FNR_MASK 0xff + +#define IFI_CANFD_TXFIFO_ID 0xbc +#define IFI_CANFD_TXFIFO_ID_ID_OFFSET 0 +#define IFI_CANFD_TXFIFO_ID_ID_STD_MASK 0x3ff +#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK 0x1fffffff +#define IFI_CANFD_TXFIFO_ID_IDE BIT(29) + +#define IFI_CANFD_TXFIFO_DATA 0xc0 /* 0xb0..0xfc */ + +#define IFI_CANFD_FILTER_MASK(n) (0x800 + ((n) * 8) + 0) +#define IFI_CANFD_FILTER_MASK_EXT BIT(29) +#define IFI_CANFD_FILTER_MASK_EDL BIT(30) +#define IFI_CANFD_FILTER_MASK_VALID ((u32)BIT(31)) + +#define IFI_CANFD_FILTER_IDENT(n) (0x800 + ((n) * 8) + 4) +#define IFI_CANFD_FILTER_IDENT_IDE BIT(29) +#define IFI_CANFD_FILTER_IDENT_CANFD BIT(30) +#define IFI_CANFD_FILTER_IDENT_VALID ((u32)BIT(31)) + +/* IFI CANFD private data structure */ +struct ifi_canfd_priv { + struct can_priv can; /* must be the first member */ + struct napi_struct napi; + struct net_device *ndev; + void __iomem *base; +}; + +static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 enirq = 0; + + if (enable) { + enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | + IFI_CANFD_IRQMASK_RXFIFO_NEMPTY; + } + + writel(IFI_CANFD_IRQMASK_SET_ERR | + IFI_CANFD_IRQMASK_SET_TS | + IFI_CANFD_IRQMASK_SET_TX | + IFI_CANFD_IRQMASK_SET_RX | enirq, + priv->base + IFI_CANFD_IRQMASK); +} + +static void ifi_canfd_read_fifo(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf; + struct sk_buff *skb; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + u32 rxdlc, rxid; + u32 dlc, id; + int i; + + rxdlc = readl(priv->base + IFI_CANFD_RXFIFO_DLC); + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + + if (!skb) { + stats->rx_dropped++; + return; + } + + dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) & + IFI_CANFD_RXFIFO_DLC_DLC_MASK; + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) + cf->len = can_dlc2len(dlc); + else + cf->len = get_can_dlc(dlc); + + rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID); + id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET); + if (id & IFI_CANFD_RXFIFO_ID_IDE) + id &= IFI_CANFD_RXFIFO_ID_ID_XTD_MASK; + else + id &= IFI_CANFD_RXFIFO_ID_ID_STD_MASK; + cf->can_id = id; + + if (rxdlc & IFI_CANFD_RXFIFO_DLC_ESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(ndev, "ESI Error\n"); + } + + if (!(rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) && + (rxdlc & IFI_CANFD_RXFIFO_DLC_RTR)) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if (rxdlc & IFI_CANFD_RXFIFO_DLC_BRS) + cf->flags |= CANFD_BRS; + + for (i = 0; i < cf->len; i += 4) { + *(u32 *)(cf->data + i) = + readl(priv->base + IFI_CANFD_RXFIFO_DATA + i); + } + } + + /* Remove the packet from FIFO */ + writel(IFI_CANFD_RXSTCMD_REMOVE_MSG, priv->base + IFI_CANFD_RXSTCMD); + writel(rx_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_receive_skb(skb); +} + +static int ifi_canfd_do_rx_poll(struct net_device *ndev, int quota) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 pkts = 0; + u32 rxst; + + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) { + netdev_dbg(ndev, "No messages in RX FIFO\n"); + return 0; + } + + for (;;) { + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) + break; + if (quota <= 0) + break; + + ifi_canfd_read_fifo(ndev); + quota--; + pkts++; + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); + } + + if (pkts) + can_led_event(ndev, CAN_LED_EVENT_RX); + + return pkts; +} + +static int ifi_canfd_handle_lost_msg(struct net_device *ndev) +{ + struct net_device_stats *stats = &ndev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(ndev, "RX FIFO overflow, message(s) lost.\n"); + + stats->rx_errors++; + stats->rx_over_errors++; + + skb = alloc_can_err_skb(ndev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 err; + + err = readl(priv->base + IFI_CANFD_ERROR); + bec->rxerr = (err >> IFI_CANFD_ERROR_RX_OFFSET) & + IFI_CANFD_ERROR_RX_MASK; + bec->txerr = (err >> IFI_CANFD_ERROR_TX_OFFSET) & + IFI_CANFD_ERROR_TX_MASK; + + return 0; +} + +static int ifi_canfd_handle_state_change(struct net_device *ndev, + enum can_state new_state) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + ifi_canfd_irq_enable(ndev, 0); + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + break; + default: + break; + } + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (unlikely(!skb)) + return 0; + + ifi_canfd_get_berr_counter(ndev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if (bec.txerr > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + cf->can_id |= CAN_ERR_BUSOFF; + break; + default: + break; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + +static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int work_done = 0; + u32 isr; + + /* + * The ErrWarn condition is a little special, since the bit is + * located in the INTERRUPT register instead of STCMD register. + */ + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + /* Clear the interrupt */ + writel(IFI_CANFD_INTERRUPT_ERROR_WARNING, + priv->base + IFI_CANFD_INTERRUPT); + netdev_dbg(ndev, "Error, entered warning state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_WARNING); + } + + if ((stcmd & IFI_CANFD_STCMD_ERROR_PASSIVE) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(ndev, "Error, entered passive state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((stcmd & IFI_CANFD_STCMD_BUSOFF) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(ndev, "Error, entered bus-off state\n"); + work_done += ifi_canfd_handle_state_change(ndev, + CAN_STATE_BUS_OFF); + } + + return work_done; +} + +static int ifi_canfd_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE | + IFI_CANFD_STCMD_BUSOFF; + int work_done = 0; + + u32 stcmd = readl(priv->base + IFI_CANFD_STCMD); + u32 rxstcmd = readl(priv->base + IFI_CANFD_STCMD); + + /* Handle bus state changes */ + if ((stcmd & stcmd_state_mask) || + ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0)) + work_done += ifi_canfd_handle_state_errors(ndev, stcmd); + + /* Handle lost messages on RX */ + if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) + work_done += ifi_canfd_handle_lost_msg(ndev); + + /* Handle normal messages on RX */ + if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) + work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); + + if (work_done < quota) { + napi_complete(napi); + ifi_canfd_irq_enable(ndev, 1); + } + + return work_done; +} + +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; + const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | + IFI_CANFD_INTERRUPT_ERROR_WARNING)); + u32 isr; + + isr = readl(priv->base + IFI_CANFD_INTERRUPT); + + /* No interrupt */ + if (isr == 0) + return IRQ_NONE; + + /* Clear all pending interrupts but ErrWarn */ + writel(clr_irq_mask, priv->base + IFI_CANFD_INTERRUPT); + + /* RX IRQ, start NAPI */ + if (isr & rx_irq_mask) { + ifi_canfd_irq_enable(ndev, 0); + napi_schedule(&priv->napi); + } + + /* TX IRQ */ + if (isr & tx_irq_mask) { + stats->tx_bytes += can_get_echo_skb(ndev, 0); + stats->tx_packets++; + can_led_event(ndev, CAN_LED_EVENT_TX); + netif_wake_queue(ndev); + } + + return IRQ_HANDLED; +} + +static const struct can_bittiming_const ifi_canfd_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 64, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +static const struct can_bittiming_const ifi_canfd_data_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + +static void ifi_canfd_set_bittiming(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 noniso_arg = 0; + u32 time_off; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) { + noniso_arg = IFI_CANFD_TIME_SET_TIMEB_BOSCH | + IFI_CANFD_TIME_SET_TIMEA_BOSCH | + IFI_CANFD_TIME_SET_PRESC_BOSCH | + IFI_CANFD_TIME_SET_SJW_BOSCH; + time_off = IFI_CANFD_TIME_SJW_OFF_BOSCH; + } else { + time_off = IFI_CANFD_TIME_SJW_OFF_ISO; + } + + /* Configure bit timing */ + brp = bt->brp - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << time_off), + priv->base + IFI_CANFD_TIME); + + /* Configure data bit timing */ + brp = dbt->brp - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | + (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | + (brp << IFI_CANFD_TIME_PRESCALE_OFF) | + (sjw << time_off) | + noniso_arg, + priv->base + IFI_CANFD_FTIME); +} + +static void ifi_canfd_set_filter(struct net_device *ndev, const u32 id, + const u32 mask, const u32 ident) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + writel(mask, priv->base + IFI_CANFD_FILTER_MASK(id)); + writel(ident, priv->base + IFI_CANFD_FILTER_IDENT(id)); +} + +static void ifi_canfd_set_filters(struct net_device *ndev) +{ + /* Receive all CAN frames (standard ID) */ + ifi_canfd_set_filter(ndev, 0, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID); + + /* Receive all CAN frames (extended ID) */ + ifi_canfd_set_filter(ndev, 1, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_IDE); + + /* Receive all CANFD frames */ + ifi_canfd_set_filter(ndev, 2, + IFI_CANFD_FILTER_MASK_VALID | + IFI_CANFD_FILTER_MASK_EDL | + IFI_CANFD_FILTER_MASK_EXT, + IFI_CANFD_FILTER_IDENT_VALID | + IFI_CANFD_FILTER_IDENT_CANFD | + IFI_CANFD_FILTER_IDENT_IDE); +} + +static void ifi_canfd_start(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + u32 stcmd; + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + writel(0, priv->base + IFI_CANFD_STCMD); + + ifi_canfd_set_bittiming(ndev); + ifi_canfd_set_filters(ndev); + + /* Reset FIFOs */ + writel(IFI_CANFD_RXSTCMD_RESET, priv->base + IFI_CANFD_RXSTCMD); + writel(0, priv->base + IFI_CANFD_RXSTCMD); + writel(IFI_CANFD_TXSTCMD_RESET, priv->base + IFI_CANFD_TXSTCMD); + writel(0, priv->base + IFI_CANFD_TXSTCMD); + + /* Repeat transmission until successful */ + writel(0, priv->base + IFI_CANFD_REPEAT); + writel(0, priv->base + IFI_CANFD_SUSPEND); + + /* Clear all pending interrupts */ + writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), + priv->base + IFI_CANFD_INTERRUPT); + + stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + stcmd |= IFI_CANFD_STCMD_BUSMONITOR; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + stcmd |= IFI_CANFD_STCMD_LOOPBACK; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + stcmd |= IFI_CANFD_STCMD_ENABLE_ISO; + + if (!(priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO))) + stcmd |= IFI_CANFD_STCMD_DISABLE_CANFD; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + ifi_canfd_irq_enable(ndev, 1); + + /* Enable controller */ + writel(stcmd, priv->base + IFI_CANFD_STCMD); +} + +static void ifi_canfd_stop(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + /* Reset the IP */ + writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); + + /* Mask all interrupts */ + writel(~0, priv->base + IFI_CANFD_IRQMASK); + + /* Clear all pending interrupts */ + writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), + priv->base + IFI_CANFD_INTERRUPT); + + /* Set the state as STOPPED */ + priv->can.state = CAN_STATE_STOPPED; +} + +static int ifi_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + ifi_canfd_start(ndev); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ifi_canfd_open(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + int ret; + + ret = open_candev(ndev); + if (ret) { + netdev_err(ndev, "Failed to open CAN device\n"); + return ret; + } + + /* Register interrupt handler */ + ret = request_irq(ndev->irq, ifi_canfd_isr, IRQF_SHARED, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "Failed to request interrupt\n"); + goto err_irq; + } + + ifi_canfd_start(ndev); + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +err_irq: + close_candev(ndev); + return ret; +} + +static int ifi_canfd_close(struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + + ifi_canfd_stop(ndev); + + free_irq(ndev->irq, ndev); + + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + return 0; +} + +static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 txst, txid; + u32 txdlc = 0; + int i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + /* Check if the TX buffer is full */ + txst = readl(priv->base + IFI_CANFD_TXSTCMD); + if (txst & IFI_CANFD_TXSTCMD_FULL) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG! TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(ndev); + + if (cf->can_id & CAN_EFF_FLAG) { + txid = cf->can_id & CAN_EFF_MASK; + txid |= IFI_CANFD_TXFIFO_ID_IDE; + } else { + txid = cf->can_id & CAN_SFF_MASK; + } + + if (priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO)) { + if (can_is_canfd_skb(skb)) { + txdlc |= IFI_CANFD_TXFIFO_DLC_EDL; + if (cf->flags & CANFD_BRS) + txdlc |= IFI_CANFD_TXFIFO_DLC_BRS; + } + } + + if (cf->can_id & CAN_RTR_FLAG) + txdlc |= IFI_CANFD_TXFIFO_DLC_RTR; + + /* message ram configuration */ + writel(txid, priv->base + IFI_CANFD_TXFIFO_ID); + writel(txdlc, priv->base + IFI_CANFD_TXFIFO_DLC); + + for (i = 0; i < cf->len; i += 4) { + writel(*(u32 *)(cf->data + i), + priv->base + IFI_CANFD_TXFIFO_DATA + i); + } + + writel(0, priv->base + IFI_CANFD_TXFIFO_REPEATCOUNT); + writel(0, priv->base + IFI_CANFD_TXFIFO_SUSPEND_US); + + can_put_echo_skb(skb, ndev, 0); + + /* Start the transmission */ + writel(IFI_CANFD_TXSTCMD_ADD_MSG, priv->base + IFI_CANFD_TXSTCMD); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops ifi_canfd_netdev_ops = { + .ndo_open = ifi_canfd_open, + .ndo_stop = ifi_canfd_close, + .ndo_start_xmit = ifi_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int ifi_canfd_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct ifi_canfd_priv *priv; + struct resource *res; + void __iomem *addr; + int irq, ret; + u32 id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(dev, res); + irq = platform_get_irq(pdev, 0); + if (IS_ERR(addr) || irq < 0) + return -EINVAL; + + id = readl(addr + IFI_CANFD_IP_ID); + if (id != IFI_CANFD_IP_ID_VALUE) { + dev_err(dev, "This block is not IFI CANFD, id=%08x\n", id); + return -EINVAL; + } + + ndev = alloc_candev(sizeof(*priv), 1); + if (!ndev) + return -ENOMEM; + + ndev->irq = irq; + ndev->flags |= IFF_ECHO; /* we support local echo */ + ndev->netdev_ops = &ifi_canfd_netdev_ops; + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + + netif_napi_add(ndev, &priv->napi, ifi_canfd_poll, 64); + + priv->can.state = CAN_STATE_STOPPED; + + priv->can.clock.freq = readl(addr + IFI_CANFD_SYSCLOCK); + + priv->can.bittiming_const = &ifi_canfd_bittiming_const; + priv->can.data_bittiming_const = &ifi_canfd_data_bittiming_const; + priv->can.do_set_mode = ifi_canfd_set_mode; + priv->can.do_get_berr_counter = ifi_canfd_get_berr_counter; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode = CAN_CTRLMODE_FD; + + /* IFI CANFD can do both Bosch FD and ISO FD */ + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO; + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, dev); + + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "Failed to register (ret=%d)\n", ret); + goto err_reg; + } + + devm_can_led_init(ndev); + + dev_info(dev, "Driver registered: regs=%p, irq=%d, clock=%d\n", + priv->base, ndev->irq, priv->can.clock.freq); + + return 0; + +err_reg: + free_candev(ndev); + return ret; +} + +static int ifi_canfd_plat_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_candev(ndev); + platform_set_drvdata(pdev, NULL); + free_candev(ndev); + + return 0; +} + +static const struct of_device_id ifi_canfd_of_table[] = { + { .compatible = "ifi,canfd-1.0", .data = NULL }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ifi_canfd_of_table); + +static struct platform_driver ifi_canfd_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ifi_canfd_of_table, + }, + .probe = ifi_canfd_plat_probe, + .remove = ifi_canfd_plat_remove, +}; + +module_platform_driver(ifi_canfd_plat_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for IFI CANFD controller"); -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:33 ` [PATCH V3 " Marek Vasut @ 2016-01-20 14:39 ` Marc Kleine-Budde 2016-01-20 14:41 ` Marek Vasut 0 siblings, 1 reply; 22+ messages in thread From: Marc Kleine-Budde @ 2016-01-20 14:39 UTC (permalink / raw) To: Marek Vasut, netdev; +Cc: Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 19868 bytes --] On 01/20/2016 03:33 PM, Marek Vasut wrote: > The patch adds support for IFI CAN/FD controller [1]. This driver > currently supports sending and receiving both standard CAN and new > CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. > > [1] http://www.ifi-pld.de/IP/CANFD/canfd.html > > Signed-off-by: Marek Vasut <marex@denx.de> > Cc: Marc Kleine-Budde <mkl@pengutronix.de> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: Oliver Hartkopp <socketcan@hartkopp.net> > Cc: Wolfgang Grandegger <wg@grandegger.com> > --- > V2: - Move request_irq()/free_irq() into ifi_canfd_open()/ifi_canfd_close() > just like other drivers do it to prevent crash when reloading module. > - Fix Woverflow complains on x86_64 and itanium, exactly the same way > as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . > > V3: - Hopefully fix all problems with BIT(31) by adding more u32 casts. > - Drop struct device from struct ifi_canfd_priv . > > NOTE: The driver is surprisingly similar to m_can, but the register > layout of the IFI core is completely different, so it's clear > that those are two different IP cores. > --- > drivers/net/can/Kconfig | 2 + > drivers/net/can/Makefile | 1 + > drivers/net/can/ifi_canfd/Kconfig | 8 + > drivers/net/can/ifi_canfd/Makefile | 5 + > drivers/net/can/ifi_canfd/ifi_canfd.c | 917 ++++++++++++++++++++++++++++++++++ > 5 files changed, 933 insertions(+) > create mode 100644 drivers/net/can/ifi_canfd/Kconfig > create mode 100644 drivers/net/can/ifi_canfd/Makefile > create mode 100644 drivers/net/can/ifi_canfd/ifi_canfd.c > > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig > index ca49d15..20be638 100644 > --- a/drivers/net/can/Kconfig > +++ b/drivers/net/can/Kconfig > @@ -151,6 +151,8 @@ source "drivers/net/can/c_can/Kconfig" > > source "drivers/net/can/cc770/Kconfig" > > +source "drivers/net/can/ifi_canfd/Kconfig" > + > source "drivers/net/can/m_can/Kconfig" > > source "drivers/net/can/mscan/Kconfig" > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile > index 4f85c2b..e3db0c8 100644 > --- a/drivers/net/can/Makefile > +++ b/drivers/net/can/Makefile > @@ -20,6 +20,7 @@ obj-$(CONFIG_CAN_CC770) += cc770/ > obj-$(CONFIG_CAN_C_CAN) += c_can/ > obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o > obj-$(CONFIG_CAN_GRCAN) += grcan.o > +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ > obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o > obj-$(CONFIG_CAN_MSCAN) += mscan/ > obj-$(CONFIG_CAN_M_CAN) += m_can/ > diff --git a/drivers/net/can/ifi_canfd/Kconfig b/drivers/net/can/ifi_canfd/Kconfig > new file mode 100644 > index 0000000..9e8934f > --- /dev/null > +++ b/drivers/net/can/ifi_canfd/Kconfig > @@ -0,0 +1,8 @@ > +config CAN_IFI_CANFD > + depends on HAS_IOMEM > + tristate "IFI CAN_FD IP" > + ---help--- > + This driver adds support for the I/F/I CAN_FD soft IP block > + connected to the "platform bus" (Linux abstraction for directly > + to the processor attached devices). The CAN_FD is most often > + synthesised into an FPGA or CPLD. > diff --git a/drivers/net/can/ifi_canfd/Makefile b/drivers/net/can/ifi_canfd/Makefile > new file mode 100644 > index 0000000..b229960 > --- /dev/null > +++ b/drivers/net/can/ifi_canfd/Makefile > @@ -0,0 +1,5 @@ > +# > +# Makefile for the IFI CANFD controller driver. > +# > + > +obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd.o > diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c > new file mode 100644 > index 0000000..639868b > --- /dev/null > +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c > @@ -0,0 +1,917 @@ > +/* > + * CAN bus driver for IFI CANFD controller > + * > + * Copyright (C) 2016 Marek Vasut <marex@denx.de> > + * > + * Details about this controller can be found at > + * http://www.ifi-pld.de/IP/CANFD/canfd.html > + * > + * This file is licensed under the terms of the GNU General Public > + * License version 2. This program is licensed "as is" without any > + * warranty of any kind, whether express or implied. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/netdevice.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/platform_device.h> > + > +#include <linux/can/dev.h> > + > +#define IFI_CANFD_STCMD 0x0 > +#define IFI_CANFD_STCMD_HARDRESET 0xDEADCAFD > +#define IFI_CANFD_STCMD_ENABLE BIT(0) > +#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) > +#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) > +#define IFI_CANFD_STCMD_BUSOFF BIT(4) > +#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) > +#define IFI_CANFD_STCMD_LOOPBACK BIT(18) > +#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) > +#define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) > +#define IFI_CANFD_STCMD_NORMAL_MODE ((u32)BIT(31)) > + > +#define IFI_CANFD_RXSTCMD 0x4 > +#define IFI_CANFD_RXSTCMD_REMOVE_MSG BIT(0) > +#define IFI_CANFD_RXSTCMD_RESET BIT(7) > +#define IFI_CANFD_RXSTCMD_EMPTY BIT(8) > +#define IFI_CANFD_RXSTCMD_OVERFLOW BIT(13) > + > +#define IFI_CANFD_TXSTCMD 0x8 > +#define IFI_CANFD_TXSTCMD_ADD_MSG BIT(0) > +#define IFI_CANFD_TXSTCMD_HIGH_PRIO BIT(1) > +#define IFI_CANFD_TXSTCMD_RESET BIT(7) > +#define IFI_CANFD_TXSTCMD_EMPTY BIT(8) > +#define IFI_CANFD_TXSTCMD_FULL BIT(12) > +#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) > + > +#define IFI_CANFD_INTERRUPT 0xc > +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) > +#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) > +#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) > +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) > +#define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER BIT(25) > +#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31)) > + > +#define IFI_CANFD_IRQMASK 0x10 > +#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) > +#define IFI_CANFD_IRQMASK_SET_TS BIT(15) > +#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) > +#define IFI_CANFD_IRQMASK_SET_TX BIT(23) > +#define IFI_CANFD_IRQMASK_RXFIFO_NEMPTY BIT(24) > +#define IFI_CANFD_IRQMASK_SET_RX ((u32)BIT(31)) > + > +#define IFI_CANFD_TIME 0x14 > +#define IFI_CANFD_FTIME 0x18 > +#define IFI_CANFD_TIME_TIMEB_OFF 0 > +#define IFI_CANFD_TIME_TIMEA_OFF 8 > +#define IFI_CANFD_TIME_PRESCALE_OFF 16 > +#define IFI_CANFD_TIME_SJW_OFF_ISO 25 > +#define IFI_CANFD_TIME_SJW_OFF_BOSCH 28 > +#define IFI_CANFD_TIME_SET_SJW_BOSCH BIT(6) > +#define IFI_CANFD_TIME_SET_TIMEB_BOSCH BIT(7) > +#define IFI_CANFD_TIME_SET_PRESC_BOSCH BIT(14) > +#define IFI_CANFD_TIME_SET_TIMEA_BOSCH BIT(15) > + > +#define IFI_CANFD_TDELAY 0x1c > + > +#define IFI_CANFD_ERROR 0x20 > +#define IFI_CANFD_ERROR_TX_OFFSET 0 > +#define IFI_CANFD_ERROR_TX_MASK 0xff > +#define IFI_CANFD_ERROR_RX_OFFSET 16 > +#define IFI_CANFD_ERROR_RX_MASK 0xff > + > +#define IFI_CANFD_ERRCNT 0x24 > + > +#define IFI_CANFD_SUSPEND 0x28 > + > +#define IFI_CANFD_REPEAT 0x2c > + > +#define IFI_CANFD_TRAFFIC 0x30 > + > +#define IFI_CANFD_TSCONTROL 0x34 > + > +#define IFI_CANFD_TSC 0x38 > + > +#define IFI_CANFD_TST 0x3c > + > +#define IFI_CANFD_RES1 0x40 > + > +#define IFI_CANFD_RES2 0x44 > + > +#define IFI_CANFD_PAR 0x48 > + > +#define IFI_CANFD_CANCLOCK 0x4c > + > +#define IFI_CANFD_SYSCLOCK 0x50 > + > +#define IFI_CANFD_VER 0x54 > + > +#define IFI_CANFD_IP_ID 0x58 > +#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD > + > +#define IFI_CANFD_TEST 0x5c > + > +#define IFI_CANFD_RXFIFO_TS_63_32 0x60 > + > +#define IFI_CANFD_RXFIFO_TS_31_0 0x64 > + > +#define IFI_CANFD_RXFIFO_DLC 0x68 > +#define IFI_CANFD_RXFIFO_DLC_DLC_OFFSET 0 > +#define IFI_CANFD_RXFIFO_DLC_DLC_MASK 0xf > +#define IFI_CANFD_RXFIFO_DLC_RTR BIT(4) > +#define IFI_CANFD_RXFIFO_DLC_EDL BIT(5) > +#define IFI_CANFD_RXFIFO_DLC_BRS BIT(6) > +#define IFI_CANFD_RXFIFO_DLC_ESI BIT(7) > +#define IFI_CANFD_RXFIFO_DLC_OBJ_OFFSET 8 > +#define IFI_CANFD_RXFIFO_DLC_OBJ_MASK 0x1ff > +#define IFI_CANFD_RXFIFO_DLC_FNR_OFFSET 24 > +#define IFI_CANFD_RXFIFO_DLC_FNR_MASK 0xff > + > +#define IFI_CANFD_RXFIFO_ID 0x6c > +#define IFI_CANFD_RXFIFO_ID_ID_OFFSET 0 > +#define IFI_CANFD_RXFIFO_ID_ID_STD_MASK 0x3ff > +#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK 0x1fffffff > +#define IFI_CANFD_RXFIFO_ID_IDE BIT(29) > + > +#define IFI_CANFD_RXFIFO_DATA 0x70 /* 0x70..0xac */ > + > +#define IFI_CANFD_TXFIFO_SUSPEND_US 0xb0 > + > +#define IFI_CANFD_TXFIFO_REPEATCOUNT 0xb4 > + > +#define IFI_CANFD_TXFIFO_DLC 0xb8 > +#define IFI_CANFD_TXFIFO_DLC_DLC_OFFSET 0 > +#define IFI_CANFD_TXFIFO_DLC_DLC_MASK 0xf > +#define IFI_CANFD_TXFIFO_DLC_RTR BIT(4) > +#define IFI_CANFD_TXFIFO_DLC_EDL BIT(5) > +#define IFI_CANFD_TXFIFO_DLC_BRS BIT(6) > +#define IFI_CANFD_TXFIFO_DLC_FNR_OFFSET 24 > +#define IFI_CANFD_TXFIFO_DLC_FNR_MASK 0xff > + > +#define IFI_CANFD_TXFIFO_ID 0xbc > +#define IFI_CANFD_TXFIFO_ID_ID_OFFSET 0 > +#define IFI_CANFD_TXFIFO_ID_ID_STD_MASK 0x3ff > +#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK 0x1fffffff > +#define IFI_CANFD_TXFIFO_ID_IDE BIT(29) > + > +#define IFI_CANFD_TXFIFO_DATA 0xc0 /* 0xb0..0xfc */ > + > +#define IFI_CANFD_FILTER_MASK(n) (0x800 + ((n) * 8) + 0) > +#define IFI_CANFD_FILTER_MASK_EXT BIT(29) > +#define IFI_CANFD_FILTER_MASK_EDL BIT(30) > +#define IFI_CANFD_FILTER_MASK_VALID ((u32)BIT(31)) > + > +#define IFI_CANFD_FILTER_IDENT(n) (0x800 + ((n) * 8) + 4) > +#define IFI_CANFD_FILTER_IDENT_IDE BIT(29) > +#define IFI_CANFD_FILTER_IDENT_CANFD BIT(30) > +#define IFI_CANFD_FILTER_IDENT_VALID ((u32)BIT(31)) > + > +/* IFI CANFD private data structure */ > +struct ifi_canfd_priv { > + struct can_priv can; /* must be the first member */ > + struct napi_struct napi; > + struct net_device *ndev; > + void __iomem *base; > +}; > + > +static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) > +{ > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + u32 enirq = 0; > + > + if (enable) { > + enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | > + IFI_CANFD_IRQMASK_RXFIFO_NEMPTY; > + } > + > + writel(IFI_CANFD_IRQMASK_SET_ERR | > + IFI_CANFD_IRQMASK_SET_TS | > + IFI_CANFD_IRQMASK_SET_TX | > + IFI_CANFD_IRQMASK_SET_RX | enirq, > + priv->base + IFI_CANFD_IRQMASK); > +} > + > +static void ifi_canfd_read_fifo(struct net_device *ndev) > +{ > + struct net_device_stats *stats = &ndev->stats; > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + struct canfd_frame *cf; > + struct sk_buff *skb; > + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | > + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; > + u32 rxdlc, rxid; > + u32 dlc, id; > + int i; > + > + rxdlc = readl(priv->base + IFI_CANFD_RXFIFO_DLC); > + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) > + skb = alloc_canfd_skb(ndev, &cf); > + else > + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); > + > + if (!skb) { > + stats->rx_dropped++; > + return; > + } > + > + dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) & > + IFI_CANFD_RXFIFO_DLC_DLC_MASK; > + if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) > + cf->len = can_dlc2len(dlc); > + else > + cf->len = get_can_dlc(dlc); > + > + rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID); > + id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET); > + if (id & IFI_CANFD_RXFIFO_ID_IDE) > + id &= IFI_CANFD_RXFIFO_ID_ID_XTD_MASK; > + else > + id &= IFI_CANFD_RXFIFO_ID_ID_STD_MASK; > + cf->can_id = id; > + > + if (rxdlc & IFI_CANFD_RXFIFO_DLC_ESI) { > + cf->flags |= CANFD_ESI; > + netdev_dbg(ndev, "ESI Error\n"); > + } > + > + if (!(rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) && > + (rxdlc & IFI_CANFD_RXFIFO_DLC_RTR)) { > + cf->can_id |= CAN_RTR_FLAG; > + } else { > + if (rxdlc & IFI_CANFD_RXFIFO_DLC_BRS) > + cf->flags |= CANFD_BRS; > + > + for (i = 0; i < cf->len; i += 4) { > + *(u32 *)(cf->data + i) = > + readl(priv->base + IFI_CANFD_RXFIFO_DATA + i); > + } > + } > + > + /* Remove the packet from FIFO */ > + writel(IFI_CANFD_RXSTCMD_REMOVE_MSG, priv->base + IFI_CANFD_RXSTCMD); > + writel(rx_irq_mask, priv->base + IFI_CANFD_INTERRUPT); > + > + stats->rx_packets++; > + stats->rx_bytes += cf->len; > + > + netif_receive_skb(skb); > +} > + > +static int ifi_canfd_do_rx_poll(struct net_device *ndev, int quota) > +{ > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + u32 pkts = 0; > + u32 rxst; > + > + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); > + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) { > + netdev_dbg(ndev, "No messages in RX FIFO\n"); > + return 0; > + } > + > + for (;;) { > + if (rxst & IFI_CANFD_RXSTCMD_EMPTY) > + break; > + if (quota <= 0) > + break; > + > + ifi_canfd_read_fifo(ndev); > + quota--; > + pkts++; > + rxst = readl(priv->base + IFI_CANFD_RXSTCMD); > + } > + > + if (pkts) > + can_led_event(ndev, CAN_LED_EVENT_RX); > + > + return pkts; > +} > + > +static int ifi_canfd_handle_lost_msg(struct net_device *ndev) > +{ > + struct net_device_stats *stats = &ndev->stats; > + struct sk_buff *skb; > + struct can_frame *frame; > + > + netdev_err(ndev, "RX FIFO overflow, message(s) lost.\n"); > + > + stats->rx_errors++; > + stats->rx_over_errors++; > + > + skb = alloc_can_err_skb(ndev, &frame); > + if (unlikely(!skb)) > + return 0; > + > + frame->can_id |= CAN_ERR_CRTL; > + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; > + > + netif_receive_skb(skb); > + > + return 1; > +} > + > +static int ifi_canfd_get_berr_counter(const struct net_device *ndev, > + struct can_berr_counter *bec) > +{ > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + u32 err; > + > + err = readl(priv->base + IFI_CANFD_ERROR); > + bec->rxerr = (err >> IFI_CANFD_ERROR_RX_OFFSET) & > + IFI_CANFD_ERROR_RX_MASK; > + bec->txerr = (err >> IFI_CANFD_ERROR_TX_OFFSET) & > + IFI_CANFD_ERROR_TX_MASK; > + > + return 0; > +} > + > +static int ifi_canfd_handle_state_change(struct net_device *ndev, > + enum can_state new_state) > +{ > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + struct net_device_stats *stats = &ndev->stats; > + struct can_frame *cf; > + struct sk_buff *skb; > + struct can_berr_counter bec; > + > + switch (new_state) { > + case CAN_STATE_ERROR_ACTIVE: > + /* error warning state */ > + priv->can.can_stats.error_warning++; > + priv->can.state = CAN_STATE_ERROR_WARNING; > + break; > + case CAN_STATE_ERROR_PASSIVE: > + /* error passive state */ > + priv->can.can_stats.error_passive++; > + priv->can.state = CAN_STATE_ERROR_PASSIVE; > + break; > + case CAN_STATE_BUS_OFF: > + /* bus-off state */ > + priv->can.state = CAN_STATE_BUS_OFF; > + ifi_canfd_irq_enable(ndev, 0); > + priv->can.can_stats.bus_off++; > + can_bus_off(ndev); > + break; > + default: > + break; > + } > + > + /* propagate the error condition to the CAN stack */ > + skb = alloc_can_err_skb(ndev, &cf); > + if (unlikely(!skb)) > + return 0; > + > + ifi_canfd_get_berr_counter(ndev, &bec); > + > + switch (new_state) { > + case CAN_STATE_ERROR_ACTIVE: > + /* error warning state */ > + cf->can_id |= CAN_ERR_CRTL; > + cf->data[1] = (bec.txerr > bec.rxerr) ? > + CAN_ERR_CRTL_TX_WARNING : > + CAN_ERR_CRTL_RX_WARNING; > + cf->data[6] = bec.txerr; > + cf->data[7] = bec.rxerr; > + break; > + case CAN_STATE_ERROR_PASSIVE: > + /* error passive state */ > + cf->can_id |= CAN_ERR_CRTL; > + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; > + if (bec.txerr > 127) > + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; > + cf->data[6] = bec.txerr; > + cf->data[7] = bec.rxerr; > + break; > + case CAN_STATE_BUS_OFF: > + /* bus-off state */ > + cf->can_id |= CAN_ERR_BUSOFF; > + break; > + default: > + break; > + } > + > + stats->rx_packets++; > + stats->rx_bytes += cf->can_dlc; > + netif_receive_skb(skb); > + > + return 1; > +} > + > +static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd) > +{ > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + int work_done = 0; > + u32 isr; > + > + /* > + * The ErrWarn condition is a little special, since the bit is > + * located in the INTERRUPT register instead of STCMD register. > + */ > + isr = readl(priv->base + IFI_CANFD_INTERRUPT); > + if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) && > + (priv->can.state != CAN_STATE_ERROR_WARNING)) { > + /* Clear the interrupt */ > + writel(IFI_CANFD_INTERRUPT_ERROR_WARNING, > + priv->base + IFI_CANFD_INTERRUPT); > + netdev_dbg(ndev, "Error, entered warning state\n"); > + work_done += ifi_canfd_handle_state_change(ndev, > + CAN_STATE_ERROR_WARNING); > + } > + > + if ((stcmd & IFI_CANFD_STCMD_ERROR_PASSIVE) && > + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { > + netdev_dbg(ndev, "Error, entered passive state\n"); > + work_done += ifi_canfd_handle_state_change(ndev, > + CAN_STATE_ERROR_PASSIVE); > + } > + > + if ((stcmd & IFI_CANFD_STCMD_BUSOFF) && > + (priv->can.state != CAN_STATE_BUS_OFF)) { > + netdev_dbg(ndev, "Error, entered bus-off state\n"); > + work_done += ifi_canfd_handle_state_change(ndev, > + CAN_STATE_BUS_OFF); > + } > + > + return work_done; > +} > + > +static int ifi_canfd_poll(struct napi_struct *napi, int quota) > +{ > + struct net_device *ndev = napi->dev; > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE | > + IFI_CANFD_STCMD_BUSOFF; > + int work_done = 0; > + > + u32 stcmd = readl(priv->base + IFI_CANFD_STCMD); > + u32 rxstcmd = readl(priv->base + IFI_CANFD_STCMD); > + > + /* Handle bus state changes */ > + if ((stcmd & stcmd_state_mask) || > + ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0)) > + work_done += ifi_canfd_handle_state_errors(ndev, stcmd); > + > + /* Handle lost messages on RX */ > + if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) > + work_done += ifi_canfd_handle_lost_msg(ndev); > + > + /* Handle normal messages on RX */ > + if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) > + work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); > + > + if (work_done < quota) { > + napi_complete(napi); > + ifi_canfd_irq_enable(ndev, 1); > + } > + > + return work_done; > +} > + > +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) > +{ > + struct net_device *ndev = (struct net_device *)dev_id; > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > + struct net_device_stats *stats = &ndev->stats; > + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | > + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; > + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | > + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; > + const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | > + IFI_CANFD_INTERRUPT_ERROR_WARNING)); I've squashed: >> - const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | >> - IFI_CANFD_INTERRUPT_ERROR_WARNING)); >> + const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | >> + IFI_CANFD_INTERRUPT_ERROR_WARNING); and the driver compiles without warnings. Applied to can-next. thanks, Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 455 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:39 ` Marc Kleine-Budde @ 2016-01-20 14:41 ` Marek Vasut 2016-01-20 14:46 ` Marc Kleine-Budde 0 siblings, 1 reply; 22+ messages in thread From: Marek Vasut @ 2016-01-20 14:41 UTC (permalink / raw) To: Marc Kleine-Budde Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger On Wednesday, January 20, 2016 at 03:39:51 PM, Marc Kleine-Budde wrote: > On 01/20/2016 03:33 PM, Marek Vasut wrote: > > The patch adds support for IFI CAN/FD controller [1]. This driver > > currently supports sending and receiving both standard CAN and new > > CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. > > > > [1] http://www.ifi-pld.de/IP/CANFD/canfd.html > > > > Signed-off-by: Marek Vasut <marex@denx.de> > > Cc: Marc Kleine-Budde <mkl@pengutronix.de> > > Cc: Mark Rutland <mark.rutland@arm.com> > > Cc: Oliver Hartkopp <socketcan@hartkopp.net> > > Cc: Wolfgang Grandegger <wg@grandegger.com> > > --- > > V2: - Move request_irq()/free_irq() into > > ifi_canfd_open()/ifi_canfd_close() > > > > just like other drivers do it to prevent crash when reloading > > module. > > > > - Fix Woverflow complains on x86_64 and itanium, exactly the same way > > > > as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . > > > > V3: - Hopefully fix all problems with BIT(31) by adding more u32 casts. > > > > - Drop struct device from struct ifi_canfd_priv . > > > > NOTE: The driver is surprisingly similar to m_can, but the register > > > > layout of the IFI core is completely different, so it's clear > > that those are two different IP cores. > > > > --- [...] Hi, > > +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) > > +{ > > + struct net_device *ndev = (struct net_device *)dev_id; > > + struct ifi_canfd_priv *priv = netdev_priv(ndev); > > + struct net_device_stats *stats = &ndev->stats; > > + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | > > + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; > > + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | > > + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; > > + const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | > > + IFI_CANFD_INTERRUPT_ERROR_WARNING)); > > I've squashed: > >> - const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | > >> - > >> IFI_CANFD_INTERRUPT_ERROR_WARNING)); + const u32 clr_irq_mask = > >> ~(IFI_CANFD_INTERRUPT_SET_IRQ | + > >> IFI_CANFD_INTERRUPT_ERROR_WARNING); > > and the driver compiles without warnings. It doesn't , try with x86_64_defconfig, it will complain with -Woverflow on gcc 4.9 or newer. That's what the kernel robot complained about in V1 of the patch too. I'd be happy to be proven wrong though. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:41 ` Marek Vasut @ 2016-01-20 14:46 ` Marc Kleine-Budde 2016-01-20 15:03 ` Marek Vasut 0 siblings, 1 reply; 22+ messages in thread From: Marc Kleine-Budde @ 2016-01-20 14:46 UTC (permalink / raw) To: Marek Vasut; +Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 3083 bytes --] On 01/20/2016 03:41 PM, Marek Vasut wrote: > On Wednesday, January 20, 2016 at 03:39:51 PM, Marc Kleine-Budde wrote: >> On 01/20/2016 03:33 PM, Marek Vasut wrote: >>> The patch adds support for IFI CAN/FD controller [1]. This driver >>> currently supports sending and receiving both standard CAN and new >>> CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. >>> >>> [1] http://www.ifi-pld.de/IP/CANFD/canfd.html >>> >>> Signed-off-by: Marek Vasut <marex@denx.de> >>> Cc: Marc Kleine-Budde <mkl@pengutronix.de> >>> Cc: Mark Rutland <mark.rutland@arm.com> >>> Cc: Oliver Hartkopp <socketcan@hartkopp.net> >>> Cc: Wolfgang Grandegger <wg@grandegger.com> >>> --- >>> V2: - Move request_irq()/free_irq() into >>> ifi_canfd_open()/ifi_canfd_close() >>> >>> just like other drivers do it to prevent crash when reloading >>> module. >>> >>> - Fix Woverflow complains on x86_64 and itanium, exactly the same way >>> >>> as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . >>> >>> V3: - Hopefully fix all problems with BIT(31) by adding more u32 casts. >>> >>> - Drop struct device from struct ifi_canfd_priv . >>> >>> NOTE: The driver is surprisingly similar to m_can, but the register >>> >>> layout of the IFI core is completely different, so it's clear >>> that those are two different IP cores. >>> >>> --- > > [...] > > Hi, > >>> +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) >>> +{ >>> + struct net_device *ndev = (struct net_device *)dev_id; >>> + struct ifi_canfd_priv *priv = netdev_priv(ndev); >>> + struct net_device_stats *stats = &ndev->stats; >>> + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | >>> + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; >>> + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | >>> + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; >>> + const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | >>> + IFI_CANFD_INTERRUPT_ERROR_WARNING)); >> >> I've squashed: >>>> - const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | >>>> - >>>> IFI_CANFD_INTERRUPT_ERROR_WARNING)); + const u32 clr_irq_mask = >>>> ~(IFI_CANFD_INTERRUPT_SET_IRQ | + >>>> IFI_CANFD_INTERRUPT_ERROR_WARNING); >> >> and the driver compiles without warnings. > > It doesn't , try with x86_64_defconfig, it will complain with -Woverflow > on gcc 4.9 or newer. That's what the kernel robot complained about in V1 > of the patch too. Doh! Right, let's try this: >> -#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) >> +#define IFI_CANFD_INTERRUPT_ERROR_WARNING ((u32)BIT(1)) > I'd be happy to be proven wrong though. /me too Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 455 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 14:46 ` Marc Kleine-Budde @ 2016-01-20 15:03 ` Marek Vasut 2016-01-20 15:09 ` Marc Kleine-Budde 0 siblings, 1 reply; 22+ messages in thread From: Marek Vasut @ 2016-01-20 15:03 UTC (permalink / raw) To: Marc Kleine-Budde Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger On Wednesday, January 20, 2016 at 03:46:58 PM, Marc Kleine-Budde wrote: > On 01/20/2016 03:41 PM, Marek Vasut wrote: > > On Wednesday, January 20, 2016 at 03:39:51 PM, Marc Kleine-Budde wrote: > >> On 01/20/2016 03:33 PM, Marek Vasut wrote: > >>> The patch adds support for IFI CAN/FD controller [1]. This driver > >>> currently supports sending and receiving both standard CAN and new > >>> CAN/FD frames. Both ISO and BOSCH variant of CAN/FD is supported. > >>> > >>> [1] http://www.ifi-pld.de/IP/CANFD/canfd.html > >>> > >>> Signed-off-by: Marek Vasut <marex@denx.de> > >>> Cc: Marc Kleine-Budde <mkl@pengutronix.de> > >>> Cc: Mark Rutland <mark.rutland@arm.com> > >>> Cc: Oliver Hartkopp <socketcan@hartkopp.net> > >>> Cc: Wolfgang Grandegger <wg@grandegger.com> > >>> --- > >>> V2: - Move request_irq()/free_irq() into > >>> ifi_canfd_open()/ifi_canfd_close() > >>> > >>> just like other drivers do it to prevent crash when reloading > >>> module. > >>> > >>> - Fix Woverflow complains on x86_64 and itanium, exactly the same > >>> way > >>> > >>> as in commit dec23dca5a9ca4b9eb2fb66926f567889028b904 . > >>> > >>> V3: - Hopefully fix all problems with BIT(31) by adding more u32 casts. > >>> > >>> - Drop struct device from struct ifi_canfd_priv . > >>> > >>> NOTE: The driver is surprisingly similar to m_can, but the register > >>> > >>> layout of the IFI core is completely different, so it's clear > >>> that those are two different IP cores. > >>> > >>> --- > > > > [...] > > > > Hi, > > > >>> +static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) > >>> +{ > >>> + struct net_device *ndev = (struct net_device *)dev_id; > >>> + struct ifi_canfd_priv *priv = netdev_priv(ndev); > >>> + struct net_device_stats *stats = &ndev->stats; > >>> + const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | > >>> + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; > >>> + const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | > >>> + IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; > >>> + const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | > >>> + IFI_CANFD_INTERRUPT_ERROR_WARNING)); > >> > >> I've squashed: > >>>> - const u32 clr_irq_mask = (u32)(~(IFI_CANFD_INTERRUPT_SET_IRQ | > >>>> - > >>>> IFI_CANFD_INTERRUPT_ERROR_WARNING)); + const u32 clr_irq_mask = > >>>> ~(IFI_CANFD_INTERRUPT_SET_IRQ | + > >>>> IFI_CANFD_INTERRUPT_ERROR_WARNING); > >> > >> and the driver compiles without warnings. > > > > It doesn't , try with x86_64_defconfig, it will complain with -Woverflow > > on gcc 4.9 or newer. That's what the kernel robot complained about in V1 > > of the patch too. > > Doh! Right, let's try this: > >> -#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) > >> +#define IFI_CANFD_INTERRUPT_ERROR_WARNING ((u32)BIT(1)) > > > > I'd be happy to be proven wrong though. > > /me too I think that will do the trick too. Do you want a V4 patch or will you fix it? ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 15:03 ` Marek Vasut @ 2016-01-20 15:09 ` Marc Kleine-Budde 2016-01-20 15:40 ` Marek Vasut 0 siblings, 1 reply; 22+ messages in thread From: Marc Kleine-Budde @ 2016-01-20 15:09 UTC (permalink / raw) To: Marek Vasut; +Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger [-- Attachment #1: Type: text/plain, Size: 662 bytes --] On 01/20/2016 04:03 PM, Marek Vasut wrote: >> >> Doh! Right, let's try this: >>>> -#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) >>>> +#define IFI_CANFD_INTERRUPT_ERROR_WARNING ((u32)BIT(1)) >>> >>> I'd be happy to be proven wrong though. >> >> /me too > > I think that will do the trick too. Do you want a V4 patch or will you fix it? I'll fix this. Marc -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 455 bytes --] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH V3 5/5] net: can: ifi: Add IFI CANFD IP support 2016-01-20 15:09 ` Marc Kleine-Budde @ 2016-01-20 15:40 ` Marek Vasut 0 siblings, 0 replies; 22+ messages in thread From: Marek Vasut @ 2016-01-20 15:40 UTC (permalink / raw) To: Marc Kleine-Budde Cc: netdev, Oliver Hartkopp, Mark Rutland, Wolfgang Grandegger On Wednesday, January 20, 2016 at 04:09:48 PM, Marc Kleine-Budde wrote: > On 01/20/2016 04:03 PM, Marek Vasut wrote: > >> Doh! Right, let's try this: > >>>> -#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) > >>>> +#define IFI_CANFD_INTERRUPT_ERROR_WARNING ((u32)BIT(1)) > >>> > >>> I'd be happy to be proven wrong though. > >> > >> /me too > > > > I think that will do the trick too. Do you want a V4 patch or will you > > fix it? > > I'll fix this. Thanks :) Best regards, Marek Vasut ^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2016-01-23 0:11 UTC | newest] Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-01-11 18:47 [PATCH 1/5] net: can: Sort the Kconfig includes Marek Vasut 2016-01-11 18:48 ` [PATCH 2/5] net: can: Sort the Makefile Marek Vasut 2016-01-11 18:48 ` [PATCH 3/5] of: Add vendor prefix for I/F/I Marek Vasut 2016-01-11 18:48 ` [PATCH 4/5] net: can: ifi: Add DT bindings for ifi,canfd Marek Vasut 2016-01-11 18:48 ` [PATCH 5/5] net: can: ifi: Add IFI CANFD IP support Marek Vasut 2016-01-11 19:29 ` kbuild test robot 2016-01-11 19:33 ` Marek Vasut 2016-01-12 11:06 ` kbuild test robot 2016-01-12 23:59 ` [PATCH V2 " Marek Vasut 2016-01-20 10:39 ` Marc Kleine-Budde 2016-01-20 10:54 ` Marc Kleine-Budde 2016-01-20 12:58 ` Marek Vasut 2016-01-20 14:06 ` Marc Kleine-Budde 2016-01-20 14:25 ` Marek Vasut 2016-01-23 0:11 ` Rustad, Mark D 2016-01-20 14:33 ` [PATCH V3 " Marek Vasut 2016-01-20 14:39 ` Marc Kleine-Budde 2016-01-20 14:41 ` Marek Vasut 2016-01-20 14:46 ` Marc Kleine-Budde 2016-01-20 15:03 ` Marek Vasut 2016-01-20 15:09 ` Marc Kleine-Budde 2016-01-20 15:40 ` Marek Vasut
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.