Files
nuttx/drivers/can/ctucanfd_pci.c
Piyush Patle 0dccc8ba21 include/debug.h: Move to include/nuttx/debug.h
debug.h is a NuttX-specific, non-POSIX header. Placing it in the
top-level include/ directory creates naming conflicts with external
projects that define their own debug.h.
This commit moves the canonical header to include/nuttx/debug.h,
following the NuttX convention for non-POSIX/non-standard headers,
and updates all in-tree references.

A backward-compatibility shim is left at include/debug.h that
emits a deprecation #warning and re-includes <nuttx/debug.h>,
allowing out-of-tree code to continue building while migrating.

Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
2026-04-07 07:50:06 -03:00

1797 lines
46 KiB
C

/*****************************************************************************
* drivers/can/ctucanfd_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 <nuttx/debug.h>
#include <errno.h>
#include <stdio.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/pci/pci.h>
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
# include <nuttx/can/can.h>
#endif
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
# include <nuttx/net/netdev_lowerhalf.h>
# include <nuttx/net/can.h>
#endif
#include "ctucanfd.h"
/*****************************************************************************
* Pre-processor Definitions
*****************************************************************************/
/* PCI BARs */
#define CTUCANFD_BAR0 0
#define CTUCANFD_CTUCAN_BAR1 1
/* Registers per channel */
#define CTUCANFD_CTUCAN_REGS 0x4000
/* ID register in BAR0 */
#define CTUCANFD_BAR0_ID 0
/* Interrupts */
#define CTUCANFD_INT_ERR (CTUCANFD_INT_DOI | CTUCANFD_INT_ALI | \
CTUCANFD_INT_BEI)
/* Supported TX buffers */
#define CTUCANFD_TXBUF_CNT 4
/* SocketCAN specific */
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
# define CTUCANFD_TX_QUOTA 1
# define CTUCANFD_RX_QUOTA 1
# if CONFIG_IOB_NBUFFERS < (CTUCANFD_RX_QUOTA + CTUCANFD_TX_QUOTA)
# error CONFIG_IOB_NBUFFERS must be > (CTUCANFD_RX_QUOTA + CTUCANFD_TX_QUOTA)
# endif
#endif /* CONFIG_CAN_CTUCANFD_SOCKET */
#ifndef CONFIG_NET_CAN_ERRORS
# define CTUCANFD_SOCK_RXINT (CTUCANFD_INT_RBNEI)
#else
# define CTUCANFD_SOCK_RXINT (CTUCANFD_INT_RBNEI | CTUCANFD_INT_ERR)
#endif
/*****************************************************************************
* Private Types
*****************************************************************************/
/* CTUCANFD channel private data */
struct ctucanfd_can_s
{
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
struct can_dev_s dev;
#endif
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
/* This holds the information visible to the NuttX network */
struct netdev_lowerhalf_s dev;
#endif
FAR struct pci_device_s *pcidev;
uint64_t base;
uint8_t txbufcnt;
};
/* CTUCANFD private data */
struct ctucanfd_driver_s
{
FAR struct ctucanfd_can_s *devs;
uint8_t count;
uintptr_t bar0_base;
uintptr_t canfd_base;
/* PCI data */
FAR struct pci_device_s *pcidev;
int irq;
uintptr_t base;
};
/*****************************************************************************
* Private Functions Definitions
*****************************************************************************/
/* Helpers */
static uint32_t ctucanfd_getreg(FAR struct ctucanfd_can_s *priv,
unsigned int offset);
static void ctucanfd_putreg(FAR struct ctucanfd_can_s *priv,
unsigned int offset,
uint32_t value);
/* Common methods */
static void ctucanfd_reset(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_shutdown(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_setup(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_rxint(FAR struct ctucanfd_can_s *priv, bool enable);
static void ctucanfd_txint(FAR struct ctucanfd_can_s *priv, bool enable);
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
/* CAN character device methods */
static void ctucanfd_chrdev_reset(FAR struct can_dev_s *dev);
static int ctucanfd_chrdev_setup(FAR struct can_dev_s *dev);
static void ctucanfd_chrdev_shutdown(FAR struct can_dev_s *dev);
static void ctucanfd_chrdev_rxint(FAR struct can_dev_s *dev, bool enable);
static void ctucanfd_chrdev_txint(FAR struct can_dev_s *dev, bool enable);
static int ctucanfd_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd,
unsigned long arg);
static int ctucanfd_chrdev_remoterequest(FAR struct can_dev_s *dev,
uint16_t id);
static int ctucanfd_chrdev_send(FAR struct can_dev_s *dev,
struct can_msg_s *msg);
static bool ctucanfd_chrdev_txready(FAR struct can_dev_s *dev);
static bool ctucanfd_chrdev_txempty(FAR struct can_dev_s *dev);
static void ctucanfd_chardev_receive(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_chardev_interrupt(FAR struct ctucanfd_driver_s *priv);
#endif
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
/* SocketCAN methods */
static int ctucanfd_sock_ifup(FAR struct netdev_lowerhalf_s *dev);
static int ctucanfd_sock_ifdown(FAR struct netdev_lowerhalf_s *dev);
static int ctucanfd_sock_transmit(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt);
static FAR netpkt_t *ctucanfd_sock_recv(FAR struct netdev_lowerhalf_s *dev);
# ifdef CONFIG_NETDEV_IOCTL
static int ctucanfd_sock_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
unsigned long arg);
# endif
# ifdef CONFIG_NET_CAN_ERRORS
static FAR netpkt_t *ctucanfd_sock_error(FAR struct netdev_lowerhalf_s *dev);
# endif
static void ctucanfd_sock_interrupt(FAR struct ctucanfd_driver_s *priv);
#endif
/* Interrupts */
static int ctucanfd_interrupt(int irq, FAR void *context, FAR void *arg);
/* PCI */
static void ctucanfd_init(FAR struct ctucanfd_driver_s *priv);
static uint8_t ctucanfd_ctucanfd_probe(FAR struct ctucanfd_driver_s *priv);
static int ctucanfd_probe(FAR struct pci_device_s *dev);
/*****************************************************************************
* Private Data
*****************************************************************************/
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
static const struct can_ops_s g_ctucanfd_can_ops =
{
.co_reset = ctucanfd_chrdev_reset,
.co_setup = ctucanfd_chrdev_setup,
.co_shutdown = ctucanfd_chrdev_shutdown,
.co_rxint = ctucanfd_chrdev_rxint,
.co_txint = ctucanfd_chrdev_txint,
.co_ioctl = ctucanfd_chrdev_ioctl,
.co_remoterequest = ctucanfd_chrdev_remoterequest,
.co_send = ctucanfd_chrdev_send,
.co_txready = ctucanfd_chrdev_txready,
.co_txempty = ctucanfd_chrdev_txempty,
};
#endif
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
static const struct netdev_ops_s g_ctucanfd_net_ops =
{
.ifup = ctucanfd_sock_ifup,
.ifdown = ctucanfd_sock_ifdown,
.transmit = ctucanfd_sock_transmit,
.receive = ctucanfd_sock_recv,
#ifdef CONFIG_NETDEV_IOCTL
.ioctl = ctucanfd_sock_ioctl,
#endif
};
#endif
static const struct pci_device_id_s g_ctucanfd_id_table[] =
{
{
PCI_DEVICE(0x1760, 0xff00),
.driver_data = 0
},
{ }
};
static struct pci_driver_s g_ctucanfd_drv =
{
.id_table = g_ctucanfd_id_table,
.probe = ctucanfd_probe,
};
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
static uint8_t g_ctucanfd_count = 0;
#endif
/*****************************************************************************
* Private Functions
*****************************************************************************/
/*****************************************************************************
* Name: ctucanfd_getreg
*****************************************************************************/
static uint32_t ctucanfd_getreg(FAR struct ctucanfd_can_s *priv,
unsigned int offset)
{
uintptr_t addr = priv->base + offset;
return *((FAR volatile uint32_t *)addr);
}
/*****************************************************************************
* Name: ctucanfd_putreg
*****************************************************************************/
static void ctucanfd_putreg(FAR struct ctucanfd_can_s *priv,
unsigned int offset,
uint32_t value)
{
uintptr_t addr = priv->base + offset;
*((FAR volatile uint32_t *)addr) = value;
}
/*****************************************************************************
* Name: ctucanfd_reset
*****************************************************************************/
static void ctucanfd_reset(FAR struct ctucanfd_can_s *priv)
{
/* Soft reset */
ctucanfd_putreg(priv, CTUCANFD_SET_MODE, CTUCANFD_MODE_RST);
}
/*****************************************************************************
* Name: ctucanfd_shutdown
*****************************************************************************/
static void ctucanfd_shutdown(FAR struct ctucanfd_can_s *priv)
{
ctucanfd_putreg(priv, CTUCANFD_SET_MODE, 0);
}
/*****************************************************************************
* Name: ctucanfd_setup
*****************************************************************************/
static void ctucanfd_setup(FAR struct ctucanfd_can_s *priv)
{
uint32_t regval = 0;
int i;
/* REVISIT: missing bus timings configuration.
*
* This driver was verified on QEMU with virtual host CAN network,
* which doesn't need bus timings.
* For real hardware, these registers must be properly configured !
*/
ctucanfd_putreg(priv, CTUCANFD_BTR, 0);
ctucanfd_putreg(priv, CTUCANFD_BTRFD, 0);
/* Enable interrupts */
#if defined(CONFIG_CAN_ERRORS) || defined(CONFIG_NET_CAN_ERRORS)
ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_ERR);
#else
ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_ERR);
#endif
/* Configure TX priority */
for (i = 0; i < priv->txbufcnt; i++)
{
regval |= 1 << (CTUCANFD_TXPRIO_SHIFT * i);
}
ctucanfd_putreg(priv, CTUCANFD_TXPRIO, regval);
/* Set MODE register */
regval = ctucanfd_getreg(priv, CTUCANFD_SET_MODE);
/* Enable CTU CAN FD */
regval |= CTUCANFD_SET_ENA << CTUCANFD_SET_SHFIT;
/* RX buffer automatic mode */
regval |= CTUCANFD_MODE_RXBAM;
/* Write SETTINGS and MODE */
ctucanfd_putreg(priv, CTUCANFD_SET_MODE, regval);
}
/*****************************************************************************
* Name: ctucanfd_rxint
*****************************************************************************/
static void ctucanfd_rxint(FAR struct ctucanfd_can_s *priv, bool enable)
{
if (enable)
{
ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_RBNEI);
}
else
{
ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_RBNEI);
}
}
/*****************************************************************************
* Name: ctucanfd_txint
*****************************************************************************/
static void ctucanfd_txint(FAR struct ctucanfd_can_s *priv, bool enable)
{
if (enable)
{
ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_TXI);
}
else
{
ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_TXI);
}
}
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
/*****************************************************************************
* Name: ctucanfd_chrdev_reset
*
* Description:
* Reset the CAN device. Called early to initialize the hardware. This
* function is called, before ctucanfd_setup() and on error conditions.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
*****************************************************************************/
static void ctucanfd_chrdev_reset(FAR struct can_dev_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_reset(priv);
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_setup(FAR struct can_dev_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_setup(priv);
return OK;
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_shutdown(FAR struct can_dev_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_shutdown(priv);
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_rxint(FAR struct can_dev_s *dev, bool enable)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_rxint(priv, enable);
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_txint(FAR struct can_dev_s *dev, bool enable)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_txint(priv, enable);
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd,
unsigned long arg)
{
return -ENOTTY;
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_remoterequest(FAR struct can_dev_s *dev,
uint16_t id)
{
return -ENOTSUP;
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_send(FAR struct can_dev_s *dev,
FAR struct can_msg_s *msg)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
union ctucanfd_frame_fmt_u fmt;
union ctucanfd_frame_id_u id;
uint32_t regval;
uint32_t offset = 0;
int txidx;
int i;
uint8_t bytes;
/* Get TX empty buffer */
regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);
for (i = 0; i < priv->txbufcnt; i++)
{
if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY)
{
offset = CTUCANFD_TXT1 + CTUCANFD_TXT_SIZE * i;
txidx = i;
break;
}
}
if (offset == 0)
{
canerr("ERROR: TX buffer not available\n");
return -EBUSY;
}
/* Reset data */
fmt.u32 = 0;
id.u32 = 0;
/* Set up the DLC */
fmt.s.dlc = msg->cm_hdr.ch_dlc;
/* Set RTR bit */
fmt.s.rtr = msg->cm_hdr.ch_rtr;
#ifdef CONFIG_CAN_EXTID
if (msg->cm_hdr.ch_extid)
{
fmt.s.ide = 1;
id.s.id_ext = msg->cm_hdr.ch_id;
}
else
#endif
{
fmt.s.ide = 0;
id.s.id = msg->cm_hdr.ch_id;
}
#ifdef CONFIG_CAN_FD
/* Set CAN FD specific flags */
fmt.s.brs = msg->cm_hdr.ch_brs;
fmt.s.esi_rsv = msg->cm_hdr.ch_esi;
fmt.s.fdf = msg->cm_hdr.ch_edl;
#endif
/* Write frame */
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0);
bytes = can_dlc2bytes(msg->cm_hdr.ch_dlc);
for (i = 0; i < bytes; i++)
{
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i,
msg->cm_data[i]);
}
/* Set TX ready */
regval = CTUCANFD_TXCMD_TXCR + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx));
ctucanfd_putreg(priv, CTUCANFD_TXINFOCMD, regval);
return OK;
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_txready(FAR struct can_dev_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
uint32_t regval;
int i;
regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);
for (i = 0; i < priv->txbufcnt; i++)
{
if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY)
{
return true;
}
}
return false;
}
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_chrdev_txempty(FAR struct can_dev_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
uint32_t regval = 0;
bool ret = true;
int i;
regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);
for (i = 0; i < priv->txbufcnt; i++)
{
if (CTUCANFD_TXSTAT_GET(regval, i) != CTUCANFD_TXSTAT_ETY)
{
ret = false;
break;
}
}
return ret;
}
/*****************************************************************************
* Name: ctucanfd_chardev_receive
*
* Description:
* Receive CAN frame
*
*****************************************************************************/
static void ctucanfd_chardev_receive(FAR struct ctucanfd_can_s *priv)
{
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)priv;
FAR struct ctucanfd_frame_s *frame;
struct can_hdr_s hdr;
uint32_t buff[sizeof(struct ctucanfd_frame_s) / 4];
int i = 0;
int ret = 0;
uint16_t frc = 0;
uint32_t regval = 0;
memset(&hdr, 0, sizeof(hdr));
/* Get frame count */
regval = ctucanfd_getreg(priv, CTUCANFD_RXSETSTAT);
frc = (regval & CTUCANFD_RXSTAT_RXFRC_MASK) >> CTUCANFD_RXSTAT_RXFRC_SHIFT;
/* Read frames */
while (frc-- > 0)
{
/* We use a pointer to buffer to avoid an unaligned pointer
* compiler errors
*/
frame = (struct ctucanfd_frame_s *)&buff;
/* RX buffer in automatic mode */
buff[0] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);
/* Read the rest of data */
for (i = 0; i < frame->fmt.rwcnt; i++)
{
buff[i + 1] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);
}
/* Get the DLC */
hdr.ch_dlc = frame->fmt.dlc;
/* Get RTR bit */
hdr.ch_rtr = frame->fmt.rtr;
#ifdef CONFIG_CAN_EXTID
/* Get the CAN identifier. */
hdr.ch_extid = frame->fmt.ide;
if (hdr.ch_extid)
{
hdr.ch_id = frame->id.id_ext;
}
else
{
hdr.ch_id = frame->id.id;
}
#else
if (frame->fmt.ide)
{
canerr("ERROR: Received message with extended"
" identifier. Dropped\n");
continue;
}
hdr.ch_id = frame->id.id;
#endif
/* Clear the error indication and unused bits */
#ifdef CONFIG_CAN_ERRORS
hdr.ch_error = 0;
#endif
hdr.ch_tcf = 0;
#ifdef CONFIG_CAN_FD
hdr.ch_esi = frame->fmt.esi_rsv;
hdr.ch_edl = frame->fmt.fdf;
hdr.ch_brs = frame->fmt.brs;
#else
if (frame->fmt.fdf)
{
/* Drop any FD CAN messages if not supported */
canerr("ERROR: Received CAN FD message. Dropped\n");
return;
}
#endif
/* Provide the data to the upper half driver */
ret = can_receive(dev, &hdr, (FAR uint8_t *)&frame->data);
if (ret < 0)
{
canerr("ERROR: can_receive failed %d\n", ret);
}
}
}
#ifdef CONFIG_CAN_ERRORS
/*****************************************************************************
* Name: ctucanfd_chardev_error
*****************************************************************************/
static void ctucanfd_chardev_error(FAR struct ctucanfd_can_s *priv,
uint32_t stat)
{
struct can_hdr_s hdr;
uint16_t errbits = 0;
uint8_t data[CAN_ERROR_DLC];
int ret;
memset(data, 0, sizeof(data));
/* Data overrun interrupt */
if (stat & CTUCANFD_INT_DOI)
{
data[1] |= CAN_ERROR1_RXOVERFLOW;
errbits |= CAN_ERROR_CONTROLLER;
/* Clear data overrun */
ctucanfd_putreg(priv, CTUCANFD_CMD,
CTUCANFD_CMD_RRB | CTUCANFD_CMD_CDO);
}
/* Arbitration lost interrupt */
if (stat & CTUCANFD_INT_ALI)
{
errbits |= CAN_ERROR_LOSTARB;
data[0] = CAN_ERROR0_UNSPEC;
}
/* BUS error interrupt */
if (stat & CTUCANFD_INT_BEI)
{
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: ctucanfd_chardev_interrupt
*****************************************************************************/
static void ctucanfd_chardev_interrupt(FAR struct ctucanfd_driver_s *priv)
{
uint32_t stat = 0;
uint32_t regval = 0;
int i = 0;
int txidx = 0;
for (i = 0; i < priv->count; i++)
{
stat = ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT);
if (stat == 0)
{
continue;
}
/* Frame received */
if (stat & CTUCANFD_INT_RXI)
{
ctucanfd_chardev_receive(&priv->devs[i]);
}
/* Frame transmitted */
if (stat & CTUCANFD_INT_TXI)
{
regval = ctucanfd_getreg(&priv->devs[i], CTUCANFD_TXSTAT);
for (txidx = 0; txidx < priv->devs[i].txbufcnt; txidx++)
{
if (CTUCANFD_TXSTAT_GET(regval, txidx) ==
CTUCANFD_TXSTAT_TOK)
{
can_txdone(&priv->devs[i].dev);
/* Mark buffer as empty */
regval = (CTUCANFD_TXCMD_TXCE +
(1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx)));
ctucanfd_putreg(&priv->devs[i], CTUCANFD_TXINFOCMD, regval);
}
}
}
#ifdef CONFIG_CAN_ERRORS
/* Handle errors */
if (stat & CTUCANFD_INT_ERR)
{
ctucanfd_chardev_error(&priv->devs[i], stat);
}
#endif
/* Clear interrupts */
ctucanfd_putreg(&priv->devs[i], CTUCANFD_INTSTAT, stat);
/* Re-enable RX/TX interrupts */
ctucanfd_rxint(&priv->devs[i], true);
ctucanfd_txint(&priv->devs[i], true);
}
}
#endif /* CONFIG_CAN_CTUCANFD_CHARDEV */
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_sock_ifup(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_reset(priv);
ctucanfd_setup(priv);
ctucanfd_txint(priv, true);
ctucanfd_rxint(priv, true);
return OK;
}
/*****************************************************************************
* Name: ctucanfd_sock_ifdown
*
* Description:
* NuttX Callback: Stop the interface.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
*****************************************************************************/
static int ctucanfd_sock_ifdown(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
ctucanfd_txint(priv, false);
ctucanfd_rxint(priv, false);
/* Shutdown */
ctucanfd_shutdown(priv);
return OK;
}
# ifdef CONFIG_NETDEV_IOCTL
/*****************************************************************************
* Name: ctucanfd_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 ctucanfd_sock_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
unsigned long arg)
{
return -ENOTTY;
}
# endif /* CONFIG_NETDEV_IOCTL */
/*****************************************************************************
* Name: ctucanfd_sock_transmit
*****************************************************************************/
static int ctucanfd_sock_transmit(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
union ctucanfd_frame_fmt_u fmt;
union ctucanfd_frame_id_u id;
uint32_t regval;
uint32_t offset;
int txidx;
int i;
/* Get TX empty buffer */
regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);
offset = 0;
for (i = 0; i < priv->txbufcnt; i++)
{
if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY)
{
offset = CTUCANFD_TXT1 + CTUCANFD_TXT_SIZE * i;
txidx = i;
break;
}
}
if (offset == 0)
{
canerr("ERROR: TX buffer not available\n");
return -EBUSY;
}
/* Reset data */
fmt.u32 = 0;
id.u32 = 0;
/* CAN 2.0 or CAN FD */
if (netpkt_getdatalen(dev, pkt) == sizeof(struct can_frame))
{
FAR struct can_frame *frame = frame =
(FAR struct can_frame *)netpkt_getdata(dev, pkt);
/* Set up the DLC */
fmt.s.dlc = frame->can_dlc;
/* Set RTR bit */
fmt.s.rtr = (frame->can_id & CAN_RTR_FLAG);
#ifdef CONFIG_NET_CAN_EXTID
if (frame->can_id & CAN_EFF_FLAG)
{
fmt.s.ide = 1;
id.s.id_ext = (frame->can_id & 0x1ffffff);
}
else
#endif
{
fmt.s.ide = 0;
id.s.id = (frame->can_id & 0x07ff);
}
/* Write frame */
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0);
for (i = 0; i < frame->can_dlc; i++)
{
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i,
frame->data[i]);
}
}
#ifdef CONFIG_NET_CAN_CANFD
else /* CAN FD frame */
{
FAR struct canfd_frame *frame =
(FAR struct canfd_frame *)netpkt_getdata(dev, pkt);
/* CAN FD frame */
fmt.s.fdf = 1;
/* Set up the DLC */
fmt.s.dlc = can_bytes2dlc(frame->len);
/* Set flags */
fmt.s.rtr = (frame->can_id & CAN_RTR_FLAG);
fmt.s.brs = (frame->flags & CANFD_BRS);
fmt.s.esi_rsv = (frame->flags & CANFD_ESI);
#ifdef CONFIG_NET_CAN_EXTID
if (frame->can_id & CAN_EFF_FLAG)
{
fmt.s.ide = 1;
id.s.id_ext = (frame->can_id & 0x1ffffff);
}
else
#endif
{
fmt.s.ide = 0;
id.s.id = (frame->can_id & 0x07ff);
}
/* Write frame */
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0);
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0);
for (i = 0; i < frame->len; i++)
{
ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i,
frame->data[i]);
}
}
#endif
/* Set TX ready */
regval = CTUCANFD_TXCMD_TXCR + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx));
ctucanfd_putreg(priv, CTUCANFD_TXINFOCMD, regval);
/* All is done - free packet */
netpkt_free(dev, pkt, NETPKT_TX);
return OK;
}
/*****************************************************************************
* Name: ctucanfd_sock_recv
*****************************************************************************/
static FAR netpkt_t *ctucanfd_sock_recv(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
FAR netpkt_t *pkt = NULL;
FAR struct ctucanfd_frame_s *rxframe;
uint32_t buff[sizeof(struct ctucanfd_frame_s) / 4];
int i = 0;
uint32_t regval = 0;
uint8_t bytes;
/* Read frame if RX buffer not empty */
regval = ctucanfd_getreg(priv, CTUCANFD_RXSETSTAT);
if ((regval & CTUCANFD_RXSTAT_RXE) != 0)
{
/* Re-enable RX interrupts if no more messages to read */
ctucanfd_rxint(priv, true);
#ifdef CONFIG_NET_CAN_ERRORS
/* Check for errors */
return ctucanfd_sock_error(dev);
#else
return NULL;
#endif
}
/* Allocate packet */
pkt = netpkt_alloc(dev, NETPKT_RX);
if (!pkt)
{
canerr("alloc pkt_new failed\n");
return NULL;
}
/* We use a pointer to buffer to avoid an unaligned pointer
* compiler errors
*/
rxframe = (struct ctucanfd_frame_s *)&buff;
/* RX buffer in automatic mode */
buff[0] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);
/* Read the rest of data */
for (i = 0; i < rxframe->fmt.rwcnt; i++)
{
buff[i + 1] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);
}
/* CAN 2.0 or CAN FD */
#ifdef CONFIG_NET_CAN_CANFD
if (rxframe->fmt.fdf)
{
struct canfd_frame *frame;
netpkt_setdatalen(dev, pkt, sizeof(struct canfd_frame));
frame = (FAR struct canfd_frame *)netpkt_getdata(dev, pkt);
/* Get the DLC */
frame->len = can_dlc2bytes(rxframe->fmt.dlc);
#ifdef CONFIG_NET_CAN_EXTID
/* Get the CAN identifier. */
if (rxframe->fmt.ide)
{
frame->can_id = rxframe->id.id_ext;
frame->can_id |= CAN_EFF_FLAG;
}
else
{
frame->can_id = rxframe->id.id;
}
#else
if (rxframe->fmt.ide)
{
canerr("ERROR: Received message with extended"
" identifier. Dropped\n");
continue;
}
frame->can_id = rxframe->id.id;
#endif
/* Extract the RTR bit */
if (rxframe->fmt.rtr)
{
frame->can_id |= CAN_RTR_FLAG;
}
/* Get CANFD flags */
frame->flags = 0;
if (rxframe->fmt.esi_rsv)
{
frame->flags |= CANFD_ESI;
}
if (rxframe->fmt.brs)
{
frame->flags |= CANFD_BRS;
}
/* Get data */
bytes = can_dlc2bytes(rxframe->fmt.dlc);
for (i = 0; i < bytes; i++)
{
frame->data[i] = rxframe->data[i];
}
}
else
#endif
{
FAR struct can_frame *frame;
netpkt_setdatalen(dev, pkt, sizeof(struct can_frame));
frame = (FAR struct can_frame *)netpkt_getdata(dev, pkt);
/* Get the DLC */
frame->can_dlc = rxframe->fmt.dlc;
#ifdef CONFIG_NET_CAN_EXTID
/* Get the CAN identifier. */
if (rxframe->fmt.ide)
{
frame->can_id = rxframe->id.id_ext;
frame->can_id |= CAN_EFF_FLAG;
}
else
{
frame->can_id = rxframe->id.id;
}
#else
if (rxframe->fmt.ide)
{
canerr("ERROR: Received message with extended"
" identifier. Dropped\n");
continue;
}
frame->can_id = rxframe->id.id;
#endif
/* Extract the RTR bit */
if (rxframe->fmt.rtr)
{
frame->can_id |= CAN_RTR_FLAG;
}
/* Get data */
for (i = 0; i < rxframe->fmt.dlc; i++)
{
frame->data[i] = rxframe->data[i];
}
}
return pkt;
}
#ifdef CONFIG_NET_CAN_ERRORS
/*****************************************************************************
* Name: ctucanfd_sock_error
*****************************************************************************/
static FAR netpkt_t *ctucanfd_sock_error(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *) dev;
FAR struct can_frame *frame = NULL;
FAR netpkt_t *pkt = NULL;
uint16_t errbits = 0;
uint32_t stat = 0;
stat = ctucanfd_getreg(priv, CTUCANFD_INTSTAT);
if ((stat & CTUCANFD_INT_ERR) == 0)
{
return NULL;
}
/* Allocate packet */
pkt = netpkt_alloc(dev, NETPKT_RX);
if (!pkt)
{
canerr("alloc pkt_new failed\n");
return NULL;
}
netpkt_setdatalen(dev, pkt, sizeof(struct can_frame));
frame = (FAR struct can_frame *)netpkt_getdata(dev, pkt);
/* Data overrun interrupt */
if (stat & CTUCANFD_INT_DOI)
{
frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
errbits |= CAN_ERR_CRTL;
/* Clear data overrun */
ctucanfd_putreg(priv, CTUCANFD_CMD,
(CTUCANFD_CMD_RRB | CTUCANFD_CMD_CDO));
}
/* Arbitration lost interrupt */
if (stat & CTUCANFD_INT_ALI)
{
errbits |= CAN_ERR_LOSTARB;
frame->data[0] = CAN_ERR_LOSTARB_UNSPEC;
}
/* BUS error interrupt */
if (stat & CTUCANFD_INT_BEI)
{
errbits |= CAN_ERR_BUSERROR;
}
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
/* Copy frame */
frame->can_id = errbits;
frame->can_dlc = CAN_ERR_DLC;
return pkt;
}
#endif
/*****************************************************************************
* Name: ctucanfd_sock_interrupt
*****************************************************************************/
static void ctucanfd_sock_interrupt(FAR struct ctucanfd_driver_s *priv)
{
uint32_t stat = 0;
uint32_t regval = 0;
int i = 0;
int txidx = 0;
for (i = 0; i < priv->count; i++)
{
stat = ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT);
if (stat == 0)
{
continue;
}
/* RX ready */
if (stat & CTUCANFD_SOCK_RXINT)
{
netdev_lower_rxready(&priv->devs[i].dev);
}
/* Transmit interrupt */
if (stat & CTUCANFD_INT_TXI)
{
regval = ctucanfd_getreg(&priv->devs[i], CTUCANFD_TXSTAT);
for (txidx = 0; txidx < priv->devs[i].txbufcnt; txidx++)
{
if (CTUCANFD_TXSTAT_GET(regval, txidx) ==
CTUCANFD_TXSTAT_TOK)
{
netdev_lower_txdone(&priv->devs[i].dev);
/* Mark buffer as empty */
regval = (CTUCANFD_TXCMD_TXCE +
(1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx)));
ctucanfd_putreg(&priv->devs[i], CTUCANFD_TXINFOCMD, regval);
}
}
}
/* Clear interrupts */
ctucanfd_putreg(&priv->devs[i], CTUCANFD_INTSTAT, stat);
/* Re-enable TX interrupts.
* NOTE: RX interrupts are re-enabled in ctucanfd_sock_recv.
*/
ctucanfd_txint(&priv->devs[i], true);
}
}
#endif /* CONFIG_CAN_CTUCANFD_SOCKET */
/*****************************************************************************
* Name: ctucanfd_interrupt
*
* Description:
* Initialize CTUCANFD device
*
*****************************************************************************/
static int ctucanfd_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct ctucanfd_driver_s *priv = (FAR struct ctucanfd_driver_s *)arg;
uint32_t regval = 0;
int i = 0;
DEBUGASSERT(priv != NULL);
for (i = 0; i < priv->count; i++)
{
regval |= ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT);
/* Break now if interrupt is pending */
if (regval != 0)
{
/* Disable RX/TX interrupts until we are done */
ctucanfd_rxint(&priv->devs[i], false);
ctucanfd_txint(&priv->devs[i], false);
break;
}
}
/* Check for pending interrupts for this card */
if (regval == 0)
{
return OK;
}
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
/* Handle character device interrupt */
ctucanfd_chardev_interrupt(priv);
#endif
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
/* Handle SocketCAN interrupt */
ctucanfd_sock_interrupt(priv);
#endif
return OK;
}
/*****************************************************************************
* Name: ctucanfd_init
*
* Description:
* Initialize CTUCANFD device
*
*****************************************************************************/
static void ctucanfd_init(FAR struct ctucanfd_driver_s *priv)
{
/* REVISIT: Only legacy IRQ supported in QEMU driver */
priv->irq = pci_get_irq(priv->pcidev);
irq_attach(priv->irq, ctucanfd_interrupt, priv);
/* REVISIT: Enable card interrupts (not implemented in QEMU) */
/* Enable interrupts */
up_enable_irq(priv->irq);
}
/*****************************************************************************
* Name: ctucanfd_ctucanfd_probe
*
* Description:
* Probe CTUCANFD devices on board and return the number of available chips.
*
*****************************************************************************/
static uint8_t ctucanfd_ctucanfd_probe(FAR struct ctucanfd_driver_s *priv)
{
uint32_t offset;
uint8_t regval;
offset = priv->bar0_base + CTUCANFD_BAR0_ID;
pci_read_io_byte(priv->pcidev, offset, &regval);
/* REVISIT: what is the maximum number of channels ? */
return regval;
}
/*****************************************************************************
* Name: ctucanfd_probe
*
* Description:
* Probe device
*
*****************************************************************************/
static int ctucanfd_probe(FAR struct pci_device_s *dev)
{
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
FAR struct netdev_lowerhalf_s *netdev = NULL;
#endif
FAR struct ctucanfd_driver_s *priv = NULL;
uint8_t i = 0;
int ret;
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
uint8_t count;
char devpath[PATH_MAX];
#endif
/* Allocate the interface structure */
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
ret = -ENOMEM;
return ret;
}
/* Initialzie PCI dev */
priv->pcidev = dev;
pci_set_master(dev);
pciinfo("Enabled bus mastering\n");
pci_enable_device(dev);
pciinfo("Enabled memory resources\n");
/* Control register BAR */
priv->bar0_base = (uintptr_t)pci_map_bar(dev, CTUCANFD_BAR0);
if (!priv->bar0_base)
{
pcierr("Not found BAR0\n");
ret = -ENODEV;
goto errout;
}
/* MEM access only supported */
if (pci_resource_flags(dev, CTUCANFD_BAR0) != PCI_RESOURCE_MEM)
{
ret = -ENOTSUP;
goto errout;
}
priv->canfd_base = (uintptr_t)pci_map_bar(dev, CTUCANFD_CTUCAN_BAR1);
if (!priv->canfd_base)
{
pcierr("Not found CANFD bar\n");
ret = -ENODEV;
goto errout;
}
/* MEM access only supported */
if (pci_resource_flags(dev, CTUCANFD_CTUCAN_BAR1) != PCI_RESOURCE_MEM)
{
ret = -ENOTSUP;
goto errout;
}
/* Get number of CTUCANFD chips */
priv->count = ctucanfd_ctucanfd_probe(priv);
pciinfo("detected %d CTUCANFD channels\n", priv->count);
/* Allocate CTUCANFD devices */
priv->devs = kmm_zalloc(sizeof(struct ctucanfd_can_s) * priv->count);
if (priv->devs == NULL)
{
ret = -ENOMEM;
goto errout;
}
/* Common initialziation for all channels */
ctucanfd_init(priv);
/* Handle all CTUCANFD devices */
for (i = 0; i < priv->count; i++)
{
/* Common initialization */
priv->devs[i].base = priv->canfd_base + (CTUCANFD_CTUCAN_REGS * i);
priv->devs[i].pcidev = dev;
priv->devs[i].txbufcnt = CTUCANFD_TXBUF_CNT;
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
count = g_ctucanfd_count++;
/* Get devpath for this CTUCANFD device */
snprintf(devpath, PATH_MAX, "/dev/can%d", count);
/* Initialize CTUCANFD channel */
priv->devs[i].dev.cd_ops = &g_ctucanfd_can_ops;
priv->devs[i].dev.cd_priv = &priv->devs[i];
/* Register CAN device */
ret = can_register(devpath, &priv->devs[i].dev);
if (ret < 0)
{
pcierr("ERROR: failed to register count=%d, %d\n", i, ret);
goto errout;
}
#endif
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
netdev = &priv->devs[i].dev;
/* Register the network device */
netdev->quota[NETPKT_TX] = CTUCANFD_TX_QUOTA;
netdev->quota[NETPKT_RX] = CTUCANFD_RX_QUOTA;
netdev->ops = &g_ctucanfd_net_ops;
/* Put the interface in the down state. This usually amounts to
* resetting the device and/or calling ctucanfd_sock_ifdown().
*/
ctucanfd_sock_ifdown(&priv->devs[i].dev);
/* Register the network interface. */
ret = netdev_lower_register(netdev, 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->devs[i].pcidev)
{
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
netdev = &priv->devs[i].dev;
netdev_lower_unregister(netdev);
#endif
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
unregister_driver(devpath);
#endif
}
}
kmm_free(priv->devs);
kmm_free(priv);
return ret;
}
/*****************************************************************************
* Public Functions
*****************************************************************************/
/*****************************************************************************
* Name: pci_ctucanfd_init
*
* Description:
* Register a pci driver
*
*****************************************************************************/
int pci_ctucanfd_init(void)
{
return pci_register_driver(&g_ctucanfd_drv);
}