cregit-Linux how code gets into the kernel

Release 4.11 drivers/net/can/ifi_canfd/ifi_canfd.c

/*
 * 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_ENABLE_7_9_8_8_TIMING	BIT(26)

#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_ERROR_COUNTER	BIT(10)

#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_7_9_8_8		25

#define IFI_CANFD_TIME_SJW_OFF_4_12_6_6		28

#define IFI_CANFD_TIME_SET_SJW_4_12_6_6		BIT(6)

#define IFI_CANFD_TIME_SET_TIMEB_4_12_6_6	BIT(7)

#define IFI_CANFD_TIME_SET_PRESC_4_12_6_6	BIT(14)

#define IFI_CANFD_TIME_SET_TIMEA_4_12_6_6	BIT(15)


#define IFI_CANFD_TDELAY			0x1c

#define IFI_CANFD_TDELAY_DEFAULT		0xb

#define IFI_CANFD_TDELAY_MASK			0x3fff

#define IFI_CANFD_TDELAY_ABS			BIT(14)

#define IFI_CANFD_TDELAY_EN			BIT(15)


#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_ERROR_CTR			0x44

#define IFI_CANFD_ERROR_CTR_UNLOCK_MAGIC	0x21302899

#define IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST	BIT(0)

#define IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST	BIT(1)

#define IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST	BIT(2)

#define IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST	BIT(3)

#define IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST	BIT(4)

#define IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST	BIT(5)

#define IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST	BIT(6)

#define IFI_CANFD_ERROR_CTR_OVERLOAD_ALL	BIT(8)

#define IFI_CANFD_ERROR_CTR_ACK_ERROR_ALL	BIT(9)

#define IFI_CANFD_ERROR_CTR_BIT0_ERROR_ALL	BIT(10)

#define IFI_CANFD_ERROR_CTR_BIT1_ERROR_ALL	BIT(11)

#define IFI_CANFD_ERROR_CTR_STUFF_ERROR_ALL	BIT(12)

#define IFI_CANFD_ERROR_CTR_CRC_ERROR_ALL	BIT(13)

#define IFI_CANFD_ERROR_CTR_FORM_ERROR_ALL	BIT(14)

#define IFI_CANFD_ERROR_CTR_BITPOSITION_OFFSET	16

#define IFI_CANFD_ERROR_CTR_BITPOSITION_MASK	0xff

#define IFI_CANFD_ERROR_CTR_ER_RESET		BIT(30)

#define IFI_CANFD_ERROR_CTR_ER_ENABLE		((u32)BIT(31))


#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		CAN_SFF_MASK

#define IFI_CANFD_RXFIFO_ID_ID_STD_OFFSET	0

#define IFI_CANFD_RXFIFO_ID_ID_STD_WIDTH	10

#define IFI_CANFD_RXFIFO_ID_ID_XTD_MASK		CAN_EFF_MASK

#define IFI_CANFD_RXFIFO_ID_ID_XTD_OFFSET	11

#define IFI_CANFD_RXFIFO_ID_ID_XTD_WIDTH	18

#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		CAN_SFF_MASK

#define IFI_CANFD_TXFIFO_ID_ID_STD_OFFSET	0

#define IFI_CANFD_TXFIFO_ID_ID_STD_WIDTH	10

#define IFI_CANFD_TXFIFO_ID_ID_XTD_MASK		CAN_EFF_MASK

#define IFI_CANFD_TXFIFO_ID_ID_XTD_OFFSET	11

#define IFI_CANFD_TXFIFO_ID_ID_XTD_WIDTH	18

#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; if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER; } 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); }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut74100.00%2100.00%
Total74100.00%2100.00%


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; /* * In case the Extended ID frame is received, the standard * and extended part of the ID are swapped in the register, * so swap them back to obtain the correct ID. */ id = (id >> IFI_CANFD_RXFIFO_ID_ID_XTD_OFFSET) | ((id & IFI_CANFD_RXFIFO_ID_ID_STD_MASK) << IFI_CANFD_RXFIFO_ID_ID_XTD_WIDTH); id |= CAN_EFF_FLAG; } 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); }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut363100.00%2100.00%
Total363100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut118100.00%1100.00%
Total118100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut92100.00%1100.00%
Total92100.00%1100.00%


static int ifi_canfd_handle_lec_err(struct net_device *ndev, const u32 errctr) { struct ifi_canfd_priv *priv = netdev_priv(ndev); struct net_device_stats *stats = &ndev->stats; struct can_frame *cf; struct sk_buff *skb; const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST | IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST | IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST | IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST | IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST | IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST | IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST; if (!(errctr & errmask)) /* No error happened. */ return 0; priv->can.can_stats.bus_error++; stats->rx_errors++; /* Propagate the error condition to the CAN stack. */ skb = alloc_can_err_skb(ndev, &cf); if (unlikely(!skb)) return 0; /* Read the error counter register and check for new errors. */ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; if (errctr & IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST) cf->data[2] |= CAN_ERR_PROT_OVERLOAD; if (errctr & IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST) cf->data[3] = CAN_ERR_PROT_LOC_ACK; if (errctr & IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST) cf->data[2] |= CAN_ERR_PROT_BIT0; if (errctr & IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST) cf->data[2] |= CAN_ERR_PROT_BIT1; if (errctr & IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST) cf->data[2] |= CAN_ERR_PROT_STUFF; if (errctr & IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST) cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; if (errctr & IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST) cf->data[2] |= CAN_ERR_PROT_FORM; /* Reset the error counter, ack the IRQ and re-enable the counter. */ writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); writel(IFI_CANFD_INTERRUPT_ERROR_COUNTER, priv->base + IFI_CANFD_INTERRUPT); writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; netif_receive_skb(skb); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut281100.00%1100.00%
Total281100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut68100.00%1100.00%
Total68100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut318100.00%1100.00%
Total318100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut167100.00%1100.00%
Total167100.00%1100.00%


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_RXSTCMD); u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR); /* 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 lec errors on the bus */ if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) work_done += ifi_canfd_handle_lec_err(ndev, errctr); /* 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_done(napi, work_done); ifi_canfd_irq_enable(ndev, 1); } return work_done; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut18697.89%250.00%
Eric Dumazet31.58%125.00%
Markus Marb10.53%125.00%
Total190100.00%4100.00%


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 | IFI_CANFD_INTERRUPT_ERROR_WARNING | IFI_CANFD_INTERRUPT_ERROR_COUNTER; 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 or bus warning, start NAPI */ if (isr & rx_irq_mask) { ifi_canfd_irq_enable(ndev, 0); napi_schedule(&priv->napi); } /* TX IRQ */ if (isr & IFI_CANFD_INTERRUPT_TXFIFO_REMOVE) { stats->tx_bytes += can_get_echo_skb(ndev, 0); stats->tx_packets++; can_led_event(ndev, CAN_LED_EVENT_TX); } if (isr & tx_irq_mask) netif_wake_queue(ndev); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut187100.00%4100.00%
Total187100.00%4100.00%

static const struct can_bittiming_const ifi_canfd_bittiming_const = { .name = KBUILD_MODNAME, .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_max = 256, .tseg2_min = 2, /* Time segment 2 = phase_seg2 */ .tseg2_max = 256, .sjw_max = 128, .brp_min = 2, .brp_max = 512, .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, tdc; /* Configure bit timing */ brp = bt->brp - 2; sjw = bt->sjw - 1; tseg1 = bt->prop_seg + bt->phase_seg1 - 1; tseg2 = bt->phase_seg2 - 2; writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | (brp << IFI_CANFD_TIME_PRESCALE_OFF) | (sjw << IFI_CANFD_TIME_SJW_OFF_7_9_8_8), priv->base + IFI_CANFD_TIME); /* Configure data bit timing */ brp = dbt->brp - 2; sjw = dbt->sjw - 1; tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; tseg2 = dbt->phase_seg2 - 2; writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | (brp << IFI_CANFD_TIME_PRESCALE_OFF) | (sjw << IFI_CANFD_TIME_SJW_OFF_7_9_8_8), priv->base + IFI_CANFD_FTIME); /* Configure transmitter delay */ tdc = (dbt->brp * (dbt->phase_seg1 + 1)) & IFI_CANFD_TDELAY_MASK; writel(IFI_CANFD_TDELAY_EN | IFI_CANFD_TDELAY_ABS | tdc, priv->base + IFI_CANFD_TDELAY); }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut232100.00%4100.00%
Total232100.00%4100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut61100.00%1100.00%
Total61100.00%1100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut61100.00%1100.00%
Total61100.00%1100.00%


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(IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING, 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 | IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING; 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) && !(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) stcmd |= IFI_CANFD_STCMD_ENABLE_ISO; if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD)) stcmd |= IFI_CANFD_STCMD_DISABLE_CANFD; priv->can.state = CAN_STATE_ERROR_ACTIVE; ifi_canfd_irq_enable(ndev, 1); /* Unlock, reset and enable the error counter. */ writel(IFI_CANFD_ERROR_CTR_UNLOCK_MAGIC, priv->base + IFI_CANFD_ERROR_CTR); writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); /* Enable controller */ writel(stcmd, priv->base + IFI_CANFD_STCMD); }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut284100.00%4100.00%
Total284100.00%4100.00%


static void ifi_canfd_stop(struct net_device *ndev) { struct ifi_canfd_priv *priv = netdev_priv(ndev); /* Reset and disable the error counter. */ writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); writel(0, priv->base + IFI_CANFD_ERROR_CTR); /* 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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut96100.00%2100.00%
Total96100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut43100.00%1100.00%
Total43100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut123100.00%1100.00%
Total123100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut63100.00%1100.00%
Total63100.00%1100.00%


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, txdlc; 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; /* * In case the Extended ID frame is transmitted, the * standard and extended part of the ID are swapped * in the register, so swap them back to send the * correct ID. */ txid = (txid >> IFI_CANFD_TXFIFO_ID_ID_XTD_WIDTH) | ((txid & IFI_CANFD_TXFIFO_ID_ID_XTD_MASK) << IFI_CANFD_TXFIFO_ID_ID_XTD_OFFSET); txid |= IFI_CANFD_TXFIFO_ID_IDE; } else { txid = cf->can_id & CAN_SFF_MASK; } txdlc = can_len2dlc(cf->len); if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && 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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut321100.00%3100.00%
Total321100.00%3100.00%

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_CANCLOCK); priv->can.bittiming_const = &ifi_canfd_bittiming_const; priv->can.data_bittiming_const = &ifi_canfd_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 | CAN_CTRLMODE_BERR_REPORTING; 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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut361100.00%4100.00%
Total361100.00%4100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut41100.00%1100.00%
Total41100.00%1100.00%

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

Overall Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut435899.91%1285.71%
Eric Dumazet30.07%17.14%
Markus Marb10.02%17.14%
Total4362100.00%14100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.