033f203e2a
add Kvaser PCI card driver support, works only with QEMU now: https://www.qemu.org/docs/master/system/devices/can.html#examples-how-to-use-can-emulation-for-sja1000-based-boards Signed-off-by: p-szafonimateusz <p-szafonimateusz@xiaomi.com>
1814 lines
48 KiB
C
1814 lines
48 KiB
C
/*****************************************************************************
|
|
* drivers/can/kvaser_pci.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Included Files
|
|
*****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/pci/pci.h>
|
|
|
|
#include <nuttx/can/can.h>
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
# include <nuttx/wqueue.h>
|
|
# include <nuttx/net/netdev.h>
|
|
# include <nuttx/net/can.h>
|
|
#endif
|
|
|
|
#include "sja1000.h"
|
|
|
|
/*****************************************************************************
|
|
* Pre-processor Definitions
|
|
*****************************************************************************/
|
|
|
|
/* Kvaser card constants */
|
|
|
|
#define KVASER_SJA_MAX 4
|
|
#define KVASER_XTAL_FREQ 16000000
|
|
|
|
/* PCI BARs */
|
|
|
|
#define KVASER_S5920_BAR 0
|
|
#define KVASER_SJA_BAR 1
|
|
#define KVASER_XILINX_BAR 2
|
|
|
|
/* Kvaser registers */
|
|
|
|
#define KVASER_SJA_REGS 0x20
|
|
|
|
#define KVASER_S5920_INTCSR 0x38
|
|
#define KVASER_S5920_INTCSR_INT_ASSERT (1 << 23) /* Interrupt asserted bit */
|
|
#define KVASER_S5920_INTCSR_INT_ADDON (1 << 13) /* Add-on interrupt pin enable */
|
|
|
|
/* Interrupts */
|
|
|
|
#define KVASER_INT_ERR (SJA1000_OVERRUN_INT_ENA | \
|
|
SJA1000_ERR_PASSIVE_INT_ENA | \
|
|
SJA1000_ARB_LOST_INT_ENA | \
|
|
SJA1000_BUS_ERR_INT_ENA)
|
|
|
|
/* SocketCAN specific */
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
|
|
# define KVASER_POOL_SIZE 1
|
|
|
|
/* Work queue support is required. */
|
|
|
|
# if !defined(CONFIG_SCHED_WORKQUEUE)
|
|
# error Work queue support is required
|
|
# endif
|
|
|
|
# define KVASER_CANWORK LPWORK
|
|
|
|
#endif /* CONFIG_CAN_KVASER_SOCKET */
|
|
|
|
/*****************************************************************************
|
|
* Private Types
|
|
*****************************************************************************/
|
|
|
|
/* SJA1000 channel private data */
|
|
|
|
struct kvaser_sja_s
|
|
{
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
struct can_dev_s dev;
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
struct net_driver_s dev; /* Interface understood by the network */
|
|
struct work_s pollwork; /* For deferring poll work to the work wq */
|
|
|
|
/* TX/RX pool */
|
|
|
|
struct can_frame tx_pool[KVASER_POOL_SIZE];
|
|
struct can_frame rx_pool[KVASER_POOL_SIZE];
|
|
#endif
|
|
|
|
/* PCI data */
|
|
|
|
FAR struct pci_device_s *pcidev;
|
|
uint64_t base;
|
|
};
|
|
|
|
/* KVASER private data */
|
|
|
|
struct kvaser_driver_s
|
|
{
|
|
FAR struct kvaser_sja_s *sja;
|
|
uint8_t count;
|
|
|
|
/* PCI data */
|
|
|
|
FAR struct pci_device_s *pcidev;
|
|
int irq;
|
|
uintptr_t sja_base;
|
|
uintptr_t s5920_base;
|
|
uintptr_t xilinx_base;
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
/* For deferring interrupt work to the wq */
|
|
|
|
struct work_s irqwork;
|
|
#endif
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Private Functions Definitions
|
|
*****************************************************************************/
|
|
|
|
/* Helpers */
|
|
|
|
static uint8_t kvaser_getreg_sja(FAR struct kvaser_sja_s *priv,
|
|
unsigned int offset);
|
|
static void kvaser_putreg_sja(FAR struct kvaser_sja_s *priv,
|
|
unsigned int offset,
|
|
uint8_t value);
|
|
|
|
static uint32_t kvaser_getreg_s5920(FAR struct kvaser_driver_s *priv,
|
|
unsigned int offset);
|
|
static void kvaser_putreg_s5920(FAR struct kvaser_driver_s *priv,
|
|
unsigned int offset,
|
|
uint32_t value);
|
|
|
|
/* Common methods */
|
|
|
|
static void kvaser_reset(FAR struct kvaser_sja_s *priv);
|
|
static void kvaser_sleep(FAR struct kvaser_sja_s *priv);
|
|
static void kvaser_setup(FAR struct kvaser_sja_s *priv);
|
|
static void kvaser_rxint(FAR struct kvaser_sja_s *priv, bool enable);
|
|
static void kvaser_txint(FAR struct kvaser_sja_s *priv, bool enable);
|
|
static bool kvaser_txready(FAR struct kvaser_sja_s *priv);
|
|
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
/* CAN character device methods */
|
|
|
|
static void kvaser_chrdev_reset(FAR struct can_dev_s *dev);
|
|
static int kvaser_chrdev_setup(FAR struct can_dev_s *dev);
|
|
static void kvaser_chrdev_shutdown(FAR struct can_dev_s *dev);
|
|
static void kvaser_chrdev_rxint(FAR struct can_dev_s *dev, bool enable);
|
|
static void kvaser_chrdev_txint(FAR struct can_dev_s *dev, bool enable);
|
|
static int kvaser_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd,
|
|
unsigned long arg);
|
|
static int kvaser_chrdev_remoterequest(FAR struct can_dev_s *dev,
|
|
uint16_t id);
|
|
static int kvaser_chrdev_send(FAR struct can_dev_s *dev,
|
|
FAR struct can_msg_s *msg);
|
|
static bool kvaser_chrdev_txready(FAR struct can_dev_s *dev);
|
|
static bool kvaser_chrdev_txempty(FAR struct can_dev_s *dev);
|
|
static void kvaser_chardev_receive(FAR struct kvaser_sja_s *priv);
|
|
static void kvaser_chardev_interrupt(FAR struct kvaser_driver_s *priv);
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
/* SocketCAN methods */
|
|
|
|
static int kvaser_sock_ifup(FAR struct net_driver_s *dev);
|
|
static int kvaser_sock_ifdown(FAR struct net_driver_s *dev);
|
|
|
|
static void kvaser_sock_txavail_work(FAR void *arg);
|
|
static int kvaser_sock_txavail(FAR struct net_driver_s *dev);
|
|
|
|
# ifdef CONFIG_NETDEV_IOCTL
|
|
static int kvaser_sock_ioctl(FAR struct net_driver_s *dev, int cmd,
|
|
unsigned long arg);
|
|
# endif
|
|
|
|
static int kvaser_sock_txpoll(FAR struct net_driver_s *dev);
|
|
static int kvaser_sock_send(FAR struct kvaser_sja_s *priv);
|
|
static void kvaser_sock_receive(FAR struct kvaser_sja_s *priv);
|
|
static void kvaser_sock_interrupt_work(FAR void *arg);
|
|
#endif
|
|
|
|
/* Interrupts */
|
|
|
|
static int kvaser_interrupt(int irq, FAR void *context, FAR void *arg);
|
|
|
|
/* PCI */
|
|
|
|
static void kvaser_init(FAR struct kvaser_driver_s *priv);
|
|
static uint8_t kvaser_count_sja(FAR struct kvaser_driver_s *priv);
|
|
static int kvaser_probe(FAR struct pci_device_s *dev);
|
|
|
|
/*****************************************************************************
|
|
* Private Data
|
|
*****************************************************************************/
|
|
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
static const struct can_ops_s g_kvaser_can_ops =
|
|
{
|
|
.co_reset = kvaser_chrdev_reset,
|
|
.co_setup = kvaser_chrdev_setup,
|
|
.co_shutdown = kvaser_chrdev_shutdown,
|
|
.co_rxint = kvaser_chrdev_rxint,
|
|
.co_txint = kvaser_chrdev_txint,
|
|
.co_ioctl = kvaser_chrdev_ioctl,
|
|
.co_remoterequest = kvaser_chrdev_remoterequest,
|
|
.co_send = kvaser_chrdev_send,
|
|
.co_txready = kvaser_chrdev_txready,
|
|
.co_txempty = kvaser_chrdev_txempty,
|
|
};
|
|
#endif
|
|
|
|
static const struct pci_device_id_s g_kvaser_id_table[] =
|
|
{
|
|
{
|
|
PCI_DEVICE(0x10e8, 0x8406),
|
|
.driver_data = 0
|
|
},
|
|
{ }
|
|
};
|
|
|
|
static struct pci_driver_s g_kvaser_drv =
|
|
{
|
|
.id_table = g_kvaser_id_table,
|
|
.probe = kvaser_probe,
|
|
};
|
|
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
static uint8_t g_kvaser_count = 0;
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* Private Functions
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_getreg_sja
|
|
*****************************************************************************/
|
|
|
|
static uint8_t kvaser_getreg_sja(FAR struct kvaser_sja_s *priv,
|
|
unsigned int offset)
|
|
{
|
|
uintptr_t addr = priv->base + offset;
|
|
uint8_t ret = 0;
|
|
|
|
pci_read_io_byte(priv->pcidev, addr, &ret);
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_putreg_sja
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_putreg_sja(FAR struct kvaser_sja_s *priv,
|
|
unsigned int offset,
|
|
uint8_t value)
|
|
{
|
|
uintptr_t addr = priv->base + offset;
|
|
pci_write_io_byte(priv->pcidev, addr, value);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_getreg_s5920
|
|
*****************************************************************************/
|
|
|
|
static uint32_t kvaser_getreg_s5920(FAR struct kvaser_driver_s *priv,
|
|
unsigned int offset)
|
|
{
|
|
uintptr_t addr = priv->s5920_base + offset;
|
|
uint32_t ret = 0;
|
|
|
|
pci_read_io_dword(priv->pcidev, addr, &ret);
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_putreg_s5920
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_putreg_s5920(FAR struct kvaser_driver_s *priv,
|
|
unsigned int offset,
|
|
uint32_t value)
|
|
{
|
|
uintptr_t addr = priv->s5920_base + offset;
|
|
pci_write_io_dword(priv->pcidev, addr, value);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_reset
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_reset(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
uint8_t regval = 0;
|
|
|
|
/* Reset mode */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_MODE_REG, SJA1000_RESET_MODE);
|
|
|
|
/* Enter PeliCAN mode */
|
|
|
|
regval = kvaser_getreg_sja(priv, SJA1000_CLOCK_DIVIDER_REG);
|
|
regval |= SJA1000_EXT_MODE;
|
|
kvaser_putreg_sja(priv, SJA1000_CLOCK_DIVIDER_REG, regval);
|
|
|
|
/* Clear counters */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_RX_ERR_CNT_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_TX_ERR_CNT_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_RX_MESSAGE_CNT_REG, 0);
|
|
|
|
/* Accept all */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_3_REG, 0);
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_4_REG, 0xff);
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_5_REG, 0xff);
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_6_REG, 0xff);
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_7_REG, 0xff);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sleep
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_sleep(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
/* Sleep mode */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_MODE_REG, SJA1000_SLEEP_MODE);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_setup
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_setup(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
/* REVISIT: missing bus timings configuration and output control.
|
|
*
|
|
* This driver was verified on QEMU with virtual host CAN netwrok,
|
|
* which doesn't need bus timings and output control registers set.
|
|
* For real hardware, these registers must be properly configured !
|
|
*/
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_BUS_TIMING_0_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_BUS_TIMING_1_REG, 0);
|
|
kvaser_putreg_sja(priv, SJA1000_OUTCTRL_REG, 0);
|
|
|
|
/* Enable interrupts */
|
|
|
|
#if defined(CONFIG_CAN_ERRORS) || defined(CONFIG_NET_CAN_ERRORS)
|
|
kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, KVASER_INT_ERR);
|
|
#else
|
|
kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, 0);
|
|
#endif
|
|
|
|
/* Exit from reset mode and set `Dual filter mode`.
|
|
*
|
|
* NOTE: `Single filter mode` is broken in older versions of QEMU:
|
|
* https://gitlab.com/qemu-project/qemu/-/issues/2028
|
|
*/
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_MODE_REG, 0);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_rxint
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_rxint(FAR struct kvaser_sja_s *priv, bool enable)
|
|
{
|
|
uint8_t regval = 0;
|
|
|
|
regval = kvaser_getreg_sja(priv, SJA1000_INT_ENA_REG);
|
|
if (enable)
|
|
{
|
|
regval |= SJA1000_RX_INT_ST;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~SJA1000_RX_INT_ST;
|
|
}
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, regval);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_txint
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_txint(FAR struct kvaser_sja_s *priv, bool enable)
|
|
{
|
|
uint8_t regval = 0;
|
|
|
|
regval = kvaser_getreg_sja(priv, SJA1000_INT_ENA_REG);
|
|
if (enable)
|
|
{
|
|
regval |= SJA1000_TX_INT_ST;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~SJA1000_TX_INT_ST;
|
|
}
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, regval);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_txready
|
|
*****************************************************************************/
|
|
|
|
static bool kvaser_txready(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
return kvaser_getreg_sja(priv, SJA1000_STATUS_REG) & SJA1000_TX_BUF_ST;
|
|
}
|
|
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_reset
|
|
*
|
|
* Description:
|
|
* Reset the CAN device. Called early to initialize the hardware. This
|
|
* function is called, before kvaser_setup() and on error conditions.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chrdev_reset(FAR struct can_dev_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
kvaser_reset(priv);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_setup
|
|
*
|
|
* Description:
|
|
* Configure the CAN. This method is called the first time that the CAN
|
|
* device is opened. This will occur when the port is first opened.
|
|
* This setup includes configuring and attaching CAN interrupts.
|
|
* All CAN interrupts are disabled upon return.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_chrdev_setup(FAR struct can_dev_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
kvaser_setup(priv);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_shutdown
|
|
*
|
|
* Description:
|
|
* Disable the CAN. This method is called when the CAN device is closed.
|
|
* This method reverses the operation the setup method.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chrdev_shutdown(FAR struct can_dev_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
kvaser_sleep(priv);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_rxint
|
|
*
|
|
* Description:
|
|
* Call to enable or disable RX interrupts.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chrdev_rxint(FAR struct can_dev_s *dev, bool enable)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
kvaser_rxint(priv, enable);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_txint
|
|
*
|
|
* Description:
|
|
* Call to enable or disable TX interrupts.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chrdev_txint(FAR struct can_dev_s *dev, bool enable)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
kvaser_txint(priv, enable);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_ioctl
|
|
*
|
|
* Description:
|
|
* All ioctl calls will be routed through this method
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_remoterequest
|
|
*
|
|
* Description:
|
|
* Send a remote request
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_chrdev_remoterequest(FAR struct can_dev_s *dev,
|
|
uint16_t id)
|
|
{
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_send
|
|
*
|
|
* Description:
|
|
* Send one can message.
|
|
*
|
|
* One CAN-message consists of a maximum of 10 bytes. A message is
|
|
* composed of at least the first 2 bytes (when there are no data bytes).
|
|
*
|
|
* Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier
|
|
* Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier
|
|
* Bit 4: Remote Transmission Request (RTR)
|
|
* Bits 0-3: Data Length Code (DLC)
|
|
* Bytes 2-10: CAN data
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_chrdev_send(FAR struct can_dev_s *dev,
|
|
FAR struct can_msg_s *msg)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
uint8_t regval = 0;
|
|
uint8_t dreg = 0;
|
|
uint8_t fi = 0;
|
|
uint8_t bytes = 0;
|
|
int i = 0;
|
|
|
|
/* Check TX buffer status */
|
|
|
|
if (!kvaser_txready(priv))
|
|
{
|
|
canerr("ERROR: TX buffer not available\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Get data bytes for this frame */
|
|
|
|
bytes = can_dlc2bytes(msg->cm_hdr.ch_dlc);
|
|
|
|
#ifdef CONFIG_CAN_FD
|
|
/* Drop CAN FD frames */
|
|
|
|
if (bytes > 8 || msg->cm_hdr.ch_edl ||
|
|
msg->cm_hdr.ch_brs || msg->cm_hdr.ch_esi)
|
|
{
|
|
canerr("ERROR: CAN FD frames not supported\n");
|
|
return -ENOTSUP;
|
|
}
|
|
#endif
|
|
|
|
/* Set up the DLC */
|
|
|
|
fi = msg->cm_hdr.ch_dlc;
|
|
|
|
#ifdef CONFIG_CAN_USE_RTR
|
|
fi |= (msg->cm_hdr.ch_rtr ? SJA1000_FI_RTR : 0);
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_EXTID
|
|
if (msg->cm_hdr.ch_extid)
|
|
{
|
|
fi |= SJA1000_FI_FF;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi);
|
|
|
|
/* Write ID */
|
|
|
|
regval = (msg->cm_hdr.ch_id & 0x1fe00000) >> 21;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval);
|
|
regval = (msg->cm_hdr.ch_id & 0x001fe000) >> 13;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval);
|
|
regval = (msg->cm_hdr.ch_id & 0x00001fe0) >> 5;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_3_REG, regval);
|
|
regval = (msg->cm_hdr.ch_id & 0x0000001f) << 3;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_4_REG, regval);
|
|
|
|
dreg = SJA1000_DATA_5_REG;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi);
|
|
|
|
/* Write ID */
|
|
|
|
regval = (msg->cm_hdr.ch_id & 0x07f8) >> 3;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval);
|
|
regval = (msg->cm_hdr.ch_id & 0x0007) << 5;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval);
|
|
|
|
dreg = SJA1000_DATA_3_REG;
|
|
}
|
|
|
|
/* Set up the data fields */
|
|
|
|
for (i = 0; i < bytes; i++)
|
|
{
|
|
kvaser_putreg_sja(priv, dreg + i, msg->cm_data[i]);
|
|
}
|
|
|
|
/* Transmit */
|
|
|
|
regval = SJA1000_TX_REQ;
|
|
#ifdef CONFIG_CAN_LOOPBACK
|
|
regval |= SJA1000_SELF_RX_REQ;
|
|
#endif
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, regval);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_txready
|
|
*
|
|
* Description:
|
|
* Return true if the CAN hardware can accept another TX message.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* True if the CAN hardware is ready to accept another TX message.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static bool kvaser_chrdev_txready(FAR struct can_dev_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
return kvaser_txready(priv);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chrdev_txempty
|
|
*
|
|
* Description:
|
|
* Return true if all message have been sent. If for example, the CAN
|
|
* hardware implements FIFOs, then this would mean the transmit FIFO is
|
|
* empty. This method is called when the driver needs to make sure that
|
|
* all characters are "drained" from the TX hardware before calling
|
|
* co_shutdown().
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* True if there are no pending TX transfers in the CAN hardware.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static bool kvaser_chrdev_txempty(FAR struct can_dev_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev;
|
|
|
|
return kvaser_txready(priv);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chardev_receive
|
|
*
|
|
* Description:
|
|
* Receive CAN frame
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chardev_receive(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)priv;
|
|
struct can_hdr_s hdr;
|
|
uint8_t data[CAN_MAXDATALEN];
|
|
uint8_t dreg;
|
|
uint8_t regval;
|
|
uint8_t i;
|
|
int ret;
|
|
|
|
/* Wait until data in RXFIFO */
|
|
|
|
while (kvaser_getreg_sja(priv, SJA1000_STATUS_REG) & SJA1000_RX_BUF_ST)
|
|
{
|
|
regval = kvaser_getreg_sja(priv, SJA1000_DATA_0_REG);
|
|
|
|
/* Get the DLC */
|
|
|
|
hdr.ch_dlc = (regval & SJA1000_FI_DLC_MASK);
|
|
|
|
/* Get RTR bit */
|
|
|
|
hdr.ch_rtr = (regval & SJA1000_FI_RTR ? 1 : 0);
|
|
|
|
if (!(regval & SJA1000_FI_FF))
|
|
{
|
|
#ifdef CONFIG_CAN_EXTID
|
|
hdr.ch_extid = 0;
|
|
#endif
|
|
|
|
hdr.ch_id = ((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 3) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) >> 5));
|
|
|
|
dreg = SJA1000_DATA_3_REG;
|
|
}
|
|
else
|
|
{
|
|
#ifdef CONFIG_CAN_EXTID
|
|
hdr.ch_extid = 1;
|
|
|
|
hdr.ch_id = ((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 21) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) << 13) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_3_REG) << 5) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_4_REG) >> 3));
|
|
|
|
dreg = SJA1000_DATA_5_REG;
|
|
#else
|
|
canerr("ERROR: Received message with extended"
|
|
" identifier. Dropped\n");
|
|
|
|
/* Relese RX buffer */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF);
|
|
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
/* Clear the error indication and unused bits */
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
hdr.ch_error = 0;
|
|
#endif
|
|
hdr.ch_tcf = 0;
|
|
|
|
/* Get data */
|
|
|
|
for (i = 0; i < can_dlc2bytes(hdr.ch_dlc); i++)
|
|
{
|
|
data[i] = kvaser_getreg_sja(priv, dreg + i);
|
|
}
|
|
|
|
/* Release RX buffer */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF);
|
|
|
|
/* Provide the data to the upper half driver */
|
|
|
|
ret = can_receive(dev, &hdr, data);
|
|
if (ret < 0)
|
|
{
|
|
canerr("ERROR: can_receive failed %d\n", ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
/*****************************************************************************
|
|
* Name: kvaser_chardev_error
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chardev_error(FAR struct kvaser_sja_s *priv, uint8_t st)
|
|
{
|
|
struct can_hdr_s hdr;
|
|
uint16_t errbits = 0;
|
|
uint8_t data[CAN_ERROR_DLC];
|
|
uint8_t regval;
|
|
int ret;
|
|
|
|
memset(data, 0, sizeof(data));
|
|
|
|
/* Data overrun interrupt */
|
|
|
|
if (st & SJA1000_OVERRUN_INT_ST)
|
|
{
|
|
data[1] |= CAN_ERROR1_RXOVERFLOW;
|
|
errbits |= CAN_ERROR_CONTROLLER;
|
|
|
|
/* Clear data overrun */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_CLR_OVERRUN);
|
|
}
|
|
|
|
/* Error passive interrupt */
|
|
|
|
if (st & SJA1000_ERR_PASSIVE_INT_ST)
|
|
{
|
|
data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
|
|
errbits |= CAN_ERROR_CONTROLLER;
|
|
}
|
|
|
|
/* Arbitration lost interrupt */
|
|
|
|
if (st & SJA1000_ARB_LOST_INT_ST)
|
|
{
|
|
errbits |= CAN_ERROR_LOSTARB;
|
|
regval = (kvaser_getreg_sja(priv, SJA1000_ARB_LOST_CAP_REG) &
|
|
SJA1000_ARB_LOST_CAP_M);
|
|
data[0] = CAN_ERROR0_BIT(regval);
|
|
}
|
|
|
|
/* BUS error interrupt */
|
|
|
|
if (st & SJA1000_BUS_ERR_INT_ST)
|
|
{
|
|
errbits |= CAN_ERROR_BUSERROR;
|
|
}
|
|
|
|
/* Report a CAN error */
|
|
|
|
if (errbits != 0)
|
|
{
|
|
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
|
|
|
|
/* Format the CAN header for the error report. */
|
|
|
|
hdr.ch_id = errbits;
|
|
hdr.ch_dlc = CAN_ERROR_DLC;
|
|
hdr.ch_rtr = 0;
|
|
hdr.ch_error = 1;
|
|
#ifdef CONFIG_CAN_EXTID
|
|
hdr.ch_extid = 0;
|
|
#endif
|
|
hdr.ch_tcf = 0;
|
|
|
|
/* And provide the error report to the upper half logic */
|
|
|
|
ret = can_receive(&priv->dev, &hdr, data);
|
|
if (ret < 0)
|
|
{
|
|
canerr("ERROR: can_receive failed: %d\n", ret);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_chardev_interrupt
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_chardev_interrupt(FAR struct kvaser_driver_s *priv)
|
|
{
|
|
uint8_t st = 0;
|
|
int i = 0;
|
|
|
|
for (i = 0; i < priv->count; i++)
|
|
{
|
|
st = kvaser_getreg_sja(&priv->sja[i], SJA1000_INT_RAW_REG);
|
|
if (st == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Receive interrupt */
|
|
|
|
if (st & SJA1000_RX_INT_ST)
|
|
{
|
|
kvaser_chardev_receive(&priv->sja[i]);
|
|
}
|
|
|
|
/* Transmit interrupt */
|
|
|
|
if (st & SJA1000_TX_INT_ST)
|
|
{
|
|
/* Tell the upper half that the transfer is finished. */
|
|
|
|
can_txdone(&priv->sja[i].dev);
|
|
}
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
/* Handle errors */
|
|
|
|
kvaser_chardev_error(&priv->sja[i], st);
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* CONFIG_CAN_KVASER_CHARDEV */
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_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
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_sock_ifup(FAR struct net_driver_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev->d_private;
|
|
|
|
priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool;
|
|
|
|
kvaser_reset(priv);
|
|
kvaser_setup(priv);
|
|
|
|
kvaser_txint(priv, true);
|
|
kvaser_rxint(priv, true);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_ifdown
|
|
*
|
|
* Description:
|
|
* NuttX Callback: Stop the interface.
|
|
*
|
|
* Input Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_sock_ifdown(FAR struct net_driver_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev->d_private;
|
|
|
|
kvaser_txint(priv, false);
|
|
kvaser_rxint(priv, false);
|
|
|
|
/* Sleep mode */
|
|
|
|
kvaser_sleep(priv);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_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 kvaser_sock_txavail_work(FAR void *arg)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)arg;
|
|
|
|
/* Ignore the notification if the interface is not yet up */
|
|
|
|
net_lock();
|
|
if (IFF_IS_UP(priv->dev.d_flags))
|
|
{
|
|
/* Check if there is room in the hardware to hold another outgoing
|
|
* packet.
|
|
*/
|
|
|
|
if (kvaser_txready(priv))
|
|
{
|
|
/* No, there is space for another transfer. Poll the network for
|
|
* new XMIT data.
|
|
*/
|
|
|
|
devif_poll(&priv->dev, kvaser_sock_txpoll);
|
|
}
|
|
}
|
|
|
|
net_unlock();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_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 kvaser_sock_txavail(FAR struct net_driver_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_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. */
|
|
|
|
kvaser_sock_txavail_work(priv);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
# ifdef CONFIG_NETDEV_IOCTL
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_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:
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_sock_ioctl(FAR struct net_driver_s *dev, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
|
|
# endif /* CONFIG_NETDEV_IOCTL */
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_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 kvaser_sock_txpoll(FAR struct net_driver_s *dev)
|
|
{
|
|
FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_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)
|
|
{
|
|
/* Send the packet */
|
|
|
|
kvaser_sock_send(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 (!kvaser_txready(priv))
|
|
{
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
/* If zero is returned, the polling will continue until all connections
|
|
* have been examined.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_send
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_sock_send(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
FAR struct can_frame *frame = (FAR struct can_frame *)priv->dev.d_buf;
|
|
uint8_t regval = 0;
|
|
uint8_t dreg = 0;
|
|
uint8_t fi = 0;
|
|
int i = 0;
|
|
|
|
/* Check TX buffer status */
|
|
|
|
if (!kvaser_txready(priv))
|
|
{
|
|
canerr("ERROR: TX buffer not available\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
#ifdef CONFIG_CAN_FD
|
|
/* Drop CAN FD frames */
|
|
|
|
if (priv->dev.d_len != sizeof(struct can_frame))
|
|
{
|
|
canerr("ERROR: CAN FD frames not supported\n");
|
|
return -ENOTSUP;
|
|
}
|
|
#endif
|
|
|
|
/* Set up the DLC */
|
|
|
|
fi = frame->can_dlc;
|
|
|
|
/* Set RTR bit */
|
|
|
|
fi |= (frame->can_id & CAN_RTR_FLAG ? SJA1000_FI_RTR : 0);
|
|
|
|
#ifdef CONFIG_NET_CAN_EXTID
|
|
if (frame->can_id & CAN_EFF_FLAG)
|
|
{
|
|
fi |= SJA1000_FI_FF;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi);
|
|
|
|
/* Write ID */
|
|
|
|
regval = (frame->can_id & 0x1fe00000) >> 21;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval);
|
|
regval = (frame->can_id & 0x001fe000) >> 13;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval);
|
|
regval = (frame->can_id & 0x00001fe0) >> 5;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_3_REG, regval);
|
|
regval = (frame->can_id & 0x0000001f) << 3;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_4_REG, regval);
|
|
|
|
dreg = SJA1000_DATA_5_REG;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi);
|
|
|
|
/* Write ID */
|
|
|
|
regval = (frame->can_id & 0x07f8) >> 3;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval);
|
|
regval = (frame->can_id & 0x0007) << 5;
|
|
kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval);
|
|
|
|
dreg = SJA1000_DATA_3_REG;
|
|
}
|
|
|
|
/* Set up the data fields */
|
|
|
|
if (frame->can_dlc > 8)
|
|
{
|
|
frame->can_dlc = 8;
|
|
}
|
|
|
|
for (i = 0; i < frame->can_dlc; i++)
|
|
{
|
|
kvaser_putreg_sja(priv, dreg + i, frame->data[i]);
|
|
}
|
|
|
|
/* Transmit */
|
|
|
|
regval = SJA1000_TX_REQ;
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, regval);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_receive
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_sock_receive(FAR struct kvaser_sja_s *priv)
|
|
{
|
|
FAR struct can_frame *frame = (FAR struct can_frame *)priv->rx_pool;
|
|
uint8_t dreg = 0;
|
|
int i = 0;
|
|
uint8_t regval;
|
|
|
|
/* Wait until data in RXFIFO */
|
|
|
|
while (kvaser_getreg_sja(priv, SJA1000_STATUS_REG) & SJA1000_RX_BUF_ST)
|
|
{
|
|
regval = kvaser_getreg_sja(priv, SJA1000_DATA_0_REG);
|
|
|
|
/* Get the DLC */
|
|
|
|
frame->can_dlc = (regval & SJA1000_FI_DLC_MASK);
|
|
|
|
/* Get the CAN identifier. */
|
|
|
|
if (!(regval & SJA1000_FI_FF))
|
|
{
|
|
frame->can_id =
|
|
((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 3) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) >> 5));
|
|
|
|
dreg = SJA1000_DATA_3_REG;
|
|
}
|
|
else
|
|
{
|
|
#ifdef CONFIG_NET_CAN_EXTID
|
|
frame->can_id =
|
|
((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 21) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) << 13) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_3_REG) << 5) |
|
|
(kvaser_getreg_sja(priv, SJA1000_DATA_4_REG) >> 3));
|
|
frame->can_id |= CAN_EFF_FLAG;
|
|
|
|
dreg = SJA1000_DATA_5_REG;
|
|
#else
|
|
canerr("ERROR: Received message with extended"
|
|
" identifier. Dropped\n");
|
|
|
|
/* Release RX buffer */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF);
|
|
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
/* Extract the RTR bit */
|
|
|
|
if (regval & SJA1000_FI_RTR)
|
|
{
|
|
frame->can_id |= CAN_RTR_FLAG;
|
|
}
|
|
|
|
/* Get data */
|
|
|
|
for (i = 0; i < can_dlc2bytes(frame->can_dlc); i++)
|
|
{
|
|
frame->data[i] = kvaser_getreg_sja(priv, dreg + i);
|
|
}
|
|
|
|
/* Release RX buffer */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF);
|
|
|
|
/* 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 = (FAR 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 = (FAR uint8_t *)priv->tx_pool;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_NET_CAN_ERRORS
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_error
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_sock_error(FAR struct kvaser_sja_s *priv, uint8_t st)
|
|
{
|
|
FAR struct can_frame *frame = NULL;
|
|
uint8_t regval;
|
|
uint16_t errbits = 0;
|
|
|
|
/* Data overrun interrupt */
|
|
|
|
if (st & SJA1000_OVERRUN_INT_ST)
|
|
{
|
|
frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
|
|
errbits |= CAN_ERR_CRTL;
|
|
|
|
/* Clear data overrun */
|
|
|
|
kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_CLR_OVERRUN);
|
|
}
|
|
|
|
/* Error passive interrupt */
|
|
|
|
if (st & SJA1000_ERR_PASSIVE_INT_ST)
|
|
{
|
|
frame->data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE);
|
|
errbits |= CAN_ERR_CRTL;
|
|
}
|
|
|
|
/* Arbitration lost interrupt */
|
|
|
|
if (st & SJA1000_ARB_LOST_INT_ST)
|
|
{
|
|
errbits |= CAN_ERR_LOSTARB;
|
|
regval = (kvaser_getreg_sja(priv, SJA1000_ARB_LOST_CAP_REG) &
|
|
SJA1000_ARB_LOST_CAP_M);
|
|
frame->data[0] = CAN_ERR_LOSTARB_BIT(regval);
|
|
}
|
|
|
|
/* BUS error interrupt */
|
|
|
|
if (st & SJA1000_BUS_ERR_INT_ST)
|
|
{
|
|
errbits |= CAN_ERR_BUSERROR;
|
|
}
|
|
|
|
if (errbits != 0)
|
|
{
|
|
frame = (FAR struct can_frame *)priv->rx_pool;
|
|
|
|
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
|
|
|
|
/* Copy frame */
|
|
|
|
frame->can_id = errbits;
|
|
frame->can_dlc = CAN_ERR_DLC;
|
|
|
|
net_lock();
|
|
|
|
/* 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 = (FAR uint8_t *)frame;
|
|
|
|
/* Send to socket interface */
|
|
|
|
NETDEV_ERRORS(&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 = (FAR uint8_t *)priv->tx_pool;
|
|
net_unlock();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_sock_interrupt_work
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_sock_interrupt_work(FAR void *arg)
|
|
{
|
|
FAR struct kvaser_driver_s *priv = arg;
|
|
uint8_t st = 0;
|
|
uint8_t i = 0;
|
|
|
|
for (i = 0; i < priv->count; i++)
|
|
{
|
|
st = kvaser_getreg_sja(&priv->sja[i], SJA1000_INT_RAW_REG);
|
|
if (st == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Receive interrupt */
|
|
|
|
if (st & SJA1000_RX_INT_ST)
|
|
{
|
|
kvaser_sock_receive(&priv->sja[i]);
|
|
}
|
|
|
|
/* Transmit interrupt */
|
|
|
|
if (st & SJA1000_TX_INT_ST)
|
|
{
|
|
NETDEV_TXDONE(&priv->sja[i].dev);
|
|
|
|
/* There should be space for a new TX in any event.
|
|
* Poll the network for new XMIT data.
|
|
*/
|
|
|
|
net_lock();
|
|
devif_poll(&priv->sja[i].dev, kvaser_sock_txpoll);
|
|
net_unlock();
|
|
}
|
|
|
|
#ifdef CONFIG_NET_CAN_ERRORS
|
|
/* Handle errors */
|
|
|
|
kvaser_sock_error(&priv->sja[i], st);
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* CONFIG_CAN_KVASER_SOCKET */
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_interrupt
|
|
*
|
|
* Description:
|
|
* Initialize SJA1000 device
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_interrupt(int irq, FAR void *context, FAR void *arg)
|
|
{
|
|
FAR struct kvaser_driver_s *priv = (FAR struct kvaser_driver_s *)arg;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
if (kvaser_getreg_s5920(priv, KVASER_S5920_INTCSR) &
|
|
KVASER_S5920_INTCSR_INT_ASSERT)
|
|
{
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
/* Handle character device interrupt */
|
|
|
|
kvaser_chardev_interrupt(priv);
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
/* Derefer SocketCAN interrupt to work queue */
|
|
|
|
work_queue(KVASER_CANWORK, &priv->irqwork,
|
|
kvaser_sock_interrupt_work, priv, 0);
|
|
#endif
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_init
|
|
*
|
|
* Description:
|
|
* Initialize SJA1000 device
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void kvaser_init(FAR struct kvaser_driver_s *priv)
|
|
{
|
|
uint32_t regval = 0;
|
|
|
|
/* Only legacy IRQ supported by kvaser */
|
|
|
|
priv->irq = pci_get_irq(priv->pcidev);
|
|
irq_attach(priv->irq, kvaser_interrupt, priv);
|
|
|
|
/* Enable card interrupts */
|
|
|
|
regval = kvaser_getreg_s5920(priv, KVASER_S5920_INTCSR);
|
|
regval |= KVASER_S5920_INTCSR_INT_ADDON;
|
|
kvaser_putreg_s5920(priv, KVASER_S5920_INTCSR, regval);
|
|
|
|
/* Enable interrupts */
|
|
|
|
up_enable_irq(priv->irq);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_count_sja
|
|
*
|
|
* Description:
|
|
* Proble SJA1000 devices on board and return the number of vailalbe chips.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static uint8_t kvaser_count_sja(FAR struct kvaser_driver_s *priv)
|
|
{
|
|
uint32_t offset;
|
|
uint8_t regval;
|
|
uint8_t i;
|
|
|
|
/* Reset chip and check if reset bit has changed */
|
|
|
|
for (i = 0; i < KVASER_SJA_MAX; i++)
|
|
{
|
|
offset = priv->sja_base + SJA1000_MODE_REG + (i * KVASER_SJA_REGS);
|
|
|
|
pci_write_io_byte(priv->pcidev, offset, SJA1000_RESET_MODE);
|
|
pci_read_io_byte(priv->pcidev, offset, ®val);
|
|
|
|
if (regval != SJA1000_RESET_MODE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: kvaser_probe
|
|
*
|
|
* Description:
|
|
* Probe device
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int kvaser_probe(FAR struct pci_device_s *dev)
|
|
{
|
|
FAR struct kvaser_driver_s *priv = NULL;
|
|
uint8_t i = 0;
|
|
int ret;
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
uint8_t count;
|
|
char devpath[PATH_MAX];
|
|
#endif
|
|
|
|
/* Allocate the interface structure */
|
|
|
|
priv = kmm_zalloc(sizeof(*priv));
|
|
if (priv == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialzie PCI dev */
|
|
|
|
priv->pcidev = dev;
|
|
|
|
pci_set_master(dev);
|
|
pciinfo("Enabled bus mastering\n");
|
|
pci_enable_device(dev);
|
|
pciinfo("Enabled memory resources\n");
|
|
|
|
/* Kvaser cards support only IO access */
|
|
|
|
if (pci_resource_flags(dev, KVASER_SJA_BAR) != PCI_RESOURCE_IO)
|
|
{
|
|
ret = -ENOTSUP;
|
|
goto errout;
|
|
}
|
|
|
|
/* Get S5920 base */
|
|
|
|
priv->s5920_base = (uintptr_t)pci_map_bar(dev, KVASER_S5920_BAR);
|
|
if (!priv->s5920_base)
|
|
{
|
|
pcierr("Not found S5920 bar\n");
|
|
ret = -ENXIO;
|
|
goto errout;
|
|
}
|
|
|
|
/* Get SJA1000 base */
|
|
|
|
priv->sja_base = (uintptr_t)pci_map_bar(dev, KVASER_SJA_BAR);
|
|
if (!priv->sja_base)
|
|
{
|
|
pcierr("Not found SJA bar\n");
|
|
ret = -ENXIO;
|
|
goto errout;
|
|
}
|
|
|
|
/* Get XILINX base */
|
|
|
|
priv->xilinx_base = (uintptr_t)pci_map_bar(dev, KVASER_XILINX_BAR);
|
|
if (!priv->xilinx_base)
|
|
{
|
|
pcierr("Not found XILINX bar\n");
|
|
ret = -ENXIO;
|
|
goto errout;
|
|
}
|
|
|
|
/* Get number of SJA1000 chips */
|
|
|
|
priv->count = kvaser_count_sja(priv);
|
|
|
|
pciinfo("detected %d SJA1000 deviced\n", priv->count);
|
|
|
|
/* Allocate SJA1000 devices */
|
|
|
|
priv->sja = kmm_zalloc(sizeof(struct kvaser_sja_s) * priv->count);
|
|
if (priv->sja == NULL)
|
|
{
|
|
ret = -ENOMEM;
|
|
goto errout;
|
|
}
|
|
|
|
/* Common initialziation for all channels */
|
|
|
|
kvaser_init(priv);
|
|
|
|
/* Handle all SJA1000 devices */
|
|
|
|
for (i = 0; i < priv->count; i++)
|
|
{
|
|
/* Common initialization */
|
|
|
|
priv->sja[i].base = priv->sja_base + (i * KVASER_SJA_REGS);
|
|
priv->sja[i].pcidev = dev;
|
|
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
count = g_kvaser_count++;
|
|
|
|
/* Get devpath for this SJA1000 device */
|
|
|
|
snprintf(devpath, PATH_MAX, "/dev/can%d", count);
|
|
|
|
/* Initialize SJA1000 channel */
|
|
|
|
priv->sja[i].dev.cd_ops = &g_kvaser_can_ops;
|
|
priv->sja[i].dev.cd_priv = &priv->sja[i];
|
|
|
|
/* Register CAN device */
|
|
|
|
ret = can_register(devpath, &priv->sja[i].dev);
|
|
if (ret < 0)
|
|
{
|
|
pcierr("ERROR: failed to register count=%d, %d\n", i, ret);
|
|
goto errout;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
/* Initialize the driver structure */
|
|
|
|
priv->sja[i].dev.d_ifup = kvaser_sock_ifup;
|
|
priv->sja[i].dev.d_ifdown = kvaser_sock_ifdown;
|
|
priv->sja[i].dev.d_txavail = kvaser_sock_txavail;
|
|
# ifdef CONFIG_NETDEV_IOCTL
|
|
priv->sja[i].dev.d_ioctl = kvaser_sock_ioctl;
|
|
# endif
|
|
priv->sja[i].dev.d_private = &priv->sja[i];
|
|
|
|
/* Put the interface in the down state. This usually amounts to
|
|
* resetting the device and/or calling kvaser_sock_ifdown().
|
|
*/
|
|
|
|
kvaser_sock_ifdown(&priv->sja[i].dev);
|
|
|
|
/* Register the device with the OS so that socket IOCTLs can be
|
|
* performed
|
|
*/
|
|
|
|
ret = netdev_register(&priv->sja[i].dev, NET_LL_CAN);
|
|
if (ret < 0)
|
|
{
|
|
pcierr("ERROR: failed to register count=%d, %d\n", i, ret);
|
|
goto errout;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return OK;
|
|
|
|
errout:
|
|
for (i = 0; i < priv->count; i++)
|
|
{
|
|
if (priv->sja[i].pcidev)
|
|
{
|
|
#ifdef CONFIG_CAN_KVASER_SOCKET
|
|
netdev_unregister(&priv->sja[i].dev);
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_KVASER_CHARDEV
|
|
snprintf(devpath, PATH_MAX, "/dev/can%d", i);
|
|
unregister_driver(devpath);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
kmm_free(priv->sja);
|
|
kmm_free(priv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Public Functions
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Name: pci_kvaser_init
|
|
*
|
|
* Description:
|
|
* Register a pci driver
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int pci_kvaser_init(void)
|
|
{
|
|
return pci_register_driver(&g_kvaser_drv);
|
|
}
|