drivers: can: add driver for the Kvaser PCIcan CAN controller PCI card
Add driver for the Kvaser PCIcan CAN controller PCI card for use with QEMU. Signed-off-by: Henrik Brix Andersen <henrik@brixandersen.dk>
This commit is contained in:
parent
466db0ced2
commit
58e12726bf
|
@ -31,6 +31,7 @@ endif()
|
|||
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_SJA1000 can_sja1000.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_ESP32_TWAI can_esp32_twai.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_KVASER_PCI can_kvaser_pci.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE can_handlers.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_SHELL can_shell.c)
|
||||
|
|
|
@ -101,6 +101,7 @@ source "drivers/can/Kconfig.loopback"
|
|||
source "drivers/can/Kconfig.native_posix_linux"
|
||||
source "drivers/can/Kconfig.sja1000"
|
||||
source "drivers/can/Kconfig.esp32"
|
||||
source "drivers/can/Kconfig.kvaser"
|
||||
|
||||
source "drivers/can/transceiver/Kconfig"
|
||||
|
||||
|
|
13
drivers/can/Kconfig.kvaser
Normal file
13
drivers/can/Kconfig.kvaser
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Kvaser PCIcan configuration options
|
||||
|
||||
# Copyright (c) 2022 Henrik Brix Andersen <henrik@brixandersen.dk>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config CAN_KVASER_PCI
|
||||
bool "Kvaser PCIcan driver"
|
||||
default y
|
||||
depends on DT_HAS_KVASER_PCICAN_ENABLED
|
||||
select PCIE
|
||||
select CAN_SJA1000
|
||||
help
|
||||
This enables support for the Kvaser PCIcan.
|
191
drivers/can/can_kvaser_pci.c
Normal file
191
drivers/can/can_kvaser_pci.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Henrik Brix Andersen <henrik@brixandersen.dk>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT kvaser_pcican
|
||||
|
||||
#include "can_sja1000.h"
|
||||
|
||||
#include <zephyr/drivers/can.h>
|
||||
#include <zephyr/drivers/pcie/pcie.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
LOG_MODULE_REGISTER(can_kvaser_pci, CONFIG_CAN_LOG_LEVEL);
|
||||
|
||||
/* AMCC S5920 I/O BAR registers */
|
||||
#define S5920_INTCSR_REG 0x38
|
||||
#define S5920_INTCSR_ADDINT_EN BIT(13)
|
||||
#define S5920_PTCR_REG 0x60
|
||||
|
||||
/* Xilinx I/O BAR registers */
|
||||
#define XLNX_VERINT_REG 0x07
|
||||
#define XLNX_VERINT_VERSION_POS 4U
|
||||
|
||||
struct can_kvaser_pci_config {
|
||||
pcie_bdf_t pcie_bdf;
|
||||
pcie_id_t pcie_id;
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
};
|
||||
|
||||
struct can_kvaser_pci_data {
|
||||
io_port_t sja1000_base;
|
||||
};
|
||||
|
||||
static uint8_t can_kvaser_pci_read_reg(const struct device *dev, uint8_t reg)
|
||||
{
|
||||
struct can_sja1000_data *sja1000_data = dev->data;
|
||||
struct can_kvaser_pci_data *kvaser_data = sja1000_data->custom;
|
||||
io_port_t addr = kvaser_data->sja1000_base + reg;
|
||||
|
||||
return sys_in8(addr);
|
||||
}
|
||||
|
||||
static void can_kvaser_pci_write_reg(const struct device *dev, uint8_t reg, uint8_t val)
|
||||
{
|
||||
struct can_sja1000_data *sja1000_data = dev->data;
|
||||
struct can_kvaser_pci_data *kvaser_data = sja1000_data->custom;
|
||||
io_port_t addr = kvaser_data->sja1000_base + reg;
|
||||
|
||||
sys_out8(val, addr);
|
||||
}
|
||||
|
||||
static int can_kvaser_pci_get_core_clock(const struct device *dev, uint32_t *rate)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/* The internal clock operates at half of the oscillator frequency */
|
||||
*rate = MHZ(16) / 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_kvaser_pci_init(const struct device *dev)
|
||||
{
|
||||
const struct can_sja1000_config *sja1000_config = dev->config;
|
||||
const struct can_kvaser_pci_config *kvaser_config = sja1000_config->custom;
|
||||
struct can_sja1000_data *sja1000_data = dev->data;
|
||||
struct can_kvaser_pci_data *kvaser_data = sja1000_data->custom;
|
||||
struct pcie_bar iobar;
|
||||
static io_port_t amcc_base;
|
||||
static io_port_t xlnx_base;
|
||||
uint32_t intcsr;
|
||||
int err;
|
||||
|
||||
if (!pcie_probe(kvaser_config->pcie_bdf, kvaser_config->pcie_id)) {
|
||||
LOG_ERR("failed to probe PCI(e)");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pcie_set_cmd(kvaser_config->pcie_bdf, PCIE_CONF_CMDSTAT_IO, true);
|
||||
|
||||
/* AMCC S5920 registers */
|
||||
if (!pcie_probe_iobar(kvaser_config->pcie_bdf, 0, &iobar)) {
|
||||
LOG_ERR("failed to probe AMCC S5920 I/O BAR");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
amcc_base = iobar.phys_addr;
|
||||
|
||||
/* SJA1000 registers */
|
||||
if (!pcie_probe_iobar(kvaser_config->pcie_bdf, 1, &iobar)) {
|
||||
LOG_ERR("failed to probe SJA1000 I/O BAR");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
kvaser_data->sja1000_base = iobar.phys_addr;
|
||||
|
||||
/* Xilinx registers */
|
||||
if (!pcie_probe_iobar(kvaser_config->pcie_bdf, 2, &iobar)) {
|
||||
LOG_ERR("failed to probe Xilinx I/O BAR");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
xlnx_base = iobar.phys_addr;
|
||||
LOG_DBG("Xilinx version: %d",
|
||||
sys_in8(xlnx_base + XLNX_VERINT_REG) >> XLNX_VERINT_VERSION_POS);
|
||||
|
||||
/*
|
||||
* Initialization sequence as per Kvaser PCIcan Hardware Reference Manual (UG 98048
|
||||
* v3.0.0).
|
||||
*/
|
||||
|
||||
/* AMCC S5920 PCI Pass-Thru Configuration Register (PTCR) */
|
||||
sys_out32(0x80808080UL, amcc_base + S5920_PTCR_REG);
|
||||
|
||||
/* AMCC S5920 PCI Interrupt Control/Status Register (INTCSR) */
|
||||
intcsr = sys_in32(amcc_base + S5920_INTCSR_REG);
|
||||
intcsr |= S5920_INTCSR_ADDINT_EN;
|
||||
sys_out32(intcsr, amcc_base + S5920_INTCSR_REG);
|
||||
|
||||
err = can_sja1000_init(dev);
|
||||
if (err != 0) {
|
||||
LOG_ERR("failed to initialize controller (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
kvaser_config->irq_config_func(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct can_driver_api can_kvaser_pci_driver_api = {
|
||||
.get_capabilities = can_sja1000_get_capabilities,
|
||||
.start = can_sja1000_start,
|
||||
.stop = can_sja1000_stop,
|
||||
.set_mode = can_sja1000_set_mode,
|
||||
.set_timing = can_sja1000_set_timing,
|
||||
.send = can_sja1000_send,
|
||||
.add_rx_filter = can_sja1000_add_rx_filter,
|
||||
.remove_rx_filter = can_sja1000_remove_rx_filter,
|
||||
.get_state = can_sja1000_get_state,
|
||||
.set_state_change_callback = can_sja1000_set_state_change_callback,
|
||||
.get_core_clock = can_kvaser_pci_get_core_clock,
|
||||
.get_max_filters = can_sja1000_get_max_filters,
|
||||
.get_max_bitrate = can_sja1000_get_max_bitrate,
|
||||
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
||||
.recover = can_sja1000_recover,
|
||||
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
|
||||
.timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER,
|
||||
.timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER,
|
||||
};
|
||||
|
||||
#define CAN_KVASER_PCI_OCR \
|
||||
(CAN_SJA1000_OCR_OCMODE_NORMAL | CAN_SJA1000_OCR_OCTN0 | CAN_SJA1000_OCR_OCTP0 | \
|
||||
CAN_SJA1000_OCR_OCTN1 | CAN_SJA1000_OCR_OCTP1)
|
||||
|
||||
#define CAN_KVASER_PCI_CDR (CAN_SJA1000_CDR_CD_DIV2 | CAN_SJA1000_CDR_CLOCK_OFF)
|
||||
|
||||
#define CAN_KVASER_PCI_INIT(inst) \
|
||||
static void can_kvaser_pci_config_func_##inst(const struct device *dev); \
|
||||
\
|
||||
static const struct can_kvaser_pci_config can_kvaser_pci_config_##inst = { \
|
||||
.pcie_bdf = DT_INST_REG_ADDR(inst), \
|
||||
.pcie_id = DT_INST_REG_SIZE(inst), \
|
||||
.irq_config_func = can_kvaser_pci_config_func_##inst \
|
||||
}; \
|
||||
\
|
||||
static const struct can_sja1000_config can_sja1000_config_##inst = \
|
||||
CAN_SJA1000_DT_CONFIG_INST_GET(inst, &can_kvaser_pci_config_##inst, \
|
||||
can_kvaser_pci_read_reg, can_kvaser_pci_write_reg, \
|
||||
CAN_KVASER_PCI_OCR, CAN_KVASER_PCI_CDR); \
|
||||
\
|
||||
static struct can_kvaser_pci_config can_kvaser_pci_data_##inst; \
|
||||
\
|
||||
static struct can_sja1000_data can_sja1000_data_##inst = \
|
||||
CAN_SJA1000_DATA_INITIALIZER(&can_kvaser_pci_data_##inst); \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, can_kvaser_pci_init, NULL, &can_sja1000_data_##inst, \
|
||||
&can_sja1000_config_##inst, POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \
|
||||
&can_kvaser_pci_driver_api); \
|
||||
\
|
||||
static void can_kvaser_pci_config_func_##inst(const struct device *dev) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), can_sja1000_isr, \
|
||||
DEVICE_DT_INST_GET(inst), DT_INST_IRQ(inst, sense)); \
|
||||
irq_enable(DT_INST_IRQN(inst)); \
|
||||
}
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(CAN_KVASER_PCI_INIT)
|
Loading…
Reference in a new issue