dd1096695d
Co-authored-by: Peter van der Perk <peter.vanderperk@nxp.com>
2322 lines
62 KiB
C
2322 lines
62 KiB
C
/****************************************************************************
|
|
* 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 <nuttx/config.h>
|
|
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/can.h>
|
|
#include <nuttx/wdog.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/net/netdev.h>
|
|
#include <nuttx/net/can.h>
|
|
|
|
#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 <arch/board/board.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#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 */
|