diff options
author | David S. Miller <davem@davemloft.net> | 2017-04-25 10:52:47 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-04-25 10:52:47 -0400 |
commit | 45a6f3bca64ac8f462a275f351dcc3f032073d92 (patch) | |
tree | a012e3e981c39e66e6c94c11984533ddb1ae5b38 /drivers | |
parent | 3133822f5ac13b04c2ca46f27cfe74606bbd4a6d (diff) | |
parent | b655f0e96d4061eac42dea2dccd37a3348d1f3f3 (diff) |
Merge tag 'linux-can-next-for-4.12-20170425' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next
Marc Kleine-Budde says:
====================
pull-request: can-next 2017-04-25
this is a pull request of 21 patches for net-next/master.
There are 4 patches by Stephane Grosjean for the PEAK PCAN-PCIe FD
CAN-FD boards. The next 7 patches are by Mario Huettel, which add
support for M_CAN IP version >= v3.1.x to the m_can driver. A patch by
Remigiusz Kołłątaj adds support for the Microchip CAN BUS Analyzer. 8
patches by Oliver Hartkopp complete the initial CAN network namespace
support. Wei Yongjun's patch for the ti_hecc driver fixes the return
value check in the probe function.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/can/Kconfig | 19 | ||||
-rw-r--r-- | drivers/net/can/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/can/m_can/m_can.c | 752 | ||||
-rw-r--r-- | drivers/net/can/peak_canfd/Kconfig | 13 | ||||
-rw-r--r-- | drivers/net/can/peak_canfd/Makefile | 5 | ||||
-rw-r--r-- | drivers/net/can/peak_canfd/peak_canfd.c | 801 | ||||
-rw-r--r-- | drivers/net/can/peak_canfd/peak_canfd_user.h | 55 | ||||
-rw-r--r-- | drivers/net/can/peak_canfd/peak_pciefd_main.c | 842 | ||||
-rw-r--r-- | drivers/net/can/ti_hecc.c | 12 | ||||
-rw-r--r-- | drivers/net/can/usb/Kconfig | 6 | ||||
-rw-r--r-- | drivers/net/can/usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/usb/mcba_usb.c | 904 | ||||
-rw-r--r-- | drivers/net/can/usb/peak_usb/pcan_ucan.h | 244 | ||||
-rw-r--r-- | drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 25 | ||||
-rw-r--r-- | drivers/net/can/vcan.c | 7 | ||||
-rw-r--r-- | drivers/net/can/vxcan.c | 316 |
16 files changed, 3573 insertions, 431 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 22570ea3a8d2..ac4ff394bc56 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -9,6 +9,24 @@ config CAN_VCAN This driver can also be built as a module. If so, the module will be called vcan. +config CAN_VXCAN + tristate "Virtual CAN Tunnel (vxcan)" + ---help--- + Similar to the virtual ethernet driver veth, vxcan implements a + local CAN traffic tunnel between two virtual CAN network devices. + When creating a vxcan, two vxcan devices are created as pair. + When one end receives the packet it appears on its pair and vice + versa. The vxcan can be used for cross namespace communication. + + In opposite to vcan loopback devices the vxcan only forwards CAN + frames to its pair and does *not* provide a local echo of sent + CAN frames. To disable a potential echo in af_can.c the vxcan driver + announces IFF_ECHO in the interface flags. To have a clean start + in each namespace the CAN GW hop counter is set to zero. + + This driver can also be built as a module. If so, the module + will be called vxcan. + config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" depends on TTY @@ -142,6 +160,7 @@ 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" +source "drivers/net/can/peak_canfd/Kconfig" source "drivers/net/can/rcar/Kconfig" source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/softing/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 0da4f2f5c7e3..4aabbee133b8 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_CAN_VCAN) += vcan.o +obj-$(CONFIG_CAN_VXCAN) += vxcan.o obj-$(CONFIG_CAN_SLCAN) += slcan.o obj-$(CONFIG_CAN_DEV) += can-dev.o @@ -26,6 +27,7 @@ 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/ +obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 7a6554efd42b..bf8fdaeb955e 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -23,7 +23,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> - +#include <linux/iopoll.h> #include <linux/can/dev.h> /* napi related */ @@ -37,17 +37,19 @@ enum m_can_reg { M_CAN_CREL = 0x0, M_CAN_ENDN = 0x4, M_CAN_CUST = 0x8, - M_CAN_FBTP = 0xc, + M_CAN_DBTP = 0xc, M_CAN_TEST = 0x10, M_CAN_RWD = 0x14, M_CAN_CCCR = 0x18, - M_CAN_BTP = 0x1c, + M_CAN_NBTP = 0x1c, M_CAN_TSCC = 0x20, M_CAN_TSCV = 0x24, M_CAN_TOCC = 0x28, M_CAN_TOCV = 0x2c, M_CAN_ECR = 0x40, M_CAN_PSR = 0x44, +/* TDCR Register only available for version >=3.1.x */ + M_CAN_TDCR = 0x48, M_CAN_IR = 0x50, M_CAN_IE = 0x54, M_CAN_ILS = 0x58, @@ -105,21 +107,29 @@ enum m_can_mram_cfg { MRAM_CFG_NUM, }; -/* Fast Bit Timing & Prescaler Register (FBTP) */ -#define FBTR_FBRP_MASK 0x1f -#define FBTR_FBRP_SHIFT 16 -#define FBTR_FTSEG1_SHIFT 8 -#define FBTR_FTSEG1_MASK (0xf << FBTR_FTSEG1_SHIFT) -#define FBTR_FTSEG2_SHIFT 4 -#define FBTR_FTSEG2_MASK (0x7 << FBTR_FTSEG2_SHIFT) -#define FBTR_FSJW_SHIFT 0 -#define FBTR_FSJW_MASK 0x3 +/* Core Release Register (CREL) */ +#define CREL_REL_SHIFT 28 +#define CREL_REL_MASK (0xF << CREL_REL_SHIFT) +#define CREL_STEP_SHIFT 24 +#define CREL_STEP_MASK (0xF << CREL_STEP_SHIFT) +#define CREL_SUBSTEP_SHIFT 20 +#define CREL_SUBSTEP_MASK (0xF << CREL_SUBSTEP_SHIFT) + +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_TDC BIT(23) +#define DBTP_DBRP_SHIFT 16 +#define DBTP_DBRP_MASK (0x1f << DBTP_DBRP_SHIFT) +#define DBTP_DTSEG1_SHIFT 8 +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT) +#define DBTP_DTSEG2_SHIFT 4 +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT) +#define DBTP_DSJW_SHIFT 0 +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT) /* Test Register (TEST) */ -#define TEST_LBCK BIT(4) +#define TEST_LBCK BIT(4) /* CC Control Register(CCCR) */ -#define CCCR_TEST BIT(7) #define CCCR_CMR_MASK 0x3 #define CCCR_CMR_SHIFT 10 #define CCCR_CMR_CANFD 0x1 @@ -130,21 +140,32 @@ enum m_can_mram_cfg { #define CCCR_CME_CAN 0 #define CCCR_CME_CANFD 0x1 #define CCCR_CME_CANFD_BRS 0x2 +#define CCCR_TXP BIT(14) #define CCCR_TEST BIT(7) #define CCCR_MON BIT(5) +#define CCCR_CSR BIT(4) +#define CCCR_CSA BIT(3) +#define CCCR_ASM BIT(2) #define CCCR_CCE BIT(1) #define CCCR_INIT BIT(0) #define CCCR_CANFD 0x10 - -/* Bit Timing & Prescaler Register (BTP) */ -#define BTR_BRP_MASK 0x3ff -#define BTR_BRP_SHIFT 16 -#define BTR_TSEG1_SHIFT 8 -#define BTR_TSEG1_MASK (0x3f << BTR_TSEG1_SHIFT) -#define BTR_TSEG2_SHIFT 4 -#define BTR_TSEG2_MASK (0xf << BTR_TSEG2_SHIFT) -#define BTR_SJW_SHIFT 0 -#define BTR_SJW_MASK 0xf +/* for version >=3.1.x */ +#define CCCR_EFBI BIT(13) +#define CCCR_PXHD BIT(12) +#define CCCR_BRSE BIT(9) +#define CCCR_FDOE BIT(8) +/* only for version >=3.2.x */ +#define CCCR_NISO BIT(15) + +/* Nominal Bit Timing & Prescaler Register (NBTP) */ +#define NBTP_NSJW_SHIFT 25 +#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT) +#define NBTP_NBRP_SHIFT 16 +#define NBTP_NBRP_MASK (0x1ff << NBTP_NBRP_SHIFT) +#define NBTP_NTSEG1_SHIFT 8 +#define NBTP_NTSEG1_MASK (0xff << NBTP_NTSEG1_SHIFT) +#define NBTP_NTSEG2_SHIFT 0 +#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT) /* Error Counter Register(ECR) */ #define ECR_RP BIT(15) @@ -161,6 +182,13 @@ enum m_can_mram_cfg { /* Interrupt Register(IR) */ #define IR_ALL_INT 0xffffffff + +/* Renamed bits for versions > 3.1.x */ +#define IR_ARA BIT(29) +#define IR_PED BIT(28) +#define IR_PEA BIT(27) + +/* Bits for version 3.0.x */ #define IR_STE BIT(31) #define IR_FOE BIT(30) #define IR_ACKE BIT(29) @@ -194,33 +222,40 @@ enum m_can_mram_cfg { #define IR_RF0W BIT(1) #define IR_RF0N BIT(0) #define IR_ERR_STATE (IR_BO | IR_EW | IR_EP) -#define IR_ERR_LEC (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE) -#define IR_ERR_BUS (IR_ERR_LEC | IR_WDI | IR_ELO | IR_BEU | \ + +/* Interrupts for version 3.0.x */ +#define IR_ERR_LEC_30X (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE) +#define IR_ERR_BUS_30X (IR_ERR_LEC_30X | IR_WDI | IR_ELO | IR_BEU | \ IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \ IR_RF1L | IR_RF0L) -#define IR_ERR_ALL (IR_ERR_STATE | IR_ERR_BUS) +#define IR_ERR_ALL_30X (IR_ERR_STATE | IR_ERR_BUS_30X) +/* Interrupts for version >= 3.1.x */ +#define IR_ERR_LEC_31X (IR_PED | IR_PEA) +#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_ELO | IR_BEU | \ + IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \ + IR_RF1L | IR_RF0L) +#define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X) /* Interrupt Line Select (ILS) */ #define ILS_ALL_INT0 0x0 #define ILS_ALL_INT1 0xFFFFFFFF /* Interrupt Line Enable (ILE) */ -#define ILE_EINT0 BIT(0) #define ILE_EINT1 BIT(1) +#define ILE_EINT0 BIT(0) /* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */ -#define RXFC_FWM_OFF 24 -#define RXFC_FWM_MASK 0x7f -#define RXFC_FWM_1 (1 << RXFC_FWM_OFF) -#define RXFC_FS_OFF 16 -#define RXFC_FS_MASK 0x7f +#define RXFC_FWM_SHIFT 24 +#define RXFC_FWM_MASK (0x7f < RXFC_FWM_SHIFT) +#define RXFC_FS_SHIFT 16 +#define RXFC_FS_MASK (0x7f << RXFC_FS_SHIFT) /* Rx FIFO 0/1 Status (RXF0S/RXF1S) */ #define RXFS_RFL BIT(25) #define RXFS_FF BIT(24) -#define RXFS_FPI_OFF 16 +#define RXFS_FPI_SHIFT 16 #define RXFS_FPI_MASK 0x3f0000 -#define RXFS_FGI_OFF 8 +#define RXFS_FGI_SHIFT 8 #define RXFS_FGI_MASK 0x3f00 #define RXFS_FFL_MASK 0x7f @@ -229,23 +264,46 @@ enum m_can_mram_cfg { #define M_CAN_RXESC_64BYTES 0x777 /* Tx Buffer Configuration(TXBC) */ -#define TXBC_NDTB_OFF 16 -#define TXBC_NDTB_MASK 0x3f +#define TXBC_NDTB_SHIFT 16 +#define TXBC_NDTB_MASK (0x3f << TXBC_NDTB_SHIFT) +#define TXBC_TFQS_SHIFT 24 +#define TXBC_TFQS_MASK (0x3f << TXBC_TFQS_SHIFT) + +/* Tx FIFO/Queue Status (TXFQS) */ +#define TXFQS_TFQF BIT(21) +#define TXFQS_TFQPI_SHIFT 16 +#define TXFQS_TFQPI_MASK (0x1f << TXFQS_TFQPI_SHIFT) +#define TXFQS_TFGI_SHIFT 8 +#define TXFQS_TFGI_MASK (0x1f << TXFQS_TFGI_SHIFT) +#define TXFQS_TFFL_SHIFT 0 +#define TXFQS_TFFL_MASK (0x3f << TXFQS_TFFL_SHIFT) /* Tx Buffer Element Size Configuration(TXESC) */ #define TXESC_TBDS_8BYTES 0x0 #define TXESC_TBDS_64BYTES 0x7 -/* Tx Event FIFO Con.guration (TXEFC) */ -#define TXEFC_EFS_OFF 16 -#define TXEFC_EFS_MASK 0x3f +/* Tx Event FIFO Configuration (TXEFC) */ +#define TXEFC_EFS_SHIFT 16 +#define TXEFC_EFS_MASK (0x3f << TXEFC_EFS_SHIFT) + +/* Tx Event FIFO Status (TXEFS) */ +#define TXEFS_TEFL BIT(25) +#define TXEFS_EFF BIT(24) +#define TXEFS_EFGI_SHIFT 8 +#define TXEFS_EFGI_MASK (0x1f << TXEFS_EFGI_SHIFT) +#define TXEFS_EFFL_SHIFT 0 +#define TXEFS_EFFL_MASK (0x3f << TXEFS_EFFL_SHIFT) + +/* Tx Event FIFO Acknowledge (TXEFA) */ +#define TXEFA_EFAI_SHIFT 0 +#define TXEFA_EFAI_MASK (0x1f << TXEFA_EFAI_SHIFT) /* Message RAM Configuration (in bytes) */ #define SIDF_ELEMENT_SIZE 4 #define XIDF_ELEMENT_SIZE 8 #define RXF0_ELEMENT_SIZE 72 #define RXF1_ELEMENT_SIZE 72 -#define RXB_ELEMENT_SIZE 16 +#define RXB_ELEMENT_SIZE 72 #define TXE_ELEMENT_SIZE 8 #define TXB_ELEMENT_SIZE 72 @@ -261,13 +319,25 @@ enum m_can_mram_cfg { #define RX_BUF_RTR BIT(29) /* R1 */ #define RX_BUF_ANMF BIT(31) -#define RX_BUF_EDL BIT(21) +#define RX_BUF_FDF BIT(21) #define RX_BUF_BRS BIT(20) /* Tx Buffer Element */ -/* R0 */ +/* T0 */ +#define TX_BUF_ESI BIT(31) #define TX_BUF_XTD BIT(30) #define TX_BUF_RTR BIT(29) +/* T1 */ +#define TX_BUF_EFC BIT(23) +#define TX_BUF_FDF BIT(21) +#define TX_BUF_BRS BIT(20) +#define TX_BUF_MM_SHIFT 24 +#define TX_BUF_MM_MASK (0xff << TX_BUF_MM_SHIFT) + +/* Tx event FIFO Element */ +/* E1 */ +#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT +#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT) /* address offset and element number for each FIFO/Buffer in the Message RAM */ struct mram_cfg { @@ -285,6 +355,7 @@ struct m_can_priv { struct clk *cclk; void __iomem *base; u32 irqstatus; + int version; /* message ram configuration */ void __iomem *mram_base; @@ -316,6 +387,18 @@ static inline void m_can_fifo_write(const struct m_can_priv *priv, fpi * TXB_ELEMENT_SIZE + offset); } +static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv, + u32 fgi, + u32 offset) { + return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off + + fgi * TXE_ELEMENT_SIZE + offset); +} + +static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv) +{ + return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF); +} + static inline void m_can_config_endisable(const struct m_can_priv *priv, bool enable) { @@ -349,7 +432,8 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv, static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv) { - m_can_write(priv, M_CAN_ILE, ILE_EINT0 | ILE_EINT1); + /* Only interrupt line 0 is used in this driver */ + m_can_write(priv, M_CAN_ILE, ILE_EINT0); } static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv) @@ -367,9 +451,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) int i; /* calculate the fifo get index for where to read data */ - fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF; + fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_SHIFT; dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); - if (dlc & RX_BUF_EDL) + if (dlc & RX_BUF_FDF) skb = alloc_canfd_skb(dev, &cf); else skb = alloc_can_skb(dev, (struct can_frame **)&cf); @@ -378,7 +462,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) return; } - if (dlc & RX_BUF_EDL) + if (dlc & RX_BUF_FDF) cf->len = can_dlc2len((dlc >> 16) & 0x0F); else cf->len = get_can_dlc((dlc >> 16) & 0x0F); @@ -394,7 +478,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) netdev_dbg(dev, "ESI Error\n"); } - if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) { + if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) { cf->can_id |= CAN_RTR_FLAG; } else { if (dlc & RX_BUF_BRS) @@ -532,7 +616,7 @@ static int __m_can_get_berr_counter(const struct net_device *dev, ecr = m_can_read(priv, M_CAN_ECR); bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; - bec->txerr = ecr & ECR_TEC_MASK; + bec->txerr = (ecr & ECR_TEC_MASK) >> ECR_TEC_SHIFT; return 0; } @@ -723,7 +807,7 @@ static int m_can_poll(struct napi_struct *napi, int quota) if (irqstatus & IR_ERR_STATE) work_done += m_can_handle_state_errors(dev, psr); - if (irqstatus & IR_ERR_BUS) + if (irqstatus & IR_ERR_BUS_30X) work_done += m_can_handle_bus_errors(dev, irqstatus, psr); if (irqstatus & IR_RF0N) @@ -738,6 +822,44 @@ end: return work_done; } +static void m_can_echo_tx_event(struct net_device *dev) +{ + u32 txe_count = 0; + u32 m_can_txefs; + u32 fgi = 0; + int i = 0; + unsigned int msg_mark; + + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + + /* read tx event fifo status */ + m_can_txefs = m_can_read(priv, M_CAN_TXEFS); + + /* Get Tx Event fifo element count */ + txe_count = (m_can_txefs & TXEFS_EFFL_MASK) + >> TXEFS_EFFL_SHIFT; + + /* Get and process all sent elements */ + for (i = 0; i < txe_count; i++) { + /* retrieve get index */ + fgi = (m_can_read(priv, M_CAN_TXEFS) & TXEFS_EFGI_MASK) + >> TXEFS_EFGI_SHIFT; + + /* get message marker */ + msg_mark = (m_can_txe_fifo_read(priv, fgi, 4) & + TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; + + /* ack txe element */ + m_can_write(priv, M_CAN_TXEFA, (TXEFA_EFAI_MASK & + (fgi << TXEFA_EFAI_SHIFT))); + + /* update stats */ + stats->tx_bytes += can_get_echo_skb(dev, msg_mark); + stats->tx_packets++; + } +} + static irqreturn_t m_can_isr(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; @@ -758,24 +880,35 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) * - state change IRQ * - bus error IRQ and bus error reporting */ - if ((ir & IR_RF0N) || (ir & IR_ERR_ALL)) { + if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) { priv->irqstatus = ir; m_can_disable_all_interrupts(priv); napi_schedule(&priv->napi); } - /* transmission complete interrupt */ - if (ir & IR_TC) { - stats->tx_bytes += can_get_echo_skb(dev, 0); - stats->tx_packets++; - can_led_event(dev, CAN_LED_EVENT_TX); - netif_wake_queue(dev); + if (priv->version == 30) { + if (ir & IR_TC) { + /* Transmission Complete Interrupt*/ + stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_packets++; + can_led_event(dev, CAN_LED_EVENT_TX); + netif_wake_queue(dev); + } + } else { + if (ir & IR_TEFN) { + /* New TX FIFO Element arrived */ + m_can_echo_tx_event(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + if (netif_queue_stopped(dev) && + !m_can_tx_fifo_full(priv)) + netif_wake_queue(dev); + } } return IRQ_HANDLED; } -static const struct can_bittiming_const m_can_bittiming_const = { +static const struct can_bittiming_const m_can_bittiming_const_30X = { .name = KBUILD_MODNAME, .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_max = 64, @@ -787,7 +920,7 @@ static const struct can_bittiming_const m_can_bittiming_const = { .brp_inc = 1, }; -static const struct can_bittiming_const m_can_data_bittiming_const = { +static const struct can_bittiming_const m_can_data_bittiming_const_30X = { .name = KBUILD_MODNAME, .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_max = 16, @@ -799,6 +932,30 @@ static const struct can_bittiming_const m_can_data_bittiming_const = { .brp_inc = 1, }; +static const struct can_bittiming_const m_can_bittiming_const_31X = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 256, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +static const struct can_bittiming_const m_can_data_bittiming_const_31X = { + .name = KBUILD_MODNAME, + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 32, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + static int m_can_set_bittiming(struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); @@ -811,19 +968,19 @@ static int m_can_set_bittiming(struct net_device *dev) sjw = bt->sjw - 1; tseg1 = bt->prop_seg + bt->phase_seg1 - 1; tseg2 = bt->phase_seg2 - 1; - reg_btp = (brp << BTR_BRP_SHIFT) | (sjw << BTR_SJW_SHIFT) | - (tseg1 << BTR_TSEG1_SHIFT) | (tseg2 << BTR_TSEG2_SHIFT); - m_can_write(priv, M_CAN_BTP, reg_btp); + reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) | + (tseg1 << NBTP_NTSEG1_SHIFT) | (tseg2 << NBTP_NTSEG2_SHIFT); + m_can_write(priv, M_CAN_NBTP, reg_btp); if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { brp = dbt->brp - 1; sjw = dbt->sjw - 1; tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; tseg2 = dbt->phase_seg2 - 1; - reg_btp = (brp << FBTR_FBRP_SHIFT) | (sjw << FBTR_FSJW_SHIFT) | - (tseg1 << FBTR_FTSEG1_SHIFT) | - (tseg2 << FBTR_FTSEG2_SHIFT); - m_can_write(priv, M_CAN_FBTP, reg_btp); + reg_btp = (brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) | + (tseg1 << DBTP_DTSEG1_SHIFT) | + (tseg2 << DBTP_DTSEG2_SHIFT); + m_can_write(priv, M_CAN_DBTP, reg_btp); } return 0; @@ -834,6 +991,7 @@ static int m_can_set_bittiming(struct net_device *dev) * - configure rx fifo * - accept non-matching frame into fifo 0 * - configure tx buffer + * - >= v3.1.x: TX FIFO is used * - configure mode * - setup bittiming */ @@ -850,49 +1008,89 @@ static void m_can_chip_config(struct net_device *dev) /* Accept Non-matching Frames Into FIFO 0 */ m_can_write(priv, M_CAN_GFC, 0x0); - /* only support one Tx Buffer currently */ - m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_OFF) | - priv->mcfg[MRAM_TXB].off); + if (priv->version == 30) { + /* only support one Tx Buffer currently */ + m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) | + priv->mcfg[MRAM_TXB].off); + } else { + /* TX FIFO is used for newer IP Core versions */ + m_can_write(priv, M_CAN_TXBC, + (priv->mcfg[MRAM_TXB].num << TXBC_TFQS_SHIFT) | + (priv->mcfg[MRAM_TXB].off)); + } /* support 64 bytes payload */ m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES); - m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_OFF) | - priv->mcfg[MRAM_TXE].off); + /* TX Event FIFO */ + if (priv->version == 30) { + m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) | + priv->mcfg[MRAM_TXE].off); + } else { + /* Full TX Event FIFO is used */ + m_can_write(priv, M_CAN_TXEFC, + ((priv->mcfg[MRAM_TXE].num << TXEFC_EFS_SHIFT) + & TXEFC_EFS_MASK) | + priv->mcfg[MRAM_TXE].off); + } /* rx fifo configuration, blocking mode, fifo size 1 */ m_can_write(priv, M_CAN_RXF0C, - (priv->mcfg[MRAM_RXF0].num << RXFC_FS_OFF) | - RXFC_FWM_1 | priv->mcfg[MRAM_RXF0].off); + (priv->mcfg[MRAM_RXF0].num << RXFC_FS_SHIFT) | + priv->mcfg[MRAM_RXF0].off); m_can_write(priv, M_CAN_RXF1C, - (priv->mcfg[MRAM_RXF1].num << RXFC_FS_OFF) | - RXFC_FWM_1 | priv->mcfg[MRAM_RXF1].off); + (priv->mcfg[MRAM_RXF1].num << RXFC_FS_SHIFT) | + priv->mcfg[MRAM_RXF1].off); cccr = m_can_read(priv, M_CAN_CCCR); - cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | - (CCCR_CME_MASK << CCCR_CME_SHIFT)); test = m_can_read(priv, M_CAN_TEST); test &= ~TEST_LBCK; + if (priv->version == 30) { + /* Version 3.0.x */ - if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) - cccr |= CCCR_MON; + cccr &= ~(CCCR_TEST | CCCR_MON | + (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | + (CCCR_CME_MASK << CCCR_CME_SHIFT)); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; + + } else { + /* Version 3.1.x or 3.2.x */ + cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE); + /* Only 3.2.x has NISO Bit implemented */ + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + cccr |= CCCR_NISO; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= (CCCR_BRSE | CCCR_FDOE); + } + + /* Loopback Mode */ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { - cccr |= CCCR_TEST; + cccr |= CCCR_TEST | CCCR_MON; test |= TEST_LBCK; } - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) - cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; + /* Enable Monitoring (all versions) */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cccr |= CCCR_MON; + /* Write config */ m_can_write(priv, M_CAN_CCCR, cccr); m_can_write(priv, M_CAN_TEST, test); - /* enable interrupts */ + /* Enable interrupts */ m_can_write(priv, M_CAN_IR, IR_ALL_INT); if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) - m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC); + if (priv->version == 30) + m_can_write(priv, M_CAN_IE, IR_ALL_INT & + ~(IR_ERR_LEC_30X)); + else + m_can_write(priv, M_CAN_IE, IR_ALL_INT & + ~(IR_ERR_LEC_31X)); else m_can_write(priv, M_CAN_IE, IR_ALL_INT); @@ -936,33 +1134,140 @@ static void free_m_can_dev(struct net_device *dev) free_candev(dev); } -static struct net_device *alloc_m_can_dev(void) +/* Checks core release number of M_CAN + * returns 0 if an unsupported device is detected + * else it returns the release and step coded as: + * return value = 10 * <release> + 1 * <step> + */ +static int m_can_check_core_release(void __iomem *m_can_base) +{ + u32 crel_reg; + u8 rel; + u8 step; + int res; + struct m_can_priv temp_priv = { + .base = m_can_base + }; + + /* Read Core Release Version and split into version number + * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1; + */ + crel_reg = m_can_read(&temp_priv, M_CAN_CREL); + rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT); + step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT); + + if (rel == 3) { + /* M_CAN v3.x.y: create return value */ + res = 30 + step; + } else { + /* Unsupported M_CAN version */ + res = 0; + } + + return res; +} + +/* Selectable Non ISO support only in version 3.2.x + * This function checks if the bit is writable. + */ +static bool m_can_niso_supported(const struct m_can_priv *priv) +{ + u32 cccr_reg, cccr_poll; + int niso_timeout; + + m_can_config_endisable(priv, true); + cccr_reg = m_can_read(priv, M_CAN_CCCR); + cccr_reg |= CCCR_NISO; + m_can_write(priv, M_CAN_CCCR, cccr_reg); + + niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll, + (cccr_poll == cccr_reg), 0, 10); + + /* Clear NISO */ + cccr_reg &= ~(CCCR_NISO); + m_can_write(priv, M_CAN_CCCR, cccr_reg); + + m_can_config_endisable(priv, false); + + /* return false if time out (-ETIMEDOUT), else return true */ + return !niso_timeout; +} + +static struct net_device *alloc_m_can_dev(struct platform_device *pdev, + void __iomem *addr, u32 tx_fifo_size) { struct net_device *dev; struct m_can_priv *priv; + int m_can_version; + unsigned int echo_buffer_count; + + m_can_version = m_can_check_core_release(addr); + /* return if unsupported version */ + if (!m_can_version) { + dev = NULL; + goto return_dev; + } - dev = alloc_candev(sizeof(*priv), 1); - if (!dev) - return NULL; + /* If version < 3.1.x, then only one echo buffer is used */ + echo_buffer_count = ((m_can_version == 30) + ? 1U + : (unsigned int)tx_fifo_size); + dev = alloc_candev(sizeof(*priv), echo_buffer_count); + if (!dev) { + dev = NULL; + goto return_dev; + } priv = netdev_priv(dev); netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT); + /* Shared properties of all M_CAN versions */ + priv->version = m_can_version; priv->dev = dev; - priv->can.bittiming_const = &m_can_bittiming_const; - priv->can.data_bittiming_const = &m_can_data_bittiming_const; + priv->base = addr; priv->can.do_set_mode = m_can_set_mode; priv->can.do_get_berr_counter = m_can_get_berr_counter; - /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */ - can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); - - /* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */ + /* Set M_CAN supported operations */ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD; + /* Set properties depending on M_CAN version */ + switch (priv->version) { + case 30: + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */ + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); + priv->can.bittiming_const = &m_can_bittiming_const_30X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_30X; + break; + case 31: + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */ + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); + priv->can.bittiming_const = &m_can_bittiming_const_31X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_31X; + break; + case 32: + priv->can.bittiming_const = &m_can_bittiming_const_31X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_31X; + priv->can.ctrlmode_supported |= (m_can_niso_supported(priv) + ? CAN_CTRLMODE_FD_NON_ISO + : 0); + break; + default: + /* Unsupported device: free candev */ + free_m_can_dev(dev); + dev_err(&pdev->dev, "Unsupported version number: %2d", + priv->version); + dev = NULL; + break; + } + +return_dev: return dev; } @@ -1040,19 +1345,34 @@ static int m_can_close(struct net_device *dev) return 0; } +static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx) +{ + struct m_can_priv *priv = netdev_priv(dev); + /*get wrap around for loopback skb index */ + unsigned int wrap = priv->can.echo_skb_max; + int next_idx; + + /* calculate next index */ + next_idx = (++putidx >= wrap ? 0 : putidx); + + /* check if occupied */ + return !!priv->can.echo_skb[next_idx]; +} + static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); struct canfd_frame *cf = (struct canfd_frame *)skb->data; - u32 id, cccr; + u32 id, cccr, fdflags; int i; + int putidx; if (can_dropped_invalid_skb(dev, skb)) return NETDEV_TX_OK; - netif_stop_queue(dev); - + /* Generate ID field for TX buffer Element */ + /* Common to all supported M_CAN versions */ if (cf->can_id & CAN_EFF_FLAG) { id = cf->can_id & CAN_EFF_MASK; id |= TX_BUF_XTD; @@ -1063,33 +1383,93 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, if (cf->can_id & CAN_RTR_FLAG) id |= TX_BUF_RTR; - /* message ram configuration */ - m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); - m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, can_len2dlc(cf->len) << 16); + if (priv->version == 30) { + netif_stop_queue(dev); - for (i = 0; i < cf->len; i += 4) - m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(i / 4), - *(u32 *)(cf->data + i)); + /* message ram configuration */ + m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); + m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, + can_len2dlc(cf->len) << 16); - can_put_echo_skb(skb, dev, 0); + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, 0, + M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + + can_put_echo_skb(skb, dev, 0); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + cccr = m_can_read(priv, M_CAN_CCCR); + cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); + if (can_is_canfd_skb(skb)) { + if (cf->flags & CANFD_BRS) + cccr |= CCCR_CMR_CANFD_BRS << + CCCR_CMR_SHIFT; + else + cccr |= CCCR_CMR_CANFD << + CCCR_CMR_SHIFT; + } else { + cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT; + } + m_can_write(priv, M_CAN_CCCR, cccr); + } + m_can_write(priv, M_CAN_TXBTIE, 0x1); + m_can_write(priv, M_CAN_TXBAR, 0x1); + /* End of xmit function for version 3.0.x */ + } else { + /* Transmit routine for version >= v3.1.x */ + + /* Check if FIFO full */ + if (m_can_tx_fifo_full(priv)) { + /* This shouldn't happen */ + netif_stop_queue(dev); + netdev_warn(dev, + "TX queue active although FIFO is full."); + return NETDEV_TX_BUSY; + } - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { - cccr = m_can_read(priv, M_CAN_CCCR); - cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); + /* get put index for frame */ + putidx = ((m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQPI_MASK) + >> TXFQS_TFQPI_SHIFT); + /* Write ID Field to FIFO Element */ + m_can_fifo_write(priv, putidx, M_CAN_FIFO_ID, id); + + /* get CAN FD configuration of frame */ + fdflags = 0; if (can_is_canfd_skb(skb)) { + fdflags |= TX_BUF_FDF; if (cf->flags & CANFD_BRS) - cccr |= CCCR_CMR_CANFD_BRS << CCCR_CMR_SHIFT; - else - cccr |= CCCR_CMR_CANFD << CCCR_CMR_SHIFT; - } else { - cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT; + fdflags |= TX_BUF_BRS; } - m_can_write(priv, M_CAN_CCCR, cccr); - } - /* enable first TX buffer to start transfer */ - m_can_write(priv, M_CAN_TXBTIE, 0x1); - m_can_write(priv, M_CAN_TXBAR, 0x1); + /* Construct DLC Field. Also contains CAN-FD configuration + * use put index of fifo as message marker + * it is used in TX interrupt for + * sending the correct echo frame + */ + m_can_fifo_write(priv, putidx, M_CAN_FIFO_DLC, + ((putidx << TX_BUF_MM_SHIFT) & + TX_BUF_MM_MASK) | + (can_len2dlc(cf->len) << 16) | + fdflags | TX_BUF_EFC); + + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, putidx, M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + + /* Push loopback echo. + * Will be looped back on TX interrupt based on message marker + */ + can_put_echo_skb(skb, dev, putidx); + + /* Enable TX FIFO element to start transfer */ + m_can_write(priv, M_CAN_TXBAR, (1 << putidx)); + + /* stop network queue if fifo full */ + if (m_can_tx_fifo_full(priv) || + m_can_next_echo_skb_occupied(dev, putidx)) + netif_stop_queue(dev); + } return NETDEV_TX_OK; } @@ -1109,55 +1489,37 @@ static int register_m_can_dev(struct net_device *dev) return register_candev(dev); } -static int m_can_of_parse_mram(struct platform_device *pdev, - struct m_can_priv *priv) +static void m_can_of_parse_mram(struct m_can_priv *priv, + const u32 *mram_config_vals) { - struct device_node *np = pdev->dev.of_node; - struct resource *res; - void __iomem *addr; - u32 out_val[MRAM_CFG_LEN]; - int i, start, end, ret; + int i, start, end; - /* message ram could be shared */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); - if (!res) - return -ENODEV; - - addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!addr) - return -ENOMEM; - - /* get message ram configuration */ - ret = of_property_read_u32_array(np, "bosch,mram-cfg", - out_val, sizeof(out_val) / 4); - if (ret) { - dev_err(&pdev->dev, "can not get message ram configuration\n"); - return -ENODEV; - } - - priv->mram_base = addr; - priv->mcfg[MRAM_SIDF].off = out_val[0]; - priv->mcfg[MRAM_SIDF].num = out_val[1]; + priv->mcfg[MRAM_SIDF].off = mram_config_vals[0]; + priv->mcfg[MRAM_SIDF].num = mram_config_vals[1]; priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off + priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE; - priv->mcfg[MRAM_XIDF].num = out_val[2]; + priv->mcfg[MRAM_XIDF].num = mram_config_vals[2]; priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off + priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE; - priv->mcfg[MRAM_RXF0].num = out_val[3] & RXFC_FS_MASK; + priv->mcfg[MRAM_RXF0].num = mram_config_vals[3] & + (RXFC_FS_MASK >> RXFC_FS_SHIFT); priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off + priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE; - priv->mcfg[MRAM_RXF1].num = out_val[4] & RXFC_FS_MASK; + priv->mcfg[MRAM_RXF1].num = mram_config_vals[4] & + (RXFC_FS_MASK >> RXFC_FS_SHIFT); priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off + priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE; - priv->mcfg[MRAM_RXB].num = out_val[5]; + priv->mcfg[MRAM_RXB].num = mram_config_vals[5]; priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off + priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE; - priv->mcfg[MRAM_TXE].num = out_val[6]; + priv->mcfg[MRAM_TXE].num = mram_config_vals[6]; priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off + priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE; - priv->mcfg[MRAM_TXB].num = out_val[7] & TXBC_NDTB_MASK; + priv->mcfg[MRAM_TXB].num = mram_config_vals[7] & + (TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT); - dev_dbg(&pdev->dev, "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", + dev_dbg(priv->device, + "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", priv->mram_base, priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num, priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num, @@ -1176,7 +1538,6 @@ static int m_can_of_parse_mram(struct platform_device *pdev, for (i = start; i < end; i += 4) writel(0x0, priv->mram_base + i); - return 0; } static int m_can_plat_probe(struct platform_device *pdev) @@ -1185,38 +1546,86 @@ static int m_can_plat_probe(struct platform_device *pdev) struct m_can_priv *priv; struct resource *res; void __iomem *addr; + void __iomem *mram_addr; struct clk *hclk, *cclk; int irq, ret; + struct device_node *np; + u32 mram_config_vals[MRAM_CFG_LEN]; + u32 tx_fifo_size; + + np = pdev->dev.of_node; hclk = devm_clk_get(&pdev->dev, "hclk"); cclk = devm_clk_get(&pdev->dev, "cclk"); + if (IS_ERR(hclk) || IS_ERR(cclk)) { - dev_err(&pdev->dev, "no clock find\n"); - return -ENODEV; + dev_err(&pdev->dev, "no clock found\n"); + ret = -ENODEV; + goto failed_ret; } + /* Enable clocks. Necessary to read Core Release in order to determine + * M_CAN version + */ + ret = clk_prepare_enable(hclk); + if (ret) + goto disable_hclk_ret; + + ret = clk_prepare_enable(cclk); + if (ret) + goto disable_cclk_ret; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); addr = devm_ioremap_resource(&pdev->dev, res); irq = platform_get_irq_byname(pdev, "int0"); - if (IS_ERR(addr) || irq < 0) - return -EINVAL; - /* allocate the m_can device */ - dev = alloc_m_can_dev(); - if (!dev) - return -ENOMEM; + if (IS_ERR(addr) || irq < 0) { + ret = -EINVAL; + goto disable_cclk_ret; + } + + /* message ram could be shared */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); + if (!res) { + ret = -ENODEV; + goto disable_cclk_ret; + } + + mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mram_addr) { + ret = -ENOMEM; + goto disable_cclk_ret; + } + + /* get message ram configuration */ + ret = of_property_read_u32_array(np, "bosch,mram-cfg", + mram_config_vals, + sizeof(mram_config_vals) / 4); + if (ret) { + dev_err(&pdev->dev, "Could not get Message RAM configuration."); + goto disable_cclk_ret; + } + /* Get TX FIFO size + * Defines the total amount of echo buffers for loopback + */ + tx_fifo_size = mram_config_vals[7]; + + /* allocate the m_can device */ + dev = alloc_m_can_dev(pdev, addr, tx_fifo_size); + if (!dev) { + ret = -ENOMEM; + goto disable_cclk_ret; + } priv = netdev_priv(dev); dev->irq = irq; - priv->base = addr; priv->device = &pdev->dev; priv->hclk = hclk; priv->cclk = cclk; priv->can.clock.freq = clk_get_rate(cclk); + priv->mram_base = mram_addr; - ret = m_can_of_parse_mram(pdev, priv); - if (ret) - goto failed_free_dev; + m_can_of_parse_mram(priv, mram_config_vals); platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -1230,13 +1639,22 @@ static int m_can_plat_probe(struct platform_device *pdev) devm_can_led_init(dev); - dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", - KBUILD_MODNAME, priv->base, dev->irq); + dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n", + KBUILD_MODNAME, dev->irq, priv->version); - return 0; + /* Probe finished + * Stop clocks. They will be reactivated once the M_CAN device is opened + */ + + goto disable_cclk_ret; failed_free_dev: free_m_can_dev(dev); +disable_cclk_ret: + clk_disable_unprepare(cclk); +disable_hclk_ret: + clk_disable_unprepare(hclk); +failed_ret: return ret; } diff --git a/drivers/net/can/peak_canfd/Kconfig b/drivers/net/can/peak_canfd/Kconfig new file mode 100644 index 000000000000..84b30978a19f --- /dev/null +++ b/drivers/net/can/peak_canfd/Kconfig @@ -0,0 +1,13 @@ +config CAN_PEAK_PCIEFD + depends on PCI + tristate "PEAK-System PCAN-PCIe FD cards" + ---help--- + This driver adds support for the PEAK-System PCI Express FD + CAN-FD cards family. + These 1x or 2x CAN-FD channels cards offer CAN 2.0 a/b as well as + CAN-FD access to the CAN bus. Besides the nominal bitrate of up to + 1 Mbit/s, the data bytes of CAN-FD frames can be transmitted with + up to 12 Mbit/s. A galvanic isolation of the CAN ports protects the + electronics of the card and the respective computer against + disturbances of up to 500 Volts. The PCAN-PCI Express FD can be + operated with ambient temperatures in a range of -40 to +85 °C. diff --git a/drivers/net/can/peak_canfd/Makefile b/drivers/net/can/peak_canfd/Makefile new file mode 100644 index 000000000000..3dc7a6a0ba59 --- /dev/null +++ b/drivers/net/can/peak_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the PEAK-System CAN-FD IP module drivers +# +obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_pciefd.o +peak_pciefd-y := peak_pciefd_main.o peak_canfd.o diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c new file mode 100644 index 000000000000..0d57be5ea97b --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_canfd.c @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Copyright (C) 2016 PEAK System-Technik GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "peak_canfd_user.h" + +/* internal IP core cache size (used as default echo skbs max number) */ +#define PCANFD_ECHO_SKB_MAX 24 + +/* bittiming ranges of the PEAK-System PC CAN-FD interfaces */ +static const struct can_bittiming_const peak_canfd_nominal_const = { + .name = "peak_canfd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const peak_canfd_data_const = { + .name = "peak_canfd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +static struct peak_canfd_priv *pucan_init_cmd(struct peak_canfd_priv *priv) +{ + priv->cmd_len = 0; + return priv; +} + +static void *pucan_add_cmd(struct peak_canfd_priv *priv, int cmd_op) +{ + struct pucan_command *cmd; + + if (priv->cmd_len + sizeof(*cmd) > priv->cmd_maxlen) + return NULL; + + cmd = priv->cmd_buffer + priv->cmd_len; + + /* reset all unused bit to default */ + memset(cmd, 0, sizeof(*cmd)); + + cmd->opcode_channel = pucan_cmd_opcode_channel(priv->index, cmd_op); + priv->cmd_len += sizeof(*cmd); + + return cmd; +} + +static int pucan_write_cmd(struct peak_canfd_priv *priv) +{ + int err; + + if (priv->pre_cmd) { + err = priv->pre_cmd(priv); + if (err) + return err; + } + + err = priv->write_cmd(priv); + if (err) + return err; + + if (priv->post_cmd) + err = priv->post_cmd(priv); + + return err; +} + +/* uCAN commands interface functions */ +static int pucan_set_reset_mode(struct peak_canfd_priv *priv) +{ + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RESET_MODE); + return pucan_write_cmd(priv); +} + +static int pucan_set_normal_mode(struct peak_canfd_priv *priv) +{ + int err; + + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_NORMAL_MODE); + err = pucan_write_cmd(priv); + if (!err) + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return err; +} + +static int pucan_set_listen_only_mode(struct peak_canfd_priv *priv) +{ + int err; + + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_LISTEN_ONLY_MODE); + err = pucan_write_cmd(priv); + if (!err) + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return err; +} + +static int pucan_set_timing_slow(struct peak_canfd_priv *priv, + const struct can_bittiming *pbt) +{ + struct pucan_timing_slow *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_SLOW); + + cmd->sjw_t = PUCAN_TSLOW_SJW_T(pbt->sjw - 1, + priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); + cmd->tseg1 = PUCAN_TSLOW_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); + cmd->tseg2 = PUCAN_TSLOW_TSEG2(pbt->phase_seg2 - 1); + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(pbt->brp - 1)); + + cmd->ewl = 96; /* default */ + + netdev_dbg(priv->ndev, + "nominal: brp=%u tseg1=%u tseg2=%u sjw=%u\n", + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw_t); + + return pucan_write_cmd(priv); +} + +static int pucan_set_timing_fast(struct peak_canfd_priv *priv, + const struct can_bittiming *pbt) +{ + struct pucan_timing_fast *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_FAST); + + cmd->sjw = PUCAN_TFAST_SJW(pbt->sjw - 1); + cmd->tseg1 = PUCAN_TFAST_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); + cmd->tseg2 = PUCAN_TFAST_TSEG2(pbt->phase_seg2 - 1); + cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(pbt->brp - 1)); + + netdev_dbg(priv->ndev, + "data: brp=%u tseg1=%u tseg2=%u sjw=%u\n", + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw); + + return pucan_write_cmd(priv); +} + +static int pucan_set_std_filter(struct peak_canfd_priv *priv, u8 row, u32 mask) +{ + struct pucan_std_filter *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_STD_FILTER); + + /* all the 11-bits CAN ID values are represented by one bit in a + * 64 rows array of 32 bits: the upper 6 bits of the CAN ID select the + * row while the lowest 5 bits select the bit in that row. + * + * bit filter + * 1 passed + * 0 discarded + */ + + /* select the row */ + cmd->idx = row; + + /* set/unset bits in the row */ + cmd->mask = cpu_to_le32(mask); + + return pucan_write_cmd(priv); +} + +static int pucan_tx_abort(struct peak_canfd_priv *priv, u16 flags) +{ + struct pucan_tx_abort *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TX_ABORT); + + cmd->flags = cpu_to_le16(flags); + + return pucan_write_cmd(priv); +} + +static int pucan_clr_err_counters(struct peak_canfd_priv *priv) +{ + struct pucan_wr_err_cnt *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_WR_ERR_CNT); + + cmd->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE | PUCAN_WRERRCNT_RE); + cmd->tx_counter = 0; + cmd->rx_counter = 0; + + return pucan_write_cmd(priv); +} + +static int pucan_set_options(struct peak_canfd_priv *priv, u16 opt_mask) +{ + struct pucan_options *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_EN_OPTION); + + cmd->options = cpu_to_le16(opt_mask); + + return pucan_write_cmd(priv); +} + +static int pucan_clr_options(struct peak_canfd_priv *priv, u16 opt_mask) +{ + struct pucan_options *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_CLR_DIS_OPTION); + + cmd->options = cpu_to_le16(opt_mask); + + return pucan_write_cmd(priv); +} + +static int pucan_setup_rx_barrier(struct peak_canfd_priv *priv) +{ + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RX_BARRIER); + + return pucan_write_cmd(priv); +} + +/* handle the reception of one CAN frame */ +static int pucan_handle_can_rx(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + const u16 rx_msg_flags = le16_to_cpu(msg->flags); + u8 cf_len; + + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) + cf_len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(msg))); + else + cf_len = get_can_dlc(pucan_msg_get_dlc(msg)); + + /* if this frame is an echo, */ + if ((rx_msg_flags & PUCAN_MSG_LOOPED_BACK) && + !(rx_msg_flags & PUCAN_MSG_SELF_RECEIVE)) { + int n; + unsigned long flags; + + spin_lock_irqsave(&priv->echo_lock, flags); + n = can_get_echo_skb(priv->ndev, msg->client); + spin_unlock_irqrestore(&priv->echo_lock, flags); + + /* count bytes of the echo instead of skb */ + stats->tx_bytes += cf_len; + stats->tx_packets++; + + if (n) { + /* restart tx queue only if a slot is free */ + netif_wake_queue(priv->ndev); + } + + return 0; + } + + /* otherwise, it should be pushed into rx fifo */ + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { + /* CANFD frame case */ + skb = alloc_canfd_skb(priv->ndev, &cf); + if (!skb) + return -ENOMEM; + + if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) + cf->flags |= CANFD_BRS; + + if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) + cf->flags |= CANFD_ESI; + } else { + /* CAN 2.0 frame case */ + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cf); + if (!skb) + return -ENOMEM; + } + + cf->can_id = le32_to_cpu(msg->can_id); + cf->len = cf_len; + + if (rx_msg_flags & PUCAN_MSG_EXT_ID) + cf->can_id |= CAN_EFF_FLAG; + + if (rx_msg_flags & PUCAN_MSG_RTR) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, msg->d, cf->len); + + stats->rx_bytes += cf->len; + stats->rx_packets++; + + netif_rx(skb); + + return 0; +} + +/* handle rx/tx error counters notification */ +static int pucan_handle_error(struct peak_canfd_priv *priv, + struct pucan_error_msg *msg) +{ + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + return 0; +} + +/* handle status notification */ +static int pucan_handle_status(struct peak_canfd_priv *priv, + struct pucan_status_msg *msg) +{ + struct net_device *ndev = priv->ndev; + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* this STATUS is the CNF of the RX_BARRIER: Tx path can be setup */ + if (pucan_status_is_rx_barrier(msg)) { + unsigned long flags; + + if (priv->enable_tx_path) { + int err = priv->enable_tx_path(priv); + + if (err) + return err; + } + + /* restart network queue only if echo skb array is free */ + spin_lock_irqsave(&priv->echo_lock, flags); + + if (!priv->can.echo_skb[priv->echo_idx]) { + spin_unlock_irqrestore(&priv->echo_lock, flags); + + netif_wake_queue(ndev); + } else { + spin_unlock_irqrestore(&priv->echo_lock, flags); + } + + return 0; + } + + skb = alloc_can_err_skb(ndev, &cf); + + /* test state error bits according to their priority */ + if (pucan_status_is_busoff(msg)) { + netdev_dbg(ndev, "Bus-off entry status\n"); + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + + } else if (pucan_status_is_passive(msg)) { + netdev_dbg(ndev, "Error passive status\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + } + + } else if (pucan_status_is_warning(msg)) { + netdev_dbg(ndev, "Error warning status\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + } + + } else if (priv->can.state != CAN_STATE_ERROR_ACTIVE) { + /* back to ERROR_ACTIVE */ + netdev_dbg(ndev, "Error active status\n"); + can_change_state(ndev, cf, CAN_STATE_ERROR_ACTIVE, + CAN_STATE_ERROR_ACTIVE); + } else { + dev_kfree_skb(skb); + return 0; + } + + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +/* handle uCAN Rx overflow notification */ +static int pucan_handle_cache_critical(struct peak_canfd_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_rx(skb); + + return 0; +} + +/* handle a single uCAN message */ +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg) +{ + u16 msg_type = le16_to_cpu(msg->type); + int msg_size = le16_to_cpu(msg->size); + int err; + + if (!msg_size || !msg_type) { + /* null packet found: end of list */ + goto exit; + } + + switch (msg_type) { + case PUCAN_MSG_CAN_RX: + err = pucan_handle_can_rx(priv, (struct pucan_rx_msg *)msg); + break; + case PUCAN_MSG_ERROR: + err = pucan_handle_error(priv, (struct pucan_error_msg *)msg); + break; + case PUCAN_MSG_STATUS: + err = pucan_handle_status(priv, (struct pucan_status_msg *)msg); + break; + case PUCAN_MSG_CACHE_CRITICAL: + err = pucan_handle_cache_critical(priv); + break; + default: + err = 0; + } + + if (err < 0) + return err; + +exit: + return msg_size; +} + +/* handle a list of rx_count messages from rx_msg memory address */ +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg_list, int msg_count) +{ + void *msg_ptr = msg_list; + int i, msg_size; + + for (i = 0; i < msg_count; i++) { + msg_size = peak_canfd_handle_msg(priv, msg_ptr); + + /* a null packet can be found at the end of a list */ + if (msg_size <= 0) + break; + + msg_ptr += msg_size; + } + + if (msg_size < 0) + return msg_size; + + return i; +} + +static int peak_canfd_start(struct peak_canfd_priv *priv) +{ + int err; + + err = pucan_clr_err_counters(priv); + if (err) + goto err_exit; + + priv->echo_idx = 0; + + priv->bec.txerr = 0; + priv->bec.rxerr = 0; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + err = pucan_set_listen_only_mode(priv); + else + err = pucan_set_normal_mode(priv); + +err_exit: + return err; +} + +static void peak_canfd_stop(struct peak_canfd_priv *priv) +{ + int err; + + /* go back to RESET mode */ + err = pucan_set_reset_mode(priv); + if (err) { + netdev_err(priv->ndev, "channel %u reset failed\n", + priv->index); + } else { + /* abort last Tx (MUST be done in RESET mode only!) */ + pucan_tx_abort(priv, PUCAN_TX_ABORT_FLUSH); + } +} + +static int peak_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + switch (mode) { + case CAN_MODE_START: + peak_canfd_start(priv); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int peak_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + *bec = priv->bec; + return 0; +} + +static int peak_canfd_open(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + int i, err = 0; + + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto err_exit; + } + + err = pucan_set_reset_mode(priv); + if (err) + goto err_close; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + err = pucan_clr_options(priv, PUCAN_OPTION_CANDFDISO); + else + err = pucan_set_options(priv, PUCAN_OPTION_CANDFDISO); + + if (err) + goto err_close; + } + + /* set option: get rx/tx error counters */ + err = pucan_set_options(priv, PUCAN_OPTION_ERROR); + if (err) + goto err_close; + + /* accept all standard CAN ID */ + for (i = 0; i <= PUCAN_FLTSTD_ROW_IDX_MAX; i++) + pucan_set_std_filter(priv, i, 0xffffffff); + + err = peak_canfd_start(priv); + if (err) + goto err_close; + + /* receiving the RB status says when Tx path is ready */ + err = pucan_setup_rx_barrier(priv); + if (!err) + goto err_exit; + +err_close: + close_candev(ndev); +err_exit: + return err; +} + +static int peak_canfd_set_bittiming(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + return pucan_set_timing_slow(priv, &priv->can.bittiming); +} + +static int peak_canfd_set_data_bittiming(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + return pucan_set_timing_fast(priv, &priv->can.data_bittiming); +} + +static int peak_canfd_close(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + peak_canfd_stop(priv); + close_candev(ndev); + + return 0; +} + +static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + struct pucan_tx_msg *msg; + u16 msg_size, msg_flags; + unsigned long flags; + bool should_stop_tx_queue; + int room_left; + u8 can_dlc; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + msg_size = ALIGN(sizeof(*msg) + cf->len, 4); + msg = priv->alloc_tx_msg(priv, msg_size, &room_left); + + /* should never happen except under bus-off condition and (auto-)restart + * mechanism + */ + if (!msg) { + stats->tx_dropped++; + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + msg->size = cpu_to_le16(msg_size); + msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); + msg_flags = 0; + + if (cf->can_id & CAN_EFF_FLAG) { + msg_flags |= PUCAN_MSG_EXT_ID; + msg->can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK); + } else { + msg->can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) { + /* CAN FD frame format */ + can_dlc = can_len2dlc(cf->len); + + msg_flags |= PUCAN_MSG_EXT_DATA_LEN; + + if (cf->flags & CANFD_BRS) + msg_flags |= PUCAN_MSG_BITRATE_SWITCH; + + if (cf->flags & CANFD_ESI) + msg_flags |= PUCAN_MSG_ERROR_STATE_IND; + } else { + /* CAN 2.0 frame format */ + can_dlc = cf->len; + + if (cf->can_id & CAN_RTR_FLAG) + msg_flags |= PUCAN_MSG_RTR; + } + + /* always ask loopback for echo management */ + msg_flags |= PUCAN_MSG_LOOPED_BACK; + + /* set driver specific bit to differentiate with application loopback */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + msg_flags |= PUCAN_MSG_SELF_RECEIVE; + + msg->flags = cpu_to_le16(msg_flags); + msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, can_dlc); + memcpy(msg->d, cf->data, cf->len); + + /* struct msg client field is used as an index in the echo skbs ring */ + msg->client = priv->echo_idx; + + spin_lock_irqsave(&priv->echo_lock, flags); + + /* prepare and save echo skb in internal slot */ + can_put_echo_skb(skb, ndev, priv->echo_idx); + + /* move echo index to the next slot */ + priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max; + + /* if next slot is not free, stop network queue (no slot free in echo + * skb ring means that the controller did not write these frames on + * the bus: no need to continue). + */ + should_stop_tx_queue = !!(priv->can.echo_skb[priv->echo_idx]); + + spin_unlock_irqrestore(&priv->echo_lock, flags); + + /* write the skb on the interface */ + priv->write_tx_msg(priv, msg); + + /* stop network tx queue if not enough room to save one more msg too */ + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + should_stop_tx_queue |= (room_left < + (sizeof(*msg) + CANFD_MAX_DLEN)); + else + should_stop_tx_queue |= (room_left < + (sizeof(*msg) + CAN_MAX_DLEN)); + + if (should_stop_tx_queue) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops peak_canfd_netdev_ops = { + .ndo_open = peak_canfd_open, + .ndo_stop = peak_canfd_close, + .ndo_start_xmit = peak_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, + int echo_skb_max) +{ + struct net_device *ndev; + struct peak_canfd_priv *priv; + + /* we DO support local echo */ + if (echo_skb_max < 0) + echo_skb_max = PCANFD_ECHO_SKB_MAX; + + /* allocate the candev object */ + ndev = alloc_candev(sizeof_priv, echo_skb_max); + if (!ndev) + return NULL; + + priv = netdev_priv(ndev); + + /* complete now socket-can initialization side */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.bittiming_const = &peak_canfd_nominal_const; + priv->can.data_bittiming_const = &peak_canfd_data_const; + + priv->can.do_set_mode = peak_canfd_set_mode; + priv->can.do_get_berr_counter = peak_canfd_get_berr_counter; + priv->can.do_set_bittiming = peak_canfd_set_bittiming; + priv->can.do_set_data_bittiming = peak_canfd_set_data_bittiming; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_BERR_REPORTING; + + priv->ndev = ndev; + priv->index = index; + priv->cmd_len = 0; + spin_lock_init(&priv->echo_lock); + + ndev->flags |= IFF_ECHO; + ndev->netdev_ops = &peak_canfd_netdev_ops; + ndev->dev_id = index; + + return ndev; +} diff --git a/drivers/net/can/peak_canfd/peak_canfd_user.h b/drivers/net/can/peak_canfd/peak_canfd_user.h new file mode 100644 index 000000000000..bf6de47f69c2 --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_canfd_user.h @@ -0,0 +1,55 @@ +/* + * CAN driver for PEAK System micro-CAN based adapters + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef PEAK_CANFD_USER_H +#define PEAK_CANFD_USER_H + +#include <linux/can/dev/peak_canfd.h> + +#define PCANFD_ECHO_SKB_DEF -1 + +/* data structure private to each uCAN interface */ +struct peak_canfd_priv { + struct can_priv can; /* socket-can private data */ + struct net_device *ndev; /* network device */ + int index; /* channel index */ + + struct can_berr_counter bec; /* rx/tx err counters */ + + int echo_idx; /* echo skb free slot index */ + spinlock_t echo_lock; + + int cmd_len; + void *cmd_buffer; + int cmd_maxlen; + + int (*pre_cmd)(struct peak_canfd_priv *priv); + int (*write_cmd)(struct peak_canfd_priv *priv); + int (*post_cmd)(struct peak_canfd_priv *priv); + + int (*enable_tx_path)(struct peak_canfd_priv *priv); + void *(*alloc_tx_msg)(struct peak_canfd_priv *priv, u16 msg_size, + int *room_left); + int (*write_tx_msg)(struct peak_canfd_priv *priv, + struct pucan_tx_msg *msg); +}; + +struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, + int echo_skb_max); +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg); +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, + struct pucan_rx_msg *rx_msg, int rx_count); +#endif diff --git a/drivers/net/can/peak_canfd/peak_pciefd_main.c b/drivers/net/can/peak_canfd/peak_pciefd_main.c new file mode 100644 index 000000000000..51c2d182a33a --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_pciefd_main.c @@ -0,0 +1,842 @@ +/* + * Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com> + * + * Derived from the PCAN project file driver/src/pcan_pci.c: + * + * Copyright (C) 2001-2006 PEAK System-Technik GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "peak_canfd_user.h" + +MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe FD family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCIe FD CAN cards"); +MODULE_LICENSE("GPL v2"); + +#define PCIEFD_DRV_NAME "peak_pciefd" + +#define PEAK_PCI_VENDOR_ID 0x001c /* The PCI device and vendor IDs */ +#define PEAK_PCIEFD_ID 0x0013 /* for PCIe slot cards */ + +/* PEAK PCIe board access description */ +#define PCIEFD_BAR0_SIZE (64 * 1024) +#define PCIEFD_RX_DMA_SIZE (4 * 1024) +#define PCIEFD_TX_DMA_SIZE (4 * 1024) + +#define PCIEFD_TX_PAGE_SIZE (2 * 1024) + +/* System Control Registers */ +#define PCIEFD_REG_SYS_CTL_SET 0x0000 /* set bits */ +#define PCIEFD_REG_SYS_CTL_CLR 0x0004 /* clear bits */ + +/* Version info registers */ +#define PCIEFD_REG_SYS_VER1 0x0040 /* version reg #1 */ +#define PCIEFD_REG_SYS_VER2 0x0044 /* version reg #2 */ + +/* System Control Registers Bits */ +#define PCIEFD_SYS_CTL_TS_RST 0x00000001 /* timestamp clock */ +#define PCIEFD_SYS_CTL_CLK_EN 0x00000002 /* system clock */ + +/* CAN-FD channel addresses */ +#define PCIEFD_CANX_OFF(c) (((c) + 1) * 0x1000) + +#define PCIEFD_ECHO_SKB_MAX PCANFD_ECHO_SKB_DEF + +/* CAN-FD channel registers */ +#define PCIEFD_REG_CAN_MISC 0x0000 /* Misc. control */ +#define PCIEFD_REG_CAN_CLK_SEL 0x0008 /* Clock selector */ +#define PCIEFD_REG_CAN_CMD_PORT_L 0x0010 /* 64-bits command port */ +#define PCIEFD_REG_CAN_CMD_PORT_H 0x0014 +#define PCIEFD_REG_CAN_TX_REQ_ACC 0x0020 /* Tx request accumulator */ +#define PCIEFD_REG_CAN_TX_CTL_SET 0x0030 /* Tx control set register */ +#define PCIEFD_REG_CAN_TX_CTL_CLR 0x0038 /* Tx control clear register */ +#define PCIEFD_REG_CAN_TX_DMA_ADDR_L 0x0040 /* 64-bits addr for Tx DMA */ +#define PCIEFD_REG_CAN_TX_DMA_ADDR_H 0x0044 +#define PCIEFD_REG_CAN_RX_CTL_SET 0x0050 /* Rx control set register */ +#define PCIEFD_REG_CAN_RX_CTL_CLR 0x0058 /* Rx control clear register */ +#define PCIEFD_REG_CAN_RX_CTL_WRT 0x0060 /* Rx control write register */ +#define PCIEFD_REG_CAN_RX_CTL_ACK 0x0068 /* Rx control ACK register */ +#define PCIEFD_REG_CAN_RX_DMA_ADDR_L 0x0070 /* 64-bits addr for Rx DMA */ +#define PCIEFD_REG_CAN_RX_DMA_ADDR_H 0x0074 + +/* CAN-FD channel misc register bits */ +#define CANFD_MISC_TS_RST 0x00000001 /* timestamp cnt rst */ + +/* CAN-FD channel Clock SELector Source & DIVider */ +#define CANFD_CLK_SEL_DIV_MASK 0x00000007 +#define CANFD_CLK_SEL_DIV_60MHZ 0x00000000 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_40MHZ 0x00000001 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_30MHZ 0x00000002 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_24MHZ 0x00000003 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_20MHZ 0x00000004 /* SRC=240MHz only */ + +#define CANFD_CLK_SEL_SRC_MASK 0x00000008 /* 0=80MHz, 1=240MHz */ +#define CANFD_CLK_SEL_SRC_240MHZ 0x00000008 +#define CANFD_CLK_SEL_SRC_80MHZ (~CANFD_CLK_SEL_SRC_240MHZ & \ + CANFD_CLK_SEL_SRC_MASK) + +#define CANFD_CLK_SEL_20MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_20MHZ) +#define CANFD_CLK_SEL_24MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_24MHZ) +#define CANFD_CLK_SEL_30MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_30MHZ) +#define CANFD_CLK_SEL_40MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_40MHZ) +#define CANFD_CLK_SEL_60MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_60MHZ) +#define CANFD_CLK_SEL_80MHZ (CANFD_CLK_SEL_SRC_80MHZ) + +/* CAN-FD channel Rx/Tx control register bits */ +#define CANFD_CTL_UNC_BIT 0x00010000 /* Uncached DMA mem */ +#define CANFD_CTL_RST_BIT 0x00020000 /* reset DMA action */ +#define CANFD_CTL_IEN_BIT 0x00040000 /* IRQ enable */ + +/* Rx IRQ Count and Time Limits */ +#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */ +#define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */ + +#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD) + +/* Tx anticipation window (link logical address should be aligned on 2K + * boundary) + */ +#define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE) + +#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */ + +/* 32-bits IRQ status fields, heading Rx DMA area */ +static inline int pciefd_irq_tag(u32 irq_status) +{ + return irq_status & 0x0000000f; +} + +static inline int pciefd_irq_rx_cnt(u32 irq_status) +{ + return (irq_status & 0x000007f0) >> 4; +} + +static inline int pciefd_irq_is_lnk(u32 irq_status) +{ + return irq_status & 0x00010000; +} + +/* Rx record */ +struct pciefd_rx_dma { + __le32 irq_status; + __le32 sys_time_low; + __le32 sys_time_high; + struct pucan_rx_msg msg[0]; +} __packed __aligned(4); + +/* Tx Link record */ +struct pciefd_tx_link { + __le16 size; + __le16 type; + __le32 laddr_lo; + __le32 laddr_hi; +} __packed __aligned(4); + +/* Tx page descriptor */ +struct pciefd_page { + void *vbase; /* page virtual address */ + dma_addr_t lbase; /* page logical address */ + u32 offset; + u32 size; +}; + +#define CANFD_IRQ_SET 0x00000001 +#define CANFD_TX_PATH_SET 0x00000002 + +/* CAN-FD channel object */ +struct pciefd_board; +struct pciefd_can { + struct peak_canfd_priv ucan; /* must be the first member */ + void __iomem *reg_base; /* channel config base addr */ + struct pciefd_board *board; /* reverse link */ + + struct pucan_command pucan_cmd; /* command buffer */ + + dma_addr_t rx_dma_laddr; /* DMA virtual and logical addr */ + void *rx_dma_vaddr; /* for Rx and Tx areas */ + dma_addr_t tx_dma_laddr; + void *tx_dma_vaddr; + + struct pciefd_page tx_pages[PCIEFD_TX_PAGE_COUNT]; + u16 tx_pages_free; /* free Tx pages counter */ + u16 tx_page_index; /* current page used for Tx */ + spinlock_t tx_lock; + + u32 irq_status; + u32 irq_tag; /* next irq tag */ +}; + +/* PEAK-PCIe FD board object */ +struct pciefd_board { + void __iomem *reg_base; + struct pci_dev *pci_dev; + int can_count; + spinlock_t cmd_lock; /* 64-bits cmds must be atomic */ + struct pciefd_can *can[0]; /* array of network devices */ +}; + +/* supported device ids. */ +static const struct pci_device_id peak_pciefd_tbl[] = { + {PEAK_PCI_VENDOR_ID, PEAK_PCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, peak_pciefd_tbl); + +/* read a 32 bits value from a SYS block register */ +static inline u32 pciefd_sys_readreg(const struct pciefd_board *priv, u16 reg) +{ + return readl(priv->reg_base + reg); +} + +/* write a 32 bits value into a SYS block register */ +static inline void pciefd_sys_writereg(const struct pciefd_board *priv, + u32 val, u16 reg) +{ + writel(val, priv->reg_base + reg); +} + +/* read a 32 bits value from CAN-FD block register */ +static inline u32 pciefd_can_readreg(const struct pciefd_can *priv, u16 reg) +{ + return readl(priv->reg_base + reg); +} + +/* write a 32 bits value into a CAN-FD block register */ +static inline void pciefd_can_writereg(const struct pciefd_can *priv, + u32 val, u16 reg) +{ + writel(val, priv->reg_base + reg); +} + +/* give a channel logical Rx DMA address to the board */ +static void pciefd_can_setup_rx_dma(struct pciefd_can *priv) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32); +#else + const u32 dma_addr_h = 0; +#endif + + /* (DMA must be reset for Rx) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET); + + /* write the logical address of the Rx DMA area for this channel */ + pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr, + PCIEFD_REG_CAN_RX_DMA_ADDR_L); + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H); + + /* also indicates that Rx DMA is cacheable */ + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_RX_CTL_CLR); +} + +/* clear channel logical Rx DMA address from the board */ +static void pciefd_can_clear_rx_dma(struct pciefd_can *priv) +{ + /* DMA must be reset for Rx */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET); + + /* clear the logical address of the Rx DMA area for this channel */ + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L); + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H); +} + +/* give a channel logical Tx DMA address to the board */ +static void pciefd_can_setup_tx_dma(struct pciefd_can *priv) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32); +#else + const u32 dma_addr_h = 0; +#endif + + /* (DMA must be reset for Tx) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET); + + /* write the logical address of the Tx DMA area for this channel */ + pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr, + PCIEFD_REG_CAN_TX_DMA_ADDR_L); + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H); + + /* also indicates that Tx DMA is cacheable */ + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_TX_CTL_CLR); +} + +/* clear channel logical Tx DMA address from the board */ +static void pciefd_can_clear_tx_dma(struct pciefd_can *priv) +{ + /* DMA must be reset for Tx */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET); + + /* clear the logical address of the Tx DMA area for this channel */ + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L); + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H); +} + +static void pciefd_can_ack_rx_dma(struct pciefd_can *priv) +{ + /* read value of current IRQ tag and inc it for next one */ + priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr); + priv->irq_tag++; + priv->irq_tag &= 0xf; + + /* write the next IRQ tag for this CAN */ + pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK); +} + +/* IRQ handler */ +static irqreturn_t pciefd_irq_handler(int irq, void *arg) +{ + struct pciefd_can *priv = arg; + struct pciefd_rx_dma *rx_dma = priv->rx_dma_vaddr; + + /* INTA mode only to sync with PCIe transaction */ + if (!pci_dev_msi_enabled(priv->board->pci_dev)) + (void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1); + + /* read IRQ status from the first 32-bits of the Rx DMA area */ + priv->irq_status = le32_to_cpu(rx_dma->irq_status); + + /* check if this (shared) IRQ is for this CAN */ + if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag) + return IRQ_NONE; + + /* handle rx messages (if any) */ + peak_canfd_handle_msgs_list(&priv->ucan, + rx_dma->msg, + pciefd_irq_rx_cnt(priv->irq_status)); + + /* handle tx link interrupt (if any) */ + if (pciefd_irq_is_lnk(priv->irq_status)) { + unsigned long flags; + + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_pages_free++; + spin_unlock_irqrestore(&priv->tx_lock, flags); + + /* wake producer up */ + netif_wake_queue(priv->ucan.ndev); + } + + /* re-enable Rx DMA transfer for this CAN */ + pciefd_can_ack_rx_dma(priv); + + return IRQ_HANDLED; +} + +static int pciefd_enable_tx_path(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + int i; + + /* initialize the Tx pages descriptors */ + priv->tx_pages_free = PCIEFD_TX_PAGE_COUNT - 1; + priv->tx_page_index = 0; + + priv->tx_pages[0].vbase = priv->tx_dma_vaddr; + priv->tx_pages[0].lbase = priv->tx_dma_laddr; + + for (i = 0; i < PCIEFD_TX_PAGE_COUNT; i++) { + priv->tx_pages[i].offset = 0; + priv->tx_pages[i].size = PCIEFD_TX_PAGE_SIZE - + sizeof(struct pciefd_tx_link); + if (i) { + priv->tx_pages[i].vbase = + priv->tx_pages[i - 1].vbase + + PCIEFD_TX_PAGE_SIZE; + priv->tx_pages[i].lbase = + priv->tx_pages[i - 1].lbase + + PCIEFD_TX_PAGE_SIZE; + } + } + + /* setup Tx DMA addresses into IP core */ + pciefd_can_setup_tx_dma(priv); + + /* start (TX_RST=0) Tx Path */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_CLR); + + return 0; +} + +/* board specific CANFD command pre-processing */ +static int pciefd_pre_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); + int err; + + /* pre-process command */ + switch (cmd) { + case PUCAN_CMD_NORMAL_MODE: + case PUCAN_CMD_LISTEN_ONLY_MODE: + + if (ucan->can.state == CAN_STATE_BUS_OFF) + break; + + /* going into operational mode: setup IRQ handler */ + err = request_irq(priv->board->pci_dev->irq, + pciefd_irq_handler, + IRQF_SHARED, + PCIEFD_DRV_NAME, + priv); + if (err) + return err; + + /* setup Rx DMA address */ + pciefd_can_setup_rx_dma(priv); + + /* setup max count of msgs per IRQ */ + pciefd_can_writereg(priv, (CANFD_CTL_IRQ_TL_DEF) << 8 | + CANFD_CTL_IRQ_CL_DEF, + PCIEFD_REG_CAN_RX_CTL_WRT); + + /* clear DMA RST for Rx (Rx start) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, + PCIEFD_REG_CAN_RX_CTL_CLR); + + /* reset timestamps */ + pciefd_can_writereg(priv, !CANFD_MISC_TS_RST, + PCIEFD_REG_CAN_MISC); + + /* do an initial ACK */ + pciefd_can_ack_rx_dma(priv); + + /* enable IRQ for this CAN after having set next irq_tag */ + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, + PCIEFD_REG_CAN_RX_CTL_SET); + + /* Tx path will be setup as soon as RX_BARRIER is received */ + break; + default: + break; + } + + return 0; +} + +/* write a command */ +static int pciefd_write_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + unsigned long flags; + + /* 64-bits command is atomic */ + spin_lock_irqsave(&priv->board->cmd_lock, flags); + + pciefd_can_writereg(priv, *(u32 *)ucan->cmd_buffer, + PCIEFD_REG_CAN_CMD_PORT_L); + pciefd_can_writereg(priv, *(u32 *)(ucan->cmd_buffer + 4), + PCIEFD_REG_CAN_CMD_PORT_H); + + spin_unlock_irqrestore(&priv->board->cmd_lock, flags); + + return 0; +} + +/* board specific CANFD command post-processing */ +static int pciefd_post_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); + + switch (cmd) { + case PUCAN_CMD_RESET_MODE: + + if (ucan->can.state == CAN_STATE_STOPPED) + break; + + /* controller now in reset mode: */ + + /* stop and reset DMA addresses in Tx/Rx engines */ + pciefd_can_clear_tx_dma(priv); + pciefd_can_clear_rx_dma(priv); + + /* disable IRQ for this CAN */ + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, + PCIEFD_REG_CAN_RX_CTL_CLR); + + free_irq(priv->board->pci_dev->irq, priv); + + ucan->can.state = CAN_STATE_STOPPED; + + break; + } + + return 0; +} + +static void *pciefd_alloc_tx_msg(struct peak_canfd_priv *ucan, u16 msg_size, + int *room_left) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; + unsigned long flags; + void *msg; + + spin_lock_irqsave(&priv->tx_lock, flags); + + if (page->offset + msg_size > page->size) { + struct pciefd_tx_link *lk; + + /* not enough space in this page: try another one */ + if (!priv->tx_pages_free) { + spin_unlock_irqrestore(&priv->tx_lock, flags); + + /* Tx overflow */ + return NULL; + } + + priv->tx_pages_free--; + + /* keep address of the very last free slot of current page */ + lk = page->vbase + page->offset; + + /* next, move on a new free page */ + priv->tx_page_index = (priv->tx_page_index + 1) % + PCIEFD_TX_PAGE_COUNT; + page = priv->tx_pages + priv->tx_page_index; + + /* put link record to this new page at the end of prev one */ + lk->size = cpu_to_le16(sizeof(*lk)); + lk->type = cpu_to_le16(CANFD_MSG_LNK_TX); + lk->laddr_lo = cpu_to_le32(page->lbase); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + lk->laddr_hi = cpu_to_le32(page->lbase >> 32); +#else + lk->laddr_hi = 0; +#endif + /* next msgs will be put from the begininng of this new page */ + page->offset = 0; + } + + *room_left = priv->tx_pages_free * page->size; + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + msg = page->vbase + page->offset; + + /* give back room left in the tx ring */ + *room_left += page->size - (page->offset + msg_size); + + return msg; +} + +static int pciefd_write_tx_msg(struct peak_canfd_priv *ucan, + struct pucan_tx_msg *msg) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; + + /* this slot is now reserved for writing the frame */ + page->offset += le16_to_cpu(msg->size); + + /* tell the board a frame has been written in Tx DMA area */ + pciefd_can_writereg(priv, 1, PCIEFD_REG_CAN_TX_REQ_ACC); + + return 0; +} + +/* probe for CAN-FD channel #pciefd_board->can_count */ +static int pciefd_can_probe(struct pciefd_board *pciefd) +{ + struct net_device *ndev; + struct pciefd_can *priv; + u32 clk; + int err; + + /* allocate the candev object with default isize of echo skbs ring */ + ndev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count, + PCIEFD_ECHO_SKB_MAX); + if (!ndev) { + dev_err(&pciefd->pci_dev->dev, + "failed to alloc candev object\n"); + goto failure; + } + + priv = netdev_priv(ndev); + + /* fill-in candev private object: */ + + /* setup PCIe-FD own callbacks */ + priv->ucan.pre_cmd = pciefd_pre_cmd; + priv->ucan.write_cmd = pciefd_write_cmd; + priv->ucan.post_cmd = pciefd_post_cmd; + priv->ucan.enable_tx_path = pciefd_enable_tx_path; + priv->ucan.alloc_tx_msg = pciefd_alloc_tx_msg; + priv->ucan.write_tx_msg = pciefd_write_tx_msg; + + /* setup PCIe-FD own command buffer */ + priv->ucan.cmd_buffer = &priv->pucan_cmd; + priv->ucan.cmd_maxlen = sizeof(priv->pucan_cmd); + + priv->board = pciefd; + + /* CAN config regs block address */ + priv->reg_base = pciefd->reg_base + PCIEFD_CANX_OFF(priv->ucan.index); + + /* allocate non-cacheable DMA'able 4KB memory area for Rx */ + priv->rx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, + PCIEFD_RX_DMA_SIZE, + &priv->rx_dma_laddr, + GFP_KERNEL); + if (!priv->rx_dma_vaddr) { + dev_err(&pciefd->pci_dev->dev, + "Rx dmam_alloc_coherent(%u) failure\n", + PCIEFD_RX_DMA_SIZE); + goto err_free_candev; + } + + /* allocate non-cacheable DMA'able 4KB memory area for Tx */ + priv->tx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, + PCIEFD_TX_DMA_SIZE, + &priv->tx_dma_laddr, + GFP_KERNEL); + if (!priv->tx_dma_vaddr) { + dev_err(&pciefd->pci_dev->dev, + "Tx dmaim_alloc_coherent(%u) failure\n", + PCIEFD_TX_DMA_SIZE); + goto err_free_candev; + } + + /* CAN clock in RST mode */ + pciefd_can_writereg(priv, CANFD_MISC_TS_RST, PCIEFD_REG_CAN_MISC); + + /* read current clock value */ + clk = pciefd_can_readreg(priv, PCIEFD_REG_CAN_CLK_SEL); + switch (clk) { + case CANFD_CLK_SEL_20MHZ: + priv->ucan.can.clock.freq = 20 * 1000 * 1000; + break; + case CANFD_CLK_SEL_24MHZ: + priv->ucan.can.clock.freq = 24 * 1000 * 1000; + break; + case CANFD_CLK_SEL_30MHZ: + priv->ucan.can.clock.freq = 30 * 1000 * 1000; + break; + case CANFD_CLK_SEL_40MHZ: + priv->ucan.can.clock.freq = 40 * 1000 * 1000; + break; + case CANFD_CLK_SEL_60MHZ: + priv->ucan.can.clock.freq = 60 * 1000 * 1000; + break; + default: + pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ, + PCIEFD_REG_CAN_CLK_SEL); + + /* fallthough */ + case CANFD_CLK_SEL_80MHZ: + priv->ucan.can.clock.freq = 80 * 1000 * 1000; + break; + } + + ndev->irq = pciefd->pci_dev->irq; + + SET_NETDEV_DEV(ndev, &pciefd->pci_dev->dev); + + err = register_candev(ndev); + if (err) { + dev_err(&pciefd->pci_dev->dev, + "couldn't register CAN device: %d\n", err); + goto err_free_candev; + } + + spin_lock_init(&priv->tx_lock); + + /* save the object address in the board structure */ + pciefd->can[pciefd->can_count] = priv; + + dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n", + ndev->name, priv->reg_base, pciefd->pci_dev->irq); + + return 0; + +err_free_candev: + free_candev(ndev); + +failure: + return -ENOMEM; +} + +/* remove a CAN-FD channel by releasing all of its resources */ +static void pciefd_can_remove(struct pciefd_can *priv) +{ + /* unregister (close) the can device to go back to RST mode first */ + unregister_candev(priv->ucan.ndev); + + /* finally, free the candev object */ + free_candev(priv->ucan.ndev); +} + +/* remove all CAN-FD channels by releasing their own resources */ +static void pciefd_can_remove_all(struct pciefd_board *pciefd) +{ + while (pciefd->can_count > 0) + pciefd_can_remove(pciefd->can[--pciefd->can_count]); +} + +/* probe for the entire device */ +static int peak_pciefd_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pciefd_board *pciefd; + int err, can_count; + u16 sub_sys_id; + u8 hw_ver_major; + u8 hw_ver_minor; + u8 hw_ver_sub; + u32 v2; + + err = pci_enable_device(pdev); + if (err) + return err; + err = pci_request_regions(pdev, PCIEFD_DRV_NAME); + if (err) + goto err_disable_pci; + + /* the number of channels depends on sub-system id */ + err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (err) + goto err_release_regions; + + dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n", + pdev->vendor, pdev->device, sub_sys_id); + + if (sub_sys_id >= 0x0012) + can_count = 4; + else if (sub_sys_id >= 0x0010) + can_count = 3; + else if (sub_sys_id >= 0x0004) + can_count = 2; + else + can_count = 1; + + /* allocate board structure object */ + pciefd = devm_kzalloc(&pdev->dev, sizeof(*pciefd) + + can_count * sizeof(*pciefd->can), + GFP_KERNEL); + if (!pciefd) { + err = -ENOMEM; + goto err_release_regions; + } + + /* initialize the board structure */ + pciefd->pci_dev = pdev; + spin_lock_init(&pciefd->cmd_lock); + + /* save the PCI BAR0 virtual address for further system regs access */ + pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE); + if (!pciefd->reg_base) { + dev_err(&pdev->dev, "failed to map PCI resource #0\n"); + err = -ENOMEM; + goto err_release_regions; + } + + /* read the firmware version number */ + v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2); + + hw_ver_major = (v2 & 0x0000f000) >> 12; + hw_ver_minor = (v2 & 0x00000f00) >> 8; + hw_ver_sub = (v2 & 0x000000f0) >> 4; + + dev_info(&pdev->dev, + "%ux CAN-FD PCAN-PCIe FPGA v%u.%u.%u:\n", can_count, + hw_ver_major, hw_ver_minor, hw_ver_sub); + + /* stop system clock */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, + PCIEFD_REG_SYS_CTL_CLR); + + pci_set_master(pdev); + + /* create now the corresponding channels objects */ + while (pciefd->can_count < can_count) { + err = pciefd_can_probe(pciefd); + if (err) + goto err_free_canfd; + + pciefd->can_count++; + } + + /* set system timestamps counter in RST mode */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, + PCIEFD_REG_SYS_CTL_SET); + + /* wait a bit (read cycle) */ + (void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1); + + /* free all clocks */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, + PCIEFD_REG_SYS_CTL_CLR); + + /* start system clock */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, + PCIEFD_REG_SYS_CTL_SET); + + /* remember the board structure address in the device user data */ + pci_set_drvdata(pdev, pciefd); + + return 0; + +err_free_canfd: + pciefd_can_remove_all(pciefd); + + pci_iounmap(pdev, pciefd->reg_base); + +err_release_regions: + pci_release_regions(pdev); + +err_disable_pci: + pci_disable_device(pdev); + + return err; +} + +/* free the board structure object, as well as its resources: */ +static void peak_pciefd_remove(struct pci_dev *pdev) +{ + struct pciefd_board *pciefd = pci_get_drvdata(pdev); + + /* release CAN-FD channels resources */ + pciefd_can_remove_all(pciefd); + + pci_iounmap(pdev, pciefd->reg_base); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_driver peak_pciefd_driver = { + .name = PCIEFD_DRV_NAME, + .id_table = peak_pciefd_tbl, + .probe = peak_pciefd_probe, + .remove = peak_pciefd_remove, +}; + +module_pci_driver(peak_pciefd_driver); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index b8aac538275c..4d4941469cfc 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -898,9 +898,9 @@ static int ti_hecc_probe(struct platform_device *pdev) } priv->base = devm_ioremap_resource(&pdev->dev, res); - if (!priv->base) { + if (IS_ERR(priv->base)) { dev_err(&pdev->dev, "hecc ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(priv->base); } /* handle hecc-ram memory */ @@ -911,9 +911,9 @@ static int ti_hecc_probe(struct platform_device *pdev) } priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res); - if (!priv->hecc_ram) { + if (IS_ERR(priv->hecc_ram)) { dev_err(&pdev->dev, "hecc-ram ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(priv->hecc_ram); } /* handle mbx memory */ @@ -924,9 +924,9 @@ static int ti_hecc_probe(struct platform_device *pdev) } priv->mbx = devm_ioremap_resource(&pdev->dev, res); - if (!priv->mbx) { + if (IS_ERR(priv->mbx)) { dev_err(&pdev->dev, "mbx ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(priv->mbx); } irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 8483a40e7e9e..2d0313eb31c0 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -81,4 +81,10 @@ config CAN_8DEV_USB This driver supports the USB2CAN interface from 8 devices (http://www.8devices.com). +config CAN_MCBA_USB + tristate "Microchip CAN BUS Analyzer interface" + ---help--- + This driver supports the CAN BUS Analyzer interface + from Microchip (http://www.microchip.com/development-tools/). + endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index a64cf983fb87..164453fd55d0 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c new file mode 100644 index 000000000000..7f0272558bef --- /dev/null +++ b/drivers/net/can/usb/mcba_usb.c @@ -0,0 +1,904 @@ +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool + * + * Copyright (C) 2017 Mobica Limited + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. + * + * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c + */ + +#include <asm/unaligned.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/usb.h> + +/* vendor and product id */ +#define MCBA_MODULE_NAME "mcba_usb" +#define MCBA_VENDOR_ID 0x04d8 +#define MCBA_PRODUCT_ID 0x0a30 + +/* driver constants */ +#define MCBA_MAX_RX_URBS 20 +#define MCBA_MAX_TX_URBS 20 +#define MCBA_CTX_FREE MCBA_MAX_TX_URBS + +/* RX buffer must be bigger than msg size since at the + * beggining USB messages are stacked. + */ +#define MCBA_USB_RX_BUFF_SIZE 64 +#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg)) + +/* MCBA endpoint numbers */ +#define MCBA_USB_EP_IN 1 +#define MCBA_USB_EP_OUT 1 + +/* Microchip command id */ +#define MBCA_CMD_RECEIVE_MESSAGE 0xE3 +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5 +#define MBCA_CMD_I_AM_ALIVE_FROM_USB 0xF7 +#define MBCA_CMD_CHANGE_BIT_RATE 0xA1 +#define MBCA_CMD_TRANSMIT_MESSAGE_EV 0xA3 +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8 +#define MBCA_CMD_READ_FW_VERSION 0xA9 +#define MBCA_CMD_NOTHING_TO_SEND 0xFF +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP 0xE2 + +#define MCBA_VER_REQ_USB 1 +#define MCBA_VER_REQ_CAN 2 + +#define MCBA_SIDL_EXID_MASK 0x8 +#define MCBA_DLC_MASK 0xf +#define MCBA_DLC_RTR_MASK 0x40 + +#define MCBA_CAN_STATE_WRN_TH 95 +#define MCBA_CAN_STATE_ERR_PSV_TH 127 + +#define MCBA_TERMINATION_DISABLED CAN_TERMINATION_DISABLED +#define MCBA_TERMINATION_ENABLED 120 + +struct mcba_usb_ctx { + struct mcba_priv *priv; + u32 ndx; + u8 dlc; + bool can; +}; + +/* Structure to hold all of our device specific stuff */ +struct mcba_priv { + struct can_priv can; /* must be the first member */ + struct sk_buff *echo_skb[MCBA_MAX_TX_URBS]; + struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS]; + struct usb_device *udev; + struct net_device *netdev; + struct usb_anchor tx_submitted; + struct usb_anchor rx_submitted; + struct can_berr_counter bec; + bool usb_ka_first_pass; + bool can_ka_first_pass; + bool can_speed_check; + atomic_t free_ctx_cnt; +}; + +/* CAN frame */ +struct __packed mcba_usb_msg_can { + u8 cmd_id; + __be16 eid; + __be16 sid; + u8 dlc; + u8 data[8]; + u8 timestamp[4]; + u8 checksum; +}; + +/* command frame */ +struct __packed mcba_usb_msg { + u8 cmd_id; + u8 unused[18]; +}; + +struct __packed mcba_usb_msg_ka_usb { + u8 cmd_id; + u8 termination_state; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 unused[15]; +}; + +struct __packed mcba_usb_msg_ka_can { + u8 cmd_id; + u8 tx_err_cnt; + u8 rx_err_cnt; + u8 rx_buff_ovfl; + u8 tx_bus_off; + __be16 can_bitrate; + __le16 rx_lost; + u8 can_stat; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 debug_mode; + u8 test_complete; + u8 test_result; + u8 unused[4]; +}; + +struct __packed mcba_usb_msg_change_bitrate { + u8 cmd_id; + __be16 bitrate; + u8 unused[16]; +}; + +struct __packed mcba_usb_msg_termination { + u8 cmd_id; + u8 termination; + u8 unused[17]; +}; + +struct __packed mcba_usb_msg_fw_ver { + u8 cmd_id; + u8 pic; + u8 unused[17]; +}; + +static const struct usb_device_id mcba_usb_table[] = { + { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mcba_usb_table); + +static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED, + MCBA_TERMINATION_ENABLED }; + +static const u32 mcba_bitrate[] = { 20000, 33333, 50000, 80000, 83333, + 100000, 125000, 150000, 175000, 200000, + 225000, 250000, 275000, 300000, 500000, + 625000, 800000, 1000000 }; + +static inline void mcba_init_ctx(struct mcba_priv *priv) +{ + int i = 0; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + priv->tx_context[i].ndx = MCBA_CTX_FREE; + priv->tx_context[i].priv = priv; + } + + atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context)); +} + +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv, + struct can_frame *cf) +{ + int i = 0; + struct mcba_usb_ctx *ctx = NULL; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + if (priv->tx_context[i].ndx == MCBA_CTX_FREE) { + ctx = &priv->tx_context[i]; + ctx->ndx = i; + + if (cf) { + ctx->can = true; + ctx->dlc = cf->can_dlc; + } else { + ctx->can = false; + ctx->dlc = 0; + } + + atomic_dec(&priv->free_ctx_cnt); + break; + } + } + + if (!atomic_read(&priv->free_ctx_cnt)) + /* That was the last free ctx. Slow down tx path */ + netif_stop_queue(priv->netdev); + + return ctx; +} + +/* mcba_usb_free_ctx and mcba_usb_get_free_ctx are executed by different + * threads. The order of execution in below function is important. + */ +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx) +{ + /* Increase number of free ctxs before freeing ctx */ + atomic_inc(&ctx->priv->free_ctx_cnt); + + ctx->ndx = MCBA_CTX_FREE; + + /* Wake up the queue once ctx is marked free */ + netif_wake_queue(ctx->priv->netdev); +} + +static void mcba_usb_write_bulk_callback(struct urb *urb) +{ + struct mcba_usb_ctx *ctx = urb->context; + struct net_device *netdev; + + WARN_ON(!ctx); + + netdev = ctx->priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + if (ctx->can) { + if (!netif_device_present(netdev)) + return; + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += ctx->dlc; + + can_led_event(netdev, CAN_LED_EVENT_TX); + can_get_echo_skb(netdev, ctx->ndx); + } + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + /* Release the context */ + mcba_usb_free_ctx(ctx); +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg, + struct mcba_usb_ctx *ctx) +{ + struct urb *urb; + u8 *buf; + int err; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + err = -ENOMEM; + goto nomembuf; + } + + memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE); + + usb_fill_bulk_urb(urb, priv->udev, + usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf, + MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback, + ctx); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + goto failed; + + /* Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return 0; + +failed: + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf, + urb->transfer_dma); + + if (err == -ENODEV) + netif_device_detach(priv->netdev); + else + netdev_warn(priv->netdev, "failed tx_urb %d\n", err); + +nomembuf: + usb_free_urb(urb); + + return err; +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct mcba_usb_ctx *ctx = NULL; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + int err; + struct mcba_usb_msg_can usb_msg = { + .cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV + }; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + ctx = mcba_usb_get_free_ctx(priv, cf); + if (!ctx) + return NETDEV_TX_BUSY; + + can_put_echo_skb(skb, priv->netdev, ctx->ndx); + + if (cf->can_id & CAN_EFF_FLAG) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + sid = MCBA_SIDL_EXID_MASK; + /* store 28-18 bits */ + sid |= (cf->can_id & 0x1ffc0000) >> 13; + /* store 17-16 bits */ + sid |= (cf->can_id & 0x30000) >> 16; + put_unaligned_be16(sid, &usb_msg.sid); + + /* store 15-0 bits */ + put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5, + &usb_msg.sid); + usb_msg.eid = 0; + } + + usb_msg.dlc = cf->can_dlc; + + memcpy(usb_msg.data, cf->data, usb_msg.dlc); + + if (cf->can_id & CAN_RTR_FLAG) + usb_msg.dlc |= MCBA_DLC_RTR_MASK; + + err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx); + if (err) + goto xmit_failed; + + return NETDEV_TX_OK; + +xmit_failed: + can_free_echo_skb(priv->netdev, ctx->ndx); + mcba_usb_free_ctx(ctx); + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +/* Send cmd to device */ +static void mcba_usb_xmit_cmd(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg) +{ + struct mcba_usb_ctx *ctx = NULL; + int err; + + ctx = mcba_usb_get_free_ctx(priv, NULL); + if (!ctx) { + netdev_err(priv->netdev, + "Lack of free ctx. Sending (%d) cmd aborted", + usb_msg->cmd_id); + + return; + } + + err = mcba_usb_xmit(priv, usb_msg, ctx); + if (err) + netdev_err(priv->netdev, "Failed to send cmd (%d)", + usb_msg->cmd_id); +} + +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate) +{ + struct mcba_usb_msg_change_bitrate usb_msg = { + .cmd_id = MBCA_CMD_CHANGE_BIT_RATE + }; + + put_unaligned_be16(bitrate, &usb_msg.bitrate); + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic) +{ + struct mcba_usb_msg_fw_ver usb_msg = { + .cmd_id = MBCA_CMD_READ_FW_VERSION, + .pic = pic + }; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_process_can(struct mcba_priv *priv, + struct mcba_usb_msg_can *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) + return; + + sid = get_unaligned_be16(&msg->sid); + + if (sid & MCBA_SIDL_EXID_MASK) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + cf->can_id = CAN_EFF_FLAG; + + /* store 28-18 bits */ + cf->can_id |= (sid & 0xffe0) << 13; + /* store 17-16 bits */ + cf->can_id |= (sid & 3) << 16; + /* store 15-0 bits */ + cf->can_id |= get_unaligned_be16(&msg->eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + cf->can_id = (sid & 0xffe0) >> 5; + } + + if (msg->dlc & MCBA_DLC_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK); + + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + can_led_event(priv->netdev, CAN_LED_EVENT_RX); + netif_rx(skb); +} + +static void mcba_usb_process_ka_usb(struct mcba_priv *priv, + struct mcba_usb_msg_ka_usb *msg) +{ + if (unlikely(priv->usb_ka_first_pass)) { + netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->usb_ka_first_pass = false; + } + + if (msg->termination_state) + priv->can.termination = MCBA_TERMINATION_ENABLED; + else + priv->can.termination = MCBA_TERMINATION_DISABLED; +} + +static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg) +{ + const u32 bitrate = get_unaligned_be16(&msg->can_bitrate); + + if ((bitrate == 33) || (bitrate == 83)) + return bitrate * 1000 + 333; + else + return bitrate * 1000; +} + +static void mcba_usb_process_ka_can(struct mcba_priv *priv, + struct mcba_usb_msg_ka_can *msg) +{ + if (unlikely(priv->can_ka_first_pass)) { + netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->can_ka_first_pass = false; + } + + if (unlikely(priv->can_speed_check)) { + const u32 bitrate = convert_can2host_bitrate(msg); + + priv->can_speed_check = false; + + if (bitrate != priv->can.bittiming.bitrate) + netdev_err( + priv->netdev, + "Wrong bitrate reported by the device (%u). Expected %u", + bitrate, priv->can.bittiming.bitrate); + } + + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + if (msg->tx_bus_off) + priv->can.state = CAN_STATE_BUS_OFF; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH)) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH)) + priv->can.state = CAN_STATE_ERROR_WARNING; +} + +static void mcba_usb_process_rx(struct mcba_priv *priv, + struct mcba_usb_msg *msg) +{ + switch (msg->cmd_id) { + case MBCA_CMD_I_AM_ALIVE_FROM_CAN: + mcba_usb_process_ka_can(priv, + (struct mcba_usb_msg_ka_can *)msg); + break; + + case MBCA_CMD_I_AM_ALIVE_FROM_USB: + mcba_usb_process_ka_usb(priv, + (struct mcba_usb_msg_ka_usb *)msg); + break; + + case MBCA_CMD_RECEIVE_MESSAGE: + mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg); + break; + + case MBCA_CMD_NOTHING_TO_SEND: + /* Side effect of communication between PIC_USB and PIC_CAN. + * PIC_CAN is telling us that it has nothing to send + */ + break; + + case MBCA_CMD_TRANSMIT_MESSAGE_RSP: + /* Transmission response from the device containing timestamp */ + break; + + default: + netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)", + msg->cmd_id); + break; + } +} + +/* Callback for reading data from device + * + * Check urb status, call read function and resubmit urb read operation. + */ +static void mcba_usb_read_bulk_callback(struct urb *urb) +{ + struct mcba_priv *priv = urb->context; + struct net_device *netdev; + int retval; + int pos = 0; + + netdev = priv->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); + + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct mcba_usb_msg *msg; + + if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) { + netdev_err(priv->netdev, "format error\n"); + break; + } + + msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos); + mcba_usb_process_rx(priv, msg); + + pos += sizeof(struct mcba_usb_msg); + } + +resubmit_urb: + + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT), + urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + netdev_err(netdev, "failed resubmitting read bulk urb: %d\n", + retval); +} + +/* Start USB device */ +static int mcba_usb_start(struct mcba_priv *priv) +{ + struct net_device *netdev = priv->netdev; + int err, i; + + mcba_init_ctx(priv); + + for (i = 0; i < MCBA_MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_IN), + buf, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + buf, urb->transfer_dma); + usb_free_urb(urb); + break; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + netdev_warn(netdev, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MCBA_MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB); + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN); + + return err; +} + +/* Open USB device */ +static int mcba_usb_open(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + priv->can_speed_check = true; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + can_led_event(netdev, CAN_LED_EVENT_OPEN); + netif_start_queue(netdev); + + return 0; +} + +static void mcba_urb_unlink(struct mcba_priv *priv) +{ + usb_kill_anchored_urbs(&priv->rx_submitted); + usb_kill_anchored_urbs(&priv->tx_submitted); +} + +/* Close USB device */ +static int mcba_usb_close(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + mcba_urb_unlink(priv); + + close_candev(netdev); + can_led_event(netdev, CAN_LED_EVENT_STOP); + + return 0; +} + +/* Set network device mode + * + * Maybe we should leave this function empty, because the device + * set mode variable with open command. + */ +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode) +{ + return 0; +} + +static int mcba_net_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +static const struct net_device_ops mcba_netdev_ops = { + .ndo_open = mcba_usb_open, + .ndo_stop = mcba_usb_close, + .ndo_start_xmit = mcba_usb_start_xmit, +}; + +/* Microchip CANBUS has hardcoded bittiming values by default. + * This function sends request via USB to change the speed and align bittiming + * values for presentation purposes only + */ +static int mcba_net_set_bittiming(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000; + + mcba_usb_xmit_change_bitrate(priv, bitrate_kbps); + + return 0; +} + +static int mcba_set_termination(struct net_device *netdev, u16 term) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct mcba_usb_msg_termination usb_msg = { + .cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE + }; + + if (term == MCBA_TERMINATION_ENABLED) + usb_msg.termination = 1; + else + usb_msg.termination = 0; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); + + return 0; +} + +static int mcba_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct mcba_priv *priv; + int err = -ENOMEM; + struct usb_device *usbdev = interface_to_usbdev(intf); + + netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + priv->udev = usbdev; + priv->netdev = netdev; + priv->usb_ka_first_pass = true; + priv->can_ka_first_pass = true; + priv->can_speed_check = false; + + init_usb_anchor(&priv->rx_submitted); + init_usb_anchor(&priv->tx_submitted); + + usb_set_intfdata(intf, priv); + + /* Init CAN device */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.termination_const = mcba_termination; + priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination); + priv->can.bitrate_const = mcba_bitrate; + priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate); + + priv->can.do_set_termination = mcba_set_termination; + priv->can.do_set_mode = mcba_net_set_mode; + priv->can.do_get_berr_counter = mcba_net_get_berr_counter; + priv->can.do_set_bittiming = mcba_net_set_bittiming; + + netdev->netdev_ops = &mcba_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register CAN device: %d\n", err); + + goto cleanup_free_candev; + } + + devm_can_led_init(netdev); + + /* Start USB dev only if we have successfully registered CAN device */ + err = mcba_usb_start(priv); + if (err) { + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", err); + + goto cleanup_unregister_candev; + } + + dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n"); + + return 0; + +cleanup_unregister_candev: + unregister_candev(priv->netdev); + +cleanup_free_candev: + free_candev(netdev); + + return err; +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void mcba_usb_disconnect(struct usb_interface *intf) +{ + struct mcba_priv *priv = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + netdev_info(priv->netdev, "device disconnected\n"); + + unregister_candev(priv->netdev); + free_candev(priv->netdev); + + mcba_urb_unlink(priv); +} + +static struct usb_driver mcba_usb_driver = { + .name = MCBA_MODULE_NAME, + .probe = mcba_usb_probe, + .disconnect = mcba_usb_disconnect, + .id_table = mcba_usb_table, +}; + +module_usb_driver(mcba_usb_driver); + +MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>"); +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h deleted file mode 100644 index 2147678f0225..000000000000 --- a/drivers/net/can/usb/peak_usb/pcan_ucan.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * CAN driver for PEAK System micro-CAN based adapters - * - * Copyright (C) 2003-2011 PEAK System-Technik GmbH - * Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#ifndef PUCAN_H -#define PUCAN_H - -/* uCAN commands opcodes list (low-order 10 bits) */ -#define PUCAN_CMD_NOP 0x000 -#define PUCAN_CMD_RESET_MODE 0x001 -#define PUCAN_CMD_NORMAL_MODE 0x002 -#define PUCAN_CMD_LISTEN_ONLY_MODE 0x003 -#define PUCAN_CMD_TIMING_SLOW 0x004 -#define PUCAN_CMD_TIMING_FAST 0x005 -#define PUCAN_CMD_FILTER_STD 0x008 -#define PUCAN_CMD_TX_ABORT 0x009 -#define PUCAN_CMD_WR_ERR_CNT 0x00a -#define PUCAN_CMD_SET_EN_OPTION 0x00b -#define PUCAN_CMD_CLR_DIS_OPTION 0x00c -#define PUCAN_CMD_END_OF_COLLECTION 0x3ff - -/* uCAN received messages list */ -#define PUCAN_MSG_CAN_RX 0x0001 -#define PUCAN_MSG_ERROR 0x0002 -#define PUCAN_MSG_STATUS 0x0003 -#define PUCAN_MSG_BUSLOAD 0x0004 -#define PUCAN_MSG_CAN_TX 0x1000 - -/* uCAN command common header */ -struct __packed pucan_command { - __le16 opcode_channel; - u16 args[3]; -}; - -#define PUCAN_TSLOW_BRP_BITS 10 -#define PUCAN_TSLOW_TSGEG1_BITS 8 -#define PUCAN_TSLOW_TSGEG2_BITS 7 -#define PUCAN_TSLOW_SJW_BITS 7 - -#define PUCAN_TSLOW_BRP_MASK ((1 << PUCAN_TSLOW_BRP_BITS) - 1) -#define PUCAN_TSLOW_TSEG1_MASK ((1 << PUCAN_TSLOW_TSGEG1_BITS) - 1) -#define PUCAN_TSLOW_TSEG2_MASK ((1 << PUCAN_TSLOW_TSGEG2_BITS) - 1) -#define PUCAN_TSLOW_SJW_MASK ((1 << PUCAN_TSLOW_SJW_BITS) - 1) - -/* uCAN TIMING_SLOW command fields */ -#define PUCAN_TSLOW_SJW_T(s, t) (((s) & PUCAN_TSLOW_SJW_MASK) | \ - ((!!(t)) << 7)) -#define PUCAN_TSLOW_TSEG2(t) ((t) & PUCAN_TSLOW_TSEG2_MASK) -#define PUCAN_TSLOW_TSEG1(t) ((t) & PUCAN_TSLOW_TSEG1_MASK) -#define PUCAN_TSLOW_BRP(b) ((b) & PUCAN_TSLOW_BRP_MASK) - -struct __packed pucan_timing_slow { - __le16 opcode_channel; - - u8 ewl; /* Error Warning limit */ - u8 sjw_t; /* Sync Jump Width + Triple sampling */ - u8 tseg2; /* Timing SEGment 2 */ - u8 tseg1; /* Timing SEGment 1 */ - - __le16 brp; /* BaudRate Prescaler */ -}; - -#define PUCAN_TFAST_BRP_BITS 10 -#define PUCAN_TFAST_TSGEG1_BITS 5 -#define PUCAN_TFAST_TSGEG2_BITS 4 -#define PUCAN_TFAST_SJW_BITS 4 - -#define PUCAN_TFAST_BRP_MASK ((1 << PUCAN_TFAST_BRP_BITS) - 1) -#define PUCAN_TFAST_TSEG1_MASK ((1 << PUCAN_TFAST_TSGEG1_BITS) - 1) -#define PUCAN_TFAST_TSEG2_MASK ((1 << PUCAN_TFAST_TSGEG2_BITS) - 1) -#define PUCAN_TFAST_SJW_MASK ((1 << PUCAN_TFAST_SJW_BITS) - 1) - -/* uCAN TIMING_FAST command fields */ -#define PUCAN_TFAST_SJW(s) ((s) & PUCAN_TFAST_SJW_MASK) -#define PUCAN_TFAST_TSEG2(t) ((t) & PUCAN_TFAST_TSEG2_MASK) -#define PUCAN_TFAST_TSEG1(t) ((t) & PUCAN_TFAST_TSEG1_MASK) -#define PUCAN_TFAST_BRP(b) ((b) & PUCAN_TFAST_BRP_MASK) - -struct __packed pucan_timing_fast { - __le16 opcode_channel; - - u8 unused; - u8 sjw; /* Sync Jump Width */ - u8 tseg2; /* Timing SEGment 2 */ - u8 tseg1; /* Timing SEGment 1 */ - - __le16 brp; /* BaudRate Prescaler */ -}; - -/* uCAN FILTER_STD command fields */ -#define PUCAN_FLTSTD_ROW_IDX_BITS 6 - -struct __packed pucan_filter_std { - __le16 opcode_channel; - - __le16 idx; - __le32 mask; /* CAN-ID bitmask in idx range */ -}; - -/* uCAN WR_ERR_CNT command fields */ -#define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */ -#define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */ - -struct __packed pucan_wr_err_cnt { - __le16 opcode_channel; - - __le16 sel_mask; - u8 tx_counter; /* Tx error counter new value */ - u8 rx_counter; /* Rx error counter new value */ - - u16 unused; -}; - -/* uCAN SET_EN/CLR_DIS _OPTION command fields */ -#define PUCAN_OPTION_ERROR 0x0001 -#define PUCAN_OPTION_BUSLOAD 0x0002 -#define PUCAN_OPTION_CANDFDISO 0x0004 - -struct __packed pucan_options { - __le16 opcode_channel; - - __le16 options; - u32 unused; -}; - -/* uCAN received messages global format */ -struct __packed pucan_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; -}; - -/* uCAN flags for CAN/CANFD messages */ -#define PUCAN_MSG_SELF_RECEIVE 0x80 -#define PUCAN_MSG_ERROR_STATE_IND 0x40 /* error state indicator */ -#define PUCAN_MSG_BITRATE_SWITCH 0x20 /* bitrate switch */ -#define PUCAN_MSG_EXT_DATA_LEN 0x10 /* extended data length */ -#define PUCAN_MSG_SINGLE_SHOT 0x08 -#define PUCAN_MSG_LOOPED_BACK 0x04 -#define PUCAN_MSG_EXT_ID 0x02 -#define PUCAN_MSG_RTR 0x01 - -struct __packed pucan_rx_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; - __le32 tag_low; - __le32 tag_high; - u8 channel_dlc; - u8 client; - __le16 flags; - __le32 can_id; - u8 d[0]; -}; - -/* uCAN error types */ -#define PUCAN_ERMSG_BIT_ERROR 0 -#define PUCAN_ERMSG_FORM_ERROR 1 -#define PUCAN_ERMSG_STUFF_ERROR 2 -#define PUCAN_ERMSG_OTHER_ERROR 3 -#define PUCAN_ERMSG_ERR_CNT_DEC 4 - -struct __packed pucan_error_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; - u8 channel_type_d; - u8 code_g; - u8 tx_err_cnt; - u8 rx_err_cnt; -}; - -#define PUCAN_BUS_PASSIVE 0x20 -#define PUCAN_BUS_WARNING 0x40 -#define PUCAN_BUS_BUSOFF 0x80 - -struct __packed pucan_status_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; - u8 channel_p_w_b; - u8 unused[3]; -}; - -/* uCAN transmitted message format */ -#define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4)) - -struct __packed pucan_tx_msg { - __le16 size; - __le16 type; - __le32 tag_low; - __le32 tag_high; - u8 channel_dlc; - u8 client; - __le16 flags; - __le32 can_id; - u8 d[0]; -}; - -/* build the cmd opcode_channel field with respect to the correct endianness */ -static inline __le16 pucan_cmd_opcode_channel(struct peak_usb_device *dev, - int opcode) -{ - return cpu_to_le16(((dev->ctrl_idx) << 12) | ((opcode) & 0x3ff)); -} - -/* return the channel number part from any received message channel_dlc field */ -static inline int pucan_msg_get_channel(struct pucan_rx_msg *rm) -{ - return rm->channel_dlc & 0xf; -} - -/* return the dlc value from any received message channel_dlc field */ -static inline int pucan_msg_get_dlc(struct pucan_rx_msg *rm) -{ - return rm->channel_dlc >> 4; -} - -static inline int pucan_ermsg_get_channel(struct pucan_error_msg *em) -{ - return em->channel_type_d & 0x0f; -} - -static inline int pucan_stmsg_get_channel(struct pucan_status_msg *sm) -{ - return sm->channel_p_w_b & 0x0f; -} - -#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 304732550f0a..175c205d3f13 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -19,10 +19,10 @@ #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> +#include <linux/can/dev/peak_canfd.h> #include "pcan_usb_core.h" #include "pcan_usb_pro.h" -#include "pcan_ucan.h" MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter"); MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter"); @@ -238,7 +238,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) /* 1st, reset error counters: */ prc = (struct pucan_wr_err_cnt *)pc; - prc->opcode_channel = pucan_cmd_opcode_channel(dev, + prc->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_WR_ERR_CNT); /* select both counters */ @@ -257,9 +257,10 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) puo->opcode_channel = (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? - pucan_cmd_opcode_channel(dev, + pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_CLR_DIS_OPTION) : - pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION); + pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_SET_EN_OPTION); puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO); @@ -274,7 +275,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) /* next, go back to operational mode */ cmd = (struct pucan_command *)pc; - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ? PUCAN_CMD_LISTEN_ONLY_MODE : PUCAN_CMD_NORMAL_MODE); @@ -296,7 +297,7 @@ static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff) struct pucan_command *cmd = (struct pucan_command *)pc; /* build cmd to go back to reset mode */ - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_RESET_MODE); l = sizeof(struct pucan_command); } @@ -332,7 +333,7 @@ static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, } for (i = idx; i < n; i++, cmd++) { - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_FILTER_STD); cmd->idx = cpu_to_le16(i); cmd->mask = cpu_to_le32(mask); @@ -352,7 +353,7 @@ static int pcan_usb_fd_set_options(struct peak_usb_device *dev, { struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, (onoff) ? PUCAN_CMD_SET_EN_OPTION : PUCAN_CMD_CLR_DIS_OPTION); @@ -368,7 +369,7 @@ static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode) { struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PCAN_UFD_CMD_LED_SET); cmd->mode = led_mode; @@ -382,7 +383,7 @@ static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev, { struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PCAN_UFD_CMD_CLK_SET); cmd->mode = clk_mode; @@ -396,7 +397,7 @@ static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev, { struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_TIMING_SLOW); cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1, dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); @@ -417,7 +418,7 @@ static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev, { struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_TIMING_FAST); cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1); cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1); diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c index 674f367087c5..facca33d53e9 100644 --- a/drivers/net/can/vcan.c +++ b/drivers/net/can/vcan.c @@ -1,7 +1,7 @@ /* * vcan.c - Virtual CAN interface * - * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * Copyright (c) 2002-2017 Volkswagen Group Electronic Research * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,9 +50,12 @@ #include <linux/slab.h> #include <net/rtnetlink.h> +#define DRV_NAME "vcan" + MODULE_DESCRIPTION("virtual CAN interface"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>"); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); /* @@ -164,7 +167,7 @@ static void vcan_setup(struct net_device *dev) } static struct rtnl_link_ops vcan_link_ops __read_mostly = { - .kind = "vcan", + .kind = DRV_NAME, .setup = vcan_setup, }; diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c new file mode 100644 index 000000000000..7fbb24795681 --- /dev/null +++ b/drivers/net/can/vxcan.c @@ -0,0 +1,316 @@ +/* + * vxcan.c - Virtual CAN Tunnel for cross namespace communication + * + * This code is derived from drivers/net/can/vcan.c for the virtual CAN + * specific parts and from drivers/net/veth.c to implement the netlink API + * for network interface pairs in a common and established way. + * + * Copyright (c) 2017 Oliver Hartkopp <socketcan@hartkopp.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/skb.h> +#include <linux/can/vxcan.h> +#include <linux/slab.h> +#include <net/rtnetlink.h> + +#define DRV_NAME "vxcan" + +MODULE_DESCRIPTION("Virtual CAN Tunnel"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>"); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); + +struct vxcan_priv { + struct net_device __rcu *peer; +}; + +static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct net_device_stats *peerstats, *srcstats = &dev->stats; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + if (unlikely(!peer)) { + kfree_skb(skb); + dev->stats.tx_dropped++; + goto out_unlock; + } + + skb = can_create_echo_skb(skb); + if (!skb) + goto out_unlock; + + /* reset CAN GW hop counter */ + skb->csum_start = 0; + skb->pkt_type = PACKET_BROADCAST; + skb->dev = peer; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (netif_rx_ni(skb) == NET_RX_SUCCESS) { + srcstats->tx_packets++; + srcstats->tx_bytes += cfd->len; + peerstats = &peer->stats; + peerstats->rx_packets++; + peerstats->rx_bytes += cfd->len; + } + +out_unlock: + rcu_read_unlock(); + return NETDEV_TX_OK; +} + + +static int vxcan_open(struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + if (!peer) + return -ENOTCONN; + + if (peer->flags & IFF_UP) { + netif_carrier_on(dev); + netif_carrier_on(peer); + } + return 0; +} + +static int vxcan_close(struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + netif_carrier_off(dev); + if (peer) + netif_carrier_off(peer); + + return 0; +} + +static int vxcan_get_iflink(const struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer; + int iflink; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + iflink = peer ? peer->ifindex : 0; + rcu_read_unlock(); + + return iflink; +} + +static int vxcan_change_mtu(struct net_device *dev, int new_mtu) +{ + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops vxcan_netdev_ops = { + .ndo_open = vxcan_open, + .ndo_stop = vxcan_close, + .ndo_start_xmit = vxcan_xmit, + .ndo_get_iflink = vxcan_get_iflink, + .ndo_change_mtu = vxcan_change_mtu, +}; + +static void vxcan_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = CAN_MTU; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 0; + dev->flags = (IFF_NOARP|IFF_ECHO); + dev->netdev_ops = &vxcan_netdev_ops; + dev->destructor = free_netdev; +} + +/* forward declaration for rtnl_create_link() */ +static struct rtnl_link_ops vxcan_link_ops; + +static int vxcan_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct vxcan_priv *priv; + struct net_device *peer; + struct net *peer_net; + + struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb; + char ifname[IFNAMSIZ]; + unsigned char name_assign_type; + struct ifinfomsg *ifmp = NULL; + int err; + + /* register peer device */ + if (data && data[VXCAN_INFO_PEER]) { + struct nlattr *nla_peer; + + nla_peer = data[VXCAN_INFO_PEER]; + ifmp = nla_data(nla_peer); + err = rtnl_nla_parse_ifla(peer_tb, + nla_data(nla_peer) + + sizeof(struct ifinfomsg), + nla_len(nla_peer) - + sizeof(struct ifinfomsg), + NULL); + if (err < 0) + return err; + + tbp = peer_tb; + } + + if (tbp[IFLA_IFNAME]) { + nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ); + name_assign_type = NET_NAME_USER; + } else { + snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); + name_assign_type = NET_NAME_ENUM; + } + + peer_net = rtnl_link_get_net(net, tbp); + if (IS_ERR(peer_net)) + return PTR_ERR(peer_net); + + peer = rtnl_create_link(peer_net, ifname, name_assign_type, + &vxcan_link_ops, tbp); + if (IS_ERR(peer)) { + put_net(peer_net); + return PTR_ERR(peer); + } + + if (ifmp && dev->ifindex) + peer->ifindex = ifmp->ifi_index; + + err = register_netdevice(peer); + put_net(peer_net); + peer_net = NULL; + if (err < 0) { + free_netdev(peer); + return err; + } + + netif_carrier_off(peer); + + err = rtnl_configure_link(peer, ifmp); + if (err < 0) { + unregister_netdevice(peer); + return err; + } + + /* register first device */ + if (tb[IFLA_IFNAME]) + nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ); + else + snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); + + err = register_netdevice(dev); + if (err < 0) { + unregister_netdevice(peer); + return err; + } + + netif_carrier_off(dev); + + /* cross link the device pair */ + priv = netdev_priv(dev); + rcu_assign_pointer(priv->peer, peer); + + priv = netdev_priv(peer); + rcu_assign_pointer(priv->peer, dev); + + return 0; +} + +static void vxcan_dellink(struct net_device *dev, struct list_head *head) +{ + struct vxcan_priv *priv; + struct net_device *peer; + + priv = netdev_priv(dev); + peer = rtnl_dereference(priv->peer); + + /* Note : dellink() is called from default_device_exit_batch(), + * before a rcu_synchronize() point. The devices are guaranteed + * not being freed before one RCU grace period. + */ + RCU_INIT_POINTER(priv->peer, NULL); + unregister_netdevice_queue(dev, head); + + if (peer) { + priv = netdev_priv(peer); + RCU_INIT_POINTER(priv->peer, NULL); + unregister_netdevice_queue(peer, head); + } +} + +static const struct nla_policy vxcan_policy[VXCAN_INFO_MAX + 1] = { + [VXCAN_INFO_PEER] = { .len = sizeof(struct ifinfomsg) }, +}; + +static struct net *vxcan_get_link_net(const struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + return peer ? dev_net(peer) : dev_net(dev); +} + +static struct rtnl_link_ops vxcan_link_ops = { + .kind = DRV_NAME, + .priv_size = sizeof(struct vxcan_priv), + .setup = vxcan_setup, + .newlink = vxcan_newlink, + .dellink = vxcan_dellink, + .policy = vxcan_policy, + .maxtype = VXCAN_INFO_MAX, + .get_link_net = vxcan_get_link_net, +}; + +static __init int vxcan_init(void) +{ + pr_info("vxcan: Virtual CAN Tunnel driver\n"); + + return rtnl_link_register(&vxcan_link_ops); +} + +static __exit void vxcan_exit(void) +{ + rtnl_link_unregister(&vxcan_link_ops); +} + +module_init(vxcan_init); +module_exit(vxcan_exit); |