diff mbox

[V2,5/5] net: can: ifi: Add IFI CANFD IP support

Message ID 1452643156-7291-1-git-send-email-marex@denx.de
State Awaiting Upstream, archived
Delegated to: David Miller
Headers show

Commit Message

Marek Vasut Jan. 12, 2016, 11:59 p.m. UTC
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

Comments

Marc Kleine-Budde Jan. 20, 2016, 10:39 a.m. UTC | #1
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
Marc Kleine-Budde Jan. 20, 2016, 10:54 a.m. UTC | #2
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
Marek Vasut Jan. 20, 2016, 12:58 p.m. UTC | #3
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
Marc Kleine-Budde Jan. 20, 2016, 2:06 p.m. UTC | #4
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
Marek Vasut Jan. 20, 2016, 2:25 p.m. UTC | #5
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
Rustad, Mark D Jan. 23, 2016, 12:11 a.m. UTC | #6
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.

--
Mark Rustad, Networking Division, Intel Corporation
diff mbox

Patch

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");