/**************************************************************************** * arch/arm/src/s32k3xx/s32k3xx_flexcan.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /* Copyright 2022 NXP */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arm_internal.h" #include "chip.h" #include "s32k3xx_config.h" #include "hardware/s32k3xx_flexcan.h" #include "hardware/s32k3xx_pinmux.h" #include "s32k3xx_periphclocks.h" #include "s32k3xx_clockconfig.h" #include "s32k3xx_pin.h" #include "s32k3xx_flexcan.h" #include #include #ifdef CONFIG_S32K3XX_FLEXCAN /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* If processing is not done at the interrupt level, then work queue support * is required. */ #define CANWORK LPWORK /* CONFIG_S32K3XX_FLEXCAN_NETHIFS determines the number of physical * interfaces that will be supported. */ #define MASKSTDID 0x000007ff #define MASKEXTID 0x1fffffff #define FLAGEFF (1 << 31) /* Extended frame format */ #define FLAGRTR (1 << 30) /* Remote transmission request */ #define RXMBCOUNT 5 #define TXMBCOUNT 2 #define TOTALMBCOUNT RXMBCOUNT + TXMBCOUNT #define IFLAG1_RX ((1 << RXMBCOUNT)-1) #define IFLAG1_TX (((1 << TXMBCOUNT)-1) << RXMBCOUNT) #define CAN_FIFO_NE (1 << 5) #define CAN_FIFO_OV (1 << 6) #define CAN_FIFO_WARN (1 << 7) #define CAN_EFF_FLAG 0x80000000 /* EFF/SFF is set in the MSB */ #define POOL_SIZE 1 #define MSG_DATA sizeof(struct timeval) /* CAN bit timing values */ #define PRESDIV_MAX 256 #define SEG_MAX 8 #define SEG_MIN 1 #define TSEG_MIN 2 #define TSEG1_MAX 17 #define TSEG2_MAX 9 #define NUMTQ_MAX 26 #define SEG_FD_MAX 32 #define SEG_FD_MIN 1 #define TSEG_FD_MIN 2 #define TSEG1_FD_MAX 39 #define TSEG2_FD_MAX 9 #define NUMTQ_FD_MAX 49 #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE # if !defined(CONFIG_SCHED_WORKQUEUE) # error Work queue support is required # endif #define TX_TIMEOUT_WQ #endif /* Interrupt flags for RX fifo */ #define IFLAG1_RXFIFO (CAN_FIFO_NE | CAN_FIFO_WARN | CAN_FIFO_OV) static int peak_tx_mailbox_index_ = 0; /**************************************************************************** * Private Types ****************************************************************************/ union cs_e { volatile uint32_t cs; struct { volatile uint32_t time_stamp : 16; volatile uint32_t dlc : 4; volatile uint32_t rtr : 1; volatile uint32_t ide : 1; volatile uint32_t srr : 1; volatile uint32_t res : 1; volatile uint32_t code : 4; volatile uint32_t res2 : 1; volatile uint32_t esi : 1; volatile uint32_t brs : 1; volatile uint32_t edl : 1; }; }; union id_e { volatile uint32_t w; struct { volatile uint32_t ext : 29; volatile uint32_t resex : 3; }; struct { volatile uint32_t res : 18; volatile uint32_t std : 11; volatile uint32_t resstd : 3; }; }; union data_e { volatile uint32_t w00; struct { volatile uint32_t b03 : 8; volatile uint32_t b02 : 8; volatile uint32_t b01 : 8; volatile uint32_t b00 : 8; }; }; struct mb_s { union cs_e cs; union id_e id; #ifdef CONFIG_NET_CAN_CANFD union data_e data[16]; #else union data_e data[2]; #endif }; #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE #define TX_ABORT -1 #define TX_FREE 0 #define TX_BUSY 1 struct txmbstats { struct timeval deadline; uint32_t pending; /* -1 = abort, 0 = free, 1 = busy */ }; #endif /* FlexCAN Device hardware configuration */ struct flexcan_config_s { uint32_t tx_pin; /* GPIO configuration for TX */ uint32_t rx_pin; /* GPIO configuration for RX */ uint32_t no_buffers; /* Number of message buffers */ uint32_t enable_pin; /* Optional enable pin */ uint32_t enable_high; /* Optional enable high/low */ uint32_t stb_pin; /* Optional stb pin */ uint32_t stb_high; /* Optional stb high/low */ uint32_t led_pin; /* Optional led pin */ uint32_t led_high; /* Optional led high/low */ uint32_t bus_irq; /* BUS IRQ */ uint32_t error_irq; /* ERROR IRQ */ uint32_t lprx_irq; /* LPRX IRQ */ uint32_t mb_irq; /* MB 0-15 IRQ */ }; struct flexcan_timeseg { uint32_t bitrate; int32_t samplep; uint8_t propseg; uint8_t pseg1; uint8_t pseg2; uint8_t presdiv; }; /* FlexCAN device structures */ #ifdef CONFIG_S32K3XX_FLEXCAN0 static const struct flexcan_config_s s32k3xx_flexcan0_config = { .tx_pin = PIN_CAN0_TX, .rx_pin = PIN_CAN0_RX, .no_buffers = 64, #ifdef PIN_CAN0_ENABLE .enable_pin = PIN_CAN0_ENABLE, .enable_high = CAN0_ENABLE_OUT, #else .enable_pin = 0, .enable_high = 0, #endif #ifdef PIN_CAN0_STB .stb_pin = PIN_CAN0_STB, .stb_high = CAN0_STB_OUT, #else .stb_pin = 0, .stb_high = 0, #endif #ifdef PIN_CAN0_LED .led_pin = PIN_CAN0_LED, .led_high = CAN0_LED_OUT, #else .stb_pin = 0, .stb_high = 0, #endif .bus_irq = 0, .error_irq = 0, .lprx_irq = 0, .mb_irq = S32K3XX_IRQ_FLEXCAN0_1, }; #endif #ifdef CONFIG_S32K3XX_FLEXCAN1 static const struct flexcan_config_s s32k3xx_flexcan1_config = { .tx_pin = PIN_CAN1_TX, .rx_pin = PIN_CAN1_RX, .no_buffers = 64, #ifdef PIN_CAN1_ENABLE .enable_pin = PIN_CAN1_ENABLE, .enable_high = CAN1_ENABLE_OUT, #else .enable_pin = 0, .enable_high = 0, #endif #ifdef PIN_CAN1_STB .stb_pin = PIN_CAN1_STB, .stb_high = CAN1_STB_OUT, #else .stb_pin = 0, .stb_high = 0, #endif #ifdef PIN_CAN1_LED .led_pin = PIN_CAN1_LED, .led_high = CAN1_LED_OUT, #else .led_pin = 0, .led_high = 0, #endif .bus_irq = 0, .error_irq = 0, .lprx_irq = 0, .mb_irq = S32K3XX_IRQ_FLEXCAN1_1, }; #endif #ifdef CONFIG_S32K3XX_FLEXCAN2 static const struct flexcan_config_s s32k3xx_flexcan2_config = { .tx_pin = PIN_CAN2_TX, .rx_pin = PIN_CAN2_RX, .no_buffers = 64, #ifdef PIN_CAN2_ENABLE .enable_pin = PIN_CAN2_ENABLE, .enable_high = CAN2_ENABLE_OUT, #else .enable_pin = 0, .rx_pin = 0, #endif #ifdef PIN_CAN2_STB .stb_pin = PIN_CAN2_STB, .stb_high = CAN2_STB_OUT, #else .stb_pin = 0, .stb_high = 0, #endif #ifdef PIN_CAN2_LED .led_pin = PIN_CAN2_LED, .led_high = CAN2_LED_OUT, #else .led_pin = 0, .led_high = 0, #endif .bus_irq = 0, .error_irq = 0, .lprx_irq = 0, .mb_irq = S32K3XX_IRQ_FLEXCAN2_1, }; #endif #ifdef CONFIG_S32K3XX_FLEXCAN3 static const struct flexcan_config_s s32k3xx_flexcan3_config = { .tx_pin = PIN_CAN3_TX, .rx_pin = PIN_CAN3_RX, .no_buffers = 32, #ifdef PIN_CAN3_ENABLE .enable_pin = PIN_CAN3_ENABLE, .enable_high = CAN3_ENABLE_OUT, #else .enable_pin = 0, .rx_pin = 0, #endif #ifdef PIN_CAN3_STB .stb_pin = PIN_CAN3_STB, .stb_high = CAN3_STB_OUT, #else .stb_pin = 0, .stb_high = 0, #endif #ifdef PIN_CAN3_LED .led_pin = PIN_CAN3_LED, .led_high = CAN3_LED_OUT, #else .led_pin = 0, .led_high = 0, #endif .bus_irq = 0, .error_irq = 0, .lprx_irq = 0, .mb_irq = S32K3XX_IRQ_FLEXCAN3_1, }; #endif #ifdef CONFIG_S32K3XX_FLEXCAN4 static const struct flexcan_config_s s32k3xx_flexcan4_config = { .tx_pin = PIN_CAN4_TX, .rx_pin = PIN_CAN4_RX, .no_buffers = 32, #ifdef PIN_CAN4_ENABLE .enable_pin = PIN_CAN4_ENABLE, .enable_high = CAN4_ENABLE_OUT, #else .enable_pin = 0, .enable_high = 0, #endif #ifdef PIN_CAN4_STB .stb_pin = PIN_CAN4_STB, .stb_high = CAN4_STB_OUT, #else .stb_pin = 0, .stb_high = 0, #endif #ifdef PIN_CAN4_LED .led_pin = PIN_CAN4_LED, .led_high = CAN4_LED_OUT, #else .led_pin = 0, .led_high = 0, #endif .bus_irq = 0, .error_irq = 0, .lprx_irq = 0, .mb_irq = S32K3XX_IRQ_FLEXCAN4_1, }; #endif #ifdef CONFIG_S32K3XX_FLEXCAN5 static const struct flexcan_config_s s32k3xx_flexcan5_config = { .tx_pin = PIN_CAN5_TX, .rx_pin = PIN_CAN5_RX, .no_buffers = 32, #ifdef PIN_CAN5_ENABLE .enable_pin = PIN_CAN5_ENABLE, .enable_high = CAN5_ENABLE_OUT, #else .enable_pin = 0, .enable_high = 0, #endif #ifdef PIN_CAN5_STB .stb_pin = PIN_CAN5_STB, .stb_high = CAN5_STB_OUT, #else .stb_pin = 0, .stb_high = 0, #endif #ifdef PIN_CAN5_LED .led_pin = PIN_CAN5_LED, .led_high = CAN5_LED_OUT, #else .led_pin = 0, .led_high = 0, #endif .bus_irq = 0, .error_irq = 0, .lprx_irq = 0, .mb_irq = S32K3XX_IRQ_FLEXCAN5_1, }; #endif /* The s32k3xx_driver_s encapsulates all state information for a single * hardware interface */ struct s32k3xx_driver_s { uint32_t base; /* FLEXCAN base address */ uint32_t clk_freq; /* Pheriperal engine clock freq */ bool bifup; /* true:ifup false:ifdown */ #ifdef TX_TIMEOUT_WQ struct wdog_s txtimeout[TXMBCOUNT]; /* TX timeout timer */ #endif struct work_s irqwork; /* For deferring interrupt work to the wq */ struct work_s pollwork; /* For deferring poll work to the work wq */ #ifdef CONFIG_NET_CAN_CANFD struct canfd_frame *txdesc; /* A pointer to the list of TX descriptor */ struct canfd_frame *rxdesc; /* A pointer to the list of RX descriptors */ #else struct can_frame *txdesc; /* A pointer to the list of TX descriptor */ struct can_frame *rxdesc; /* A pointer to the list of RX descriptors */ #endif /* This holds the information visible to the NuttX network */ struct net_driver_s dev; /* Interface understood by the network */ struct mb_s *rx; struct mb_s *tx; struct flexcan_timeseg arbi_timing; /* Timing for arbitration phase */ #ifdef CONFIG_NET_CAN_CANFD struct flexcan_timeseg data_timing; /* Timing for data phase */ #endif const struct flexcan_config_s *config; #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE struct txmbstats txmb[TXMBCOUNT]; #endif }; /**************************************************************************** * Private Data ****************************************************************************/ #ifdef CONFIG_S32K3XX_FLEXCAN0 static struct s32k3xx_driver_s g_flexcan0; #endif #ifdef CONFIG_S32K3XX_FLEXCAN1 static struct s32k3xx_driver_s g_flexcan1; #endif #ifdef CONFIG_S32K3XX_FLEXCAN2 static struct s32k3xx_driver_s g_flexcan2; #endif #ifdef CONFIG_S32K3XX_FLEXCAN3 static struct s32k3xx_driver_s g_flexcan3; #endif #ifdef CONFIG_S32K3XX_FLEXCAN4 static struct s32k3xx_driver_s g_flexcan4; #endif #ifdef CONFIG_S32K3XX_FLEXCAN5 static struct s32k3xx_driver_s g_flexcan5; #endif #ifdef CONFIG_NET_CAN_CANFD static uint8_t g_tx_pool[(sizeof(struct canfd_frame)+MSG_DATA)*POOL_SIZE]; static uint8_t g_rx_pool[(sizeof(struct canfd_frame)+MSG_DATA)*POOL_SIZE]; #else static uint8_t g_tx_pool[sizeof(struct can_frame)*POOL_SIZE]; static uint8_t g_rx_pool[sizeof(struct can_frame)*POOL_SIZE]; #endif /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /**************************************************************************** * Name: arm_lsb * * Description: * Calculate position of lsb that's equal to 1 * * Input Parameters: * value - The value to perform the operation on * * Returned Value: * location of lsb which is equal to 1, returns 32 when value is 0 * ****************************************************************************/ static inline uint32_t arm_lsb(unsigned int value) { uint32_t ret; volatile uint32_t rvalue = value; __asm__ __volatile__ ("rbit %1,%0" : "=r" (rvalue) : "r" (rvalue)); __asm__ __volatile__ ("clz %0, %1" : "=r"(ret) : "r"(rvalue)); return ret; } /**************************************************************************** * Name: s32k3xx_bitratetotimeseg * * Description: * Convert bitrate to timeseg * * Input Parameters: * timeseg - structure to store bit timing * sp_tolerance - allowed difference in sample point from calculated * bit timings (recommended value: 1) * can_fd - if set to calculate CAN FD bit timings, otherwise calculate * classical can timings * * Returned Value: * return 1 on success, return 0 on failure * ****************************************************************************/ uint32_t s32k3xx_bitratetotimeseg(struct flexcan_timeseg *timeseg, int32_t sp_tolerance, uint32_t can_fd, uint32_t clk_freq) { int32_t tmppresdiv; int32_t numtq; int32_t tmpsample; int32_t tseg1; int32_t tseg2; int32_t tmppseg1; int32_t tmppseg2; int32_t tmppropseg; const int32_t TSEG1MAX = (can_fd ? TSEG1_FD_MAX : TSEG1_MAX); const int32_t TSEG2MAX = (can_fd ? TSEG2_FD_MAX : TSEG2_MAX); const int32_t SEGMAX = (can_fd ? SEG_FD_MAX : SEG_MAX); const int32_t NUMTQMAX = (can_fd ? NUMTQ_FD_MAX : NUMTQ_MAX); for (tmppresdiv = 0; tmppresdiv < PRESDIV_MAX; tmppresdiv++) { numtq = (clk_freq / ((tmppresdiv + 1) * timeseg->bitrate)); if (numtq == 0) { continue; } /* The number of time quanta in 1 bit time must be * lower than the one supported */ if ((clk_freq / ((tmppresdiv + 1) * numtq) == timeseg->bitrate) && (numtq >= 8) && (numtq < NUMTQMAX)) { /* Compute time segments based on the value of the sampling point */ tseg1 = (numtq * timeseg->samplep / 100) - 1; tseg2 = numtq - 1 - tseg1; /* Adjust time segment 1 and time segment 2 */ while (tseg1 >= TSEG1MAX || tseg2 < TSEG_MIN) { tseg2++; tseg1--; } tmppseg2 = tseg2 - 1; /* Start from pseg1 = pseg2 and adjust until propseg is valid */ tmppseg1 = tmppseg2; tmppropseg = tseg1 - tmppseg1 - 2; while (tmppropseg <= 0) { tmppropseg++; tmppseg1--; } while (tmppropseg >= SEGMAX) { tmppropseg--; tmppseg1++; } if (((tseg1 >= TSEG1MAX) || (tseg2 >= TSEG2MAX) || (tseg2 < TSEG_MIN) || (tseg1 < TSEG_MIN)) || ((tmppropseg >= SEGMAX) || (tmppseg1 >= SEGMAX) || (tmppseg2 < SEG_MIN) || (tmppseg2 >= SEGMAX))) { continue; } tmpsample = ((tseg1 + 1) * 100) / numtq; if ((tmpsample - timeseg->samplep) <= sp_tolerance && (timeseg->samplep - tmpsample) <= sp_tolerance) { if (can_fd == 1) { timeseg->propseg = tmppropseg + 1; } else { timeseg->propseg = tmppropseg; } timeseg->pseg1 = tmppseg1; timeseg->pseg2 = tmppseg2; timeseg->presdiv = tmppresdiv; timeseg->samplep = tmpsample; return 1; } } } return 0; } /* Common TX logic */ static bool s32k3xx_txringfull(struct s32k3xx_driver_s *priv); static int s32k3xx_transmit(struct s32k3xx_driver_s *priv); static int s32k3xx_txpoll(struct net_driver_s *dev); /* Helper functions */ static void s32k3xx_setenable(uint32_t base, uint32_t enable); static void s32k3xx_setfreeze(uint32_t base, uint32_t freeze); static uint32_t s32k3xx_waitmcr_change(uint32_t base, uint32_t mask, uint32_t target_state); static uint32_t s32k3xx_waitesr2_change(uint32_t base, uint32_t mask, uint32_t target_state); /* Interrupt handling */ static void s32k3xx_receive(struct s32k3xx_driver_s *priv, uint32_t flags); static void s32k3xx_txdone_work(void *arg); static void s32k3xx_txdone(struct s32k3xx_driver_s *priv); static int s32k3xx_flexcan_interrupt(int irq, void *context, void *arg); /* Watchdog timer expirations */ #ifdef TX_TIMEOUT_WQ static void s32k3xx_txtimeout_work(void *arg); static void s32k3xx_txtimeout_expiry(wdparm_t arg); #endif /* NuttX callback functions */ static int s32k3xx_ifup(struct net_driver_s *dev); static int s32k3xx_ifdown(struct net_driver_s *dev); static void s32k3xx_txavail_work(void *arg); static int s32k3xx_txavail(struct net_driver_s *dev); #ifdef CONFIG_NETDEV_IOCTL static int s32k3xx_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg); #endif /* Initialization */ static int s32k3xx_initialize(struct s32k3xx_driver_s *priv); static void s32k3xx_reset(struct s32k3xx_driver_s *priv); /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Function: s32k3xx_txringfull * * Description: * Check if all of the TX descriptors are in use. * * Input Parameters: * priv - Reference to the driver state structure * * Returned Value: * true is the TX ring is full; false if there are free slots at the * head index. * ****************************************************************************/ static bool s32k3xx_txringfull(struct s32k3xx_driver_s *priv) { uint32_t mbi = 0; while (mbi < TXMBCOUNT) { if (priv->tx[mbi].cs.code != CAN_TXMB_DATAORREMOTE) { return 0; } mbi++; } return 1; } /**************************************************************************** * Function: s32k3xx_transmit * * Description: * Start hardware transmission. Called either from the txdone interrupt * handling or from watchdog based polling. * * Input Parameters: * priv - Reference to the driver state structure * * Returned Value: * OK on success; a negated errno on failure * * Assumptions: * May or may not be called from an interrupt handler. In either case, * global interrupts are disabled, either explicitly or indirectly through * interrupt handling logic. * ****************************************************************************/ static int s32k3xx_transmit(struct s32k3xx_driver_s *priv) { /* Attempt to write frame */ uint32_t mbi = 0; uint32_t mb_bit; uint32_t regval; #ifdef CONFIG_NET_CAN_CANFD uint32_t *frame_data_word; uint32_t i; #endif #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE int32_t timeout; #endif if ((getreg32(priv->base + S32K3XX_CAN_ESR2_OFFSET) & (CAN_ESR2_IMB | CAN_ESR2_VPS)) == (CAN_ESR2_IMB | CAN_ESR2_VPS)) { mbi = ((getreg32(priv->base + S32K3XX_CAN_ESR2_OFFSET) & CAN_ESR2_LPTM_MASK) >> CAN_ESR2_LPTM_SHIFT); mbi -= RXMBCOUNT; } mb_bit = 1 << (RXMBCOUNT + mbi); while (mbi < TXMBCOUNT) { if (priv->tx[mbi].cs.code != CAN_TXMB_DATAORREMOTE) { putreg32(mb_bit, priv->base + S32K3XX_CAN_IFLAG1_OFFSET); break; } mb_bit <<= 1; mbi++; } if (mbi == TXMBCOUNT) { nwarn("No TX MB available mbi %" PRIu32 "\n", mbi); NETDEV_TXERRORS(&priv->dev); return 0; /* No transmission for you! */ } #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE struct timespec ts; clock_systime_timespec(&ts); if (priv->dev.d_sndlen > priv->dev.d_len) { struct timeval *tv = (struct timeval *)(priv->dev.d_buf + priv->dev.d_len); priv->txmb[mbi].deadline = *tv; timeout = (tv->tv_sec - ts.tv_sec)*CLK_TCK + ((tv->tv_usec - ts.tv_nsec / 1000)*CLK_TCK) / 1000000; if (timeout < 0) { return 0; /* No transmission for you! */ } } else { /* Default TX deadline defined in NET_CAN_RAW_DEFAULT_TX_DEADLINE */ if (CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE > 0) { timeout = ((CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000) *CLK_TCK); priv->txmb[mbi].deadline.tv_sec = ts.tv_sec + CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000; priv->txmb[mbi].deadline.tv_usec = (ts.tv_nsec / 1000) + CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE % 1000000; } else { priv->txmb[mbi].deadline.tv_sec = 0; priv->txmb[mbi].deadline.tv_usec = 0; timeout = -1; } } #endif peak_tx_mailbox_index_ = (peak_tx_mailbox_index_ > mbi ? peak_tx_mailbox_index_ : mbi); union cs_e cs; cs.code = CAN_TXMB_DATAORREMOTE; struct mb_s *mb = &priv->tx[mbi]; mb->cs.code = CAN_TXMB_INACTIVE; if (priv->dev.d_len <= sizeof(struct can_frame)) { struct can_frame *frame = (struct can_frame *)priv->dev.d_buf; if (frame->can_id & CAN_EFF_FLAG) { cs.ide = 1; mb->id.ext = frame->can_id & MASKEXTID; } else { mb->id.std = frame->can_id & MASKSTDID; } cs.rtr = frame->can_id & FLAGRTR ? 1 : 0; cs.dlc = frame->can_dlc; mb->data[0].w00 = __builtin_bswap32(*(uint32_t *)&frame->data[0]); mb->data[1].w00 = __builtin_bswap32(*(uint32_t *)&frame->data[4]); } #ifdef CONFIG_NET_CAN_CANFD else /* CAN FD frame */ { struct canfd_frame *frame = (struct canfd_frame *)priv->dev.d_buf; cs.edl = 1; /* CAN FD Frame */ if (frame->can_id & CAN_EFF_FLAG) { cs.ide = 1; mb->id.ext = frame->can_id & MASKEXTID; } else { mb->id.std = frame->can_id & MASKSTDID; } cs.rtr = frame->can_id & FLAGRTR ? 1 : 0; cs.dlc = len_to_can_dlc[frame->len]; frame_data_word = (uint32_t *)&frame->data[0]; for (i = 0; i < (frame->len + 4 - 1) / 4; i++) { mb->data[i].w00 = __builtin_bswap32(frame_data_word[i]); } } #endif mb->cs = cs; /* Go. */ regval = getreg32(priv->base + S32K3XX_CAN_IMASK1_OFFSET); regval |= mb_bit; putreg32(regval, priv->base + S32K3XX_CAN_IMASK1_OFFSET); /* Increment statistics */ NETDEV_TXPACKETS(&priv->dev); #ifdef TX_TIMEOUT_WQ /* Setup the TX timeout watchdog (perhaps restarting the timer) */ if (timeout >= 0) { wd_start(&priv->txtimeout[mbi], timeout + 1, s32k3xx_txtimeout_expiry, (wdparm_t)priv); } #endif return OK; } /**************************************************************************** * Function: s32k3xx_txpoll * * Description: * The transmitter is available, check if the network has any outgoing * packets ready to send. This is a callback from devif_poll(). * devif_poll() may be called: * * 1. When the preceding TX packet send is complete, * 2. When the preceding TX packet send timesout and the interface is reset * 3. During normal TX polling * * Input Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * OK on success; a negated errno on failure * * Assumptions: * May or may not be called from an interrupt handler. In either case, * global interrupts are disabled, either explicitly or indirectly through * interrupt handling logic. * ****************************************************************************/ static int s32k3xx_txpoll(struct net_driver_s *dev) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)dev->d_private; /* If the polling resulted in data that should be sent out on the network, * the field d_len is set to a value > 0. */ if (priv->dev.d_len > 0) { if (!devif_loopback(&priv->dev)) { s32k3xx_txdone(priv); /* Send the packet */ s32k3xx_transmit(priv); /* Check if there is room in the device to hold another packet. If * not, return a non-zero value to terminate the poll. */ if ((getreg32(priv->base + S32K3XX_CAN_ESR2_OFFSET) & (CAN_ESR2_IMB | CAN_ESR2_VPS)) == (CAN_ESR2_IMB | CAN_ESR2_VPS)) { if (s32k3xx_txringfull(priv)) { return -EBUSY; } } } } /* If zero is returned, the polling will continue until all connections * have been examined. */ return 0; } /**************************************************************************** * Function: s32k3xx_receive * * Description: * An interrupt was received indicating the availability of a new RX packet * * Input Parameters: * priv - Reference to the driver state structure * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by interrupt handling logic. * ****************************************************************************/ static void s32k3xx_receive(struct s32k3xx_driver_s *priv, uint32_t flags) { uint32_t mb_index; struct mb_s *rf; #ifdef CONFIG_NET_CAN_CANFD uint32_t *frame_data_word; uint32_t i; #endif while ((mb_index = arm_lsb(flags)) != 32) { rf = &priv->rx[mb_index]; /* Read the frame contents */ #ifdef CONFIG_NET_CAN_CANFD if (rf->cs.edl) /* CAN FD frame */ { struct canfd_frame *frame = (struct canfd_frame *)priv->rxdesc; if (rf->cs.ide) { frame->can_id = MASKEXTID & rf->id.ext; frame->can_id |= FLAGEFF; } else { frame->can_id = MASKSTDID & rf->id.std; } if (rf->cs.rtr) { frame->can_id |= FLAGRTR; } frame->len = can_dlc_to_len[rf->cs.dlc]; frame_data_word = (uint32_t *)&frame->data[0]; for (i = 0; i < (frame->len + 4 - 1) / 4; i++) { frame_data_word[i] = __builtin_bswap32(rf->data[i].w00); } /* Clear MB interrupt flag */ putreg32(1 << mb_index, priv->base + S32K3XX_CAN_IFLAG1_OFFSET); /* Copy the buffer pointer to priv->dev.. Set amount of data * in priv->dev.d_len */ priv->dev.d_len = sizeof(struct canfd_frame); priv->dev.d_buf = (uint8_t *)frame; } else /* CAN 2.0 Frame */ #endif { struct can_frame *frame = (struct can_frame *)priv->rxdesc; if (rf->cs.ide) { frame->can_id = MASKEXTID & rf->id.ext; frame->can_id |= FLAGEFF; } else { frame->can_id = MASKSTDID & rf->id.std; } if (rf->cs.rtr) { frame->can_id |= FLAGRTR; } frame->can_dlc = rf->cs.dlc; *(uint32_t *)&frame->data[0] = __builtin_bswap32(rf->data[0].w00); *(uint32_t *)&frame->data[4] = __builtin_bswap32(rf->data[1].w00); /* Clear MB interrupt flag */ putreg32(1 << mb_index, priv->base + S32K3XX_CAN_IFLAG1_OFFSET); /* Copy the buffer pointer to priv->dev.. Set amount of data * in priv->dev.d_len */ priv->dev.d_len = sizeof(struct can_frame); priv->dev.d_buf = (uint8_t *)frame; } /* Send to socket interface */ NETDEV_RXPACKETS(&priv->dev); can_input(&priv->dev); /* Point the packet buffer back to the next Tx buffer that will be * used during the next write. If the write queue is full, then * this will point at an active buffer, which must not be written * to. This is OK because devif_poll won't be called unless the * queue is not full. */ priv->dev.d_buf = (uint8_t *)priv->txdesc; flags &= ~(1 << mb_index); /* Reread interrupt flags and process them in this loop */ if (flags == 0) { flags = getreg32(priv->base + S32K3XX_CAN_IFLAG1_OFFSET); flags &= IFLAG1_RX; } } } /**************************************************************************** * Function: s32k3xx_txdone * * Description: * Check transmit interrupt flags and clear them * * Input Parameters: * priv - Reference to the driver state structure * * Returned Value: * None * * Assumptions: * None * ****************************************************************************/ static void s32k3xx_txdone(struct s32k3xx_driver_s *priv) { uint32_t flags; uint32_t mbi; uint32_t mb_bit; flags = getreg32(priv->base + S32K3XX_CAN_IFLAG1_OFFSET); flags &= IFLAG1_TX; /* TODO First Process Error aborts */ /* Process TX completions */ mb_bit = 1 << RXMBCOUNT; for (mbi = 0; flags && mbi < TXMBCOUNT; mbi++) { if (flags & mb_bit) { putreg32(mb_bit, priv->base + S32K3XX_CAN_IFLAG1_OFFSET); flags &= ~mb_bit; NETDEV_TXDONE(&priv->dev); #ifdef TX_TIMEOUT_WQ /* We are here because a transmission completed, so the * corresponding watchdog can be canceled * mailbox be set to inactive */ wd_cancel(&priv->txtimeout[mbi]); struct mb_s *mb = &priv->tx[mbi]; mb->cs.code = CAN_TXMB_INACTIVE; #endif } mb_bit <<= 1; } } /**************************************************************************** * Function: s32k3xx_txdone_work * * Description: * An interrupt was received indicating that the last TX packet(s) is done * * Input Parameters: * priv - Reference to the driver state structure * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by the watchdog logic. * We are not in an interrupt context so that we can lock the network. * ****************************************************************************/ static void s32k3xx_txdone_work(void *arg) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)arg; s32k3xx_txdone(priv); /* There should be space for a new TX in any event. Poll the network for * new XMIT data */ net_lock(); devif_poll(&priv->dev, s32k3xx_txpoll); net_unlock(); } /**************************************************************************** * Function: s32k3xx_flexcan_interrupt * * Description: * Three interrupt sources will vector to this function: * 1. CAN MB transmit interrupt handler * 2. CAN MB receive interrupt handler * 3. * * Input Parameters: * irq - Number of the IRQ that generated the interrupt * context - Interrupt register state save info (architecture-specific) * * Returned Value: * OK on success * * Assumptions: * ****************************************************************************/ static int s32k3xx_flexcan_interrupt(int irq, void *context, void *arg) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)arg; if (irq == priv->config->mb_irq) { uint32_t flags; flags = getreg32(priv->base + S32K3XX_CAN_IFLAG1_OFFSET); flags &= IFLAG1_RX; if (flags) { /* Process immediately since scheduling a workqueue is too slow * which causes us to drop CAN frames */ s32k3xx_receive(priv, flags); } flags = getreg32(priv->base + S32K3XX_CAN_IFLAG1_OFFSET); flags &= IFLAG1_TX; if (flags) { /* Disable further TX MB CAN interrupts. here can be no race * condition here. */ flags = getreg32(priv->base + S32K3XX_CAN_IMASK1_OFFSET); flags &= ~(IFLAG1_TX); putreg32(flags, priv->base + S32K3XX_CAN_IMASK1_OFFSET); work_queue(CANWORK, &priv->irqwork, s32k3xx_txdone_work, priv, 0); } } return OK; } /**************************************************************************** * Function: s32k3xx_txtimeout_work * * Description: * Perform TX timeout related work from the worker thread * * Input Parameters: * arg - The argument passed when work_queue() as called. * * Returned Value: * OK on success * * Assumptions: * ****************************************************************************/ #ifdef TX_TIMEOUT_WQ static void s32k3xx_txtimeout_work(void *arg) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)arg; uint32_t flags; uint32_t mbi; uint32_t mb_bit; struct timespec ts; struct timeval *now = (struct timeval *)&ts; clock_systime_timespec(&ts); now->tv_usec = ts.tv_nsec / 1000; /* timespec to timeval conversion */ /* The watchdog timed out, yet we still check mailboxes in case the * transmit function transmitted a new frame */ flags = getreg32(priv->base + S32K3XX_CAN_IFLAG1_OFFSET); for (mbi = 0; mbi < TXMBCOUNT; mbi++) { if (priv->txmb[mbi].deadline.tv_sec != 0 && (now->tv_sec > priv->txmb[mbi].deadline.tv_sec || now->tv_usec > priv->txmb[mbi].deadline.tv_usec)) { NETDEV_TXTIMEOUTS(&priv->dev); mb_bit = 1 << (RXMBCOUNT + mbi); if (flags & mb_bit) { putreg32(mb_bit, priv->base + S32K3XX_CAN_IFLAG1_OFFSET); } struct mb_s *mb = &priv->tx[mbi]; mb->cs.code = CAN_TXMB_ABORT; priv->txmb[mbi].pending = TX_ABORT; } } } /**************************************************************************** * Function: s32k3xx_txtimeout_expiry * * Description: * Our TX watchdog timed out. Called from the timer interrupt handler. * The last TX never completed. Reset the hardware and start again. * * Input Parameters: * arg - The argument * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by the watchdog logic. * ****************************************************************************/ static void s32k3xx_txtimeout_expiry(wdparm_t arg) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)arg; /* Schedule to perform the TX timeout processing on the worker thread */ work_queue(CANWORK, &priv->irqwork, s32k3xx_txtimeout_work, priv, 0); } #endif static void s32k3xx_setenable(uint32_t base, uint32_t enable) { uint32_t regval; if (enable) { regval = getreg32(base + S32K3XX_CAN_MCR_OFFSET); regval &= ~(CAN_MCR_MDIS); putreg32(regval, base + S32K3XX_CAN_MCR_OFFSET); } else { regval = getreg32(base + S32K3XX_CAN_MCR_OFFSET); regval |= CAN_MCR_MDIS; putreg32(regval, base + S32K3XX_CAN_MCR_OFFSET); } s32k3xx_waitmcr_change(base, CAN_MCR_LPMACK, 1); } static uint32_t s32k3xx_waitesr2_change(uint32_t base, uint32_t mask, uint32_t target_state) { const uint32_t timeout = 1000; uint32_t wait_ack; for (wait_ack = 0; wait_ack < timeout; wait_ack++) { uint32_t state = (getreg32(base + S32K3XX_CAN_ESR2_OFFSET) & mask); if (state == target_state) { return true; } up_udelay(10); } return false; } static void s32k3xx_setfreeze(uint32_t base, uint32_t freeze) { uint32_t regval; if (freeze) { /* Enter freeze mode */ regval = getreg32(base + S32K3XX_CAN_MCR_OFFSET); regval |= (CAN_MCR_HALT | CAN_MCR_FRZ); putreg32(regval, base + S32K3XX_CAN_MCR_OFFSET); } else { /* Exit freeze mode */ regval = getreg32(base + S32K3XX_CAN_MCR_OFFSET); regval &= ~(CAN_MCR_HALT | CAN_MCR_FRZ); putreg32(regval, base + S32K3XX_CAN_MCR_OFFSET); } } static uint32_t s32k3xx_waitmcr_change(uint32_t base, uint32_t mask, uint32_t target_state) { const uint32_t timeout = 1000; uint32_t wait_ack; for (wait_ack = 0; wait_ack < timeout; wait_ack++) { const bool state = (getreg32(base + S32K3XX_CAN_MCR_OFFSET) & mask) != 0; if (state == target_state) { return true; } up_udelay(10); } return false; } static uint32_t s32k3xx_waitfreezeack_change(uint32_t base, uint32_t target_state) { return s32k3xx_waitmcr_change(base, CAN_MCR_FRZACK, target_state); } /**************************************************************************** * Function: s32k3xx_ifup * * Description: * NuttX Callback: Bring up the Ethernet interface when an IP address is * provided * * Input Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static int s32k3xx_ifup(struct net_driver_s *dev) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)dev->d_private; if (!s32k3xx_initialize(priv)) { nerr("initialize failed"); return -1; } /* Exit freeze mode */ s32k3xx_setfreeze(priv->base, 0); if (!s32k3xx_waitfreezeack_change(priv->base, 0)) { ninfo("FLEXCAN: unfreeze fail\n"); return -1; } priv->bifup = true; #ifdef CONFIG_NET_CAN_CANFD priv->txdesc = (struct canfd_frame *)&g_tx_pool; priv->rxdesc = (struct canfd_frame *)&g_rx_pool; #else priv->txdesc = (struct can_frame *)&g_tx_pool; priv->rxdesc = (struct can_frame *)&g_rx_pool; #endif priv->dev.d_buf = (uint8_t *)priv->txdesc; /* Set interrupts */ if (priv->config->bus_irq > 0) { up_enable_irq(priv->config->bus_irq); } if (priv->config->error_irq > 0) { up_enable_irq(priv->config->error_irq); } if (priv->config->lprx_irq > 0) { up_enable_irq(priv->config->lprx_irq); } up_enable_irq(priv->config->mb_irq); /* Indicate can turned on */ if (priv->config->led_pin > 0) { s32k3xx_pinconfig(priv->config->led_pin); s32k3xx_gpiowrite(priv->config->led_pin, priv->config->led_high); } return OK; } /**************************************************************************** * Function: s32k3xx_ifdown * * Description: * NuttX Callback: Stop the interface. * * Input Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static int s32k3xx_ifdown(struct net_driver_s *dev) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)dev->d_private; s32k3xx_reset(priv); priv->bifup = false; /* Indicate can turned off */ if (priv->config->led_pin > 0) { s32k3xx_pinconfig(priv->config->led_pin); s32k3xx_gpiowrite(priv->config->led_pin, !priv->config->led_high); } return OK; } /**************************************************************************** * Function: s32k3xx_txavail_work * * Description: * Perform an out-of-cycle poll on the worker thread. * * Input Parameters: * arg - Reference to the NuttX driver state structure (cast to void*) * * Returned Value: * None * * Assumptions: * Called on the higher priority worker thread. * ****************************************************************************/ static void s32k3xx_txavail_work(void *arg) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)arg; /* Ignore the notification if the interface is not yet up */ net_lock(); if (priv->bifup) { /* Check if there is room in the hardware to hold another outgoing * packet. */ if (s32k3xx_waitesr2_change(priv->base, (CAN_ESR2_IMB | CAN_ESR2_VPS), (CAN_ESR2_IMB | CAN_ESR2_VPS))) { /* No, there is space for another transfer. Poll the network for * new XMIT data. */ if (!s32k3xx_txringfull(priv)) { devif_poll(&priv->dev, s32k3xx_txpoll); } } } net_unlock(); } /**************************************************************************** * Function: s32k3xx_txavail * * Description: * Driver callback invoked when new TX data is available. This is a * stimulus perform an out-of-cycle poll and, thereby, reduce the TX * latency. * * Input Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * Called in normal user mode * ****************************************************************************/ static int s32k3xx_txavail(struct net_driver_s *dev) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)dev->d_private; /* Is our single work structure available? It may not be if there are * pending interrupt actions and we will have to ignore the Tx * availability action. */ if (work_available(&priv->pollwork)) { /* Schedule to serialize the poll on the worker thread. */ s32k3xx_txavail_work(priv); } return OK; } /**************************************************************************** * Function: s32k3xx_ioctl * * Description: * PHY ioctl command handler * * Input Parameters: * dev - Reference to the NuttX driver state structure * cmd - ioctl command * arg - Argument accompanying the command * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * * Assumptions: * ****************************************************************************/ #ifdef CONFIG_NETDEV_CAN_BITRATE_IOCTL static int s32k3xx_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg) { struct s32k3xx_driver_s *priv = (struct s32k3xx_driver_s *)dev->d_private; int ret; switch (cmd) { case SIOCGCANBITRATE: /* Get bitrate from a CAN controller */ { struct can_ioctl_data_s *req = (struct can_ioctl_data_s *)((uintptr_t)arg); req->arbi_bitrate = priv->arbi_timing.bitrate / 1000; /* kbit/s */ req->arbi_samplep = priv->arbi_timing.samplep; #ifdef CONFIG_NET_CAN_CANFD req->data_bitrate = priv->data_timing.bitrate / 1000; /* kbit/s */ req->data_samplep = priv->data_timing.samplep; #else req->data_bitrate = 0; req->data_samplep = 0; #endif ret = OK; } break; case SIOCSCANBITRATE: /* Set bitrate of a CAN controller */ { struct can_ioctl_data_s *req = (struct can_ioctl_data_s *)((uintptr_t)arg); struct flexcan_timeseg arbi_timing; arbi_timing.bitrate = req->arbi_bitrate * 1000; arbi_timing.samplep = req->arbi_samplep; if (s32k3xx_bitratetotimeseg(&arbi_timing, 10, 0, priv->clk_freq)) { ret = OK; } else { ret = -EINVAL; } #ifdef CONFIG_NET_CAN_CANFD struct flexcan_timeseg data_timing; data_timing.bitrate = req->data_bitrate * 1000; data_timing.samplep = req->data_samplep; if (ret == OK && s32k3xx_bitratetotimeseg(&data_timing, 10, 1, priv->clk_freq)) { ret = OK; } else { ret = -EINVAL; } #endif if (ret == OK) { /* Reset CAN controller and start with new timings */ priv->arbi_timing = arbi_timing; #ifdef CONFIG_NET_CAN_CANFD priv->data_timing = data_timing; #endif s32k3xx_ifup(dev); } } break; default: ret = -ENOTTY; break; } return ret; } #endif /* CONFIG_NETDEV_IOCTL */ /**************************************************************************** * Function: s32k3xx_init_eccram * * Description: * Initialize FLEXCAN ECC RAM * * Input Parameters: * priv - Reference to the private FLEXCAN driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static int s32k3xx_init_eccram(struct s32k3xx_driver_s *priv) { uint32_t i; uint32_t regval; irqstate_t flags; flags = enter_critical_section(); regval = getreg32(priv->base + S32K3XX_CAN_CTRL2_OFFSET); /* Set WRMFRZ bit in CTRL2 Register to grant write access to memory */ regval |= CAN_CTRL2_WRMFRZ; putreg32(regval, priv->base + S32K3XX_CAN_CTRL2_OFFSET); for (i = S32K3XX_CAN_MB_OFFSET; i < 0xadf + 1; i += 4) { putreg32(0, priv->base + i); } for (i = 0xc20; i < 0x31ff + 1; i += 4) { putreg32(0, priv->base + i); } leave_critical_section(flags); return 0; } /**************************************************************************** * Function: s32k3xx_initalize * * Description: * Initialize FLEXCAN device * * Input Parameters: * priv - Reference to the private FLEXCAN driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static int s32k3xx_initialize(struct s32k3xx_driver_s *priv) { uint32_t regval; uint32_t i; /* initialize CAN device */ s32k3xx_setenable(priv->base, 1); s32k3xx_init_eccram(priv); s32k3xx_reset(priv); /* Enter freeze mode */ s32k3xx_setfreeze(priv->base, 1); if (!s32k3xx_waitfreezeack_change(priv->base, 1)) { ninfo("FLEXCAN: freeze fail\n"); return -1; } /* Reset CTRL1 register to reset value */ regval = getreg32(priv->base + S32K3XX_CAN_CTRL1_OFFSET); regval &= ~(CAN_CTRL1_LOM | CAN_CTRL1_LBUF | CAN_CTRL1_TSYN | CAN_CTRL1_BOFFREC | CAN_CTRL1_SMP | CAN_CTRL1_RWRNMSK | CAN_CTRL1_TWRNMSK | CAN_CTRL1_LPB | CAN_CTRL1_ERRMSK | CAN_CTRL1_BOFFMSK); putreg32(regval, priv->base + S32K3XX_CAN_CTRL1_OFFSET); #ifndef CONFIG_NET_CAN_CANFD regval = getreg32(priv->base + S32K3XX_CAN_CTRL1_OFFSET); regval &= ~(CAN_CTRL1_TIMINGMSK); /* Reset timings */ regval |= CAN_CTRL1_PRESDIV(priv->arbi_timing.presdiv) | /* Prescaler divisor factor */ CAN_CTRL1_PROPSEG(priv->arbi_timing.propseg) | /* Propagation segment */ CAN_CTRL1_PSEG1(priv->arbi_timing.pseg1) | /* Phase buffer segment 1 */ CAN_CTRL1_PSEG2(priv->arbi_timing.pseg2) | /* Phase buffer segment 2 */ CAN_CTRL1_RJW(1); /* Resynchronization jump width */ putreg32(regval, priv->base + S32K3XX_CAN_CTRL1_OFFSET); #else regval = CAN_CBT_BTF | /* Enable extended bit timing * configurations for CAN-FD for setting up * separately nominal and data phase */ CAN_CBT_EPRESDIV(priv->arbi_timing.presdiv) | /* Prescaler divisor factor */ CAN_CBT_EPROPSEG(priv->arbi_timing.propseg) | /* Propagation segment */ CAN_CBT_EPSEG1(priv->arbi_timing.pseg1) | /* Phase buffer segment 1 */ CAN_CBT_EPSEG2(priv->arbi_timing.pseg2) | /* Phase buffer segment 2 */ CAN_CBT_ERJW(1); /* Resynchronization jump width */ putreg32(regval, priv->base + S32K3XX_CAN_CBT_OFFSET); /* Enable CAN FD feature */ regval = getreg32(priv->base + S32K3XX_CAN_MCR_OFFSET); regval |= CAN_MCR_FDEN; putreg32(regval, priv->base + S32K3XX_CAN_MCR_OFFSET); regval = CAN_FDCBT_FPRESDIV(priv->data_timing.presdiv) | /* Prescaler divisor factor of 1 */ CAN_FDCBT_FPROPSEG(priv->data_timing.propseg) | /* Propagation * segment (only register that doesn't add 1) */ CAN_FDCBT_FPSEG1(priv->data_timing.pseg1) | /* Phase buffer segment 1 */ CAN_FDCBT_FPSEG2(priv->data_timing.pseg2) | /* Phase buffer segment 2 */ CAN_FDCBT_FRJW(priv->data_timing.pseg2); /* Resynchorinzation jump width same as PSEG2 */ putreg32(regval, priv->base + S32K3XX_CAN_FDCBT_OFFSET); /* Additional CAN-FD configurations */ regval = CAN_FDCTRL_FDRATE | /* Enable bit rate switch in data phase of frame */ CAN_FDCTRL_TDCEN | /* Enable transceiver delay compensation */ CAN_FDCTRL_TDCOFF(5) | /* Setup 5 cycles for data phase sampling delay */ CAN_FDCTRL_MBDSR0(3); /* Setup 64 bytes per message buffer (7 MB's) */ putreg32(regval, priv->base + S32K3XX_CAN_FDCTRL_OFFSET); regval = getreg32(priv->base + S32K3XX_CAN_CTRL2_OFFSET); regval |= CAN_CTRL2_ISOCANFDEN; putreg32(regval, priv->base + S32K3XX_CAN_CTRL2_OFFSET); #endif for (i = TXMBCOUNT; i < TOTALMBCOUNT; i++) { priv->rx[i].id.w = 0x0; /* FIXME sometimes we get a hard fault here */ } putreg32(0x0, priv->base + S32K3XX_CAN_RXFGMASK_OFFSET); for (i = 0; i < priv->config->no_buffers; i++) { putreg32(0, priv->base + S32K3XX_CAN_RXIMR_OFFSET(i)); } for (i = 0; i < RXMBCOUNT; i++) { ninfo("Set MB%" PRIu32 " to receive %p\n", i, &priv->rx[i]); priv->rx[i].id.w = 0x0; priv->rx[i].cs.edl = 0x1; priv->rx[i].cs.brs = 0x1; priv->rx[i].cs.esi = 0x0; priv->rx[i].cs.code = 0x4; priv->rx[i].cs.srr = 0x0; priv->rx[i].cs.ide = 0x1; priv->rx[i].cs.rtr = 0x0; } putreg32(IFLAG1_RX, priv->base + S32K3XX_CAN_IFLAG1_OFFSET); putreg32(IFLAG1_RX, priv->base + S32K3XX_CAN_IMASK1_OFFSET); /* Exit freeze mode */ s32k3xx_setfreeze(priv->base, 0); if (!s32k3xx_waitfreezeack_change(priv->base, 0)) { ninfo("FLEXCAN: unfreeze fail\n"); return -1; } return 1; } /**************************************************************************** * Function: s32k3xx_reset * * Description: * Put the EMAC in the non-operational, reset state * * Input Parameters: * priv - Reference to the private FLEXCAN driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static void s32k3xx_reset(struct s32k3xx_driver_s *priv) { uint32_t regval; uint32_t i; regval = getreg32(priv->base + S32K3XX_CAN_MCR_OFFSET); regval |= CAN_MCR_SOFTRST; putreg32(regval, priv->base + S32K3XX_CAN_MCR_OFFSET); if (!s32k3xx_waitmcr_change(priv->base, CAN_MCR_SOFTRST, 0)) { nerr("Reset failed"); return; } regval = getreg32(priv->base + S32K3XX_CAN_MCR_OFFSET); regval &= ~(CAN_MCR_SUPV); putreg32(regval, priv->base + S32K3XX_CAN_MCR_OFFSET); /* Initialize all MB rx and tx */ for (i = 0; i < TOTALMBCOUNT; i++) { ninfo("MB %" PRIu32 " %p\n", i, &priv->rx[i]); ninfo("MB %" PRIu32 " %p\n", i, &priv->rx[i].id.w); priv->rx[i].cs.cs = 0x0; priv->rx[i].id.w = 0x0; priv->rx[i].data[0].w00 = 0x0; priv->rx[i].data[1].w00 = 0x0; } regval = getreg32(priv->base + S32K3XX_CAN_MCR_OFFSET); regval |= CAN_MCR_WRNEN | CAN_MCR_SRXDIS | CAN_MCR_IRMQ | CAN_MCR_AEN | (((TOTALMBCOUNT - 1) << CAN_MCR_MAXMB_SHIFT) & CAN_MCR_MAXMB_MASK); putreg32(regval, priv->base + S32K3XX_CAN_MCR_OFFSET); regval = CAN_CTRL2_RRS | CAN_CTRL2_EACEN; putreg32(regval, priv->base + S32K3XX_CAN_CTRL2_OFFSET); for (i = 0; i < TOTALMBCOUNT; i++) { putreg32(0, priv->base + S32K3XX_CAN_RXIMR_OFFSET(i)); } /* Filtering catchall */ putreg32(0x3fffffff, priv->base + S32K3XX_CAN_RX14MASK_OFFSET); putreg32(0x3fffffff, priv->base + S32K3XX_CAN_RX15MASK_OFFSET); putreg32(0x3fffffff, priv->base + S32K3XX_CAN_RXMGMASK_OFFSET); putreg32(0x0, priv->base + S32K3XX_CAN_RXFGMASK_OFFSET); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Function: s32k3xx_caninitialize * * Description: * Initialize the CAN controller and driver * * Input Parameters: * intf - In the case where there are multiple CAN devices, this value * identifies which CAN device is to be initialized. * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ int s32k3xx_caninitialize(int intf) { struct s32k3xx_driver_s *priv; int ret; switch (intf) { #ifdef CONFIG_S32K3XX_FLEXCAN0 case 0: priv = &g_flexcan0; memset(priv, 0, sizeof(struct s32k3xx_driver_s)); priv->base = S32K3XX_FLEXCAN0_BASE; priv->config = &s32k3xx_flexcan0_config; priv->clk_freq = s32k3xx_get_freq(FLEXCAN0_CLK); /* Default bitrate configuration */ # ifdef CONFIG_NET_CAN_CANFD priv->arbi_timing.bitrate = CONFIG_FLEXCAN0_ARBI_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN0_ARBI_SAMPLEP; priv->data_timing.bitrate = CONFIG_FLEXCAN0_DATA_BITRATE; priv->data_timing.samplep = CONFIG_FLEXCAN0_DATA_SAMPLEP; # else priv->arbi_timing.bitrate = CONFIG_FLEXCAN0_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN0_SAMPLEP; # endif break; #endif #ifdef CONFIG_S32K3XX_FLEXCAN1 case 1: priv = &g_flexcan1; memset(priv, 0, sizeof(struct s32k3xx_driver_s)); priv->base = S32K3XX_FLEXCAN1_BASE; priv->config = &s32k3xx_flexcan1_config; priv->clk_freq = s32k3xx_get_freq(FLEXCAN1_CLK); /* Default bitrate configuration */ # ifdef CONFIG_NET_CAN_CANFD priv->arbi_timing.bitrate = CONFIG_FLEXCAN1_ARBI_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN1_ARBI_SAMPLEP; priv->data_timing.bitrate = CONFIG_FLEXCAN1_DATA_BITRATE; priv->data_timing.samplep = CONFIG_FLEXCAN1_DATA_SAMPLEP; # else priv->arbi_timing.bitrate = CONFIG_FLEXCAN1_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN1_SAMPLEP; # endif break; #endif #ifdef CONFIG_S32K3XX_FLEXCAN2 case 2: priv = &g_flexcan2; memset(priv, 0, sizeof(struct s32k3xx_driver_s)); priv->base = S32K3XX_FLEXCAN2_BASE; priv->config = &s32k3xx_flexcan2_config; priv->clk_freq = s32k3xx_get_freq(FLEXCAN2_CLK); /* Default bitrate configuration */ # ifdef CONFIG_NET_CAN_CANFD priv->arbi_timing.bitrate = CONFIG_FLEXCAN2_ARBI_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN2_ARBI_SAMPLEP; priv->data_timing.bitrate = CONFIG_FLEXCAN2_DATA_BITRATE; priv->data_timing.samplep = CONFIG_FLEXCAN2_DATA_SAMPLEP; # else priv->arbi_timing.bitrate = CONFIG_FLEXCAN2_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN2_SAMPLEP; # endif break; #endif #ifdef CONFIG_S32K3XX_FLEXCAN3 case 3: priv = &g_flexcan3; memset(priv, 0, sizeof(struct s32k3xx_driver_s)); priv->base = S32K3XX_FLEXCAN3_BASE; priv->config = &s32k3xx_flexcan3_config; priv->clk_freq = s32k3xx_get_freq(FLEXCAN3_CLK); /* Default bitrate configuration */ # ifdef CONFIG_NET_CAN_CANFD priv->arbi_timing.bitrate = CONFIG_FLEXCAN3_ARBI_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN3_ARBI_SAMPLEP; priv->data_timing.bitrate = CONFIG_FLEXCAN3_DATA_BITRATE; priv->data_timing.samplep = CONFIG_FLEXCAN3_DATA_SAMPLEP; # else priv->arbi_timing.bitrate = CONFIG_FLEXCAN3_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN3_SAMPLEP; # endif break; #endif #ifdef CONFIG_S32K3XX_FLEXCAN4 case 4: priv = &g_flexcan4; memset(priv, 0, sizeof(struct s32k3xx_driver_s)); priv->base = S32K3XX_FLEXCAN4_BASE; priv->config = &s32k3xx_flexcan4_config; priv->clk_freq = s32k3xx_get_freq(FLEXCAN4_CLK); /* Default bitrate configuration */ # ifdef CONFIG_NET_CAN_CANFD priv->arbi_timing.bitrate = CONFIG_FLEXCAN4_ARBI_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN4_ARBI_SAMPLEP; priv->data_timing.bitrate = CONFIG_FLEXCAN4_DATA_BITRATE; priv->data_timing.samplep = CONFIG_FLEXCAN4_DATA_SAMPLEP; # else priv->arbi_timing.bitrate = CONFIG_FLEXCAN4_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN4_SAMPLEP; # endif break; #endif #ifdef CONFIG_S32K3XX_FLEXCAN5 case 5: priv = &g_flexcan5; memset(priv, 0, sizeof(struct s32k3xx_driver_s)); priv->base = S32K3XX_FLEXCAN5_BASE; priv->config = &s32k3xx_flexcan5_config; priv->clk_freq = s32k3xx_get_freq(FLEXCAN5_CLK); /* Default bitrate configuration */ # ifdef CONFIG_NET_CAN_CANFD priv->arbi_timing.bitrate = CONFIG_FLEXCAN5_ARBI_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN5_ARBI_SAMPLEP; priv->data_timing.bitrate = CONFIG_FLEXCAN5_DATA_BITRATE; priv->data_timing.samplep = CONFIG_FLEXCAN5_DATA_SAMPLEP; # else priv->arbi_timing.bitrate = CONFIG_FLEXCAN5_BITRATE; priv->arbi_timing.samplep = CONFIG_FLEXCAN5_SAMPLEP; # endif break; #endif default: return -ENODEV; } if (!s32k3xx_bitratetotimeseg(&priv->arbi_timing, 1, 0, priv->clk_freq)) { nerr("ERROR: Invalid CAN timings please try another sample point " "or refer to the reference manual\n"); return -1; } #ifdef CONFIG_NET_CAN_CANFD if (!s32k3xx_bitratetotimeseg(&priv->data_timing, 1, 1, priv->clk_freq)) { nerr("ERROR: Invalid CAN data phase timings please try another " "sample point or refer to the reference manual\n"); return -1; } #endif s32k3xx_pinconfig(priv->config->tx_pin); s32k3xx_pinconfig(priv->config->rx_pin); if (priv->config->enable_pin > 0) { s32k3xx_pinconfig(priv->config->enable_pin); s32k3xx_gpiowrite(priv->config->enable_pin, priv->config->enable_high); } if (priv->config->stb_pin > 0) { s32k3xx_pinconfig(priv->config->stb_pin); s32k3xx_gpiowrite(priv->config->stb_pin, priv->config->stb_high); } /* Attach the flexcan interrupt handler */ if (priv->config->bus_irq > 0) { if (irq_attach(priv->config->bus_irq, s32k3xx_flexcan_interrupt, priv)) { /* We could not attach the ISR to the interrupt */ nerr("ERROR: Failed to attach CAN bus IRQ\n"); return -EAGAIN; } } if (priv->config->error_irq > 0) { if (irq_attach(priv->config->error_irq, s32k3xx_flexcan_interrupt, priv)) { /* We could not attach the ISR to the interrupt */ nerr("ERROR: Failed to attach CAN error IRQ\n"); return -EAGAIN; } } if (priv->config->lprx_irq > 0) { if (irq_attach(priv->config->lprx_irq, s32k3xx_flexcan_interrupt, priv)) { /* We could not attach the ISR to the interrupt */ nerr("ERROR: Failed to attach CAN LPRX IRQ\n"); return -EAGAIN; } } if (irq_attach(priv->config->mb_irq, s32k3xx_flexcan_interrupt, priv)) { /* We could not attach the ISR to the interrupt */ nerr("ERROR: Failed to attach CAN OR'ed Message buffer (0-15) IRQ\n"); return -EAGAIN; } /* Initialize the driver structure */ priv->dev.d_ifup = s32k3xx_ifup; /* I/F up (new IP address) callback */ priv->dev.d_ifdown = s32k3xx_ifdown; /* I/F down callback */ priv->dev.d_txavail = s32k3xx_txavail; /* New TX data callback */ #ifdef CONFIG_NETDEV_IOCTL priv->dev.d_ioctl = s32k3xx_ioctl; /* Support CAN ioctl() calls */ #endif priv->dev.d_private = priv; /* Used to recover private state from dev */ priv->rx = (struct mb_s *)(priv->base + S32K3XX_CAN_MB_OFFSET); priv->tx = (struct mb_s *)(priv->base + S32K3XX_CAN_MB_OFFSET + (sizeof(struct mb_s) * RXMBCOUNT)); /* Put the interface in the down state. This usually amounts to resetting * the device and/or calling s32k3xx_ifdown(). */ ninfo("callbacks done\n"); s32k3xx_initialize(priv); s32k3xx_ifdown(&priv->dev); /* Register the device with the OS so that socket IOCTLs can be performed */ netdev_register(&priv->dev, NET_LL_CAN); UNUSED(ret); return OK; } /**************************************************************************** * Name: arm_netinitialize * * Description: * Initialize the enabled CAN device interfaces. If there are more * different network devices in the chip, then board-specific logic will * have to provide this function to determine which, if any, network * devices should be initialized. * ****************************************************************************/ #if !defined(CONFIG_NETDEV_LATEINIT) void arm_netinitialize(void) { #ifdef CONFIG_S32K3XX_FLEXCAN0 s32k3xx_caninitialize(0); #endif #ifdef CONFIG_S32K3XX_FLEXCAN1 s32k3xx_caninitialize(1); #endif #ifdef CONFIG_S32K3XX_FLEXCAN2 s32k3xx_caninitialize(2); #endif #ifdef CONFIG_S32K3XX_FLEXCAN3 s32k3xx_caninitialize(3); #endif #ifdef CONFIG_S32K3XX_FLEXCAN4 s32k3xx_caninitialize(4); #endif #ifdef CONFIG_S32K3XX_FLEXCAN5 s32k3xx_caninitialize(5); #endif } #endif #endif /* CONFIG_S32K3XX_FLEXCAN */